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;";
9131 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9134 splithide = 'display:none;';
9137 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9138 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9143 Roo.log(styles.join(''));
9144 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9150 onContextMenu : function(e, t)
9152 this.processEvent("contextmenu", e);
9155 processEvent : function(name, e)
9157 if (name != 'touchstart' ) {
9158 this.fireEvent(name, e);
9161 var t = e.getTarget();
9163 var cell = Roo.get(t);
9169 if(cell.findParent('tfoot', false, true)){
9173 if(cell.findParent('thead', false, true)){
9175 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9176 cell = Roo.get(t).findParent('th', false, true);
9178 Roo.log("failed to find th in thead?");
9179 Roo.log(e.getTarget());
9184 var cellIndex = cell.dom.cellIndex;
9186 var ename = name == 'touchstart' ? 'click' : name;
9187 this.fireEvent("header" + ename, this, cellIndex, e);
9192 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9193 cell = Roo.get(t).findParent('td', false, true);
9195 Roo.log("failed to find th in tbody?");
9196 Roo.log(e.getTarget());
9201 var row = cell.findParent('tr', false, true);
9202 var cellIndex = cell.dom.cellIndex;
9203 var rowIndex = row.dom.rowIndex - 1;
9207 this.fireEvent("row" + name, this, rowIndex, e);
9211 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9217 onMouseover : function(e, el)
9219 var cell = Roo.get(el);
9225 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9226 cell = cell.findParent('td', false, true);
9229 var row = cell.findParent('tr', false, true);
9230 var cellIndex = cell.dom.cellIndex;
9231 var rowIndex = row.dom.rowIndex - 1; // start from 0
9233 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9237 onMouseout : function(e, el)
9239 var cell = Roo.get(el);
9245 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9246 cell = cell.findParent('td', false, true);
9249 var row = cell.findParent('tr', false, true);
9250 var cellIndex = cell.dom.cellIndex;
9251 var rowIndex = row.dom.rowIndex - 1; // start from 0
9253 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9257 onClick : function(e, el)
9259 var cell = Roo.get(el);
9261 if(!cell || (!this.cellSelection && !this.rowSelection)){
9265 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9266 cell = cell.findParent('td', false, true);
9269 if(!cell || typeof(cell) == 'undefined'){
9273 var row = cell.findParent('tr', false, true);
9275 if(!row || typeof(row) == 'undefined'){
9279 var cellIndex = cell.dom.cellIndex;
9280 var rowIndex = this.getRowIndex(row);
9282 // why??? - should these not be based on SelectionModel?
9283 //if(this.cellSelection){
9284 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9287 //if(this.rowSelection){
9288 this.fireEvent('rowclick', this, row, rowIndex, e);
9293 onDblClick : function(e,el)
9295 var cell = Roo.get(el);
9297 if(!cell || (!this.cellSelection && !this.rowSelection)){
9301 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9302 cell = cell.findParent('td', false, true);
9305 if(!cell || typeof(cell) == 'undefined'){
9309 var row = cell.findParent('tr', false, true);
9311 if(!row || typeof(row) == 'undefined'){
9315 var cellIndex = cell.dom.cellIndex;
9316 var rowIndex = this.getRowIndex(row);
9318 if(this.cellSelection){
9319 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9322 if(this.rowSelection){
9323 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9326 findRowIndex : function(el)
9328 var cell = Roo.get(el);
9332 var row = cell.findParent('tr', false, true);
9334 if(!row || typeof(row) == 'undefined'){
9337 return this.getRowIndex(row);
9339 sort : function(e,el)
9341 var col = Roo.get(el);
9343 if(!col.hasClass('sortable')){
9347 var sort = col.attr('sort');
9350 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9354 this.store.sortInfo = {field : sort, direction : dir};
9357 Roo.log("calling footer first");
9358 this.footer.onClick('first');
9361 this.store.load({ params : { start : 0 } });
9365 renderHeader : function()
9373 this.totalWidth = 0;
9375 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9377 var config = cm.config[i];
9381 cls : 'x-hcol-' + i,
9384 html: cm.getColumnHeader(i)
9387 var tooltip = cm.getColumnTooltip(i);
9389 c.tooltip = tooltip;
9395 if(typeof(config.sortable) != 'undefined' && config.sortable){
9396 c.cls += ' sortable';
9397 c.html = '<i class="fa"></i>' + c.html;
9400 // could use BS4 hidden-..-down
9402 if(typeof(config.lgHeader) != 'undefined'){
9403 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9406 if(typeof(config.mdHeader) != 'undefined'){
9407 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9410 if(typeof(config.smHeader) != 'undefined'){
9411 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9414 if(typeof(config.xsHeader) != 'undefined'){
9415 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9422 if(typeof(config.tooltip) != 'undefined'){
9423 c.tooltip = config.tooltip;
9426 if(typeof(config.colspan) != 'undefined'){
9427 c.colspan = config.colspan;
9430 // hidden is handled by CSS now
9432 if(typeof(config.dataIndex) != 'undefined'){
9433 c.sort = config.dataIndex;
9438 if(typeof(config.align) != 'undefined' && config.align.length){
9439 c.style += ' text-align:' + config.align + ';';
9442 /* width is done in CSS
9443 *if(typeof(config.width) != 'undefined'){
9444 c.style += ' width:' + config.width + 'px;';
9445 this.totalWidth += config.width;
9447 this.totalWidth += 100; // assume minimum of 100 per column?
9451 if(typeof(config.cls) != 'undefined'){
9452 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9454 // this is the bit that doesnt reall work at all...
9458 ['xs','sm','md','lg'].map(function(size){
9460 if(typeof(config[size]) == 'undefined'){
9464 if (!config[size]) { // 0 = hidden
9465 // BS 4 '0' is treated as hide that column and below.
9466 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9470 c.cls += ' col-' + size + '-' + config[size] + (
9471 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9479 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9490 renderBody : function()
9500 colspan : this.cm.getColumnCount()
9510 renderFooter : function()
9520 colspan : this.cm.getColumnCount()
9534 // Roo.log('ds onload');
9539 var ds = this.store;
9541 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9542 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9543 if (_this.store.sortInfo) {
9545 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9546 e.select('i', true).addClass(['fa-arrow-up']);
9549 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9550 e.select('i', true).addClass(['fa-arrow-down']);
9555 var tbody = this.bodyEl;
9557 if(ds.getCount() > 0){
9558 ds.data.each(function(d,rowIndex){
9559 var row = this.renderRow(cm, ds, rowIndex);
9561 tbody.createChild(row);
9565 if(row.cellObjects.length){
9566 Roo.each(row.cellObjects, function(r){
9567 _this.renderCellObject(r);
9574 var tfoot = this.el.select('tfoot', true).first();
9576 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9578 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9580 var total = this.ds.getTotalCount();
9582 if(this.footer.pageSize < total){
9583 this.mainFoot.show();
9587 Roo.each(this.el.select('tbody td', true).elements, function(e){
9588 e.on('mouseover', _this.onMouseover, _this);
9591 Roo.each(this.el.select('tbody td', true).elements, function(e){
9592 e.on('mouseout', _this.onMouseout, _this);
9594 this.fireEvent('rowsrendered', this);
9598 this.initCSS(); /// resize cols
9604 onUpdate : function(ds,record)
9606 this.refreshRow(record);
9610 onRemove : function(ds, record, index, isUpdate){
9611 if(isUpdate !== true){
9612 this.fireEvent("beforerowremoved", this, index, record);
9614 var bt = this.bodyEl.dom;
9616 var rows = this.el.select('tbody > tr', true).elements;
9618 if(typeof(rows[index]) != 'undefined'){
9619 bt.removeChild(rows[index].dom);
9622 // if(bt.rows[index]){
9623 // bt.removeChild(bt.rows[index]);
9626 if(isUpdate !== true){
9627 //this.stripeRows(index);
9628 //this.syncRowHeights(index, index);
9630 this.fireEvent("rowremoved", this, index, record);
9634 onAdd : function(ds, records, rowIndex)
9636 //Roo.log('on Add called');
9637 // - note this does not handle multiple adding very well..
9638 var bt = this.bodyEl.dom;
9639 for (var i =0 ; i < records.length;i++) {
9640 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9641 //Roo.log(records[i]);
9642 //Roo.log(this.store.getAt(rowIndex+i));
9643 this.insertRow(this.store, rowIndex + i, false);
9650 refreshRow : function(record){
9651 var ds = this.store, index;
9652 if(typeof record == 'number'){
9654 record = ds.getAt(index);
9656 index = ds.indexOf(record);
9658 return; // should not happen - but seems to
9661 this.insertRow(ds, index, true);
9663 this.onRemove(ds, record, index+1, true);
9665 //this.syncRowHeights(index, index);
9667 this.fireEvent("rowupdated", this, index, record);
9669 // private - called by RowSelection
9670 onRowSelect : function(rowIndex){
9671 var row = this.getRowDom(rowIndex);
9672 row.addClass(['bg-info','info']);
9674 // private - called by RowSelection
9675 onRowDeselect : function(rowIndex)
9680 var row = this.getRowDom(rowIndex);
9681 row.removeClass(['bg-info','info']);
9684 * Focuses the specified row.
9685 * @param {Number} row The row index
9687 focusRow : function(row)
9689 //Roo.log('GridView.focusRow');
9690 var x = this.bodyEl.dom.scrollLeft;
9691 this.focusCell(row, 0, false);
9692 this.bodyEl.dom.scrollLeft = x;
9696 * Focuses the specified cell.
9697 * @param {Number} row The row index
9698 * @param {Number} col The column index
9699 * @param {Boolean} hscroll false to disable horizontal scrolling
9701 focusCell : function(row, col, hscroll)
9703 //Roo.log('GridView.focusCell');
9704 var el = this.ensureVisible(row, col, hscroll);
9705 // not sure what focusEL achives = it's a <a> pos relative
9706 //this.focusEl.alignTo(el, "tl-tl");
9708 // this.focusEl.focus();
9710 // this.focusEl.focus.defer(1, this.focusEl);
9715 * Scrolls the specified cell into view
9716 * @param {Number} row The row index
9717 * @param {Number} col The column index
9718 * @param {Boolean} hscroll false to disable horizontal scrolling
9720 ensureVisible : function(row, col, hscroll)
9722 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9723 //return null; //disable for testing.
9724 if(typeof row != "number"){
9727 if(row < 0 && row >= this.ds.getCount()){
9730 col = (col !== undefined ? col : 0);
9732 while(cm.isHidden(col)){
9736 var el = this.getCellDom(row, col);
9740 var c = this.bodyEl.dom;
9742 var ctop = parseInt(el.offsetTop, 10);
9743 var cleft = parseInt(el.offsetLeft, 10);
9744 var cbot = ctop + el.offsetHeight;
9745 var cright = cleft + el.offsetWidth;
9747 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9748 var ch = 0; //?? header is not withing the area?
9749 var stop = parseInt(c.scrollTop, 10);
9750 var sleft = parseInt(c.scrollLeft, 10);
9751 var sbot = stop + ch;
9752 var sright = sleft + c.clientWidth;
9754 Roo.log('GridView.ensureVisible:' +
9756 ' c.clientHeight:' + c.clientHeight +
9757 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9766 //Roo.log("set scrolltop to ctop DISABLE?");
9767 }else if(cbot > sbot){
9768 //Roo.log("set scrolltop to cbot-ch");
9769 c.scrollTop = cbot-ch;
9772 if(hscroll !== false){
9774 c.scrollLeft = cleft;
9775 }else if(cright > sright){
9776 c.scrollLeft = cright-c.clientWidth;
9784 insertRow : function(dm, rowIndex, isUpdate){
9787 this.fireEvent("beforerowsinserted", this, rowIndex);
9789 //var s = this.getScrollState();
9790 var row = this.renderRow(this.cm, this.store, rowIndex);
9791 // insert before rowIndex..
9792 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9796 if(row.cellObjects.length){
9797 Roo.each(row.cellObjects, function(r){
9798 _this.renderCellObject(r);
9803 this.fireEvent("rowsinserted", this, rowIndex);
9804 //this.syncRowHeights(firstRow, lastRow);
9805 //this.stripeRows(firstRow);
9812 getRowDom : function(rowIndex)
9814 var rows = this.el.select('tbody > tr', true).elements;
9816 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9819 getCellDom : function(rowIndex, colIndex)
9821 var row = this.getRowDom(rowIndex);
9822 if (row === false) {
9825 var cols = row.select('td', true).elements;
9826 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9830 // returns the object tree for a tr..
9833 renderRow : function(cm, ds, rowIndex)
9835 var d = ds.getAt(rowIndex);
9839 cls : 'x-row-' + rowIndex,
9843 var cellObjects = [];
9845 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9846 var config = cm.config[i];
9848 var renderer = cm.getRenderer(i);
9852 if(typeof(renderer) !== 'undefined'){
9853 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9855 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9856 // and are rendered into the cells after the row is rendered - using the id for the element.
9858 if(typeof(value) === 'object'){
9868 rowIndex : rowIndex,
9873 this.fireEvent('rowclass', this, rowcfg);
9877 // this might end up displaying HTML?
9878 // this is too messy... - better to only do it on columsn you know are going to be too long
9879 //tooltip : (typeof(value) === 'object') ? '' : value,
9880 cls : rowcfg.rowClass + ' x-col-' + i,
9882 html: (typeof(value) === 'object') ? '' : value
9889 if(typeof(config.colspan) != 'undefined'){
9890 td.colspan = config.colspan;
9895 if(typeof(config.align) != 'undefined' && config.align.length){
9896 td.style += ' text-align:' + config.align + ';';
9898 if(typeof(config.valign) != 'undefined' && config.valign.length){
9899 td.style += ' vertical-align:' + config.valign + ';';
9902 if(typeof(config.width) != 'undefined'){
9903 td.style += ' width:' + config.width + 'px;';
9907 if(typeof(config.cursor) != 'undefined'){
9908 td.style += ' cursor:' + config.cursor + ';';
9911 if(typeof(config.cls) != 'undefined'){
9912 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9915 ['xs','sm','md','lg'].map(function(size){
9917 if(typeof(config[size]) == 'undefined'){
9923 if (!config[size]) { // 0 = hidden
9924 // BS 4 '0' is treated as hide that column and below.
9925 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9929 td.cls += ' col-' + size + '-' + config[size] + (
9930 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9940 row.cellObjects = cellObjects;
9948 onBeforeLoad : function()
9957 this.el.select('tbody', true).first().dom.innerHTML = '';
9960 * Show or hide a row.
9961 * @param {Number} rowIndex to show or hide
9962 * @param {Boolean} state hide
9964 setRowVisibility : function(rowIndex, state)
9966 var bt = this.bodyEl.dom;
9968 var rows = this.el.select('tbody > tr', true).elements;
9970 if(typeof(rows[rowIndex]) == 'undefined'){
9973 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9978 getSelectionModel : function(){
9980 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9982 return this.selModel;
9985 * Render the Roo.bootstrap object from renderder
9987 renderCellObject : function(r)
9991 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9993 var t = r.cfg.render(r.container);
9996 Roo.each(r.cfg.cn, function(c){
9998 container: t.getChildContainer(),
10001 _this.renderCellObject(child);
10006 * get the Row Index from a dom element.
10007 * @param {Roo.Element} row The row to look for
10008 * @returns {Number} the row
10010 getRowIndex : function(row)
10014 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10025 * get the header TH element for columnIndex
10026 * @param {Number} columnIndex
10027 * @returns {Roo.Element}
10029 getHeaderIndex: function(colIndex)
10031 var cols = this.headEl.select('th', true).elements;
10032 return cols[colIndex];
10035 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10036 * @param {domElement} cell to look for
10037 * @returns {Number} the column
10039 getCellIndex : function(cell)
10041 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10043 return parseInt(id[1], 10);
10048 * Returns the grid's underlying element = used by panel.Grid
10049 * @return {Element} The element
10051 getGridEl : function(){
10055 * Forces a resize - used by panel.Grid
10056 * @return {Element} The element
10058 autoSize : function()
10060 //var ctr = Roo.get(this.container.dom.parentElement);
10061 var ctr = Roo.get(this.el.dom);
10063 var thd = this.getGridEl().select('thead',true).first();
10064 var tbd = this.getGridEl().select('tbody', true).first();
10065 var tfd = this.getGridEl().select('tfoot', true).first();
10067 var cw = ctr.getWidth();
10068 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10072 tbd.setWidth(ctr.getWidth());
10073 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10074 // this needs fixing for various usage - currently only hydra job advers I think..
10076 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10078 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10081 cw = Math.max(cw, this.totalWidth);
10082 this.getGridEl().select('tbody tr',true).setWidth(cw);
10085 // resize 'expandable coloumn?
10087 return; // we doe not have a view in this design..
10090 onBodyScroll: function()
10092 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10094 this.headEl.setStyle({
10095 'position' : 'relative',
10096 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10102 var scrollHeight = this.bodyEl.dom.scrollHeight;
10104 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10106 var height = this.bodyEl.getHeight();
10108 if(scrollHeight - height == scrollTop) {
10110 var total = this.ds.getTotalCount();
10112 if(this.footer.cursor + this.footer.pageSize < total){
10114 this.footer.ds.load({
10116 start : this.footer.cursor + this.footer.pageSize,
10117 limit : this.footer.pageSize
10126 onColumnSplitterMoved : function(i, diff)
10128 this.userResized = true;
10130 var cm = this.colModel;
10132 var w = this.getHeaderIndex(i).getWidth() + diff;
10135 cm.setColumnWidth(i, w, true);
10137 //var cid = cm.getColumnId(i); << not used in this version?
10138 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10140 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10141 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10142 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10144 //this.updateSplitters();
10145 //this.layout(); << ??
10146 this.fireEvent("columnresize", i, w);
10148 onHeaderChange : function()
10150 var header = this.renderHeader();
10151 var table = this.el.select('table', true).first();
10153 this.headEl.remove();
10154 this.headEl = table.createChild(header, this.bodyEl, false);
10156 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10157 e.on('click', this.sort, this);
10160 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10161 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10166 onHiddenChange : function(colModel, colIndex, hidden)
10168 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10169 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10171 this.CSS.updateRule(thSelector, "display", "");
10172 this.CSS.updateRule(tdSelector, "display", "");
10175 this.CSS.updateRule(thSelector, "display", "none");
10176 this.CSS.updateRule(tdSelector, "display", "none");
10179 this.onHeaderChange();
10183 setColumnWidth: function(col_index, width)
10185 // width = "md-2 xs-2..."
10186 if(!this.colModel.config[col_index]) {
10190 var w = width.split(" ");
10192 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10194 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10197 for(var j = 0; j < w.length; j++) {
10203 var size_cls = w[j].split("-");
10205 if(!Number.isInteger(size_cls[1] * 1)) {
10209 if(!this.colModel.config[col_index][size_cls[0]]) {
10213 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10217 h_row[0].classList.replace(
10218 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10219 "col-"+size_cls[0]+"-"+size_cls[1]
10222 for(var i = 0; i < rows.length; i++) {
10224 var size_cls = w[j].split("-");
10226 if(!Number.isInteger(size_cls[1] * 1)) {
10230 if(!this.colModel.config[col_index][size_cls[0]]) {
10234 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10238 rows[i].classList.replace(
10239 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10240 "col-"+size_cls[0]+"-"+size_cls[1]
10244 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10249 // currently only used to find the split on drag..
10250 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10255 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10256 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10265 * @class Roo.bootstrap.TableCell
10266 * @extends Roo.bootstrap.Component
10267 * Bootstrap TableCell class
10268 * @cfg {String} html cell contain text
10269 * @cfg {String} cls cell class
10270 * @cfg {String} tag cell tag (td|th) default td
10271 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10272 * @cfg {String} align Aligns the content in a cell
10273 * @cfg {String} axis Categorizes cells
10274 * @cfg {String} bgcolor Specifies the background color of a cell
10275 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10276 * @cfg {Number} colspan Specifies the number of columns a cell should span
10277 * @cfg {String} headers Specifies one or more header cells a cell is related to
10278 * @cfg {Number} height Sets the height of a cell
10279 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10280 * @cfg {Number} rowspan Sets the number of rows a cell should span
10281 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10282 * @cfg {String} valign Vertical aligns the content in a cell
10283 * @cfg {Number} width Specifies the width of a cell
10286 * Create a new TableCell
10287 * @param {Object} config The config object
10290 Roo.bootstrap.TableCell = function(config){
10291 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10294 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10314 getAutoCreate : function(){
10315 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10322 cfg.tag = this.tag;
10335 cfg.align=this.align
10340 if (this.bgcolor) {
10341 cfg.bgcolor=this.bgcolor
10343 if (this.charoff) {
10344 cfg.charoff=this.charoff
10346 if (this.colspan) {
10347 cfg.colspan=this.colspan
10349 if (this.headers) {
10350 cfg.headers=this.headers
10353 cfg.height=this.height
10356 cfg.nowrap=this.nowrap
10358 if (this.rowspan) {
10359 cfg.rowspan=this.rowspan
10362 cfg.scope=this.scope
10365 cfg.valign=this.valign
10368 cfg.width=this.width
10387 * @class Roo.bootstrap.TableRow
10388 * @extends Roo.bootstrap.Component
10389 * Bootstrap TableRow class
10390 * @cfg {String} cls row class
10391 * @cfg {String} align Aligns the content in a table row
10392 * @cfg {String} bgcolor Specifies a background color for a table row
10393 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10394 * @cfg {String} valign Vertical aligns the content in a table row
10397 * Create a new TableRow
10398 * @param {Object} config The config object
10401 Roo.bootstrap.TableRow = function(config){
10402 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10405 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10413 getAutoCreate : function(){
10414 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10421 cfg.cls = this.cls;
10424 cfg.align = this.align;
10427 cfg.bgcolor = this.bgcolor;
10430 cfg.charoff = this.charoff;
10433 cfg.valign = this.valign;
10451 * @class Roo.bootstrap.TableBody
10452 * @extends Roo.bootstrap.Component
10453 * Bootstrap TableBody class
10454 * @cfg {String} cls element class
10455 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10456 * @cfg {String} align Aligns the content inside the element
10457 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10458 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10461 * Create a new TableBody
10462 * @param {Object} config The config object
10465 Roo.bootstrap.TableBody = function(config){
10466 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10469 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10477 getAutoCreate : function(){
10478 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10488 cfg.tag = this.tag;
10492 cfg.align = this.align;
10495 cfg.charoff = this.charoff;
10498 cfg.valign = this.valign;
10505 // initEvents : function()
10508 // if(!this.store){
10512 // this.store = Roo.factory(this.store, Roo.data);
10513 // this.store.on('load', this.onLoad, this);
10515 // this.store.load();
10519 // onLoad: function ()
10521 // this.fireEvent('load', this);
10531 * Ext JS Library 1.1.1
10532 * Copyright(c) 2006-2007, Ext JS, LLC.
10534 * Originally Released Under LGPL - original licence link has changed is not relivant.
10537 * <script type="text/javascript">
10540 // as we use this in bootstrap.
10541 Roo.namespace('Roo.form');
10543 * @class Roo.form.Action
10544 * Internal Class used to handle form actions
10546 * @param {Roo.form.BasicForm} el The form element or its id
10547 * @param {Object} config Configuration options
10552 // define the action interface
10553 Roo.form.Action = function(form, options){
10555 this.options = options || {};
10558 * Client Validation Failed
10561 Roo.form.Action.CLIENT_INVALID = 'client';
10563 * Server Validation Failed
10566 Roo.form.Action.SERVER_INVALID = 'server';
10568 * Connect to Server Failed
10571 Roo.form.Action.CONNECT_FAILURE = 'connect';
10573 * Reading Data from Server Failed
10576 Roo.form.Action.LOAD_FAILURE = 'load';
10578 Roo.form.Action.prototype = {
10580 failureType : undefined,
10581 response : undefined,
10582 result : undefined,
10584 // interface method
10585 run : function(options){
10589 // interface method
10590 success : function(response){
10594 // interface method
10595 handleResponse : function(response){
10599 // default connection failure
10600 failure : function(response){
10602 this.response = response;
10603 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10604 this.form.afterAction(this, false);
10607 processResponse : function(response){
10608 this.response = response;
10609 if(!response.responseText){
10612 this.result = this.handleResponse(response);
10613 return this.result;
10616 // utility functions used internally
10617 getUrl : function(appendParams){
10618 var url = this.options.url || this.form.url || this.form.el.dom.action;
10620 var p = this.getParams();
10622 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10628 getMethod : function(){
10629 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10632 getParams : function(){
10633 var bp = this.form.baseParams;
10634 var p = this.options.params;
10636 if(typeof p == "object"){
10637 p = Roo.urlEncode(Roo.applyIf(p, bp));
10638 }else if(typeof p == 'string' && bp){
10639 p += '&' + Roo.urlEncode(bp);
10642 p = Roo.urlEncode(bp);
10647 createCallback : function(){
10649 success: this.success,
10650 failure: this.failure,
10652 timeout: (this.form.timeout*1000),
10653 upload: this.form.fileUpload ? this.success : undefined
10658 Roo.form.Action.Submit = function(form, options){
10659 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10662 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10665 haveProgress : false,
10666 uploadComplete : false,
10668 // uploadProgress indicator.
10669 uploadProgress : function()
10671 if (!this.form.progressUrl) {
10675 if (!this.haveProgress) {
10676 Roo.MessageBox.progress("Uploading", "Uploading");
10678 if (this.uploadComplete) {
10679 Roo.MessageBox.hide();
10683 this.haveProgress = true;
10685 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10687 var c = new Roo.data.Connection();
10689 url : this.form.progressUrl,
10694 success : function(req){
10695 //console.log(data);
10699 rdata = Roo.decode(req.responseText)
10701 Roo.log("Invalid data from server..");
10705 if (!rdata || !rdata.success) {
10707 Roo.MessageBox.alert(Roo.encode(rdata));
10710 var data = rdata.data;
10712 if (this.uploadComplete) {
10713 Roo.MessageBox.hide();
10718 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10719 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10722 this.uploadProgress.defer(2000,this);
10725 failure: function(data) {
10726 Roo.log('progress url failed ');
10737 // run get Values on the form, so it syncs any secondary forms.
10738 this.form.getValues();
10740 var o = this.options;
10741 var method = this.getMethod();
10742 var isPost = method == 'POST';
10743 if(o.clientValidation === false || this.form.isValid()){
10745 if (this.form.progressUrl) {
10746 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10747 (new Date() * 1) + '' + Math.random());
10752 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10753 form:this.form.el.dom,
10754 url:this.getUrl(!isPost),
10756 params:isPost ? this.getParams() : null,
10757 isUpload: this.form.fileUpload,
10758 formData : this.form.formData
10761 this.uploadProgress();
10763 }else if (o.clientValidation !== false){ // client validation failed
10764 this.failureType = Roo.form.Action.CLIENT_INVALID;
10765 this.form.afterAction(this, false);
10769 success : function(response)
10771 this.uploadComplete= true;
10772 if (this.haveProgress) {
10773 Roo.MessageBox.hide();
10777 var result = this.processResponse(response);
10778 if(result === true || result.success){
10779 this.form.afterAction(this, true);
10783 this.form.markInvalid(result.errors);
10784 this.failureType = Roo.form.Action.SERVER_INVALID;
10786 this.form.afterAction(this, false);
10788 failure : function(response)
10790 this.uploadComplete= true;
10791 if (this.haveProgress) {
10792 Roo.MessageBox.hide();
10795 this.response = response;
10796 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10797 this.form.afterAction(this, false);
10800 handleResponse : function(response){
10801 if(this.form.errorReader){
10802 var rs = this.form.errorReader.read(response);
10805 for(var i = 0, len = rs.records.length; i < len; i++) {
10806 var r = rs.records[i];
10807 errors[i] = r.data;
10810 if(errors.length < 1){
10814 success : rs.success,
10820 ret = Roo.decode(response.responseText);
10824 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10834 Roo.form.Action.Load = function(form, options){
10835 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10836 this.reader = this.form.reader;
10839 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10844 Roo.Ajax.request(Roo.apply(
10845 this.createCallback(), {
10846 method:this.getMethod(),
10847 url:this.getUrl(false),
10848 params:this.getParams()
10852 success : function(response){
10854 var result = this.processResponse(response);
10855 if(result === true || !result.success || !result.data){
10856 this.failureType = Roo.form.Action.LOAD_FAILURE;
10857 this.form.afterAction(this, false);
10860 this.form.clearInvalid();
10861 this.form.setValues(result.data);
10862 this.form.afterAction(this, true);
10865 handleResponse : function(response){
10866 if(this.form.reader){
10867 var rs = this.form.reader.read(response);
10868 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10870 success : rs.success,
10874 return Roo.decode(response.responseText);
10878 Roo.form.Action.ACTION_TYPES = {
10879 'load' : Roo.form.Action.Load,
10880 'submit' : Roo.form.Action.Submit
10889 * @class Roo.bootstrap.Form
10890 * @extends Roo.bootstrap.Component
10891 * Bootstrap Form class
10892 * @cfg {String} method GET | POST (default POST)
10893 * @cfg {String} labelAlign top | left (default top)
10894 * @cfg {String} align left | right - for navbars
10895 * @cfg {Boolean} loadMask load mask when submit (default true)
10899 * Create a new Form
10900 * @param {Object} config The config object
10904 Roo.bootstrap.Form = function(config){
10906 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10908 Roo.bootstrap.Form.popover.apply();
10912 * @event clientvalidation
10913 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10914 * @param {Form} this
10915 * @param {Boolean} valid true if the form has passed client-side validation
10917 clientvalidation: true,
10919 * @event beforeaction
10920 * Fires before any action is performed. Return false to cancel the action.
10921 * @param {Form} this
10922 * @param {Action} action The action to be performed
10924 beforeaction: true,
10926 * @event actionfailed
10927 * Fires when an action fails.
10928 * @param {Form} this
10929 * @param {Action} action The action that failed
10931 actionfailed : true,
10933 * @event actioncomplete
10934 * Fires when an action is completed.
10935 * @param {Form} this
10936 * @param {Action} action The action that completed
10938 actioncomplete : true
10942 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10945 * @cfg {String} method
10946 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10950 * @cfg {String} url
10951 * The URL to use for form actions if one isn't supplied in the action options.
10954 * @cfg {Boolean} fileUpload
10955 * Set to true if this form is a file upload.
10959 * @cfg {Object} baseParams
10960 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10964 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10968 * @cfg {Sting} align (left|right) for navbar forms
10973 activeAction : null,
10976 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10977 * element by passing it or its id or mask the form itself by passing in true.
10980 waitMsgTarget : false,
10985 * @cfg {Boolean} errorMask (true|false) default false
10990 * @cfg {Number} maskOffset Default 100
10995 * @cfg {Boolean} maskBody
10999 getAutoCreate : function(){
11003 method : this.method || 'POST',
11004 id : this.id || Roo.id(),
11007 if (this.parent().xtype.match(/^Nav/)) {
11008 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11012 if (this.labelAlign == 'left' ) {
11013 cfg.cls += ' form-horizontal';
11019 initEvents : function()
11021 this.el.on('submit', this.onSubmit, this);
11022 // this was added as random key presses on the form where triggering form submit.
11023 this.el.on('keypress', function(e) {
11024 if (e.getCharCode() != 13) {
11027 // we might need to allow it for textareas.. and some other items.
11028 // check e.getTarget().
11030 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11034 Roo.log("keypress blocked");
11036 e.preventDefault();
11042 onSubmit : function(e){
11047 * Returns true if client-side validation on the form is successful.
11050 isValid : function(){
11051 var items = this.getItems();
11053 var target = false;
11055 items.each(function(f){
11061 Roo.log('invalid field: ' + f.name);
11065 if(!target && f.el.isVisible(true)){
11071 if(this.errorMask && !valid){
11072 Roo.bootstrap.Form.popover.mask(this, target);
11079 * Returns true if any fields in this form have changed since their original load.
11082 isDirty : function(){
11084 var items = this.getItems();
11085 items.each(function(f){
11095 * Performs a predefined action (submit or load) or custom actions you define on this form.
11096 * @param {String} actionName The name of the action type
11097 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11098 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11099 * accept other config options):
11101 Property Type Description
11102 ---------------- --------------- ----------------------------------------------------------------------------------
11103 url String The url for the action (defaults to the form's url)
11104 method String The form method to use (defaults to the form's method, or POST if not defined)
11105 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11106 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11107 validate the form on the client (defaults to false)
11109 * @return {BasicForm} this
11111 doAction : function(action, options){
11112 if(typeof action == 'string'){
11113 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11115 if(this.fireEvent('beforeaction', this, action) !== false){
11116 this.beforeAction(action);
11117 action.run.defer(100, action);
11123 beforeAction : function(action){
11124 var o = action.options;
11129 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11131 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11134 // not really supported yet.. ??
11136 //if(this.waitMsgTarget === true){
11137 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11138 //}else if(this.waitMsgTarget){
11139 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11140 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11142 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11148 afterAction : function(action, success){
11149 this.activeAction = null;
11150 var o = action.options;
11155 Roo.get(document.body).unmask();
11161 //if(this.waitMsgTarget === true){
11162 // this.el.unmask();
11163 //}else if(this.waitMsgTarget){
11164 // this.waitMsgTarget.unmask();
11166 // Roo.MessageBox.updateProgress(1);
11167 // Roo.MessageBox.hide();
11174 Roo.callback(o.success, o.scope, [this, action]);
11175 this.fireEvent('actioncomplete', this, action);
11179 // failure condition..
11180 // we have a scenario where updates need confirming.
11181 // eg. if a locking scenario exists..
11182 // we look for { errors : { needs_confirm : true }} in the response.
11184 (typeof(action.result) != 'undefined') &&
11185 (typeof(action.result.errors) != 'undefined') &&
11186 (typeof(action.result.errors.needs_confirm) != 'undefined')
11189 Roo.log("not supported yet");
11192 Roo.MessageBox.confirm(
11193 "Change requires confirmation",
11194 action.result.errorMsg,
11199 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11209 Roo.callback(o.failure, o.scope, [this, action]);
11210 // show an error message if no failed handler is set..
11211 if (!this.hasListener('actionfailed')) {
11212 Roo.log("need to add dialog support");
11214 Roo.MessageBox.alert("Error",
11215 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11216 action.result.errorMsg :
11217 "Saving Failed, please check your entries or try again"
11222 this.fireEvent('actionfailed', this, action);
11227 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11228 * @param {String} id The value to search for
11231 findField : function(id){
11232 var items = this.getItems();
11233 var field = items.get(id);
11235 items.each(function(f){
11236 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11243 return field || null;
11246 * Mark fields in this form invalid in bulk.
11247 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11248 * @return {BasicForm} this
11250 markInvalid : function(errors){
11251 if(errors instanceof Array){
11252 for(var i = 0, len = errors.length; i < len; i++){
11253 var fieldError = errors[i];
11254 var f = this.findField(fieldError.id);
11256 f.markInvalid(fieldError.msg);
11262 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11263 field.markInvalid(errors[id]);
11267 //Roo.each(this.childForms || [], function (f) {
11268 // f.markInvalid(errors);
11275 * Set values for fields in this form in bulk.
11276 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11277 * @return {BasicForm} this
11279 setValues : function(values){
11280 if(values instanceof Array){ // array of objects
11281 for(var i = 0, len = values.length; i < len; i++){
11283 var f = this.findField(v.id);
11285 f.setValue(v.value);
11286 if(this.trackResetOnLoad){
11287 f.originalValue = f.getValue();
11291 }else{ // object hash
11294 if(typeof values[id] != 'function' && (field = this.findField(id))){
11296 if (field.setFromData &&
11297 field.valueField &&
11298 field.displayField &&
11299 // combos' with local stores can
11300 // be queried via setValue()
11301 // to set their value..
11302 (field.store && !field.store.isLocal)
11306 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11307 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11308 field.setFromData(sd);
11310 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11312 field.setFromData(values);
11315 field.setValue(values[id]);
11319 if(this.trackResetOnLoad){
11320 field.originalValue = field.getValue();
11326 //Roo.each(this.childForms || [], function (f) {
11327 // f.setValues(values);
11334 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11335 * they are returned as an array.
11336 * @param {Boolean} asString
11339 getValues : function(asString){
11340 //if (this.childForms) {
11341 // copy values from the child forms
11342 // Roo.each(this.childForms, function (f) {
11343 // this.setValues(f.getValues());
11349 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11350 if(asString === true){
11353 return Roo.urlDecode(fs);
11357 * Returns the fields in this form as an object with key/value pairs.
11358 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11361 getFieldValues : function(with_hidden)
11363 var items = this.getItems();
11365 items.each(function(f){
11367 if (!f.getName()) {
11371 var v = f.getValue();
11373 if (f.inputType =='radio') {
11374 if (typeof(ret[f.getName()]) == 'undefined') {
11375 ret[f.getName()] = ''; // empty..
11378 if (!f.el.dom.checked) {
11382 v = f.el.dom.value;
11386 if(f.xtype == 'MoneyField'){
11387 ret[f.currencyName] = f.getCurrency();
11390 // not sure if this supported any more..
11391 if ((typeof(v) == 'object') && f.getRawValue) {
11392 v = f.getRawValue() ; // dates..
11394 // combo boxes where name != hiddenName...
11395 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11396 ret[f.name] = f.getRawValue();
11398 ret[f.getName()] = v;
11405 * Clears all invalid messages in this form.
11406 * @return {BasicForm} this
11408 clearInvalid : function(){
11409 var items = this.getItems();
11411 items.each(function(f){
11419 * Resets this form.
11420 * @return {BasicForm} this
11422 reset : function(){
11423 var items = this.getItems();
11424 items.each(function(f){
11428 Roo.each(this.childForms || [], function (f) {
11436 getItems : function()
11438 var r=new Roo.util.MixedCollection(false, function(o){
11439 return o.id || (o.id = Roo.id());
11441 var iter = function(el) {
11448 Roo.each(el.items,function(e) {
11457 hideFields : function(items)
11459 Roo.each(items, function(i){
11461 var f = this.findField(i);
11472 showFields : function(items)
11474 Roo.each(items, function(i){
11476 var f = this.findField(i);
11489 Roo.apply(Roo.bootstrap.Form, {
11505 intervalID : false,
11511 if(this.isApplied){
11516 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11517 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11518 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11519 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11522 this.maskEl.top.enableDisplayMode("block");
11523 this.maskEl.left.enableDisplayMode("block");
11524 this.maskEl.bottom.enableDisplayMode("block");
11525 this.maskEl.right.enableDisplayMode("block");
11527 this.toolTip = new Roo.bootstrap.Tooltip({
11528 cls : 'roo-form-error-popover',
11530 'left' : ['r-l', [-2,0], 'right'],
11531 'right' : ['l-r', [2,0], 'left'],
11532 'bottom' : ['tl-bl', [0,2], 'top'],
11533 'top' : [ 'bl-tl', [0,-2], 'bottom']
11537 this.toolTip.render(Roo.get(document.body));
11539 this.toolTip.el.enableDisplayMode("block");
11541 Roo.get(document.body).on('click', function(){
11545 Roo.get(document.body).on('touchstart', function(){
11549 this.isApplied = true
11552 mask : function(form, target)
11556 this.target = target;
11558 if(!this.form.errorMask || !target.el){
11562 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11564 Roo.log(scrollable);
11566 var ot = this.target.el.calcOffsetsTo(scrollable);
11568 var scrollTo = ot[1] - this.form.maskOffset;
11570 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11572 scrollable.scrollTo('top', scrollTo);
11574 var box = this.target.el.getBox();
11576 var zIndex = Roo.bootstrap.Modal.zIndex++;
11579 this.maskEl.top.setStyle('position', 'absolute');
11580 this.maskEl.top.setStyle('z-index', zIndex);
11581 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11582 this.maskEl.top.setLeft(0);
11583 this.maskEl.top.setTop(0);
11584 this.maskEl.top.show();
11586 this.maskEl.left.setStyle('position', 'absolute');
11587 this.maskEl.left.setStyle('z-index', zIndex);
11588 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11589 this.maskEl.left.setLeft(0);
11590 this.maskEl.left.setTop(box.y - this.padding);
11591 this.maskEl.left.show();
11593 this.maskEl.bottom.setStyle('position', 'absolute');
11594 this.maskEl.bottom.setStyle('z-index', zIndex);
11595 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11596 this.maskEl.bottom.setLeft(0);
11597 this.maskEl.bottom.setTop(box.bottom + this.padding);
11598 this.maskEl.bottom.show();
11600 this.maskEl.right.setStyle('position', 'absolute');
11601 this.maskEl.right.setStyle('z-index', zIndex);
11602 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11603 this.maskEl.right.setLeft(box.right + this.padding);
11604 this.maskEl.right.setTop(box.y - this.padding);
11605 this.maskEl.right.show();
11607 this.toolTip.bindEl = this.target.el;
11609 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11611 var tip = this.target.blankText;
11613 if(this.target.getValue() !== '' ) {
11615 if (this.target.invalidText.length) {
11616 tip = this.target.invalidText;
11617 } else if (this.target.regexText.length){
11618 tip = this.target.regexText;
11622 this.toolTip.show(tip);
11624 this.intervalID = window.setInterval(function() {
11625 Roo.bootstrap.Form.popover.unmask();
11628 window.onwheel = function(){ return false;};
11630 (function(){ this.isMasked = true; }).defer(500, this);
11634 unmask : function()
11636 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11640 this.maskEl.top.setStyle('position', 'absolute');
11641 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11642 this.maskEl.top.hide();
11644 this.maskEl.left.setStyle('position', 'absolute');
11645 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11646 this.maskEl.left.hide();
11648 this.maskEl.bottom.setStyle('position', 'absolute');
11649 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11650 this.maskEl.bottom.hide();
11652 this.maskEl.right.setStyle('position', 'absolute');
11653 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11654 this.maskEl.right.hide();
11656 this.toolTip.hide();
11658 this.toolTip.el.hide();
11660 window.onwheel = function(){ return true;};
11662 if(this.intervalID){
11663 window.clearInterval(this.intervalID);
11664 this.intervalID = false;
11667 this.isMasked = false;
11677 * Ext JS Library 1.1.1
11678 * Copyright(c) 2006-2007, Ext JS, LLC.
11680 * Originally Released Under LGPL - original licence link has changed is not relivant.
11683 * <script type="text/javascript">
11686 * @class Roo.form.VTypes
11687 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11690 Roo.form.VTypes = function(){
11691 // closure these in so they are only created once.
11692 var alpha = /^[a-zA-Z_]+$/;
11693 var alphanum = /^[a-zA-Z0-9_]+$/;
11694 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11695 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11697 // All these messages and functions are configurable
11700 * The function used to validate email addresses
11701 * @param {String} value The email address
11703 'email' : function(v){
11704 return email.test(v);
11707 * The error text to display when the email validation function returns false
11710 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11712 * The keystroke filter mask to be applied on email input
11715 'emailMask' : /[a-z0-9_\.\-@]/i,
11718 * The function used to validate URLs
11719 * @param {String} value The URL
11721 'url' : function(v){
11722 return url.test(v);
11725 * The error text to display when the url validation function returns false
11728 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11731 * The function used to validate alpha values
11732 * @param {String} value The value
11734 'alpha' : function(v){
11735 return alpha.test(v);
11738 * The error text to display when the alpha validation function returns false
11741 'alphaText' : 'This field should only contain letters and _',
11743 * The keystroke filter mask to be applied on alpha input
11746 'alphaMask' : /[a-z_]/i,
11749 * The function used to validate alphanumeric values
11750 * @param {String} value The value
11752 'alphanum' : function(v){
11753 return alphanum.test(v);
11756 * The error text to display when the alphanumeric validation function returns false
11759 'alphanumText' : 'This field should only contain letters, numbers and _',
11761 * The keystroke filter mask to be applied on alphanumeric input
11764 'alphanumMask' : /[a-z0-9_]/i
11774 * @class Roo.bootstrap.Input
11775 * @extends Roo.bootstrap.Component
11776 * Bootstrap Input class
11777 * @cfg {Boolean} disabled is it disabled
11778 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11779 * @cfg {String} name name of the input
11780 * @cfg {string} fieldLabel - the label associated
11781 * @cfg {string} placeholder - placeholder to put in text.
11782 * @cfg {string} before - input group add on before
11783 * @cfg {string} after - input group add on after
11784 * @cfg {string} size - (lg|sm) or leave empty..
11785 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11786 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11787 * @cfg {Number} md colspan out of 12 for computer-sized screens
11788 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11789 * @cfg {string} value default value of the input
11790 * @cfg {Number} labelWidth set the width of label
11791 * @cfg {Number} labellg set the width of label (1-12)
11792 * @cfg {Number} labelmd set the width of label (1-12)
11793 * @cfg {Number} labelsm set the width of label (1-12)
11794 * @cfg {Number} labelxs set the width of label (1-12)
11795 * @cfg {String} labelAlign (top|left)
11796 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11797 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11798 * @cfg {String} indicatorpos (left|right) default left
11799 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11800 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11801 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11803 * @cfg {String} align (left|center|right) Default left
11804 * @cfg {Boolean} forceFeedback (true|false) Default false
11807 * Create a new Input
11808 * @param {Object} config The config object
11811 Roo.bootstrap.Input = function(config){
11813 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11818 * Fires when this field receives input focus.
11819 * @param {Roo.form.Field} this
11824 * Fires when this field loses input focus.
11825 * @param {Roo.form.Field} this
11829 * @event specialkey
11830 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11831 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11832 * @param {Roo.form.Field} this
11833 * @param {Roo.EventObject} e The event object
11838 * Fires just before the field blurs if the field value has changed.
11839 * @param {Roo.form.Field} this
11840 * @param {Mixed} newValue The new value
11841 * @param {Mixed} oldValue The original value
11846 * Fires after the field has been marked as invalid.
11847 * @param {Roo.form.Field} this
11848 * @param {String} msg The validation message
11853 * Fires after the field has been validated with no errors.
11854 * @param {Roo.form.Field} this
11859 * Fires after the key up
11860 * @param {Roo.form.Field} this
11861 * @param {Roo.EventObject} e The event Object
11866 * Fires after the user pastes into input
11867 * @param {Roo.form.Field} this
11868 * @param {Roo.EventObject} e The event Object
11874 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11876 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11877 automatic validation (defaults to "keyup").
11879 validationEvent : "keyup",
11881 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11883 validateOnBlur : true,
11885 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11887 validationDelay : 250,
11889 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11891 focusClass : "x-form-focus", // not needed???
11895 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11897 invalidClass : "has-warning",
11900 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11902 validClass : "has-success",
11905 * @cfg {Boolean} hasFeedback (true|false) default true
11907 hasFeedback : true,
11910 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11912 invalidFeedbackClass : "glyphicon-warning-sign",
11915 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11917 validFeedbackClass : "glyphicon-ok",
11920 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11922 selectOnFocus : false,
11925 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11929 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11934 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11936 disableKeyFilter : false,
11939 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11943 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11947 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11949 blankText : "Please complete this mandatory field",
11952 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11956 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11958 maxLength : Number.MAX_VALUE,
11960 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11962 minLengthText : "The minimum length for this field is {0}",
11964 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11966 maxLengthText : "The maximum length for this field is {0}",
11970 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11971 * If available, this function will be called only after the basic validators all return true, and will be passed the
11972 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11976 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11977 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11978 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
11982 * @cfg {String} regexText -- Depricated - use Invalid Text
11987 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11993 autocomplete: false,
11997 inputType : 'text',
12000 placeholder: false,
12005 preventMark: false,
12006 isFormField : true,
12009 labelAlign : false,
12012 formatedValue : false,
12013 forceFeedback : false,
12015 indicatorpos : 'left',
12025 parentLabelAlign : function()
12028 while (parent.parent()) {
12029 parent = parent.parent();
12030 if (typeof(parent.labelAlign) !='undefined') {
12031 return parent.labelAlign;
12038 getAutoCreate : function()
12040 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12046 if(this.inputType != 'hidden'){
12047 cfg.cls = 'form-group' //input-group
12053 type : this.inputType,
12054 value : this.value,
12055 cls : 'form-control',
12056 placeholder : this.placeholder || '',
12057 autocomplete : this.autocomplete || 'new-password'
12059 if (this.inputType == 'file') {
12060 input.style = 'overflow:hidden'; // why not in CSS?
12063 if(this.capture.length){
12064 input.capture = this.capture;
12067 if(this.accept.length){
12068 input.accept = this.accept + "/*";
12072 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12075 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12076 input.maxLength = this.maxLength;
12079 if (this.disabled) {
12080 input.disabled=true;
12083 if (this.readOnly) {
12084 input.readonly=true;
12088 input.name = this.name;
12092 input.cls += ' input-' + this.size;
12096 ['xs','sm','md','lg'].map(function(size){
12097 if (settings[size]) {
12098 cfg.cls += ' col-' + size + '-' + settings[size];
12102 var inputblock = input;
12106 cls: 'glyphicon form-control-feedback'
12109 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12112 cls : 'has-feedback',
12120 if (this.before || this.after) {
12123 cls : 'input-group',
12127 if (this.before && typeof(this.before) == 'string') {
12129 inputblock.cn.push({
12131 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12135 if (this.before && typeof(this.before) == 'object') {
12136 this.before = Roo.factory(this.before);
12138 inputblock.cn.push({
12140 cls : 'roo-input-before input-group-prepend input-group-' +
12141 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12145 inputblock.cn.push(input);
12147 if (this.after && typeof(this.after) == 'string') {
12148 inputblock.cn.push({
12150 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12154 if (this.after && typeof(this.after) == 'object') {
12155 this.after = Roo.factory(this.after);
12157 inputblock.cn.push({
12159 cls : 'roo-input-after input-group-append input-group-' +
12160 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12164 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12165 inputblock.cls += ' has-feedback';
12166 inputblock.cn.push(feedback);
12171 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12172 tooltip : 'This field is required'
12174 if (this.allowBlank ) {
12175 indicator.style = this.allowBlank ? ' display:none' : '';
12177 if (align ==='left' && this.fieldLabel.length) {
12179 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12186 cls : 'control-label col-form-label',
12187 html : this.fieldLabel
12198 var labelCfg = cfg.cn[1];
12199 var contentCfg = cfg.cn[2];
12201 if(this.indicatorpos == 'right'){
12206 cls : 'control-label col-form-label',
12210 html : this.fieldLabel
12224 labelCfg = cfg.cn[0];
12225 contentCfg = cfg.cn[1];
12229 if(this.labelWidth > 12){
12230 labelCfg.style = "width: " + this.labelWidth + 'px';
12233 if(this.labelWidth < 13 && this.labelmd == 0){
12234 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12237 if(this.labellg > 0){
12238 labelCfg.cls += ' col-lg-' + this.labellg;
12239 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12242 if(this.labelmd > 0){
12243 labelCfg.cls += ' col-md-' + this.labelmd;
12244 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12247 if(this.labelsm > 0){
12248 labelCfg.cls += ' col-sm-' + this.labelsm;
12249 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12252 if(this.labelxs > 0){
12253 labelCfg.cls += ' col-xs-' + this.labelxs;
12254 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12258 } else if ( this.fieldLabel.length) {
12265 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12266 tooltip : 'This field is required',
12267 style : this.allowBlank ? ' display:none' : ''
12271 //cls : 'input-group-addon',
12272 html : this.fieldLabel
12280 if(this.indicatorpos == 'right'){
12285 //cls : 'input-group-addon',
12286 html : this.fieldLabel
12291 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12292 tooltip : 'This field is required',
12293 style : this.allowBlank ? ' display:none' : ''
12313 if (this.parentType === 'Navbar' && this.parent().bar) {
12314 cfg.cls += ' navbar-form';
12317 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12318 // on BS4 we do this only if not form
12319 cfg.cls += ' navbar-form';
12327 * return the real input element.
12329 inputEl: function ()
12331 return this.el.select('input.form-control',true).first();
12334 tooltipEl : function()
12336 return this.inputEl();
12339 indicatorEl : function()
12341 if (Roo.bootstrap.version == 4) {
12342 return false; // not enabled in v4 yet.
12345 var indicator = this.el.select('i.roo-required-indicator',true).first();
12355 setDisabled : function(v)
12357 var i = this.inputEl().dom;
12359 i.removeAttribute('disabled');
12363 i.setAttribute('disabled','true');
12365 initEvents : function()
12368 this.inputEl().on("keydown" , this.fireKey, this);
12369 this.inputEl().on("focus", this.onFocus, this);
12370 this.inputEl().on("blur", this.onBlur, this);
12372 this.inputEl().relayEvent('keyup', this);
12373 this.inputEl().relayEvent('paste', this);
12375 this.indicator = this.indicatorEl();
12377 if(this.indicator){
12378 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12381 // reference to original value for reset
12382 this.originalValue = this.getValue();
12383 //Roo.form.TextField.superclass.initEvents.call(this);
12384 if(this.validationEvent == 'keyup'){
12385 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12386 this.inputEl().on('keyup', this.filterValidation, this);
12388 else if(this.validationEvent !== false){
12389 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12392 if(this.selectOnFocus){
12393 this.on("focus", this.preFocus, this);
12396 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12397 this.inputEl().on("keypress", this.filterKeys, this);
12399 this.inputEl().relayEvent('keypress', this);
12402 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12403 this.el.on("click", this.autoSize, this);
12406 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12407 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12410 if (typeof(this.before) == 'object') {
12411 this.before.render(this.el.select('.roo-input-before',true).first());
12413 if (typeof(this.after) == 'object') {
12414 this.after.render(this.el.select('.roo-input-after',true).first());
12417 this.inputEl().on('change', this.onChange, this);
12420 filterValidation : function(e){
12421 if(!e.isNavKeyPress()){
12422 this.validationTask.delay(this.validationDelay);
12426 * Validates the field value
12427 * @return {Boolean} True if the value is valid, else false
12429 validate : function(){
12430 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12431 if(this.disabled || this.validateValue(this.getRawValue())){
12436 this.markInvalid();
12442 * Validates a value according to the field's validation rules and marks the field as invalid
12443 * if the validation fails
12444 * @param {Mixed} value The value to validate
12445 * @return {Boolean} True if the value is valid, else false
12447 validateValue : function(value)
12449 if(this.getVisibilityEl().hasClass('hidden')){
12453 if(value.length < 1) { // if it's blank
12454 if(this.allowBlank){
12460 if(value.length < this.minLength){
12463 if(value.length > this.maxLength){
12467 var vt = Roo.form.VTypes;
12468 if(!vt[this.vtype](value, this)){
12472 if(typeof this.validator == "function"){
12473 var msg = this.validator(value);
12477 if (typeof(msg) == 'string') {
12478 this.invalidText = msg;
12482 if(this.regex && !this.regex.test(value)){
12490 fireKey : function(e){
12491 //Roo.log('field ' + e.getKey());
12492 if(e.isNavKeyPress()){
12493 this.fireEvent("specialkey", this, e);
12496 focus : function (selectText){
12498 this.inputEl().focus();
12499 if(selectText === true){
12500 this.inputEl().dom.select();
12506 onFocus : function(){
12507 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12508 // this.el.addClass(this.focusClass);
12510 if(!this.hasFocus){
12511 this.hasFocus = true;
12512 this.startValue = this.getValue();
12513 this.fireEvent("focus", this);
12517 beforeBlur : Roo.emptyFn,
12521 onBlur : function(){
12523 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12524 //this.el.removeClass(this.focusClass);
12526 this.hasFocus = false;
12527 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12530 var v = this.getValue();
12531 if(String(v) !== String(this.startValue)){
12532 this.fireEvent('change', this, v, this.startValue);
12534 this.fireEvent("blur", this);
12537 onChange : function(e)
12539 var v = this.getValue();
12540 if(String(v) !== String(this.startValue)){
12541 this.fireEvent('change', this, v, this.startValue);
12547 * Resets the current field value to the originally loaded value and clears any validation messages
12549 reset : function(){
12550 this.setValue(this.originalValue);
12554 * Returns the name of the field
12555 * @return {Mixed} name The name field
12557 getName: function(){
12561 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12562 * @return {Mixed} value The field value
12564 getValue : function(){
12566 var v = this.inputEl().getValue();
12571 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12572 * @return {Mixed} value The field value
12574 getRawValue : function(){
12575 var v = this.inputEl().getValue();
12581 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12582 * @param {Mixed} value The value to set
12584 setRawValue : function(v){
12585 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12588 selectText : function(start, end){
12589 var v = this.getRawValue();
12591 start = start === undefined ? 0 : start;
12592 end = end === undefined ? v.length : end;
12593 var d = this.inputEl().dom;
12594 if(d.setSelectionRange){
12595 d.setSelectionRange(start, end);
12596 }else if(d.createTextRange){
12597 var range = d.createTextRange();
12598 range.moveStart("character", start);
12599 range.moveEnd("character", v.length-end);
12606 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12607 * @param {Mixed} value The value to set
12609 setValue : function(v){
12612 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12618 processValue : function(value){
12619 if(this.stripCharsRe){
12620 var newValue = value.replace(this.stripCharsRe, '');
12621 if(newValue !== value){
12622 this.setRawValue(newValue);
12629 preFocus : function(){
12631 if(this.selectOnFocus){
12632 this.inputEl().dom.select();
12635 filterKeys : function(e){
12636 var k = e.getKey();
12637 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12640 var c = e.getCharCode(), cc = String.fromCharCode(c);
12641 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12644 if(!this.maskRe.test(cc)){
12649 * Clear any invalid styles/messages for this field
12651 clearInvalid : function(){
12653 if(!this.el || this.preventMark){ // not rendered
12658 this.el.removeClass([this.invalidClass, 'is-invalid']);
12660 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12662 var feedback = this.el.select('.form-control-feedback', true).first();
12665 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12670 if(this.indicator){
12671 this.indicator.removeClass('visible');
12672 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12675 this.fireEvent('valid', this);
12679 * Mark this field as valid
12681 markValid : function()
12683 if(!this.el || this.preventMark){ // not rendered...
12687 this.el.removeClass([this.invalidClass, this.validClass]);
12688 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12690 var feedback = this.el.select('.form-control-feedback', true).first();
12693 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12696 if(this.indicator){
12697 this.indicator.removeClass('visible');
12698 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12706 if(this.allowBlank && !this.getRawValue().length){
12709 if (Roo.bootstrap.version == 3) {
12710 this.el.addClass(this.validClass);
12712 this.inputEl().addClass('is-valid');
12715 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12717 var feedback = this.el.select('.form-control-feedback', true).first();
12720 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12721 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12726 this.fireEvent('valid', this);
12730 * Mark this field as invalid
12731 * @param {String} msg The validation message
12733 markInvalid : function(msg)
12735 if(!this.el || this.preventMark){ // not rendered
12739 this.el.removeClass([this.invalidClass, this.validClass]);
12740 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12742 var feedback = this.el.select('.form-control-feedback', true).first();
12745 this.el.select('.form-control-feedback', true).first().removeClass(
12746 [this.invalidFeedbackClass, this.validFeedbackClass]);
12753 if(this.allowBlank && !this.getRawValue().length){
12757 if(this.indicator){
12758 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12759 this.indicator.addClass('visible');
12761 if (Roo.bootstrap.version == 3) {
12762 this.el.addClass(this.invalidClass);
12764 this.inputEl().addClass('is-invalid');
12769 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12771 var feedback = this.el.select('.form-control-feedback', true).first();
12774 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12776 if(this.getValue().length || this.forceFeedback){
12777 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12784 this.fireEvent('invalid', this, msg);
12787 SafariOnKeyDown : function(event)
12789 // this is a workaround for a password hang bug on chrome/ webkit.
12790 if (this.inputEl().dom.type != 'password') {
12794 var isSelectAll = false;
12796 if(this.inputEl().dom.selectionEnd > 0){
12797 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12799 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12800 event.preventDefault();
12805 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12807 event.preventDefault();
12808 // this is very hacky as keydown always get's upper case.
12810 var cc = String.fromCharCode(event.getCharCode());
12811 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12815 adjustWidth : function(tag, w){
12816 tag = tag.toLowerCase();
12817 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12818 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12819 if(tag == 'input'){
12822 if(tag == 'textarea'){
12825 }else if(Roo.isOpera){
12826 if(tag == 'input'){
12829 if(tag == 'textarea'){
12837 setFieldLabel : function(v)
12839 if(!this.rendered){
12843 if(this.indicatorEl()){
12844 var ar = this.el.select('label > span',true);
12846 if (ar.elements.length) {
12847 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12848 this.fieldLabel = v;
12852 var br = this.el.select('label',true);
12854 if(br.elements.length) {
12855 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12856 this.fieldLabel = v;
12860 Roo.log('Cannot Found any of label > span || label in input');
12864 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12865 this.fieldLabel = v;
12880 * @class Roo.bootstrap.TextArea
12881 * @extends Roo.bootstrap.Input
12882 * Bootstrap TextArea class
12883 * @cfg {Number} cols Specifies the visible width of a text area
12884 * @cfg {Number} rows Specifies the visible number of lines in a text area
12885 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12886 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12887 * @cfg {string} html text
12890 * Create a new TextArea
12891 * @param {Object} config The config object
12894 Roo.bootstrap.TextArea = function(config){
12895 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12899 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12909 getAutoCreate : function(){
12911 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12917 if(this.inputType != 'hidden'){
12918 cfg.cls = 'form-group' //input-group
12926 value : this.value || '',
12927 html: this.html || '',
12928 cls : 'form-control',
12929 placeholder : this.placeholder || ''
12933 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12934 input.maxLength = this.maxLength;
12938 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12942 input.cols = this.cols;
12945 if (this.readOnly) {
12946 input.readonly = true;
12950 input.name = this.name;
12954 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12958 ['xs','sm','md','lg'].map(function(size){
12959 if (settings[size]) {
12960 cfg.cls += ' col-' + size + '-' + settings[size];
12964 var inputblock = input;
12966 if(this.hasFeedback && !this.allowBlank){
12970 cls: 'glyphicon form-control-feedback'
12974 cls : 'has-feedback',
12983 if (this.before || this.after) {
12986 cls : 'input-group',
12990 inputblock.cn.push({
12992 cls : 'input-group-addon',
12997 inputblock.cn.push(input);
12999 if(this.hasFeedback && !this.allowBlank){
13000 inputblock.cls += ' has-feedback';
13001 inputblock.cn.push(feedback);
13005 inputblock.cn.push({
13007 cls : 'input-group-addon',
13014 if (align ==='left' && this.fieldLabel.length) {
13019 cls : 'control-label',
13020 html : this.fieldLabel
13031 if(this.labelWidth > 12){
13032 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13035 if(this.labelWidth < 13 && this.labelmd == 0){
13036 this.labelmd = this.labelWidth;
13039 if(this.labellg > 0){
13040 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13041 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13044 if(this.labelmd > 0){
13045 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13046 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13049 if(this.labelsm > 0){
13050 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13051 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13054 if(this.labelxs > 0){
13055 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13056 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13059 } else if ( this.fieldLabel.length) {
13064 //cls : 'input-group-addon',
13065 html : this.fieldLabel
13083 if (this.disabled) {
13084 input.disabled=true;
13091 * return the real textarea element.
13093 inputEl: function ()
13095 return this.el.select('textarea.form-control',true).first();
13099 * Clear any invalid styles/messages for this field
13101 clearInvalid : function()
13104 if(!this.el || this.preventMark){ // not rendered
13108 var label = this.el.select('label', true).first();
13109 var icon = this.el.select('i.fa-star', true).first();
13114 this.el.removeClass( this.validClass);
13115 this.inputEl().removeClass('is-invalid');
13117 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13119 var feedback = this.el.select('.form-control-feedback', true).first();
13122 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13127 this.fireEvent('valid', this);
13131 * Mark this field as valid
13133 markValid : function()
13135 if(!this.el || this.preventMark){ // not rendered
13139 this.el.removeClass([this.invalidClass, this.validClass]);
13140 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13142 var feedback = this.el.select('.form-control-feedback', true).first();
13145 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13148 if(this.disabled || this.allowBlank){
13152 var label = this.el.select('label', true).first();
13153 var icon = this.el.select('i.fa-star', true).first();
13158 if (Roo.bootstrap.version == 3) {
13159 this.el.addClass(this.validClass);
13161 this.inputEl().addClass('is-valid');
13165 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13167 var feedback = this.el.select('.form-control-feedback', true).first();
13170 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13171 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13176 this.fireEvent('valid', this);
13180 * Mark this field as invalid
13181 * @param {String} msg The validation message
13183 markInvalid : function(msg)
13185 if(!this.el || this.preventMark){ // not rendered
13189 this.el.removeClass([this.invalidClass, this.validClass]);
13190 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13192 var feedback = this.el.select('.form-control-feedback', true).first();
13195 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13198 if(this.disabled || this.allowBlank){
13202 var label = this.el.select('label', true).first();
13203 var icon = this.el.select('i.fa-star', true).first();
13205 if(!this.getValue().length && label && !icon){
13206 this.el.createChild({
13208 cls : 'text-danger fa fa-lg fa-star',
13209 tooltip : 'This field is required',
13210 style : 'margin-right:5px;'
13214 if (Roo.bootstrap.version == 3) {
13215 this.el.addClass(this.invalidClass);
13217 this.inputEl().addClass('is-invalid');
13220 // fixme ... this may be depricated need to test..
13221 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13223 var feedback = this.el.select('.form-control-feedback', true).first();
13226 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13228 if(this.getValue().length || this.forceFeedback){
13229 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13236 this.fireEvent('invalid', this, msg);
13244 * trigger field - base class for combo..
13249 * @class Roo.bootstrap.TriggerField
13250 * @extends Roo.bootstrap.Input
13251 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13252 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13253 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13254 * for which you can provide a custom implementation. For example:
13256 var trigger = new Roo.bootstrap.TriggerField();
13257 trigger.onTriggerClick = myTriggerFn;
13258 trigger.applyTo('my-field');
13261 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13262 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13263 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13264 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13265 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13268 * Create a new TriggerField.
13269 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13270 * to the base TextField)
13272 Roo.bootstrap.TriggerField = function(config){
13273 this.mimicing = false;
13274 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13277 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13279 * @cfg {String} triggerClass A CSS class to apply to the trigger
13282 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13287 * @cfg {Boolean} removable (true|false) special filter default false
13291 /** @cfg {Boolean} grow @hide */
13292 /** @cfg {Number} growMin @hide */
13293 /** @cfg {Number} growMax @hide */
13299 autoSize: Roo.emptyFn,
13303 deferHeight : true,
13306 actionMode : 'wrap',
13311 getAutoCreate : function(){
13313 var align = this.labelAlign || this.parentLabelAlign();
13318 cls: 'form-group' //input-group
13325 type : this.inputType,
13326 cls : 'form-control',
13327 autocomplete: 'new-password',
13328 placeholder : this.placeholder || ''
13332 input.name = this.name;
13335 input.cls += ' input-' + this.size;
13338 if (this.disabled) {
13339 input.disabled=true;
13342 var inputblock = input;
13344 if(this.hasFeedback && !this.allowBlank){
13348 cls: 'glyphicon form-control-feedback'
13351 if(this.removable && !this.editable ){
13353 cls : 'has-feedback',
13359 cls : 'roo-combo-removable-btn close'
13366 cls : 'has-feedback',
13375 if(this.removable && !this.editable ){
13377 cls : 'roo-removable',
13383 cls : 'roo-combo-removable-btn close'
13390 if (this.before || this.after) {
13393 cls : 'input-group',
13397 inputblock.cn.push({
13399 cls : 'input-group-addon input-group-prepend input-group-text',
13404 inputblock.cn.push(input);
13406 if(this.hasFeedback && !this.allowBlank){
13407 inputblock.cls += ' has-feedback';
13408 inputblock.cn.push(feedback);
13412 inputblock.cn.push({
13414 cls : 'input-group-addon input-group-append input-group-text',
13423 var ibwrap = inputblock;
13428 cls: 'roo-select2-choices',
13432 cls: 'roo-select2-search-field',
13444 cls: 'roo-select2-container input-group',
13449 cls: 'form-hidden-field'
13455 if(!this.multiple && this.showToggleBtn){
13461 if (this.caret != false) {
13464 cls: 'fa fa-' + this.caret
13471 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13473 Roo.bootstrap.version == 3 ? caret : '',
13476 cls: 'combobox-clear',
13490 combobox.cls += ' roo-select2-container-multi';
13494 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13495 tooltip : 'This field is required'
13497 if (Roo.bootstrap.version == 4) {
13500 style : 'display:none'
13505 if (align ==='left' && this.fieldLabel.length) {
13507 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13514 cls : 'control-label',
13515 html : this.fieldLabel
13527 var labelCfg = cfg.cn[1];
13528 var contentCfg = cfg.cn[2];
13530 if(this.indicatorpos == 'right'){
13535 cls : 'control-label',
13539 html : this.fieldLabel
13553 labelCfg = cfg.cn[0];
13554 contentCfg = cfg.cn[1];
13557 if(this.labelWidth > 12){
13558 labelCfg.style = "width: " + this.labelWidth + 'px';
13561 if(this.labelWidth < 13 && this.labelmd == 0){
13562 this.labelmd = this.labelWidth;
13565 if(this.labellg > 0){
13566 labelCfg.cls += ' col-lg-' + this.labellg;
13567 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13570 if(this.labelmd > 0){
13571 labelCfg.cls += ' col-md-' + this.labelmd;
13572 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13575 if(this.labelsm > 0){
13576 labelCfg.cls += ' col-sm-' + this.labelsm;
13577 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13580 if(this.labelxs > 0){
13581 labelCfg.cls += ' col-xs-' + this.labelxs;
13582 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13585 } else if ( this.fieldLabel.length) {
13586 // Roo.log(" label");
13591 //cls : 'input-group-addon',
13592 html : this.fieldLabel
13600 if(this.indicatorpos == 'right'){
13608 html : this.fieldLabel
13622 // Roo.log(" no label && no align");
13629 ['xs','sm','md','lg'].map(function(size){
13630 if (settings[size]) {
13631 cfg.cls += ' col-' + size + '-' + settings[size];
13642 onResize : function(w, h){
13643 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13644 // if(typeof w == 'number'){
13645 // var x = w - this.trigger.getWidth();
13646 // this.inputEl().setWidth(this.adjustWidth('input', x));
13647 // this.trigger.setStyle('left', x+'px');
13652 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13655 getResizeEl : function(){
13656 return this.inputEl();
13660 getPositionEl : function(){
13661 return this.inputEl();
13665 alignErrorIcon : function(){
13666 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13670 initEvents : function(){
13674 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13675 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13676 if(!this.multiple && this.showToggleBtn){
13677 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13678 if(this.hideTrigger){
13679 this.trigger.setDisplayed(false);
13681 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13685 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13688 if(this.removable && !this.editable && !this.tickable){
13689 var close = this.closeTriggerEl();
13692 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13693 close.on('click', this.removeBtnClick, this, close);
13697 //this.trigger.addClassOnOver('x-form-trigger-over');
13698 //this.trigger.addClassOnClick('x-form-trigger-click');
13701 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13705 closeTriggerEl : function()
13707 var close = this.el.select('.roo-combo-removable-btn', true).first();
13708 return close ? close : false;
13711 removeBtnClick : function(e, h, el)
13713 e.preventDefault();
13715 if(this.fireEvent("remove", this) !== false){
13717 this.fireEvent("afterremove", this)
13721 createList : function()
13723 this.list = Roo.get(document.body).createChild({
13724 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13725 cls: 'typeahead typeahead-long dropdown-menu shadow',
13726 style: 'display:none'
13729 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13734 initTrigger : function(){
13739 onDestroy : function(){
13741 this.trigger.removeAllListeners();
13742 // this.trigger.remove();
13745 // this.wrap.remove();
13747 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13751 onFocus : function(){
13752 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13754 if(!this.mimicing){
13755 this.wrap.addClass('x-trigger-wrap-focus');
13756 this.mimicing = true;
13757 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13758 if(this.monitorTab){
13759 this.el.on("keydown", this.checkTab, this);
13766 checkTab : function(e){
13767 if(e.getKey() == e.TAB){
13768 this.triggerBlur();
13773 onBlur : function(){
13778 mimicBlur : function(e, t){
13780 if(!this.wrap.contains(t) && this.validateBlur()){
13781 this.triggerBlur();
13787 triggerBlur : function(){
13788 this.mimicing = false;
13789 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13790 if(this.monitorTab){
13791 this.el.un("keydown", this.checkTab, this);
13793 //this.wrap.removeClass('x-trigger-wrap-focus');
13794 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13798 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13799 validateBlur : function(e, t){
13804 onDisable : function(){
13805 this.inputEl().dom.disabled = true;
13806 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13808 // this.wrap.addClass('x-item-disabled');
13813 onEnable : function(){
13814 this.inputEl().dom.disabled = false;
13815 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13817 // this.el.removeClass('x-item-disabled');
13822 onShow : function(){
13823 var ae = this.getActionEl();
13826 ae.dom.style.display = '';
13827 ae.dom.style.visibility = 'visible';
13833 onHide : function(){
13834 var ae = this.getActionEl();
13835 ae.dom.style.display = 'none';
13839 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13840 * by an implementing function.
13842 * @param {EventObject} e
13844 onTriggerClick : Roo.emptyFn
13852 * @class Roo.bootstrap.CardUploader
13853 * @extends Roo.bootstrap.Button
13854 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13855 * @cfg {Number} errorTimeout default 3000
13856 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13857 * @cfg {Array} html The button text.
13861 * Create a new CardUploader
13862 * @param {Object} config The config object
13865 Roo.bootstrap.CardUploader = function(config){
13869 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13872 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13880 * When a image is clicked on - and needs to display a slideshow or similar..
13881 * @param {Roo.bootstrap.Card} this
13882 * @param {Object} The image information data
13888 * When a the download link is clicked
13889 * @param {Roo.bootstrap.Card} this
13890 * @param {Object} The image information data contains
13897 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13900 errorTimeout : 3000,
13904 fileCollection : false,
13907 getAutoCreate : function()
13911 cls :'form-group' ,
13916 //cls : 'input-group-addon',
13917 html : this.fieldLabel
13925 value : this.value,
13926 cls : 'd-none form-control'
13931 multiple : 'multiple',
13933 cls : 'd-none roo-card-upload-selector'
13937 cls : 'roo-card-uploader-button-container w-100 mb-2'
13940 cls : 'card-columns roo-card-uploader-container'
13950 getChildContainer : function() /// what children are added to.
13952 return this.containerEl;
13955 getButtonContainer : function() /// what children are added to.
13957 return this.el.select(".roo-card-uploader-button-container").first();
13960 initEvents : function()
13963 Roo.bootstrap.Input.prototype.initEvents.call(this);
13967 xns: Roo.bootstrap,
13970 container_method : 'getButtonContainer' ,
13971 html : this.html, // fix changable?
13974 'click' : function(btn, e) {
13983 this.urlAPI = (window.createObjectURL && window) ||
13984 (window.URL && URL.revokeObjectURL && URL) ||
13985 (window.webkitURL && webkitURL);
13990 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
13992 this.selectorEl.on('change', this.onFileSelected, this);
13995 this.images.forEach(function(img) {
13998 this.images = false;
14000 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14006 onClick : function(e)
14008 e.preventDefault();
14010 this.selectorEl.dom.click();
14014 onFileSelected : function(e)
14016 e.preventDefault();
14018 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14022 Roo.each(this.selectorEl.dom.files, function(file){
14023 this.addFile(file);
14032 addFile : function(file)
14035 if(typeof(file) === 'string'){
14036 throw "Add file by name?"; // should not happen
14040 if(!file || !this.urlAPI){
14050 var url = _this.urlAPI.createObjectURL( file);
14053 id : Roo.bootstrap.CardUploader.ID--,
14054 is_uploaded : false,
14058 mimetype : file.type,
14066 * addCard - add an Attachment to the uploader
14067 * @param data - the data about the image to upload
14071 title : "Title of file",
14072 is_uploaded : false,
14073 src : "http://.....",
14074 srcfile : { the File upload object },
14075 mimetype : file.type,
14078 .. any other data...
14084 addCard : function (data)
14086 // hidden input element?
14087 // if the file is not an image...
14088 //then we need to use something other that and header_image
14093 xns : Roo.bootstrap,
14094 xtype : 'CardFooter',
14097 xns : Roo.bootstrap,
14103 xns : Roo.bootstrap,
14105 html : String.format("<small>{0}</small>", data.title),
14106 cls : 'col-10 text-left',
14111 click : function() {
14113 t.fireEvent( "download", t, data );
14119 xns : Roo.bootstrap,
14121 style: 'max-height: 28px; ',
14127 click : function() {
14128 t.removeCard(data.id)
14140 var cn = this.addxtype(
14143 xns : Roo.bootstrap,
14146 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14147 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14148 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14153 initEvents : function() {
14154 Roo.bootstrap.Card.prototype.initEvents.call(this);
14156 this.imgEl = this.el.select('.card-img-top').first();
14158 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14159 this.imgEl.set({ 'pointer' : 'cursor' });
14162 this.getCardFooter().addClass('p-1');
14169 // dont' really need ot update items.
14170 // this.items.push(cn);
14171 this.fileCollection.add(cn);
14173 if (!data.srcfile) {
14174 this.updateInput();
14179 var reader = new FileReader();
14180 reader.addEventListener("load", function() {
14181 data.srcdata = reader.result;
14184 reader.readAsDataURL(data.srcfile);
14189 removeCard : function(id)
14192 var card = this.fileCollection.get(id);
14193 card.data.is_deleted = 1;
14194 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14195 //this.fileCollection.remove(card);
14196 //this.items = this.items.filter(function(e) { return e != card });
14197 // dont' really need ot update items.
14198 card.el.dom.parentNode.removeChild(card.el.dom);
14199 this.updateInput();
14205 this.fileCollection.each(function(card) {
14206 if (card.el.dom && card.el.dom.parentNode) {
14207 card.el.dom.parentNode.removeChild(card.el.dom);
14210 this.fileCollection.clear();
14211 this.updateInput();
14214 updateInput : function()
14217 this.fileCollection.each(function(e) {
14221 this.inputEl().dom.value = JSON.stringify(data);
14231 Roo.bootstrap.CardUploader.ID = -1;/*
14233 * Ext JS Library 1.1.1
14234 * Copyright(c) 2006-2007, Ext JS, LLC.
14236 * Originally Released Under LGPL - original licence link has changed is not relivant.
14239 * <script type="text/javascript">
14244 * @class Roo.data.SortTypes
14246 * Defines the default sorting (casting?) comparison functions used when sorting data.
14248 Roo.data.SortTypes = {
14250 * Default sort that does nothing
14251 * @param {Mixed} s The value being converted
14252 * @return {Mixed} The comparison value
14254 none : function(s){
14259 * The regular expression used to strip tags
14263 stripTagsRE : /<\/?[^>]+>/gi,
14266 * Strips all HTML tags to sort on text only
14267 * @param {Mixed} s The value being converted
14268 * @return {String} The comparison value
14270 asText : function(s){
14271 return String(s).replace(this.stripTagsRE, "");
14275 * Strips all HTML tags to sort on text only - Case insensitive
14276 * @param {Mixed} s The value being converted
14277 * @return {String} The comparison value
14279 asUCText : function(s){
14280 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14284 * Case insensitive string
14285 * @param {Mixed} s The value being converted
14286 * @return {String} The comparison value
14288 asUCString : function(s) {
14289 return String(s).toUpperCase();
14294 * @param {Mixed} s The value being converted
14295 * @return {Number} The comparison value
14297 asDate : function(s) {
14301 if(s instanceof Date){
14302 return s.getTime();
14304 return Date.parse(String(s));
14309 * @param {Mixed} s The value being converted
14310 * @return {Float} The comparison value
14312 asFloat : function(s) {
14313 var val = parseFloat(String(s).replace(/,/g, ""));
14322 * @param {Mixed} s The value being converted
14323 * @return {Number} The comparison value
14325 asInt : function(s) {
14326 var val = parseInt(String(s).replace(/,/g, ""));
14334 * Ext JS Library 1.1.1
14335 * Copyright(c) 2006-2007, Ext JS, LLC.
14337 * Originally Released Under LGPL - original licence link has changed is not relivant.
14340 * <script type="text/javascript">
14344 * @class Roo.data.Record
14345 * Instances of this class encapsulate both record <em>definition</em> information, and record
14346 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14347 * to access Records cached in an {@link Roo.data.Store} object.<br>
14349 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14350 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14353 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14355 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14356 * {@link #create}. The parameters are the same.
14357 * @param {Array} data An associative Array of data values keyed by the field name.
14358 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14359 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14360 * not specified an integer id is generated.
14362 Roo.data.Record = function(data, id){
14363 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14368 * Generate a constructor for a specific record layout.
14369 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14370 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14371 * Each field definition object may contain the following properties: <ul>
14372 * <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,
14373 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14374 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14375 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14376 * is being used, then this is a string containing the javascript expression to reference the data relative to
14377 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14378 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14379 * this may be omitted.</p></li>
14380 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14381 * <ul><li>auto (Default, implies no conversion)</li>
14386 * <li>date</li></ul></p></li>
14387 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14388 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14389 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14390 * by the Reader into an object that will be stored in the Record. It is passed the
14391 * following parameters:<ul>
14392 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14394 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14396 * <br>usage:<br><pre><code>
14397 var TopicRecord = Roo.data.Record.create(
14398 {name: 'title', mapping: 'topic_title'},
14399 {name: 'author', mapping: 'username'},
14400 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14401 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14402 {name: 'lastPoster', mapping: 'user2'},
14403 {name: 'excerpt', mapping: 'post_text'}
14406 var myNewRecord = new TopicRecord({
14407 title: 'Do my job please',
14410 lastPost: new Date(),
14411 lastPoster: 'Animal',
14412 excerpt: 'No way dude!'
14414 myStore.add(myNewRecord);
14419 Roo.data.Record.create = function(o){
14420 var f = function(){
14421 f.superclass.constructor.apply(this, arguments);
14423 Roo.extend(f, Roo.data.Record);
14424 var p = f.prototype;
14425 p.fields = new Roo.util.MixedCollection(false, function(field){
14428 for(var i = 0, len = o.length; i < len; i++){
14429 p.fields.add(new Roo.data.Field(o[i]));
14431 f.getField = function(name){
14432 return p.fields.get(name);
14437 Roo.data.Record.AUTO_ID = 1000;
14438 Roo.data.Record.EDIT = 'edit';
14439 Roo.data.Record.REJECT = 'reject';
14440 Roo.data.Record.COMMIT = 'commit';
14442 Roo.data.Record.prototype = {
14444 * Readonly flag - true if this record has been modified.
14453 join : function(store){
14454 this.store = store;
14458 * Set the named field to the specified value.
14459 * @param {String} name The name of the field to set.
14460 * @param {Object} value The value to set the field to.
14462 set : function(name, value){
14463 if(this.data[name] == value){
14467 if(!this.modified){
14468 this.modified = {};
14470 if(typeof this.modified[name] == 'undefined'){
14471 this.modified[name] = this.data[name];
14473 this.data[name] = value;
14474 if(!this.editing && this.store){
14475 this.store.afterEdit(this);
14480 * Get the value of the named field.
14481 * @param {String} name The name of the field to get the value of.
14482 * @return {Object} The value of the field.
14484 get : function(name){
14485 return this.data[name];
14489 beginEdit : function(){
14490 this.editing = true;
14491 this.modified = {};
14495 cancelEdit : function(){
14496 this.editing = false;
14497 delete this.modified;
14501 endEdit : function(){
14502 this.editing = false;
14503 if(this.dirty && this.store){
14504 this.store.afterEdit(this);
14509 * Usually called by the {@link Roo.data.Store} which owns the Record.
14510 * Rejects all changes made to the Record since either creation, or the last commit operation.
14511 * Modified fields are reverted to their original values.
14513 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14514 * of reject operations.
14516 reject : function(){
14517 var m = this.modified;
14519 if(typeof m[n] != "function"){
14520 this.data[n] = m[n];
14523 this.dirty = false;
14524 delete this.modified;
14525 this.editing = false;
14527 this.store.afterReject(this);
14532 * Usually called by the {@link Roo.data.Store} which owns the Record.
14533 * Commits all changes made to the Record since either creation, or the last commit operation.
14535 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14536 * of commit operations.
14538 commit : function(){
14539 this.dirty = false;
14540 delete this.modified;
14541 this.editing = false;
14543 this.store.afterCommit(this);
14548 hasError : function(){
14549 return this.error != null;
14553 clearError : function(){
14558 * Creates a copy of this record.
14559 * @param {String} id (optional) A new record id if you don't want to use this record's id
14562 copy : function(newId) {
14563 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14567 * Ext JS Library 1.1.1
14568 * Copyright(c) 2006-2007, Ext JS, LLC.
14570 * Originally Released Under LGPL - original licence link has changed is not relivant.
14573 * <script type="text/javascript">
14579 * @class Roo.data.Store
14580 * @extends Roo.util.Observable
14581 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14582 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14584 * 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
14585 * has no knowledge of the format of the data returned by the Proxy.<br>
14587 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14588 * instances from the data object. These records are cached and made available through accessor functions.
14590 * Creates a new Store.
14591 * @param {Object} config A config object containing the objects needed for the Store to access data,
14592 * and read the data into Records.
14594 Roo.data.Store = function(config){
14595 this.data = new Roo.util.MixedCollection(false);
14596 this.data.getKey = function(o){
14599 this.baseParams = {};
14601 this.paramNames = {
14606 "multisort" : "_multisort"
14609 if(config && config.data){
14610 this.inlineData = config.data;
14611 delete config.data;
14614 Roo.apply(this, config);
14616 if(this.reader){ // reader passed
14617 this.reader = Roo.factory(this.reader, Roo.data);
14618 this.reader.xmodule = this.xmodule || false;
14619 if(!this.recordType){
14620 this.recordType = this.reader.recordType;
14622 if(this.reader.onMetaChange){
14623 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14627 if(this.recordType){
14628 this.fields = this.recordType.prototype.fields;
14630 this.modified = [];
14634 * @event datachanged
14635 * Fires when the data cache has changed, and a widget which is using this Store
14636 * as a Record cache should refresh its view.
14637 * @param {Store} this
14639 datachanged : true,
14641 * @event metachange
14642 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14643 * @param {Store} this
14644 * @param {Object} meta The JSON metadata
14649 * Fires when Records have been added to the Store
14650 * @param {Store} this
14651 * @param {Roo.data.Record[]} records The array of Records added
14652 * @param {Number} index The index at which the record(s) were added
14657 * Fires when a Record has been removed from the Store
14658 * @param {Store} this
14659 * @param {Roo.data.Record} record The Record that was removed
14660 * @param {Number} index The index at which the record was removed
14665 * Fires when a Record has been updated
14666 * @param {Store} this
14667 * @param {Roo.data.Record} record The Record that was updated
14668 * @param {String} operation The update operation being performed. Value may be one of:
14670 Roo.data.Record.EDIT
14671 Roo.data.Record.REJECT
14672 Roo.data.Record.COMMIT
14678 * Fires when the data cache has been cleared.
14679 * @param {Store} this
14683 * @event beforeload
14684 * Fires before a request is made for a new data object. If the beforeload handler returns false
14685 * the load action will be canceled.
14686 * @param {Store} this
14687 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14691 * @event beforeloadadd
14692 * Fires after a new set of Records has been loaded.
14693 * @param {Store} this
14694 * @param {Roo.data.Record[]} records The Records that were loaded
14695 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14697 beforeloadadd : true,
14700 * Fires after a new set of Records has been loaded, before they are added to the store.
14701 * @param {Store} this
14702 * @param {Roo.data.Record[]} records The Records that were loaded
14703 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14704 * @params {Object} return from reader
14708 * @event loadexception
14709 * Fires if an exception occurs in the Proxy during loading.
14710 * Called with the signature of the Proxy's "loadexception" event.
14711 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14714 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14715 * @param {Object} load options
14716 * @param {Object} jsonData from your request (normally this contains the Exception)
14718 loadexception : true
14722 this.proxy = Roo.factory(this.proxy, Roo.data);
14723 this.proxy.xmodule = this.xmodule || false;
14724 this.relayEvents(this.proxy, ["loadexception"]);
14726 this.sortToggle = {};
14727 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14729 Roo.data.Store.superclass.constructor.call(this);
14731 if(this.inlineData){
14732 this.loadData(this.inlineData);
14733 delete this.inlineData;
14737 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14739 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14740 * without a remote query - used by combo/forms at present.
14744 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14747 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14750 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14751 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14754 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14755 * on any HTTP request
14758 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14761 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14765 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14766 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14768 remoteSort : false,
14771 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14772 * loaded or when a record is removed. (defaults to false).
14774 pruneModifiedRecords : false,
14777 lastOptions : null,
14780 * Add Records to the Store and fires the add event.
14781 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14783 add : function(records){
14784 records = [].concat(records);
14785 for(var i = 0, len = records.length; i < len; i++){
14786 records[i].join(this);
14788 var index = this.data.length;
14789 this.data.addAll(records);
14790 this.fireEvent("add", this, records, index);
14794 * Remove a Record from the Store and fires the remove event.
14795 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14797 remove : function(record){
14798 var index = this.data.indexOf(record);
14799 this.data.removeAt(index);
14801 if(this.pruneModifiedRecords){
14802 this.modified.remove(record);
14804 this.fireEvent("remove", this, record, index);
14808 * Remove all Records from the Store and fires the clear event.
14810 removeAll : function(){
14812 if(this.pruneModifiedRecords){
14813 this.modified = [];
14815 this.fireEvent("clear", this);
14819 * Inserts Records to the Store at the given index and fires the add event.
14820 * @param {Number} index The start index at which to insert the passed Records.
14821 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14823 insert : function(index, records){
14824 records = [].concat(records);
14825 for(var i = 0, len = records.length; i < len; i++){
14826 this.data.insert(index, records[i]);
14827 records[i].join(this);
14829 this.fireEvent("add", this, records, index);
14833 * Get the index within the cache of the passed Record.
14834 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14835 * @return {Number} The index of the passed Record. Returns -1 if not found.
14837 indexOf : function(record){
14838 return this.data.indexOf(record);
14842 * Get the index within the cache of the Record with the passed id.
14843 * @param {String} id The id of the Record to find.
14844 * @return {Number} The index of the Record. Returns -1 if not found.
14846 indexOfId : function(id){
14847 return this.data.indexOfKey(id);
14851 * Get the Record with the specified id.
14852 * @param {String} id The id of the Record to find.
14853 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14855 getById : function(id){
14856 return this.data.key(id);
14860 * Get the Record at the specified index.
14861 * @param {Number} index The index of the Record to find.
14862 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14864 getAt : function(index){
14865 return this.data.itemAt(index);
14869 * Returns a range of Records between specified indices.
14870 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14871 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14872 * @return {Roo.data.Record[]} An array of Records
14874 getRange : function(start, end){
14875 return this.data.getRange(start, end);
14879 storeOptions : function(o){
14880 o = Roo.apply({}, o);
14883 this.lastOptions = o;
14887 * Loads the Record cache from the configured Proxy using the configured Reader.
14889 * If using remote paging, then the first load call must specify the <em>start</em>
14890 * and <em>limit</em> properties in the options.params property to establish the initial
14891 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14893 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14894 * and this call will return before the new data has been loaded. Perform any post-processing
14895 * in a callback function, or in a "load" event handler.</strong>
14897 * @param {Object} options An object containing properties which control loading options:<ul>
14898 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14899 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14900 * passed the following arguments:<ul>
14901 * <li>r : Roo.data.Record[]</li>
14902 * <li>options: Options object from the load call</li>
14903 * <li>success: Boolean success indicator</li></ul></li>
14904 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14905 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14908 load : function(options){
14909 options = options || {};
14910 if(this.fireEvent("beforeload", this, options) !== false){
14911 this.storeOptions(options);
14912 var p = Roo.apply(options.params || {}, this.baseParams);
14913 // if meta was not loaded from remote source.. try requesting it.
14914 if (!this.reader.metaFromRemote) {
14915 p._requestMeta = 1;
14917 if(this.sortInfo && this.remoteSort){
14918 var pn = this.paramNames;
14919 p[pn["sort"]] = this.sortInfo.field;
14920 p[pn["dir"]] = this.sortInfo.direction;
14922 if (this.multiSort) {
14923 var pn = this.paramNames;
14924 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14927 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14932 * Reloads the Record cache from the configured Proxy using the configured Reader and
14933 * the options from the last load operation performed.
14934 * @param {Object} options (optional) An object containing properties which may override the options
14935 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14936 * the most recently used options are reused).
14938 reload : function(options){
14939 this.load(Roo.applyIf(options||{}, this.lastOptions));
14943 // Called as a callback by the Reader during a load operation.
14944 loadRecords : function(o, options, success){
14945 if(!o || success === false){
14946 if(success !== false){
14947 this.fireEvent("load", this, [], options, o);
14949 if(options.callback){
14950 options.callback.call(options.scope || this, [], options, false);
14954 // if data returned failure - throw an exception.
14955 if (o.success === false) {
14956 // show a message if no listener is registered.
14957 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14958 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14960 // loadmask wil be hooked into this..
14961 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14964 var r = o.records, t = o.totalRecords || r.length;
14966 this.fireEvent("beforeloadadd", this, r, options, o);
14968 if(!options || options.add !== true){
14969 if(this.pruneModifiedRecords){
14970 this.modified = [];
14972 for(var i = 0, len = r.length; i < len; i++){
14976 this.data = this.snapshot;
14977 delete this.snapshot;
14980 this.data.addAll(r);
14981 this.totalLength = t;
14983 this.fireEvent("datachanged", this);
14985 this.totalLength = Math.max(t, this.data.length+r.length);
14989 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
14991 var e = new Roo.data.Record({});
14993 e.set(this.parent.displayField, this.parent.emptyTitle);
14994 e.set(this.parent.valueField, '');
14999 this.fireEvent("load", this, r, options, o);
15000 if(options.callback){
15001 options.callback.call(options.scope || this, r, options, true);
15007 * Loads data from a passed data block. A Reader which understands the format of the data
15008 * must have been configured in the constructor.
15009 * @param {Object} data The data block from which to read the Records. The format of the data expected
15010 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15011 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15013 loadData : function(o, append){
15014 var r = this.reader.readRecords(o);
15015 this.loadRecords(r, {add: append}, true);
15019 * using 'cn' the nested child reader read the child array into it's child stores.
15020 * @param {Object} rec The record with a 'children array
15022 loadDataFromChildren : function(rec)
15024 this.loadData(this.reader.toLoadData(rec));
15029 * Gets the number of cached records.
15031 * <em>If using paging, this may not be the total size of the dataset. If the data object
15032 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15033 * the data set size</em>
15035 getCount : function(){
15036 return this.data.length || 0;
15040 * Gets the total number of records in the dataset as returned by the server.
15042 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15043 * the dataset size</em>
15045 getTotalCount : function(){
15046 return this.totalLength || 0;
15050 * Returns the sort state of the Store as an object with two properties:
15052 field {String} The name of the field by which the Records are sorted
15053 direction {String} The sort order, "ASC" or "DESC"
15056 getSortState : function(){
15057 return this.sortInfo;
15061 applySort : function(){
15062 if(this.sortInfo && !this.remoteSort){
15063 var s = this.sortInfo, f = s.field;
15064 var st = this.fields.get(f).sortType;
15065 var fn = function(r1, r2){
15066 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15067 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15069 this.data.sort(s.direction, fn);
15070 if(this.snapshot && this.snapshot != this.data){
15071 this.snapshot.sort(s.direction, fn);
15077 * Sets the default sort column and order to be used by the next load operation.
15078 * @param {String} fieldName The name of the field to sort by.
15079 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15081 setDefaultSort : function(field, dir){
15082 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15086 * Sort the Records.
15087 * If remote sorting is used, the sort is performed on the server, and the cache is
15088 * reloaded. If local sorting is used, the cache is sorted internally.
15089 * @param {String} fieldName The name of the field to sort by.
15090 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15092 sort : function(fieldName, dir){
15093 var f = this.fields.get(fieldName);
15095 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15097 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15098 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15103 this.sortToggle[f.name] = dir;
15104 this.sortInfo = {field: f.name, direction: dir};
15105 if(!this.remoteSort){
15107 this.fireEvent("datachanged", this);
15109 this.load(this.lastOptions);
15114 * Calls the specified function for each of the Records in the cache.
15115 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15116 * Returning <em>false</em> aborts and exits the iteration.
15117 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15119 each : function(fn, scope){
15120 this.data.each(fn, scope);
15124 * Gets all records modified since the last commit. Modified records are persisted across load operations
15125 * (e.g., during paging).
15126 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15128 getModifiedRecords : function(){
15129 return this.modified;
15133 createFilterFn : function(property, value, anyMatch){
15134 if(!value.exec){ // not a regex
15135 value = String(value);
15136 if(value.length == 0){
15139 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15141 return function(r){
15142 return value.test(r.data[property]);
15147 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15148 * @param {String} property A field on your records
15149 * @param {Number} start The record index to start at (defaults to 0)
15150 * @param {Number} end The last record index to include (defaults to length - 1)
15151 * @return {Number} The sum
15153 sum : function(property, start, end){
15154 var rs = this.data.items, v = 0;
15155 start = start || 0;
15156 end = (end || end === 0) ? end : rs.length-1;
15158 for(var i = start; i <= end; i++){
15159 v += (rs[i].data[property] || 0);
15165 * Filter the records by a specified property.
15166 * @param {String} field A field on your records
15167 * @param {String/RegExp} value Either a string that the field
15168 * should start with or a RegExp to test against the field
15169 * @param {Boolean} anyMatch True to match any part not just the beginning
15171 filter : function(property, value, anyMatch){
15172 var fn = this.createFilterFn(property, value, anyMatch);
15173 return fn ? this.filterBy(fn) : this.clearFilter();
15177 * Filter by a function. The specified function will be called with each
15178 * record in this data source. If the function returns true the record is included,
15179 * otherwise it is filtered.
15180 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15181 * @param {Object} scope (optional) The scope of the function (defaults to this)
15183 filterBy : function(fn, scope){
15184 this.snapshot = this.snapshot || this.data;
15185 this.data = this.queryBy(fn, scope||this);
15186 this.fireEvent("datachanged", this);
15190 * Query the records by a specified property.
15191 * @param {String} field A field on your records
15192 * @param {String/RegExp} value Either a string that the field
15193 * should start with or a RegExp to test against the field
15194 * @param {Boolean} anyMatch True to match any part not just the beginning
15195 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15197 query : function(property, value, anyMatch){
15198 var fn = this.createFilterFn(property, value, anyMatch);
15199 return fn ? this.queryBy(fn) : this.data.clone();
15203 * Query by a function. The specified function will be called with each
15204 * record in this data source. If the function returns true the record is included
15206 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15207 * @param {Object} scope (optional) The scope of the function (defaults to this)
15208 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15210 queryBy : function(fn, scope){
15211 var data = this.snapshot || this.data;
15212 return data.filterBy(fn, scope||this);
15216 * Collects unique values for a particular dataIndex from this store.
15217 * @param {String} dataIndex The property to collect
15218 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15219 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15220 * @return {Array} An array of the unique values
15222 collect : function(dataIndex, allowNull, bypassFilter){
15223 var d = (bypassFilter === true && this.snapshot) ?
15224 this.snapshot.items : this.data.items;
15225 var v, sv, r = [], l = {};
15226 for(var i = 0, len = d.length; i < len; i++){
15227 v = d[i].data[dataIndex];
15229 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15238 * Revert to a view of the Record cache with no filtering applied.
15239 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15241 clearFilter : function(suppressEvent){
15242 if(this.snapshot && this.snapshot != this.data){
15243 this.data = this.snapshot;
15244 delete this.snapshot;
15245 if(suppressEvent !== true){
15246 this.fireEvent("datachanged", this);
15252 afterEdit : function(record){
15253 if(this.modified.indexOf(record) == -1){
15254 this.modified.push(record);
15256 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15260 afterReject : function(record){
15261 this.modified.remove(record);
15262 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15266 afterCommit : function(record){
15267 this.modified.remove(record);
15268 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15272 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15273 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15275 commitChanges : function(){
15276 var m = this.modified.slice(0);
15277 this.modified = [];
15278 for(var i = 0, len = m.length; i < len; i++){
15284 * Cancel outstanding changes on all changed records.
15286 rejectChanges : function(){
15287 var m = this.modified.slice(0);
15288 this.modified = [];
15289 for(var i = 0, len = m.length; i < len; i++){
15294 onMetaChange : function(meta, rtype, o){
15295 this.recordType = rtype;
15296 this.fields = rtype.prototype.fields;
15297 delete this.snapshot;
15298 this.sortInfo = meta.sortInfo || this.sortInfo;
15299 this.modified = [];
15300 this.fireEvent('metachange', this, this.reader.meta);
15303 moveIndex : function(data, type)
15305 var index = this.indexOf(data);
15307 var newIndex = index + type;
15311 this.insert(newIndex, data);
15316 * Ext JS Library 1.1.1
15317 * Copyright(c) 2006-2007, Ext JS, LLC.
15319 * Originally Released Under LGPL - original licence link has changed is not relivant.
15322 * <script type="text/javascript">
15326 * @class Roo.data.SimpleStore
15327 * @extends Roo.data.Store
15328 * Small helper class to make creating Stores from Array data easier.
15329 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15330 * @cfg {Array} fields An array of field definition objects, or field name strings.
15331 * @cfg {Object} an existing reader (eg. copied from another store)
15332 * @cfg {Array} data The multi-dimensional array of data
15334 * @param {Object} config
15336 Roo.data.SimpleStore = function(config)
15338 Roo.data.SimpleStore.superclass.constructor.call(this, {
15340 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15343 Roo.data.Record.create(config.fields)
15345 proxy : new Roo.data.MemoryProxy(config.data)
15349 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15351 * Ext JS Library 1.1.1
15352 * Copyright(c) 2006-2007, Ext JS, LLC.
15354 * Originally Released Under LGPL - original licence link has changed is not relivant.
15357 * <script type="text/javascript">
15362 * @extends Roo.data.Store
15363 * @class Roo.data.JsonStore
15364 * Small helper class to make creating Stores for JSON data easier. <br/>
15366 var store = new Roo.data.JsonStore({
15367 url: 'get-images.php',
15369 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15372 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15373 * JsonReader and HttpProxy (unless inline data is provided).</b>
15374 * @cfg {Array} fields An array of field definition objects, or field name strings.
15376 * @param {Object} config
15378 Roo.data.JsonStore = function(c){
15379 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15380 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15381 reader: new Roo.data.JsonReader(c, c.fields)
15384 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15386 * Ext JS Library 1.1.1
15387 * Copyright(c) 2006-2007, Ext JS, LLC.
15389 * Originally Released Under LGPL - original licence link has changed is not relivant.
15392 * <script type="text/javascript">
15396 Roo.data.Field = function(config){
15397 if(typeof config == "string"){
15398 config = {name: config};
15400 Roo.apply(this, config);
15403 this.type = "auto";
15406 var st = Roo.data.SortTypes;
15407 // named sortTypes are supported, here we look them up
15408 if(typeof this.sortType == "string"){
15409 this.sortType = st[this.sortType];
15412 // set default sortType for strings and dates
15413 if(!this.sortType){
15416 this.sortType = st.asUCString;
15419 this.sortType = st.asDate;
15422 this.sortType = st.none;
15427 var stripRe = /[\$,%]/g;
15429 // prebuilt conversion function for this field, instead of
15430 // switching every time we're reading a value
15432 var cv, dateFormat = this.dateFormat;
15437 cv = function(v){ return v; };
15440 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15444 return v !== undefined && v !== null && v !== '' ?
15445 parseInt(String(v).replace(stripRe, ""), 10) : '';
15450 return v !== undefined && v !== null && v !== '' ?
15451 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15456 cv = function(v){ return v === true || v === "true" || v == 1; };
15463 if(v instanceof Date){
15467 if(dateFormat == "timestamp"){
15468 return new Date(v*1000);
15470 return Date.parseDate(v, dateFormat);
15472 var parsed = Date.parse(v);
15473 return parsed ? new Date(parsed) : null;
15482 Roo.data.Field.prototype = {
15490 * Ext JS Library 1.1.1
15491 * Copyright(c) 2006-2007, Ext JS, LLC.
15493 * Originally Released Under LGPL - original licence link has changed is not relivant.
15496 * <script type="text/javascript">
15499 // Base class for reading structured data from a data source. This class is intended to be
15500 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15503 * @class Roo.data.DataReader
15504 * Base class for reading structured data from a data source. This class is intended to be
15505 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15508 Roo.data.DataReader = function(meta, recordType){
15512 this.recordType = recordType instanceof Array ?
15513 Roo.data.Record.create(recordType) : recordType;
15516 Roo.data.DataReader.prototype = {
15519 readerType : 'Data',
15521 * Create an empty record
15522 * @param {Object} data (optional) - overlay some values
15523 * @return {Roo.data.Record} record created.
15525 newRow : function(d) {
15527 this.recordType.prototype.fields.each(function(c) {
15529 case 'int' : da[c.name] = 0; break;
15530 case 'date' : da[c.name] = new Date(); break;
15531 case 'float' : da[c.name] = 0.0; break;
15532 case 'boolean' : da[c.name] = false; break;
15533 default : da[c.name] = ""; break;
15537 return new this.recordType(Roo.apply(da, d));
15543 * Ext JS Library 1.1.1
15544 * Copyright(c) 2006-2007, Ext JS, LLC.
15546 * Originally Released Under LGPL - original licence link has changed is not relivant.
15549 * <script type="text/javascript">
15553 * @class Roo.data.DataProxy
15554 * @extends Roo.data.Observable
15555 * This class is an abstract base class for implementations which provide retrieval of
15556 * unformatted data objects.<br>
15558 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15559 * (of the appropriate type which knows how to parse the data object) to provide a block of
15560 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15562 * Custom implementations must implement the load method as described in
15563 * {@link Roo.data.HttpProxy#load}.
15565 Roo.data.DataProxy = function(){
15568 * @event beforeload
15569 * Fires before a network request is made to retrieve a data object.
15570 * @param {Object} This DataProxy object.
15571 * @param {Object} params The params parameter to the load function.
15576 * Fires before the load method's callback is called.
15577 * @param {Object} This DataProxy object.
15578 * @param {Object} o The data object.
15579 * @param {Object} arg The callback argument object passed to the load function.
15583 * @event loadexception
15584 * Fires if an Exception occurs during data retrieval.
15585 * @param {Object} This DataProxy object.
15586 * @param {Object} o The data object.
15587 * @param {Object} arg The callback argument object passed to the load function.
15588 * @param {Object} e The Exception.
15590 loadexception : true
15592 Roo.data.DataProxy.superclass.constructor.call(this);
15595 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15598 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15602 * Ext JS Library 1.1.1
15603 * Copyright(c) 2006-2007, Ext JS, LLC.
15605 * Originally Released Under LGPL - original licence link has changed is not relivant.
15608 * <script type="text/javascript">
15611 * @class Roo.data.MemoryProxy
15612 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15613 * to the Reader when its load method is called.
15615 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15617 Roo.data.MemoryProxy = function(data){
15621 Roo.data.MemoryProxy.superclass.constructor.call(this);
15625 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15628 * Load data from the requested source (in this case an in-memory
15629 * data object passed to the constructor), read the data object into
15630 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15631 * process that block using the passed callback.
15632 * @param {Object} params This parameter is not used by the MemoryProxy class.
15633 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15634 * object into a block of Roo.data.Records.
15635 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15636 * The function must be passed <ul>
15637 * <li>The Record block object</li>
15638 * <li>The "arg" argument from the load function</li>
15639 * <li>A boolean success indicator</li>
15641 * @param {Object} scope The scope in which to call the callback
15642 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15644 load : function(params, reader, callback, scope, arg){
15645 params = params || {};
15648 result = reader.readRecords(params.data ? params.data :this.data);
15650 this.fireEvent("loadexception", this, arg, null, e);
15651 callback.call(scope, null, arg, false);
15654 callback.call(scope, result, arg, true);
15658 update : function(params, records){
15663 * Ext JS Library 1.1.1
15664 * Copyright(c) 2006-2007, Ext JS, LLC.
15666 * Originally Released Under LGPL - original licence link has changed is not relivant.
15669 * <script type="text/javascript">
15672 * @class Roo.data.HttpProxy
15673 * @extends Roo.data.DataProxy
15674 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15675 * configured to reference a certain URL.<br><br>
15677 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15678 * from which the running page was served.<br><br>
15680 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15682 * Be aware that to enable the browser to parse an XML document, the server must set
15683 * the Content-Type header in the HTTP response to "text/xml".
15685 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15686 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15687 * will be used to make the request.
15689 Roo.data.HttpProxy = function(conn){
15690 Roo.data.HttpProxy.superclass.constructor.call(this);
15691 // is conn a conn config or a real conn?
15693 this.useAjax = !conn || !conn.events;
15697 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15698 // thse are take from connection...
15701 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15704 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15705 * extra parameters to each request made by this object. (defaults to undefined)
15708 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15709 * to each request made by this object. (defaults to undefined)
15712 * @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)
15715 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15718 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15724 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15728 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15729 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15730 * a finer-grained basis than the DataProxy events.
15732 getConnection : function(){
15733 return this.useAjax ? Roo.Ajax : this.conn;
15737 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15738 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15739 * process that block using the passed callback.
15740 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15741 * for the request to the remote server.
15742 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15743 * object into a block of Roo.data.Records.
15744 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15745 * The function must be passed <ul>
15746 * <li>The Record block object</li>
15747 * <li>The "arg" argument from the load function</li>
15748 * <li>A boolean success indicator</li>
15750 * @param {Object} scope The scope in which to call the callback
15751 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15753 load : function(params, reader, callback, scope, arg){
15754 if(this.fireEvent("beforeload", this, params) !== false){
15756 params : params || {},
15758 callback : callback,
15763 callback : this.loadResponse,
15767 Roo.applyIf(o, this.conn);
15768 if(this.activeRequest){
15769 Roo.Ajax.abort(this.activeRequest);
15771 this.activeRequest = Roo.Ajax.request(o);
15773 this.conn.request(o);
15776 callback.call(scope||this, null, arg, false);
15781 loadResponse : function(o, success, response){
15782 delete this.activeRequest;
15784 this.fireEvent("loadexception", this, o, response);
15785 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15790 result = o.reader.read(response);
15792 this.fireEvent("loadexception", this, o, response, e);
15793 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15797 this.fireEvent("load", this, o, o.request.arg);
15798 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15802 update : function(dataSet){
15807 updateResponse : function(dataSet){
15812 * Ext JS Library 1.1.1
15813 * Copyright(c) 2006-2007, Ext JS, LLC.
15815 * Originally Released Under LGPL - original licence link has changed is not relivant.
15818 * <script type="text/javascript">
15822 * @class Roo.data.ScriptTagProxy
15823 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15824 * other than the originating domain of the running page.<br><br>
15826 * <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
15827 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15829 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15830 * source code that is used as the source inside a <script> tag.<br><br>
15832 * In order for the browser to process the returned data, the server must wrap the data object
15833 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15834 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15835 * depending on whether the callback name was passed:
15838 boolean scriptTag = false;
15839 String cb = request.getParameter("callback");
15842 response.setContentType("text/javascript");
15844 response.setContentType("application/x-json");
15846 Writer out = response.getWriter();
15848 out.write(cb + "(");
15850 out.print(dataBlock.toJsonString());
15857 * @param {Object} config A configuration object.
15859 Roo.data.ScriptTagProxy = function(config){
15860 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15861 Roo.apply(this, config);
15862 this.head = document.getElementsByTagName("head")[0];
15865 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15867 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15869 * @cfg {String} url The URL from which to request the data object.
15872 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15876 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15877 * the server the name of the callback function set up by the load call to process the returned data object.
15878 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15879 * javascript output which calls this named function passing the data object as its only parameter.
15881 callbackParam : "callback",
15883 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15884 * name to the request.
15889 * Load data from the configured URL, read the data object into
15890 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15891 * process that block using the passed callback.
15892 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15893 * for the request to the remote server.
15894 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15895 * object into a block of Roo.data.Records.
15896 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15897 * The function must be passed <ul>
15898 * <li>The Record block object</li>
15899 * <li>The "arg" argument from the load function</li>
15900 * <li>A boolean success indicator</li>
15902 * @param {Object} scope The scope in which to call the callback
15903 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15905 load : function(params, reader, callback, scope, arg){
15906 if(this.fireEvent("beforeload", this, params) !== false){
15908 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15910 var url = this.url;
15911 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15913 url += "&_dc=" + (new Date().getTime());
15915 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15918 cb : "stcCallback"+transId,
15919 scriptId : "stcScript"+transId,
15923 callback : callback,
15929 window[trans.cb] = function(o){
15930 conn.handleResponse(o, trans);
15933 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15935 if(this.autoAbort !== false){
15939 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15941 var script = document.createElement("script");
15942 script.setAttribute("src", url);
15943 script.setAttribute("type", "text/javascript");
15944 script.setAttribute("id", trans.scriptId);
15945 this.head.appendChild(script);
15947 this.trans = trans;
15949 callback.call(scope||this, null, arg, false);
15954 isLoading : function(){
15955 return this.trans ? true : false;
15959 * Abort the current server request.
15961 abort : function(){
15962 if(this.isLoading()){
15963 this.destroyTrans(this.trans);
15968 destroyTrans : function(trans, isLoaded){
15969 this.head.removeChild(document.getElementById(trans.scriptId));
15970 clearTimeout(trans.timeoutId);
15972 window[trans.cb] = undefined;
15974 delete window[trans.cb];
15977 // if hasn't been loaded, wait for load to remove it to prevent script error
15978 window[trans.cb] = function(){
15979 window[trans.cb] = undefined;
15981 delete window[trans.cb];
15988 handleResponse : function(o, trans){
15989 this.trans = false;
15990 this.destroyTrans(trans, true);
15993 result = trans.reader.readRecords(o);
15995 this.fireEvent("loadexception", this, o, trans.arg, e);
15996 trans.callback.call(trans.scope||window, null, trans.arg, false);
15999 this.fireEvent("load", this, o, trans.arg);
16000 trans.callback.call(trans.scope||window, result, trans.arg, true);
16004 handleFailure : function(trans){
16005 this.trans = false;
16006 this.destroyTrans(trans, false);
16007 this.fireEvent("loadexception", this, null, trans.arg);
16008 trans.callback.call(trans.scope||window, null, trans.arg, false);
16012 * Ext JS Library 1.1.1
16013 * Copyright(c) 2006-2007, Ext JS, LLC.
16015 * Originally Released Under LGPL - original licence link has changed is not relivant.
16018 * <script type="text/javascript">
16022 * @class Roo.data.JsonReader
16023 * @extends Roo.data.DataReader
16024 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16025 * based on mappings in a provided Roo.data.Record constructor.
16027 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16028 * in the reply previously.
16033 var RecordDef = Roo.data.Record.create([
16034 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16035 {name: 'occupation'} // This field will use "occupation" as the mapping.
16037 var myReader = new Roo.data.JsonReader({
16038 totalProperty: "results", // The property which contains the total dataset size (optional)
16039 root: "rows", // The property which contains an Array of row objects
16040 id: "id" // The property within each row object that provides an ID for the record (optional)
16044 * This would consume a JSON file like this:
16046 { 'results': 2, 'rows': [
16047 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16048 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16051 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16052 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16053 * paged from the remote server.
16054 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16055 * @cfg {String} root name of the property which contains the Array of row objects.
16056 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16057 * @cfg {Array} fields Array of field definition objects
16059 * Create a new JsonReader
16060 * @param {Object} meta Metadata configuration options
16061 * @param {Object} recordType Either an Array of field definition objects,
16062 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16064 Roo.data.JsonReader = function(meta, recordType){
16067 // set some defaults:
16068 Roo.applyIf(meta, {
16069 totalProperty: 'total',
16070 successProperty : 'success',
16075 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16077 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16079 readerType : 'Json',
16082 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16083 * Used by Store query builder to append _requestMeta to params.
16086 metaFromRemote : false,
16088 * This method is only used by a DataProxy which has retrieved data from a remote server.
16089 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16090 * @return {Object} data A data block which is used by an Roo.data.Store object as
16091 * a cache of Roo.data.Records.
16093 read : function(response){
16094 var json = response.responseText;
16096 var o = /* eval:var:o */ eval("("+json+")");
16098 throw {message: "JsonReader.read: Json object not found"};
16104 this.metaFromRemote = true;
16105 this.meta = o.metaData;
16106 this.recordType = Roo.data.Record.create(o.metaData.fields);
16107 this.onMetaChange(this.meta, this.recordType, o);
16109 return this.readRecords(o);
16112 // private function a store will implement
16113 onMetaChange : function(meta, recordType, o){
16120 simpleAccess: function(obj, subsc) {
16127 getJsonAccessor: function(){
16129 return function(expr) {
16131 return(re.test(expr))
16132 ? new Function("obj", "return obj." + expr)
16137 return Roo.emptyFn;
16142 * Create a data block containing Roo.data.Records from an XML document.
16143 * @param {Object} o An object which contains an Array of row objects in the property specified
16144 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16145 * which contains the total size of the dataset.
16146 * @return {Object} data A data block which is used by an Roo.data.Store object as
16147 * a cache of Roo.data.Records.
16149 readRecords : function(o){
16151 * After any data loads, the raw JSON data is available for further custom processing.
16155 var s = this.meta, Record = this.recordType,
16156 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16158 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16160 if(s.totalProperty) {
16161 this.getTotal = this.getJsonAccessor(s.totalProperty);
16163 if(s.successProperty) {
16164 this.getSuccess = this.getJsonAccessor(s.successProperty);
16166 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16168 var g = this.getJsonAccessor(s.id);
16169 this.getId = function(rec) {
16171 return (r === undefined || r === "") ? null : r;
16174 this.getId = function(){return null;};
16177 for(var jj = 0; jj < fl; jj++){
16179 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16180 this.ef[jj] = this.getJsonAccessor(map);
16184 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16185 if(s.totalProperty){
16186 var vt = parseInt(this.getTotal(o), 10);
16191 if(s.successProperty){
16192 var vs = this.getSuccess(o);
16193 if(vs === false || vs === 'false'){
16198 for(var i = 0; i < c; i++){
16201 var id = this.getId(n);
16202 for(var j = 0; j < fl; j++){
16204 var v = this.ef[j](n);
16206 Roo.log('missing convert for ' + f.name);
16210 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16212 var record = new Record(values, id);
16214 records[i] = record;
16220 totalRecords : totalRecords
16223 // used when loading children.. @see loadDataFromChildren
16224 toLoadData: function(rec)
16226 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16227 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16228 return { data : data, total : data.length };
16233 * Ext JS Library 1.1.1
16234 * Copyright(c) 2006-2007, Ext JS, LLC.
16236 * Originally Released Under LGPL - original licence link has changed is not relivant.
16239 * <script type="text/javascript">
16243 * @class Roo.data.ArrayReader
16244 * @extends Roo.data.DataReader
16245 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16246 * Each element of that Array represents a row of data fields. The
16247 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16248 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16252 var RecordDef = Roo.data.Record.create([
16253 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16254 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16256 var myReader = new Roo.data.ArrayReader({
16257 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16261 * This would consume an Array like this:
16263 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16267 * Create a new JsonReader
16268 * @param {Object} meta Metadata configuration options.
16269 * @param {Object|Array} recordType Either an Array of field definition objects
16271 * @cfg {Array} fields Array of field definition objects
16272 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16273 * as specified to {@link Roo.data.Record#create},
16274 * or an {@link Roo.data.Record} object
16277 * created using {@link Roo.data.Record#create}.
16279 Roo.data.ArrayReader = function(meta, recordType)
16281 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16284 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16287 * Create a data block containing Roo.data.Records from an XML document.
16288 * @param {Object} o An Array of row objects which represents the dataset.
16289 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16290 * a cache of Roo.data.Records.
16292 readRecords : function(o)
16294 var sid = this.meta ? this.meta.id : null;
16295 var recordType = this.recordType, fields = recordType.prototype.fields;
16298 for(var i = 0; i < root.length; i++){
16301 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16302 for(var j = 0, jlen = fields.length; j < jlen; j++){
16303 var f = fields.items[j];
16304 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16305 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16307 values[f.name] = v;
16309 var record = new recordType(values, id);
16311 records[records.length] = record;
16315 totalRecords : records.length
16318 // used when loading children.. @see loadDataFromChildren
16319 toLoadData: function(rec)
16321 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16322 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16333 * @class Roo.bootstrap.ComboBox
16334 * @extends Roo.bootstrap.TriggerField
16335 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16336 * @cfg {Boolean} append (true|false) default false
16337 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16338 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16339 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16340 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16341 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16342 * @cfg {Boolean} animate default true
16343 * @cfg {Boolean} emptyResultText only for touch device
16344 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16345 * @cfg {String} emptyTitle default ''
16346 * @cfg {Number} width fixed with? experimental
16348 * Create a new ComboBox.
16349 * @param {Object} config Configuration options
16351 Roo.bootstrap.ComboBox = function(config){
16352 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16356 * Fires when the dropdown list is expanded
16357 * @param {Roo.bootstrap.ComboBox} combo This combo box
16362 * Fires when the dropdown list is collapsed
16363 * @param {Roo.bootstrap.ComboBox} combo This combo box
16367 * @event beforeselect
16368 * Fires before a list item is selected. Return false to cancel the selection.
16369 * @param {Roo.bootstrap.ComboBox} combo This combo box
16370 * @param {Roo.data.Record} record The data record returned from the underlying store
16371 * @param {Number} index The index of the selected item in the dropdown list
16373 'beforeselect' : true,
16376 * Fires when a list item is selected
16377 * @param {Roo.bootstrap.ComboBox} combo This combo box
16378 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16379 * @param {Number} index The index of the selected item in the dropdown list
16383 * @event beforequery
16384 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16385 * The event object passed has these properties:
16386 * @param {Roo.bootstrap.ComboBox} combo This combo box
16387 * @param {String} query The query
16388 * @param {Boolean} forceAll true to force "all" query
16389 * @param {Boolean} cancel true to cancel the query
16390 * @param {Object} e The query event object
16392 'beforequery': true,
16395 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16396 * @param {Roo.bootstrap.ComboBox} combo This combo box
16401 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16402 * @param {Roo.bootstrap.ComboBox} combo This combo box
16403 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16408 * Fires when the remove value from the combobox array
16409 * @param {Roo.bootstrap.ComboBox} combo This combo box
16413 * @event afterremove
16414 * Fires when the remove value from the combobox array
16415 * @param {Roo.bootstrap.ComboBox} combo This combo box
16417 'afterremove' : true,
16419 * @event specialfilter
16420 * Fires when specialfilter
16421 * @param {Roo.bootstrap.ComboBox} combo This combo box
16423 'specialfilter' : true,
16426 * Fires when tick the element
16427 * @param {Roo.bootstrap.ComboBox} combo This combo box
16431 * @event touchviewdisplay
16432 * Fires when touch view require special display (default is using displayField)
16433 * @param {Roo.bootstrap.ComboBox} combo This combo box
16434 * @param {Object} cfg set html .
16436 'touchviewdisplay' : true
16441 this.tickItems = [];
16443 this.selectedIndex = -1;
16444 if(this.mode == 'local'){
16445 if(config.queryDelay === undefined){
16446 this.queryDelay = 10;
16448 if(config.minChars === undefined){
16454 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16457 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16458 * rendering into an Roo.Editor, defaults to false)
16461 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16462 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16465 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16468 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16469 * the dropdown list (defaults to undefined, with no header element)
16473 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16477 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16479 listWidth: undefined,
16481 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16482 * mode = 'remote' or 'text' if mode = 'local')
16484 displayField: undefined,
16487 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16488 * mode = 'remote' or 'value' if mode = 'local').
16489 * Note: use of a valueField requires the user make a selection
16490 * in order for a value to be mapped.
16492 valueField: undefined,
16494 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16499 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16500 * field's data value (defaults to the underlying DOM element's name)
16502 hiddenName: undefined,
16504 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16508 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16510 selectedClass: 'active',
16513 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16517 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16518 * anchor positions (defaults to 'tl-bl')
16520 listAlign: 'tl-bl?',
16522 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16526 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16527 * query specified by the allQuery config option (defaults to 'query')
16529 triggerAction: 'query',
16531 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16532 * (defaults to 4, does not apply if editable = false)
16536 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16537 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16541 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16542 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16546 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16547 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16551 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16552 * when editable = true (defaults to false)
16554 selectOnFocus:false,
16556 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16558 queryParam: 'query',
16560 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16561 * when mode = 'remote' (defaults to 'Loading...')
16563 loadingText: 'Loading...',
16565 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16569 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16573 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16574 * traditional select (defaults to true)
16578 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16582 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16586 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16587 * listWidth has a higher value)
16591 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16592 * allow the user to set arbitrary text into the field (defaults to false)
16594 forceSelection:false,
16596 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16597 * if typeAhead = true (defaults to 250)
16599 typeAheadDelay : 250,
16601 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16602 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16604 valueNotFoundText : undefined,
16606 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16608 blockFocus : false,
16611 * @cfg {Boolean} disableClear Disable showing of clear button.
16613 disableClear : false,
16615 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16617 alwaysQuery : false,
16620 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16625 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16627 invalidClass : "has-warning",
16630 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16632 validClass : "has-success",
16635 * @cfg {Boolean} specialFilter (true|false) special filter default false
16637 specialFilter : false,
16640 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16642 mobileTouchView : true,
16645 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16647 useNativeIOS : false,
16650 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16652 mobile_restrict_height : false,
16654 ios_options : false,
16666 btnPosition : 'right',
16667 triggerList : true,
16668 showToggleBtn : true,
16670 emptyResultText: 'Empty',
16671 triggerText : 'Select',
16675 // element that contains real text value.. (when hidden is used..)
16677 getAutoCreate : function()
16682 * Render classic select for iso
16685 if(Roo.isIOS && this.useNativeIOS){
16686 cfg = this.getAutoCreateNativeIOS();
16694 if(Roo.isTouch && this.mobileTouchView){
16695 cfg = this.getAutoCreateTouchView();
16702 if(!this.tickable){
16703 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16708 * ComboBox with tickable selections
16711 var align = this.labelAlign || this.parentLabelAlign();
16714 cls : 'form-group roo-combobox-tickable' //input-group
16717 var btn_text_select = '';
16718 var btn_text_done = '';
16719 var btn_text_cancel = '';
16721 if (this.btn_text_show) {
16722 btn_text_select = 'Select';
16723 btn_text_done = 'Done';
16724 btn_text_cancel = 'Cancel';
16729 cls : 'tickable-buttons',
16734 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16735 //html : this.triggerText
16736 html: btn_text_select
16742 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16744 html: btn_text_done
16750 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16752 html: btn_text_cancel
16758 buttons.cn.unshift({
16760 cls: 'roo-select2-search-field-input'
16766 Roo.each(buttons.cn, function(c){
16768 c.cls += ' btn-' + _this.size;
16771 if (_this.disabled) {
16778 style : 'display: contents',
16783 cls: 'form-hidden-field'
16787 cls: 'roo-select2-choices',
16791 cls: 'roo-select2-search-field',
16802 cls: 'roo-select2-container input-group roo-select2-container-multi',
16808 // cls: 'typeahead typeahead-long dropdown-menu',
16809 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16814 if(this.hasFeedback && !this.allowBlank){
16818 cls: 'glyphicon form-control-feedback'
16821 combobox.cn.push(feedback);
16828 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16829 tooltip : 'This field is required'
16831 if (Roo.bootstrap.version == 4) {
16834 style : 'display:none'
16837 if (align ==='left' && this.fieldLabel.length) {
16839 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16846 cls : 'control-label col-form-label',
16847 html : this.fieldLabel
16859 var labelCfg = cfg.cn[1];
16860 var contentCfg = cfg.cn[2];
16863 if(this.indicatorpos == 'right'){
16869 cls : 'control-label col-form-label',
16873 html : this.fieldLabel
16889 labelCfg = cfg.cn[0];
16890 contentCfg = cfg.cn[1];
16894 if(this.labelWidth > 12){
16895 labelCfg.style = "width: " + this.labelWidth + 'px';
16897 if(this.width * 1 > 0){
16898 contentCfg.style = "width: " + this.width + 'px';
16900 if(this.labelWidth < 13 && this.labelmd == 0){
16901 this.labelmd = this.labelWidth;
16904 if(this.labellg > 0){
16905 labelCfg.cls += ' col-lg-' + this.labellg;
16906 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16909 if(this.labelmd > 0){
16910 labelCfg.cls += ' col-md-' + this.labelmd;
16911 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16914 if(this.labelsm > 0){
16915 labelCfg.cls += ' col-sm-' + this.labelsm;
16916 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16919 if(this.labelxs > 0){
16920 labelCfg.cls += ' col-xs-' + this.labelxs;
16921 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16925 } else if ( this.fieldLabel.length) {
16926 // Roo.log(" label");
16931 //cls : 'input-group-addon',
16932 html : this.fieldLabel
16937 if(this.indicatorpos == 'right'){
16941 //cls : 'input-group-addon',
16942 html : this.fieldLabel
16952 // Roo.log(" no label && no align");
16959 ['xs','sm','md','lg'].map(function(size){
16960 if (settings[size]) {
16961 cfg.cls += ' col-' + size + '-' + settings[size];
16969 _initEventsCalled : false,
16972 initEvents: function()
16974 if (this._initEventsCalled) { // as we call render... prevent looping...
16977 this._initEventsCalled = true;
16980 throw "can not find store for combo";
16983 this.indicator = this.indicatorEl();
16985 this.store = Roo.factory(this.store, Roo.data);
16986 this.store.parent = this;
16988 // if we are building from html. then this element is so complex, that we can not really
16989 // use the rendered HTML.
16990 // so we have to trash and replace the previous code.
16991 if (Roo.XComponent.build_from_html) {
16992 // remove this element....
16993 var e = this.el.dom, k=0;
16994 while (e ) { e = e.previousSibling; ++k;}
16999 this.rendered = false;
17001 this.render(this.parent().getChildContainer(true), k);
17004 if(Roo.isIOS && this.useNativeIOS){
17005 this.initIOSView();
17013 if(Roo.isTouch && this.mobileTouchView){
17014 this.initTouchView();
17019 this.initTickableEvents();
17023 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17025 if(this.hiddenName){
17027 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17029 this.hiddenField.dom.value =
17030 this.hiddenValue !== undefined ? this.hiddenValue :
17031 this.value !== undefined ? this.value : '';
17033 // prevent input submission
17034 this.el.dom.removeAttribute('name');
17035 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17040 // this.el.dom.setAttribute('autocomplete', 'off');
17043 var cls = 'x-combo-list';
17045 //this.list = new Roo.Layer({
17046 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17052 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17053 _this.list.setWidth(lw);
17056 this.list.on('mouseover', this.onViewOver, this);
17057 this.list.on('mousemove', this.onViewMove, this);
17058 this.list.on('scroll', this.onViewScroll, this);
17061 this.list.swallowEvent('mousewheel');
17062 this.assetHeight = 0;
17065 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17066 this.assetHeight += this.header.getHeight();
17069 this.innerList = this.list.createChild({cls:cls+'-inner'});
17070 this.innerList.on('mouseover', this.onViewOver, this);
17071 this.innerList.on('mousemove', this.onViewMove, this);
17072 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17074 if(this.allowBlank && !this.pageSize && !this.disableClear){
17075 this.footer = this.list.createChild({cls:cls+'-ft'});
17076 this.pageTb = new Roo.Toolbar(this.footer);
17080 this.footer = this.list.createChild({cls:cls+'-ft'});
17081 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17082 {pageSize: this.pageSize});
17086 if (this.pageTb && this.allowBlank && !this.disableClear) {
17088 this.pageTb.add(new Roo.Toolbar.Fill(), {
17089 cls: 'x-btn-icon x-btn-clear',
17091 handler: function()
17094 _this.clearValue();
17095 _this.onSelect(false, -1);
17100 this.assetHeight += this.footer.getHeight();
17105 this.tpl = Roo.bootstrap.version == 4 ?
17106 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17107 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17110 this.view = new Roo.View(this.list, this.tpl, {
17111 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17113 //this.view.wrapEl.setDisplayed(false);
17114 this.view.on('click', this.onViewClick, this);
17117 this.store.on('beforeload', this.onBeforeLoad, this);
17118 this.store.on('load', this.onLoad, this);
17119 this.store.on('loadexception', this.onLoadException, this);
17121 if(this.resizable){
17122 this.resizer = new Roo.Resizable(this.list, {
17123 pinned:true, handles:'se'
17125 this.resizer.on('resize', function(r, w, h){
17126 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17127 this.listWidth = w;
17128 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17129 this.restrictHeight();
17131 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17134 if(!this.editable){
17135 this.editable = true;
17136 this.setEditable(false);
17141 if (typeof(this.events.add.listeners) != 'undefined') {
17143 this.addicon = this.wrap.createChild(
17144 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17146 this.addicon.on('click', function(e) {
17147 this.fireEvent('add', this);
17150 if (typeof(this.events.edit.listeners) != 'undefined') {
17152 this.editicon = this.wrap.createChild(
17153 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17154 if (this.addicon) {
17155 this.editicon.setStyle('margin-left', '40px');
17157 this.editicon.on('click', function(e) {
17159 // we fire even if inothing is selected..
17160 this.fireEvent('edit', this, this.lastData );
17166 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17167 "up" : function(e){
17168 this.inKeyMode = true;
17172 "down" : function(e){
17173 if(!this.isExpanded()){
17174 this.onTriggerClick();
17176 this.inKeyMode = true;
17181 "enter" : function(e){
17182 // this.onViewClick();
17186 if(this.fireEvent("specialkey", this, e)){
17187 this.onViewClick(false);
17193 "esc" : function(e){
17197 "tab" : function(e){
17200 if(this.fireEvent("specialkey", this, e)){
17201 this.onViewClick(false);
17209 doRelay : function(foo, bar, hname){
17210 if(hname == 'down' || this.scope.isExpanded()){
17211 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17220 this.queryDelay = Math.max(this.queryDelay || 10,
17221 this.mode == 'local' ? 10 : 250);
17224 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17226 if(this.typeAhead){
17227 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17229 if(this.editable !== false){
17230 this.inputEl().on("keyup", this.onKeyUp, this);
17232 if(this.forceSelection){
17233 this.inputEl().on('blur', this.doForce, this);
17237 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17238 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17242 initTickableEvents: function()
17246 if(this.hiddenName){
17248 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17250 this.hiddenField.dom.value =
17251 this.hiddenValue !== undefined ? this.hiddenValue :
17252 this.value !== undefined ? this.value : '';
17254 // prevent input submission
17255 this.el.dom.removeAttribute('name');
17256 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17261 // this.list = this.el.select('ul.dropdown-menu',true).first();
17263 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17264 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17265 if(this.triggerList){
17266 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17269 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17270 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17272 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17273 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17275 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17276 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17278 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17279 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17280 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17283 this.cancelBtn.hide();
17288 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17289 _this.list.setWidth(lw);
17292 this.list.on('mouseover', this.onViewOver, this);
17293 this.list.on('mousemove', this.onViewMove, this);
17295 this.list.on('scroll', this.onViewScroll, this);
17298 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17299 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17302 this.view = new Roo.View(this.list, this.tpl, {
17307 selectedClass: this.selectedClass
17310 //this.view.wrapEl.setDisplayed(false);
17311 this.view.on('click', this.onViewClick, this);
17315 this.store.on('beforeload', this.onBeforeLoad, this);
17316 this.store.on('load', this.onLoad, this);
17317 this.store.on('loadexception', this.onLoadException, this);
17320 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17321 "up" : function(e){
17322 this.inKeyMode = true;
17326 "down" : function(e){
17327 this.inKeyMode = true;
17331 "enter" : function(e){
17332 if(this.fireEvent("specialkey", this, e)){
17333 this.onViewClick(false);
17339 "esc" : function(e){
17340 this.onTickableFooterButtonClick(e, false, false);
17343 "tab" : function(e){
17344 this.fireEvent("specialkey", this, e);
17346 this.onTickableFooterButtonClick(e, false, false);
17353 doRelay : function(e, fn, key){
17354 if(this.scope.isExpanded()){
17355 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17364 this.queryDelay = Math.max(this.queryDelay || 10,
17365 this.mode == 'local' ? 10 : 250);
17368 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17370 if(this.typeAhead){
17371 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17374 if(this.editable !== false){
17375 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17378 this.indicator = this.indicatorEl();
17380 if(this.indicator){
17381 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17382 this.indicator.hide();
17387 onDestroy : function(){
17389 this.view.setStore(null);
17390 this.view.el.removeAllListeners();
17391 this.view.el.remove();
17392 this.view.purgeListeners();
17395 this.list.dom.innerHTML = '';
17399 this.store.un('beforeload', this.onBeforeLoad, this);
17400 this.store.un('load', this.onLoad, this);
17401 this.store.un('loadexception', this.onLoadException, this);
17403 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17407 fireKey : function(e){
17408 if(e.isNavKeyPress() && !this.list.isVisible()){
17409 this.fireEvent("specialkey", this, e);
17414 onResize: function(w, h)
17418 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17420 // if(typeof w != 'number'){
17421 // // we do not handle it!?!?
17424 // var tw = this.trigger.getWidth();
17425 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17426 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17428 // this.inputEl().setWidth( this.adjustWidth('input', x));
17430 // //this.trigger.setStyle('left', x+'px');
17432 // if(this.list && this.listWidth === undefined){
17433 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17434 // this.list.setWidth(lw);
17435 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17443 * Allow or prevent the user from directly editing the field text. If false is passed,
17444 * the user will only be able to select from the items defined in the dropdown list. This method
17445 * is the runtime equivalent of setting the 'editable' config option at config time.
17446 * @param {Boolean} value True to allow the user to directly edit the field text
17448 setEditable : function(value){
17449 if(value == this.editable){
17452 this.editable = value;
17454 this.inputEl().dom.setAttribute('readOnly', true);
17455 this.inputEl().on('mousedown', this.onTriggerClick, this);
17456 this.inputEl().addClass('x-combo-noedit');
17458 this.inputEl().dom.removeAttribute('readOnly');
17459 this.inputEl().un('mousedown', this.onTriggerClick, this);
17460 this.inputEl().removeClass('x-combo-noedit');
17466 onBeforeLoad : function(combo,opts){
17467 if(!this.hasFocus){
17471 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17473 this.restrictHeight();
17474 this.selectedIndex = -1;
17478 onLoad : function(){
17480 this.hasQuery = false;
17482 if(!this.hasFocus){
17486 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17487 this.loading.hide();
17490 if(this.store.getCount() > 0){
17493 this.restrictHeight();
17494 if(this.lastQuery == this.allQuery){
17495 if(this.editable && !this.tickable){
17496 this.inputEl().dom.select();
17500 !this.selectByValue(this.value, true) &&
17503 !this.store.lastOptions ||
17504 typeof(this.store.lastOptions.add) == 'undefined' ||
17505 this.store.lastOptions.add != true
17508 this.select(0, true);
17511 if(this.autoFocus){
17514 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17515 this.taTask.delay(this.typeAheadDelay);
17519 this.onEmptyResults();
17525 onLoadException : function()
17527 this.hasQuery = false;
17529 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17530 this.loading.hide();
17533 if(this.tickable && this.editable){
17538 // only causes errors at present
17539 //Roo.log(this.store.reader.jsonData);
17540 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17542 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17548 onTypeAhead : function(){
17549 if(this.store.getCount() > 0){
17550 var r = this.store.getAt(0);
17551 var newValue = r.data[this.displayField];
17552 var len = newValue.length;
17553 var selStart = this.getRawValue().length;
17555 if(selStart != len){
17556 this.setRawValue(newValue);
17557 this.selectText(selStart, newValue.length);
17563 onSelect : function(record, index){
17565 if(this.fireEvent('beforeselect', this, record, index) !== false){
17567 this.setFromData(index > -1 ? record.data : false);
17570 this.fireEvent('select', this, record, index);
17575 * Returns the currently selected field value or empty string if no value is set.
17576 * @return {String} value The selected value
17578 getValue : function()
17580 if(Roo.isIOS && this.useNativeIOS){
17581 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17585 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17588 if(this.valueField){
17589 return typeof this.value != 'undefined' ? this.value : '';
17591 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17595 getRawValue : function()
17597 if(Roo.isIOS && this.useNativeIOS){
17598 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17601 var v = this.inputEl().getValue();
17607 * Clears any text/value currently set in the field
17609 clearValue : function(){
17611 if(this.hiddenField){
17612 this.hiddenField.dom.value = '';
17615 this.setRawValue('');
17616 this.lastSelectionText = '';
17617 this.lastData = false;
17619 var close = this.closeTriggerEl();
17630 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17631 * will be displayed in the field. If the value does not match the data value of an existing item,
17632 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17633 * Otherwise the field will be blank (although the value will still be set).
17634 * @param {String} value The value to match
17636 setValue : function(v)
17638 if(Roo.isIOS && this.useNativeIOS){
17639 this.setIOSValue(v);
17649 if(this.valueField){
17650 var r = this.findRecord(this.valueField, v);
17652 text = r.data[this.displayField];
17653 }else if(this.valueNotFoundText !== undefined){
17654 text = this.valueNotFoundText;
17657 this.lastSelectionText = text;
17658 if(this.hiddenField){
17659 this.hiddenField.dom.value = v;
17661 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17664 var close = this.closeTriggerEl();
17667 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17673 * @property {Object} the last set data for the element
17678 * Sets the value of the field based on a object which is related to the record format for the store.
17679 * @param {Object} value the value to set as. or false on reset?
17681 setFromData : function(o){
17688 var dv = ''; // display value
17689 var vv = ''; // value value..
17691 if (this.displayField) {
17692 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17694 // this is an error condition!!!
17695 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17698 if(this.valueField){
17699 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17702 var close = this.closeTriggerEl();
17705 if(dv.length || vv * 1 > 0){
17707 this.blockFocus=true;
17713 if(this.hiddenField){
17714 this.hiddenField.dom.value = vv;
17716 this.lastSelectionText = dv;
17717 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17721 // no hidden field.. - we store the value in 'value', but still display
17722 // display field!!!!
17723 this.lastSelectionText = dv;
17724 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17731 reset : function(){
17732 // overridden so that last data is reset..
17739 this.setValue(this.originalValue);
17740 //this.clearInvalid();
17741 this.lastData = false;
17743 this.view.clearSelections();
17749 findRecord : function(prop, value){
17751 if(this.store.getCount() > 0){
17752 this.store.each(function(r){
17753 if(r.data[prop] == value){
17763 getName: function()
17765 // returns hidden if it's set..
17766 if (!this.rendered) {return ''};
17767 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17771 onViewMove : function(e, t){
17772 this.inKeyMode = false;
17776 onViewOver : function(e, t){
17777 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17780 var item = this.view.findItemFromChild(t);
17783 var index = this.view.indexOf(item);
17784 this.select(index, false);
17789 onViewClick : function(view, doFocus, el, e)
17791 var index = this.view.getSelectedIndexes()[0];
17793 var r = this.store.getAt(index);
17797 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17804 Roo.each(this.tickItems, function(v,k){
17806 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17808 _this.tickItems.splice(k, 1);
17810 if(typeof(e) == 'undefined' && view == false){
17811 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17823 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17824 this.tickItems.push(r.data);
17827 if(typeof(e) == 'undefined' && view == false){
17828 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17835 this.onSelect(r, index);
17837 if(doFocus !== false && !this.blockFocus){
17838 this.inputEl().focus();
17843 restrictHeight : function(){
17844 //this.innerList.dom.style.height = '';
17845 //var inner = this.innerList.dom;
17846 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17847 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17848 //this.list.beginUpdate();
17849 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17850 this.list.alignTo(this.inputEl(), this.listAlign);
17851 this.list.alignTo(this.inputEl(), this.listAlign);
17852 //this.list.endUpdate();
17856 onEmptyResults : function(){
17858 if(this.tickable && this.editable){
17859 this.hasFocus = false;
17860 this.restrictHeight();
17868 * Returns true if the dropdown list is expanded, else false.
17870 isExpanded : function(){
17871 return this.list.isVisible();
17875 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17876 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17877 * @param {String} value The data value of the item to select
17878 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17879 * selected item if it is not currently in view (defaults to true)
17880 * @return {Boolean} True if the value matched an item in the list, else false
17882 selectByValue : function(v, scrollIntoView){
17883 if(v !== undefined && v !== null){
17884 var r = this.findRecord(this.valueField || this.displayField, v);
17886 this.select(this.store.indexOf(r), scrollIntoView);
17894 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17895 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17896 * @param {Number} index The zero-based index of the list item to select
17897 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17898 * selected item if it is not currently in view (defaults to true)
17900 select : function(index, scrollIntoView){
17901 this.selectedIndex = index;
17902 this.view.select(index);
17903 if(scrollIntoView !== false){
17904 var el = this.view.getNode(index);
17906 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17909 this.list.scrollChildIntoView(el, false);
17915 selectNext : function(){
17916 var ct = this.store.getCount();
17918 if(this.selectedIndex == -1){
17920 }else if(this.selectedIndex < ct-1){
17921 this.select(this.selectedIndex+1);
17927 selectPrev : function(){
17928 var ct = this.store.getCount();
17930 if(this.selectedIndex == -1){
17932 }else if(this.selectedIndex != 0){
17933 this.select(this.selectedIndex-1);
17939 onKeyUp : function(e){
17940 if(this.editable !== false && !e.isSpecialKey()){
17941 this.lastKey = e.getKey();
17942 this.dqTask.delay(this.queryDelay);
17947 validateBlur : function(){
17948 return !this.list || !this.list.isVisible();
17952 initQuery : function(){
17954 var v = this.getRawValue();
17956 if(this.tickable && this.editable){
17957 v = this.tickableInputEl().getValue();
17964 doForce : function(){
17965 if(this.inputEl().dom.value.length > 0){
17966 this.inputEl().dom.value =
17967 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17973 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17974 * query allowing the query action to be canceled if needed.
17975 * @param {String} query The SQL query to execute
17976 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17977 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17978 * saved in the current store (defaults to false)
17980 doQuery : function(q, forceAll){
17982 if(q === undefined || q === null){
17987 forceAll: forceAll,
17991 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
17996 forceAll = qe.forceAll;
17997 if(forceAll === true || (q.length >= this.minChars)){
17999 this.hasQuery = true;
18001 if(this.lastQuery != q || this.alwaysQuery){
18002 this.lastQuery = q;
18003 if(this.mode == 'local'){
18004 this.selectedIndex = -1;
18006 this.store.clearFilter();
18009 if(this.specialFilter){
18010 this.fireEvent('specialfilter', this);
18015 this.store.filter(this.displayField, q);
18018 this.store.fireEvent("datachanged", this.store);
18025 this.store.baseParams[this.queryParam] = q;
18027 var options = {params : this.getParams(q)};
18030 options.add = true;
18031 options.params.start = this.page * this.pageSize;
18034 this.store.load(options);
18037 * this code will make the page width larger, at the beginning, the list not align correctly,
18038 * we should expand the list on onLoad
18039 * so command out it
18044 this.selectedIndex = -1;
18049 this.loadNext = false;
18053 getParams : function(q){
18055 //p[this.queryParam] = q;
18059 p.limit = this.pageSize;
18065 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18067 collapse : function(){
18068 if(!this.isExpanded()){
18074 this.hasFocus = false;
18078 this.cancelBtn.hide();
18079 this.trigger.show();
18082 this.tickableInputEl().dom.value = '';
18083 this.tickableInputEl().blur();
18088 Roo.get(document).un('mousedown', this.collapseIf, this);
18089 Roo.get(document).un('mousewheel', this.collapseIf, this);
18090 if (!this.editable) {
18091 Roo.get(document).un('keydown', this.listKeyPress, this);
18093 this.fireEvent('collapse', this);
18099 collapseIf : function(e){
18100 var in_combo = e.within(this.el);
18101 var in_list = e.within(this.list);
18102 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18104 if (in_combo || in_list || is_list) {
18105 //e.stopPropagation();
18110 this.onTickableFooterButtonClick(e, false, false);
18118 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18120 expand : function(){
18122 if(this.isExpanded() || !this.hasFocus){
18126 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18127 this.list.setWidth(lw);
18133 this.restrictHeight();
18137 this.tickItems = Roo.apply([], this.item);
18140 this.cancelBtn.show();
18141 this.trigger.hide();
18144 this.tickableInputEl().focus();
18149 Roo.get(document).on('mousedown', this.collapseIf, this);
18150 Roo.get(document).on('mousewheel', this.collapseIf, this);
18151 if (!this.editable) {
18152 Roo.get(document).on('keydown', this.listKeyPress, this);
18155 this.fireEvent('expand', this);
18159 // Implements the default empty TriggerField.onTriggerClick function
18160 onTriggerClick : function(e)
18162 Roo.log('trigger click');
18164 if(this.disabled || !this.triggerList){
18169 this.loadNext = false;
18171 if(this.isExpanded()){
18173 if (!this.blockFocus) {
18174 this.inputEl().focus();
18178 this.hasFocus = true;
18179 if(this.triggerAction == 'all') {
18180 this.doQuery(this.allQuery, true);
18182 this.doQuery(this.getRawValue());
18184 if (!this.blockFocus) {
18185 this.inputEl().focus();
18190 onTickableTriggerClick : function(e)
18197 this.loadNext = false;
18198 this.hasFocus = true;
18200 if(this.triggerAction == 'all') {
18201 this.doQuery(this.allQuery, true);
18203 this.doQuery(this.getRawValue());
18207 onSearchFieldClick : function(e)
18209 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18210 this.onTickableFooterButtonClick(e, false, false);
18214 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18219 this.loadNext = false;
18220 this.hasFocus = true;
18222 if(this.triggerAction == 'all') {
18223 this.doQuery(this.allQuery, true);
18225 this.doQuery(this.getRawValue());
18229 listKeyPress : function(e)
18231 //Roo.log('listkeypress');
18232 // scroll to first matching element based on key pres..
18233 if (e.isSpecialKey()) {
18236 var k = String.fromCharCode(e.getKey()).toUpperCase();
18239 var csel = this.view.getSelectedNodes();
18240 var cselitem = false;
18242 var ix = this.view.indexOf(csel[0]);
18243 cselitem = this.store.getAt(ix);
18244 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18250 this.store.each(function(v) {
18252 // start at existing selection.
18253 if (cselitem.id == v.id) {
18259 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18260 match = this.store.indexOf(v);
18266 if (match === false) {
18267 return true; // no more action?
18270 this.view.select(match);
18271 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18272 sn.scrollIntoView(sn.dom.parentNode, false);
18275 onViewScroll : function(e, t){
18277 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){
18281 this.hasQuery = true;
18283 this.loading = this.list.select('.loading', true).first();
18285 if(this.loading === null){
18286 this.list.createChild({
18288 cls: 'loading roo-select2-more-results roo-select2-active',
18289 html: 'Loading more results...'
18292 this.loading = this.list.select('.loading', true).first();
18294 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18296 this.loading.hide();
18299 this.loading.show();
18304 this.loadNext = true;
18306 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18311 addItem : function(o)
18313 var dv = ''; // display value
18315 if (this.displayField) {
18316 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18318 // this is an error condition!!!
18319 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18326 var choice = this.choices.createChild({
18328 cls: 'roo-select2-search-choice',
18337 cls: 'roo-select2-search-choice-close fa fa-times',
18342 }, this.searchField);
18344 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18346 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18354 this.inputEl().dom.value = '';
18359 onRemoveItem : function(e, _self, o)
18361 e.preventDefault();
18363 this.lastItem = Roo.apply([], this.item);
18365 var index = this.item.indexOf(o.data) * 1;
18368 Roo.log('not this item?!');
18372 this.item.splice(index, 1);
18377 this.fireEvent('remove', this, e);
18383 syncValue : function()
18385 if(!this.item.length){
18392 Roo.each(this.item, function(i){
18393 if(_this.valueField){
18394 value.push(i[_this.valueField]);
18401 this.value = value.join(',');
18403 if(this.hiddenField){
18404 this.hiddenField.dom.value = this.value;
18407 this.store.fireEvent("datachanged", this.store);
18412 clearItem : function()
18414 if(!this.multiple){
18420 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18428 if(this.tickable && !Roo.isTouch){
18429 this.view.refresh();
18433 inputEl: function ()
18435 if(Roo.isIOS && this.useNativeIOS){
18436 return this.el.select('select.roo-ios-select', true).first();
18439 if(Roo.isTouch && this.mobileTouchView){
18440 return this.el.select('input.form-control',true).first();
18444 return this.searchField;
18447 return this.el.select('input.form-control',true).first();
18450 onTickableFooterButtonClick : function(e, btn, el)
18452 e.preventDefault();
18454 this.lastItem = Roo.apply([], this.item);
18456 if(btn && btn.name == 'cancel'){
18457 this.tickItems = Roo.apply([], this.item);
18466 Roo.each(this.tickItems, function(o){
18474 validate : function()
18476 if(this.getVisibilityEl().hasClass('hidden')){
18480 var v = this.getRawValue();
18483 v = this.getValue();
18486 if(this.disabled || this.allowBlank || v.length){
18491 this.markInvalid();
18495 tickableInputEl : function()
18497 if(!this.tickable || !this.editable){
18498 return this.inputEl();
18501 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18505 getAutoCreateTouchView : function()
18510 cls: 'form-group' //input-group
18516 type : this.inputType,
18517 cls : 'form-control x-combo-noedit',
18518 autocomplete: 'new-password',
18519 placeholder : this.placeholder || '',
18524 input.name = this.name;
18528 input.cls += ' input-' + this.size;
18531 if (this.disabled) {
18532 input.disabled = true;
18536 cls : 'roo-combobox-wrap',
18543 inputblock.cls += ' input-group';
18545 inputblock.cn.unshift({
18547 cls : 'input-group-addon input-group-prepend input-group-text',
18552 if(this.removable && !this.multiple){
18553 inputblock.cls += ' roo-removable';
18555 inputblock.cn.push({
18558 cls : 'roo-combo-removable-btn close'
18562 if(this.hasFeedback && !this.allowBlank){
18564 inputblock.cls += ' has-feedback';
18566 inputblock.cn.push({
18568 cls: 'glyphicon form-control-feedback'
18575 inputblock.cls += (this.before) ? '' : ' input-group';
18577 inputblock.cn.push({
18579 cls : 'input-group-addon input-group-append input-group-text',
18585 var ibwrap = inputblock;
18590 cls: 'roo-select2-choices',
18594 cls: 'roo-select2-search-field',
18607 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18612 cls: 'form-hidden-field'
18618 if(!this.multiple && this.showToggleBtn){
18624 if (this.caret != false) {
18627 cls: 'fa fa-' + this.caret
18634 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18636 Roo.bootstrap.version == 3 ? caret : '',
18639 cls: 'combobox-clear',
18653 combobox.cls += ' roo-select2-container-multi';
18656 var required = this.allowBlank ? {
18658 style: 'display: none'
18661 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18662 tooltip : 'This field is required'
18665 var align = this.labelAlign || this.parentLabelAlign();
18667 if (align ==='left' && this.fieldLabel.length) {
18673 cls : 'control-label col-form-label',
18674 html : this.fieldLabel
18678 cls : 'roo-combobox-wrap ',
18685 var labelCfg = cfg.cn[1];
18686 var contentCfg = cfg.cn[2];
18689 if(this.indicatorpos == 'right'){
18694 cls : 'control-label col-form-label',
18698 html : this.fieldLabel
18704 cls : "roo-combobox-wrap ",
18712 labelCfg = cfg.cn[0];
18713 contentCfg = cfg.cn[1];
18718 if(this.labelWidth > 12){
18719 labelCfg.style = "width: " + this.labelWidth + 'px';
18722 if(this.labelWidth < 13 && this.labelmd == 0){
18723 this.labelmd = this.labelWidth;
18726 if(this.labellg > 0){
18727 labelCfg.cls += ' col-lg-' + this.labellg;
18728 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18731 if(this.labelmd > 0){
18732 labelCfg.cls += ' col-md-' + this.labelmd;
18733 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18736 if(this.labelsm > 0){
18737 labelCfg.cls += ' col-sm-' + this.labelsm;
18738 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18741 if(this.labelxs > 0){
18742 labelCfg.cls += ' col-xs-' + this.labelxs;
18743 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18747 } else if ( this.fieldLabel.length) {
18752 cls : 'control-label',
18753 html : this.fieldLabel
18764 if(this.indicatorpos == 'right'){
18768 cls : 'control-label',
18769 html : this.fieldLabel,
18787 var settings = this;
18789 ['xs','sm','md','lg'].map(function(size){
18790 if (settings[size]) {
18791 cfg.cls += ' col-' + size + '-' + settings[size];
18798 initTouchView : function()
18800 this.renderTouchView();
18802 this.touchViewEl.on('scroll', function(){
18803 this.el.dom.scrollTop = 0;
18806 this.originalValue = this.getValue();
18808 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18810 this.inputEl().on("click", this.showTouchView, this);
18811 if (this.triggerEl) {
18812 this.triggerEl.on("click", this.showTouchView, this);
18816 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18817 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18819 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18821 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18822 this.store.on('load', this.onTouchViewLoad, this);
18823 this.store.on('loadexception', this.onTouchViewLoadException, this);
18825 if(this.hiddenName){
18827 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18829 this.hiddenField.dom.value =
18830 this.hiddenValue !== undefined ? this.hiddenValue :
18831 this.value !== undefined ? this.value : '';
18833 this.el.dom.removeAttribute('name');
18834 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18838 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18839 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18842 if(this.removable && !this.multiple){
18843 var close = this.closeTriggerEl();
18845 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18846 close.on('click', this.removeBtnClick, this, close);
18850 * fix the bug in Safari iOS8
18852 this.inputEl().on("focus", function(e){
18853 document.activeElement.blur();
18856 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18863 renderTouchView : function()
18865 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18866 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18868 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18869 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18871 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18872 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18873 this.touchViewBodyEl.setStyle('overflow', 'auto');
18875 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18876 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18878 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18879 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18883 showTouchView : function()
18889 this.touchViewHeaderEl.hide();
18891 if(this.modalTitle.length){
18892 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18893 this.touchViewHeaderEl.show();
18896 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18897 this.touchViewEl.show();
18899 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18901 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18902 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18904 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18906 if(this.modalTitle.length){
18907 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18910 this.touchViewBodyEl.setHeight(bodyHeight);
18914 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18916 this.touchViewEl.addClass(['in','show']);
18919 if(this._touchViewMask){
18920 Roo.get(document.body).addClass("x-body-masked");
18921 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18922 this._touchViewMask.setStyle('z-index', 10000);
18923 this._touchViewMask.addClass('show');
18926 this.doTouchViewQuery();
18930 hideTouchView : function()
18932 this.touchViewEl.removeClass(['in','show']);
18936 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18938 this.touchViewEl.setStyle('display', 'none');
18941 if(this._touchViewMask){
18942 this._touchViewMask.removeClass('show');
18943 Roo.get(document.body).removeClass("x-body-masked");
18947 setTouchViewValue : function()
18954 Roo.each(this.tickItems, function(o){
18959 this.hideTouchView();
18962 doTouchViewQuery : function()
18971 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18975 if(!this.alwaysQuery || this.mode == 'local'){
18976 this.onTouchViewLoad();
18983 onTouchViewBeforeLoad : function(combo,opts)
18989 onTouchViewLoad : function()
18991 if(this.store.getCount() < 1){
18992 this.onTouchViewEmptyResults();
18996 this.clearTouchView();
18998 var rawValue = this.getRawValue();
19000 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19002 this.tickItems = [];
19004 this.store.data.each(function(d, rowIndex){
19005 var row = this.touchViewListGroup.createChild(template);
19007 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19008 row.addClass(d.data.cls);
19011 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19014 html : d.data[this.displayField]
19017 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19018 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19021 row.removeClass('selected');
19022 if(!this.multiple && this.valueField &&
19023 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19026 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19027 row.addClass('selected');
19030 if(this.multiple && this.valueField &&
19031 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19035 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19036 this.tickItems.push(d.data);
19039 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19043 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19045 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19047 if(this.modalTitle.length){
19048 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19051 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19053 if(this.mobile_restrict_height && listHeight < bodyHeight){
19054 this.touchViewBodyEl.setHeight(listHeight);
19059 if(firstChecked && listHeight > bodyHeight){
19060 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19065 onTouchViewLoadException : function()
19067 this.hideTouchView();
19070 onTouchViewEmptyResults : function()
19072 this.clearTouchView();
19074 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19076 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19080 clearTouchView : function()
19082 this.touchViewListGroup.dom.innerHTML = '';
19085 onTouchViewClick : function(e, el, o)
19087 e.preventDefault();
19090 var rowIndex = o.rowIndex;
19092 var r = this.store.getAt(rowIndex);
19094 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19096 if(!this.multiple){
19097 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19098 c.dom.removeAttribute('checked');
19101 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19103 this.setFromData(r.data);
19105 var close = this.closeTriggerEl();
19111 this.hideTouchView();
19113 this.fireEvent('select', this, r, rowIndex);
19118 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19119 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19120 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19124 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19125 this.addItem(r.data);
19126 this.tickItems.push(r.data);
19130 getAutoCreateNativeIOS : function()
19133 cls: 'form-group' //input-group,
19138 cls : 'roo-ios-select'
19142 combobox.name = this.name;
19145 if (this.disabled) {
19146 combobox.disabled = true;
19149 var settings = this;
19151 ['xs','sm','md','lg'].map(function(size){
19152 if (settings[size]) {
19153 cfg.cls += ' col-' + size + '-' + settings[size];
19163 initIOSView : function()
19165 this.store.on('load', this.onIOSViewLoad, this);
19170 onIOSViewLoad : function()
19172 if(this.store.getCount() < 1){
19176 this.clearIOSView();
19178 if(this.allowBlank) {
19180 var default_text = '-- SELECT --';
19182 if(this.placeholder.length){
19183 default_text = this.placeholder;
19186 if(this.emptyTitle.length){
19187 default_text += ' - ' + this.emptyTitle + ' -';
19190 var opt = this.inputEl().createChild({
19193 html : default_text
19197 o[this.valueField] = 0;
19198 o[this.displayField] = default_text;
19200 this.ios_options.push({
19207 this.store.data.each(function(d, rowIndex){
19211 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19212 html = d.data[this.displayField];
19217 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19218 value = d.data[this.valueField];
19227 if(this.value == d.data[this.valueField]){
19228 option['selected'] = true;
19231 var opt = this.inputEl().createChild(option);
19233 this.ios_options.push({
19240 this.inputEl().on('change', function(){
19241 this.fireEvent('select', this);
19246 clearIOSView: function()
19248 this.inputEl().dom.innerHTML = '';
19250 this.ios_options = [];
19253 setIOSValue: function(v)
19257 if(!this.ios_options){
19261 Roo.each(this.ios_options, function(opts){
19263 opts.el.dom.removeAttribute('selected');
19265 if(opts.data[this.valueField] != v){
19269 opts.el.dom.setAttribute('selected', true);
19275 * @cfg {Boolean} grow
19279 * @cfg {Number} growMin
19283 * @cfg {Number} growMax
19292 Roo.apply(Roo.bootstrap.ComboBox, {
19296 cls: 'modal-header',
19318 cls: 'list-group-item',
19322 cls: 'roo-combobox-list-group-item-value'
19326 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19340 listItemCheckbox : {
19342 cls: 'list-group-item',
19346 cls: 'roo-combobox-list-group-item-value'
19350 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19366 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19371 cls: 'modal-footer',
19379 cls: 'col-xs-6 text-left',
19382 cls: 'btn btn-danger roo-touch-view-cancel',
19388 cls: 'col-xs-6 text-right',
19391 cls: 'btn btn-success roo-touch-view-ok',
19402 Roo.apply(Roo.bootstrap.ComboBox, {
19404 touchViewTemplate : {
19406 cls: 'modal fade roo-combobox-touch-view',
19410 cls: 'modal-dialog',
19411 style : 'position:fixed', // we have to fix position....
19415 cls: 'modal-content',
19417 Roo.bootstrap.ComboBox.header,
19418 Roo.bootstrap.ComboBox.body,
19419 Roo.bootstrap.ComboBox.footer
19428 * Ext JS Library 1.1.1
19429 * Copyright(c) 2006-2007, Ext JS, LLC.
19431 * Originally Released Under LGPL - original licence link has changed is not relivant.
19434 * <script type="text/javascript">
19439 * @extends Roo.util.Observable
19440 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19441 * This class also supports single and multi selection modes. <br>
19442 * Create a data model bound view:
19444 var store = new Roo.data.Store(...);
19446 var view = new Roo.View({
19448 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19450 singleSelect: true,
19451 selectedClass: "ydataview-selected",
19455 // listen for node click?
19456 view.on("click", function(vw, index, node, e){
19457 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19461 dataModel.load("foobar.xml");
19463 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19465 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19466 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19468 * Note: old style constructor is still suported (container, template, config)
19471 * Create a new View
19472 * @param {Object} config The config object
19475 Roo.View = function(config, depreciated_tpl, depreciated_config){
19477 this.parent = false;
19479 if (typeof(depreciated_tpl) == 'undefined') {
19480 // new way.. - universal constructor.
19481 Roo.apply(this, config);
19482 this.el = Roo.get(this.el);
19485 this.el = Roo.get(config);
19486 this.tpl = depreciated_tpl;
19487 Roo.apply(this, depreciated_config);
19489 this.wrapEl = this.el.wrap().wrap();
19490 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19493 if(typeof(this.tpl) == "string"){
19494 this.tpl = new Roo.Template(this.tpl);
19496 // support xtype ctors..
19497 this.tpl = new Roo.factory(this.tpl, Roo);
19501 this.tpl.compile();
19506 * @event beforeclick
19507 * Fires before a click is processed. Returns false to cancel the default action.
19508 * @param {Roo.View} this
19509 * @param {Number} index The index of the target node
19510 * @param {HTMLElement} node The target node
19511 * @param {Roo.EventObject} e The raw event object
19513 "beforeclick" : true,
19516 * Fires when a template node is clicked.
19517 * @param {Roo.View} this
19518 * @param {Number} index The index of the target node
19519 * @param {HTMLElement} node The target node
19520 * @param {Roo.EventObject} e The raw event object
19525 * Fires when a template node is double clicked.
19526 * @param {Roo.View} this
19527 * @param {Number} index The index of the target node
19528 * @param {HTMLElement} node The target node
19529 * @param {Roo.EventObject} e The raw event object
19533 * @event contextmenu
19534 * Fires when a template node is right clicked.
19535 * @param {Roo.View} this
19536 * @param {Number} index The index of the target node
19537 * @param {HTMLElement} node The target node
19538 * @param {Roo.EventObject} e The raw event object
19540 "contextmenu" : true,
19542 * @event selectionchange
19543 * Fires when the selected nodes change.
19544 * @param {Roo.View} this
19545 * @param {Array} selections Array of the selected nodes
19547 "selectionchange" : true,
19550 * @event beforeselect
19551 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19552 * @param {Roo.View} this
19553 * @param {HTMLElement} node The node to be selected
19554 * @param {Array} selections Array of currently selected nodes
19556 "beforeselect" : true,
19558 * @event preparedata
19559 * Fires on every row to render, to allow you to change the data.
19560 * @param {Roo.View} this
19561 * @param {Object} data to be rendered (change this)
19563 "preparedata" : true
19571 "click": this.onClick,
19572 "dblclick": this.onDblClick,
19573 "contextmenu": this.onContextMenu,
19577 this.selections = [];
19579 this.cmp = new Roo.CompositeElementLite([]);
19581 this.store = Roo.factory(this.store, Roo.data);
19582 this.setStore(this.store, true);
19585 if ( this.footer && this.footer.xtype) {
19587 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19589 this.footer.dataSource = this.store;
19590 this.footer.container = fctr;
19591 this.footer = Roo.factory(this.footer, Roo);
19592 fctr.insertFirst(this.el);
19594 // this is a bit insane - as the paging toolbar seems to detach the el..
19595 // dom.parentNode.parentNode.parentNode
19596 // they get detached?
19600 Roo.View.superclass.constructor.call(this);
19605 Roo.extend(Roo.View, Roo.util.Observable, {
19608 * @cfg {Roo.data.Store} store Data store to load data from.
19613 * @cfg {String|Roo.Element} el The container element.
19618 * @cfg {String|Roo.Template} tpl The template used by this View
19622 * @cfg {String} dataName the named area of the template to use as the data area
19623 * Works with domtemplates roo-name="name"
19627 * @cfg {String} selectedClass The css class to add to selected nodes
19629 selectedClass : "x-view-selected",
19631 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19636 * @cfg {String} text to display on mask (default Loading)
19640 * @cfg {Boolean} multiSelect Allow multiple selection
19642 multiSelect : false,
19644 * @cfg {Boolean} singleSelect Allow single selection
19646 singleSelect: false,
19649 * @cfg {Boolean} toggleSelect - selecting
19651 toggleSelect : false,
19654 * @cfg {Boolean} tickable - selecting
19659 * Returns the element this view is bound to.
19660 * @return {Roo.Element}
19662 getEl : function(){
19663 return this.wrapEl;
19669 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19671 refresh : function(){
19672 //Roo.log('refresh');
19675 // if we are using something like 'domtemplate', then
19676 // the what gets used is:
19677 // t.applySubtemplate(NAME, data, wrapping data..)
19678 // the outer template then get' applied with
19679 // the store 'extra data'
19680 // and the body get's added to the
19681 // roo-name="data" node?
19682 // <span class='roo-tpl-{name}'></span> ?????
19686 this.clearSelections();
19687 this.el.update("");
19689 var records = this.store.getRange();
19690 if(records.length < 1) {
19692 // is this valid?? = should it render a template??
19694 this.el.update(this.emptyText);
19698 if (this.dataName) {
19699 this.el.update(t.apply(this.store.meta)); //????
19700 el = this.el.child('.roo-tpl-' + this.dataName);
19703 for(var i = 0, len = records.length; i < len; i++){
19704 var data = this.prepareData(records[i].data, i, records[i]);
19705 this.fireEvent("preparedata", this, data, i, records[i]);
19707 var d = Roo.apply({}, data);
19710 Roo.apply(d, {'roo-id' : Roo.id()});
19714 Roo.each(this.parent.item, function(item){
19715 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19718 Roo.apply(d, {'roo-data-checked' : 'checked'});
19722 html[html.length] = Roo.util.Format.trim(
19724 t.applySubtemplate(this.dataName, d, this.store.meta) :
19731 el.update(html.join(""));
19732 this.nodes = el.dom.childNodes;
19733 this.updateIndexes(0);
19738 * Function to override to reformat the data that is sent to
19739 * the template for each node.
19740 * DEPRICATED - use the preparedata event handler.
19741 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19742 * a JSON object for an UpdateManager bound view).
19744 prepareData : function(data, index, record)
19746 this.fireEvent("preparedata", this, data, index, record);
19750 onUpdate : function(ds, record){
19751 // Roo.log('on update');
19752 this.clearSelections();
19753 var index = this.store.indexOf(record);
19754 var n = this.nodes[index];
19755 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19756 n.parentNode.removeChild(n);
19757 this.updateIndexes(index, index);
19763 onAdd : function(ds, records, index)
19765 //Roo.log(['on Add', ds, records, index] );
19766 this.clearSelections();
19767 if(this.nodes.length == 0){
19771 var n = this.nodes[index];
19772 for(var i = 0, len = records.length; i < len; i++){
19773 var d = this.prepareData(records[i].data, i, records[i]);
19775 this.tpl.insertBefore(n, d);
19778 this.tpl.append(this.el, d);
19781 this.updateIndexes(index);
19784 onRemove : function(ds, record, index){
19785 // Roo.log('onRemove');
19786 this.clearSelections();
19787 var el = this.dataName ?
19788 this.el.child('.roo-tpl-' + this.dataName) :
19791 el.dom.removeChild(this.nodes[index]);
19792 this.updateIndexes(index);
19796 * Refresh an individual node.
19797 * @param {Number} index
19799 refreshNode : function(index){
19800 this.onUpdate(this.store, this.store.getAt(index));
19803 updateIndexes : function(startIndex, endIndex){
19804 var ns = this.nodes;
19805 startIndex = startIndex || 0;
19806 endIndex = endIndex || ns.length - 1;
19807 for(var i = startIndex; i <= endIndex; i++){
19808 ns[i].nodeIndex = i;
19813 * Changes the data store this view uses and refresh the view.
19814 * @param {Store} store
19816 setStore : function(store, initial){
19817 if(!initial && this.store){
19818 this.store.un("datachanged", this.refresh);
19819 this.store.un("add", this.onAdd);
19820 this.store.un("remove", this.onRemove);
19821 this.store.un("update", this.onUpdate);
19822 this.store.un("clear", this.refresh);
19823 this.store.un("beforeload", this.onBeforeLoad);
19824 this.store.un("load", this.onLoad);
19825 this.store.un("loadexception", this.onLoad);
19829 store.on("datachanged", this.refresh, this);
19830 store.on("add", this.onAdd, this);
19831 store.on("remove", this.onRemove, this);
19832 store.on("update", this.onUpdate, this);
19833 store.on("clear", this.refresh, this);
19834 store.on("beforeload", this.onBeforeLoad, this);
19835 store.on("load", this.onLoad, this);
19836 store.on("loadexception", this.onLoad, this);
19844 * onbeforeLoad - masks the loading area.
19847 onBeforeLoad : function(store,opts)
19849 //Roo.log('onBeforeLoad');
19851 this.el.update("");
19853 this.el.mask(this.mask ? this.mask : "Loading" );
19855 onLoad : function ()
19862 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19863 * @param {HTMLElement} node
19864 * @return {HTMLElement} The template node
19866 findItemFromChild : function(node){
19867 var el = this.dataName ?
19868 this.el.child('.roo-tpl-' + this.dataName,true) :
19871 if(!node || node.parentNode == el){
19874 var p = node.parentNode;
19875 while(p && p != el){
19876 if(p.parentNode == el){
19885 onClick : function(e){
19886 var item = this.findItemFromChild(e.getTarget());
19888 var index = this.indexOf(item);
19889 if(this.onItemClick(item, index, e) !== false){
19890 this.fireEvent("click", this, index, item, e);
19893 this.clearSelections();
19898 onContextMenu : function(e){
19899 var item = this.findItemFromChild(e.getTarget());
19901 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19906 onDblClick : function(e){
19907 var item = this.findItemFromChild(e.getTarget());
19909 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19913 onItemClick : function(item, index, e)
19915 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19918 if (this.toggleSelect) {
19919 var m = this.isSelected(item) ? 'unselect' : 'select';
19922 _t[m](item, true, false);
19925 if(this.multiSelect || this.singleSelect){
19926 if(this.multiSelect && e.shiftKey && this.lastSelection){
19927 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19929 this.select(item, this.multiSelect && e.ctrlKey);
19930 this.lastSelection = item;
19933 if(!this.tickable){
19934 e.preventDefault();
19942 * Get the number of selected nodes.
19945 getSelectionCount : function(){
19946 return this.selections.length;
19950 * Get the currently selected nodes.
19951 * @return {Array} An array of HTMLElements
19953 getSelectedNodes : function(){
19954 return this.selections;
19958 * Get the indexes of the selected nodes.
19961 getSelectedIndexes : function(){
19962 var indexes = [], s = this.selections;
19963 for(var i = 0, len = s.length; i < len; i++){
19964 indexes.push(s[i].nodeIndex);
19970 * Clear all selections
19971 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19973 clearSelections : function(suppressEvent){
19974 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19975 this.cmp.elements = this.selections;
19976 this.cmp.removeClass(this.selectedClass);
19977 this.selections = [];
19978 if(!suppressEvent){
19979 this.fireEvent("selectionchange", this, this.selections);
19985 * Returns true if the passed node is selected
19986 * @param {HTMLElement/Number} node The node or node index
19987 * @return {Boolean}
19989 isSelected : function(node){
19990 var s = this.selections;
19994 node = this.getNode(node);
19995 return s.indexOf(node) !== -1;
20000 * @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
20001 * @param {Boolean} keepExisting (optional) true to keep existing selections
20002 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20004 select : function(nodeInfo, keepExisting, suppressEvent){
20005 if(nodeInfo instanceof Array){
20007 this.clearSelections(true);
20009 for(var i = 0, len = nodeInfo.length; i < len; i++){
20010 this.select(nodeInfo[i], true, true);
20014 var node = this.getNode(nodeInfo);
20015 if(!node || this.isSelected(node)){
20016 return; // already selected.
20019 this.clearSelections(true);
20022 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20023 Roo.fly(node).addClass(this.selectedClass);
20024 this.selections.push(node);
20025 if(!suppressEvent){
20026 this.fireEvent("selectionchange", this, this.selections);
20034 * @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
20035 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20036 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20038 unselect : function(nodeInfo, keepExisting, suppressEvent)
20040 if(nodeInfo instanceof Array){
20041 Roo.each(this.selections, function(s) {
20042 this.unselect(s, nodeInfo);
20046 var node = this.getNode(nodeInfo);
20047 if(!node || !this.isSelected(node)){
20048 //Roo.log("not selected");
20049 return; // not selected.
20053 Roo.each(this.selections, function(s) {
20055 Roo.fly(node).removeClass(this.selectedClass);
20062 this.selections= ns;
20063 this.fireEvent("selectionchange", this, this.selections);
20067 * Gets a template node.
20068 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20069 * @return {HTMLElement} The node or null if it wasn't found
20071 getNode : function(nodeInfo){
20072 if(typeof nodeInfo == "string"){
20073 return document.getElementById(nodeInfo);
20074 }else if(typeof nodeInfo == "number"){
20075 return this.nodes[nodeInfo];
20081 * Gets a range template nodes.
20082 * @param {Number} startIndex
20083 * @param {Number} endIndex
20084 * @return {Array} An array of nodes
20086 getNodes : function(start, end){
20087 var ns = this.nodes;
20088 start = start || 0;
20089 end = typeof end == "undefined" ? ns.length - 1 : end;
20092 for(var i = start; i <= end; i++){
20096 for(var i = start; i >= end; i--){
20104 * Finds the index of the passed node
20105 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20106 * @return {Number} The index of the node or -1
20108 indexOf : function(node){
20109 node = this.getNode(node);
20110 if(typeof node.nodeIndex == "number"){
20111 return node.nodeIndex;
20113 var ns = this.nodes;
20114 for(var i = 0, len = ns.length; i < len; i++){
20125 * based on jquery fullcalendar
20129 Roo.bootstrap = Roo.bootstrap || {};
20131 * @class Roo.bootstrap.Calendar
20132 * @extends Roo.bootstrap.Component
20133 * Bootstrap Calendar class
20134 * @cfg {Boolean} loadMask (true|false) default false
20135 * @cfg {Object} header generate the user specific header of the calendar, default false
20138 * Create a new Container
20139 * @param {Object} config The config object
20144 Roo.bootstrap.Calendar = function(config){
20145 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20149 * Fires when a date is selected
20150 * @param {DatePicker} this
20151 * @param {Date} date The selected date
20155 * @event monthchange
20156 * Fires when the displayed month changes
20157 * @param {DatePicker} this
20158 * @param {Date} date The selected month
20160 'monthchange': true,
20162 * @event evententer
20163 * Fires when mouse over an event
20164 * @param {Calendar} this
20165 * @param {event} Event
20167 'evententer': true,
20169 * @event eventleave
20170 * Fires when the mouse leaves an
20171 * @param {Calendar} this
20174 'eventleave': true,
20176 * @event eventclick
20177 * Fires when the mouse click an
20178 * @param {Calendar} this
20187 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20190 * @cfg {Number} startDay
20191 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20199 getAutoCreate : function(){
20202 var fc_button = function(name, corner, style, content ) {
20203 return Roo.apply({},{
20205 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20207 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20210 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20221 style : 'width:100%',
20228 cls : 'fc-header-left',
20230 fc_button('prev', 'left', 'arrow', '‹' ),
20231 fc_button('next', 'right', 'arrow', '›' ),
20232 { tag: 'span', cls: 'fc-header-space' },
20233 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20241 cls : 'fc-header-center',
20245 cls: 'fc-header-title',
20248 html : 'month / year'
20256 cls : 'fc-header-right',
20258 /* fc_button('month', 'left', '', 'month' ),
20259 fc_button('week', '', '', 'week' ),
20260 fc_button('day', 'right', '', 'day' )
20272 header = this.header;
20275 var cal_heads = function() {
20277 // fixme - handle this.
20279 for (var i =0; i < Date.dayNames.length; i++) {
20280 var d = Date.dayNames[i];
20283 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20284 html : d.substring(0,3)
20288 ret[0].cls += ' fc-first';
20289 ret[6].cls += ' fc-last';
20292 var cal_cell = function(n) {
20295 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20300 cls: 'fc-day-number',
20304 cls: 'fc-day-content',
20308 style: 'position: relative;' // height: 17px;
20320 var cal_rows = function() {
20323 for (var r = 0; r < 6; r++) {
20330 for (var i =0; i < Date.dayNames.length; i++) {
20331 var d = Date.dayNames[i];
20332 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20335 row.cn[0].cls+=' fc-first';
20336 row.cn[0].cn[0].style = 'min-height:90px';
20337 row.cn[6].cls+=' fc-last';
20341 ret[0].cls += ' fc-first';
20342 ret[4].cls += ' fc-prev-last';
20343 ret[5].cls += ' fc-last';
20350 cls: 'fc-border-separate',
20351 style : 'width:100%',
20359 cls : 'fc-first fc-last',
20377 cls : 'fc-content',
20378 style : "position: relative;",
20381 cls : 'fc-view fc-view-month fc-grid',
20382 style : 'position: relative',
20383 unselectable : 'on',
20386 cls : 'fc-event-container',
20387 style : 'position:absolute;z-index:8;top:0;left:0;'
20405 initEvents : function()
20408 throw "can not find store for calendar";
20414 style: "text-align:center",
20418 style: "background-color:white;width:50%;margin:250 auto",
20422 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20433 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20435 var size = this.el.select('.fc-content', true).first().getSize();
20436 this.maskEl.setSize(size.width, size.height);
20437 this.maskEl.enableDisplayMode("block");
20438 if(!this.loadMask){
20439 this.maskEl.hide();
20442 this.store = Roo.factory(this.store, Roo.data);
20443 this.store.on('load', this.onLoad, this);
20444 this.store.on('beforeload', this.onBeforeLoad, this);
20448 this.cells = this.el.select('.fc-day',true);
20449 //Roo.log(this.cells);
20450 this.textNodes = this.el.query('.fc-day-number');
20451 this.cells.addClassOnOver('fc-state-hover');
20453 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20454 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20455 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20456 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20458 this.on('monthchange', this.onMonthChange, this);
20460 this.update(new Date().clearTime());
20463 resize : function() {
20464 var sz = this.el.getSize();
20466 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20467 this.el.select('.fc-day-content div',true).setHeight(34);
20472 showPrevMonth : function(e){
20473 this.update(this.activeDate.add("mo", -1));
20475 showToday : function(e){
20476 this.update(new Date().clearTime());
20479 showNextMonth : function(e){
20480 this.update(this.activeDate.add("mo", 1));
20484 showPrevYear : function(){
20485 this.update(this.activeDate.add("y", -1));
20489 showNextYear : function(){
20490 this.update(this.activeDate.add("y", 1));
20495 update : function(date)
20497 var vd = this.activeDate;
20498 this.activeDate = date;
20499 // if(vd && this.el){
20500 // var t = date.getTime();
20501 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20502 // Roo.log('using add remove');
20504 // this.fireEvent('monthchange', this, date);
20506 // this.cells.removeClass("fc-state-highlight");
20507 // this.cells.each(function(c){
20508 // if(c.dateValue == t){
20509 // c.addClass("fc-state-highlight");
20510 // setTimeout(function(){
20511 // try{c.dom.firstChild.focus();}catch(e){}
20521 var days = date.getDaysInMonth();
20523 var firstOfMonth = date.getFirstDateOfMonth();
20524 var startingPos = firstOfMonth.getDay()-this.startDay;
20526 if(startingPos < this.startDay){
20530 var pm = date.add(Date.MONTH, -1);
20531 var prevStart = pm.getDaysInMonth()-startingPos;
20533 this.cells = this.el.select('.fc-day',true);
20534 this.textNodes = this.el.query('.fc-day-number');
20535 this.cells.addClassOnOver('fc-state-hover');
20537 var cells = this.cells.elements;
20538 var textEls = this.textNodes;
20540 Roo.each(cells, function(cell){
20541 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20544 days += startingPos;
20546 // convert everything to numbers so it's fast
20547 var day = 86400000;
20548 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20551 //Roo.log(prevStart);
20553 var today = new Date().clearTime().getTime();
20554 var sel = date.clearTime().getTime();
20555 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20556 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20557 var ddMatch = this.disabledDatesRE;
20558 var ddText = this.disabledDatesText;
20559 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20560 var ddaysText = this.disabledDaysText;
20561 var format = this.format;
20563 var setCellClass = function(cal, cell){
20567 //Roo.log('set Cell Class');
20569 var t = d.getTime();
20573 cell.dateValue = t;
20575 cell.className += " fc-today";
20576 cell.className += " fc-state-highlight";
20577 cell.title = cal.todayText;
20580 // disable highlight in other month..
20581 //cell.className += " fc-state-highlight";
20586 cell.className = " fc-state-disabled";
20587 cell.title = cal.minText;
20591 cell.className = " fc-state-disabled";
20592 cell.title = cal.maxText;
20596 if(ddays.indexOf(d.getDay()) != -1){
20597 cell.title = ddaysText;
20598 cell.className = " fc-state-disabled";
20601 if(ddMatch && format){
20602 var fvalue = d.dateFormat(format);
20603 if(ddMatch.test(fvalue)){
20604 cell.title = ddText.replace("%0", fvalue);
20605 cell.className = " fc-state-disabled";
20609 if (!cell.initialClassName) {
20610 cell.initialClassName = cell.dom.className;
20613 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20618 for(; i < startingPos; i++) {
20619 textEls[i].innerHTML = (++prevStart);
20620 d.setDate(d.getDate()+1);
20622 cells[i].className = "fc-past fc-other-month";
20623 setCellClass(this, cells[i]);
20628 for(; i < days; i++){
20629 intDay = i - startingPos + 1;
20630 textEls[i].innerHTML = (intDay);
20631 d.setDate(d.getDate()+1);
20633 cells[i].className = ''; // "x-date-active";
20634 setCellClass(this, cells[i]);
20638 for(; i < 42; i++) {
20639 textEls[i].innerHTML = (++extraDays);
20640 d.setDate(d.getDate()+1);
20642 cells[i].className = "fc-future fc-other-month";
20643 setCellClass(this, cells[i]);
20646 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20648 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20650 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20651 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20653 if(totalRows != 6){
20654 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20655 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20658 this.fireEvent('monthchange', this, date);
20662 if(!this.internalRender){
20663 var main = this.el.dom.firstChild;
20664 var w = main.offsetWidth;
20665 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20666 Roo.fly(main).setWidth(w);
20667 this.internalRender = true;
20668 // opera does not respect the auto grow header center column
20669 // then, after it gets a width opera refuses to recalculate
20670 // without a second pass
20671 if(Roo.isOpera && !this.secondPass){
20672 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20673 this.secondPass = true;
20674 this.update.defer(10, this, [date]);
20681 findCell : function(dt) {
20682 dt = dt.clearTime().getTime();
20684 this.cells.each(function(c){
20685 //Roo.log("check " +c.dateValue + '?=' + dt);
20686 if(c.dateValue == dt){
20696 findCells : function(ev) {
20697 var s = ev.start.clone().clearTime().getTime();
20699 var e= ev.end.clone().clearTime().getTime();
20702 this.cells.each(function(c){
20703 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20705 if(c.dateValue > e){
20708 if(c.dateValue < s){
20717 // findBestRow: function(cells)
20721 // for (var i =0 ; i < cells.length;i++) {
20722 // ret = Math.max(cells[i].rows || 0,ret);
20729 addItem : function(ev)
20731 // look for vertical location slot in
20732 var cells = this.findCells(ev);
20734 // ev.row = this.findBestRow(cells);
20736 // work out the location.
20740 for(var i =0; i < cells.length; i++) {
20742 cells[i].row = cells[0].row;
20745 cells[i].row = cells[i].row + 1;
20755 if (crow.start.getY() == cells[i].getY()) {
20757 crow.end = cells[i];
20774 cells[0].events.push(ev);
20776 this.calevents.push(ev);
20779 clearEvents: function() {
20781 if(!this.calevents){
20785 Roo.each(this.cells.elements, function(c){
20791 Roo.each(this.calevents, function(e) {
20792 Roo.each(e.els, function(el) {
20793 el.un('mouseenter' ,this.onEventEnter, this);
20794 el.un('mouseleave' ,this.onEventLeave, this);
20799 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20805 renderEvents: function()
20809 this.cells.each(function(c) {
20818 if(c.row != c.events.length){
20819 r = 4 - (4 - (c.row - c.events.length));
20822 c.events = ev.slice(0, r);
20823 c.more = ev.slice(r);
20825 if(c.more.length && c.more.length == 1){
20826 c.events.push(c.more.pop());
20829 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20833 this.cells.each(function(c) {
20835 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20838 for (var e = 0; e < c.events.length; e++){
20839 var ev = c.events[e];
20840 var rows = ev.rows;
20842 for(var i = 0; i < rows.length; i++) {
20844 // how many rows should it span..
20847 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20848 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20850 unselectable : "on",
20853 cls: 'fc-event-inner',
20857 // cls: 'fc-event-time',
20858 // html : cells.length > 1 ? '' : ev.time
20862 cls: 'fc-event-title',
20863 html : String.format('{0}', ev.title)
20870 cls: 'ui-resizable-handle ui-resizable-e',
20871 html : '  '
20878 cfg.cls += ' fc-event-start';
20880 if ((i+1) == rows.length) {
20881 cfg.cls += ' fc-event-end';
20884 var ctr = _this.el.select('.fc-event-container',true).first();
20885 var cg = ctr.createChild(cfg);
20887 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20888 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20890 var r = (c.more.length) ? 1 : 0;
20891 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20892 cg.setWidth(ebox.right - sbox.x -2);
20894 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20895 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20896 cg.on('click', _this.onEventClick, _this, ev);
20907 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20908 style : 'position: absolute',
20909 unselectable : "on",
20912 cls: 'fc-event-inner',
20916 cls: 'fc-event-title',
20924 cls: 'ui-resizable-handle ui-resizable-e',
20925 html : '  '
20931 var ctr = _this.el.select('.fc-event-container',true).first();
20932 var cg = ctr.createChild(cfg);
20934 var sbox = c.select('.fc-day-content',true).first().getBox();
20935 var ebox = c.select('.fc-day-content',true).first().getBox();
20937 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20938 cg.setWidth(ebox.right - sbox.x -2);
20940 cg.on('click', _this.onMoreEventClick, _this, c.more);
20950 onEventEnter: function (e, el,event,d) {
20951 this.fireEvent('evententer', this, el, event);
20954 onEventLeave: function (e, el,event,d) {
20955 this.fireEvent('eventleave', this, el, event);
20958 onEventClick: function (e, el,event,d) {
20959 this.fireEvent('eventclick', this, el, event);
20962 onMonthChange: function () {
20966 onMoreEventClick: function(e, el, more)
20970 this.calpopover.placement = 'right';
20971 this.calpopover.setTitle('More');
20973 this.calpopover.setContent('');
20975 var ctr = this.calpopover.el.select('.popover-content', true).first();
20977 Roo.each(more, function(m){
20979 cls : 'fc-event-hori fc-event-draggable',
20982 var cg = ctr.createChild(cfg);
20984 cg.on('click', _this.onEventClick, _this, m);
20987 this.calpopover.show(el);
20992 onLoad: function ()
20994 this.calevents = [];
20997 if(this.store.getCount() > 0){
20998 this.store.data.each(function(d){
21001 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21002 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21003 time : d.data.start_time,
21004 title : d.data.title,
21005 description : d.data.description,
21006 venue : d.data.venue
21011 this.renderEvents();
21013 if(this.calevents.length && this.loadMask){
21014 this.maskEl.hide();
21018 onBeforeLoad: function()
21020 this.clearEvents();
21022 this.maskEl.show();
21036 * @class Roo.bootstrap.Popover
21037 * @extends Roo.bootstrap.Component
21038 * Bootstrap Popover class
21039 * @cfg {String} html contents of the popover (or false to use children..)
21040 * @cfg {String} title of popover (or false to hide)
21041 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21042 * @cfg {String} trigger click || hover (or false to trigger manually)
21043 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21044 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21045 * - if false and it has a 'parent' then it will be automatically added to that element
21046 * - if string - Roo.get will be called
21047 * @cfg {Number} delay - delay before showing
21050 * Create a new Popover
21051 * @param {Object} config The config object
21054 Roo.bootstrap.Popover = function(config){
21055 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21061 * After the popover show
21063 * @param {Roo.bootstrap.Popover} this
21068 * After the popover hide
21070 * @param {Roo.bootstrap.Popover} this
21076 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21081 placement : 'right',
21082 trigger : 'hover', // hover
21088 can_build_overlaid : false,
21090 maskEl : false, // the mask element
21093 alignEl : false, // when show is called with an element - this get's stored.
21095 getChildContainer : function()
21097 return this.contentEl;
21100 getPopoverHeader : function()
21102 this.title = true; // flag not to hide it..
21103 this.headerEl.addClass('p-0');
21104 return this.headerEl
21108 getAutoCreate : function(){
21111 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21112 style: 'display:block',
21118 cls : 'popover-inner ',
21122 cls: 'popover-title popover-header',
21123 html : this.title === false ? '' : this.title
21126 cls : 'popover-content popover-body ' + (this.cls || ''),
21127 html : this.html || ''
21138 * @param {string} the title
21140 setTitle: function(str)
21144 this.headerEl.dom.innerHTML = str;
21149 * @param {string} the body content
21151 setContent: function(str)
21154 if (this.contentEl) {
21155 this.contentEl.dom.innerHTML = str;
21159 // as it get's added to the bottom of the page.
21160 onRender : function(ct, position)
21162 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21167 var cfg = Roo.apply({}, this.getAutoCreate());
21171 cfg.cls += ' ' + this.cls;
21174 cfg.style = this.style;
21176 //Roo.log("adding to ");
21177 this.el = Roo.get(document.body).createChild(cfg, position);
21178 // Roo.log(this.el);
21181 this.contentEl = this.el.select('.popover-content',true).first();
21182 this.headerEl = this.el.select('.popover-title',true).first();
21185 if(typeof(this.items) != 'undefined'){
21186 var items = this.items;
21189 for(var i =0;i < items.length;i++) {
21190 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21194 this.items = nitems;
21196 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21197 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21204 resizeMask : function()
21206 this.maskEl.setSize(
21207 Roo.lib.Dom.getViewWidth(true),
21208 Roo.lib.Dom.getViewHeight(true)
21212 initEvents : function()
21216 Roo.bootstrap.Popover.register(this);
21219 this.arrowEl = this.el.select('.arrow',true).first();
21220 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21221 this.el.enableDisplayMode('block');
21225 if (this.over === false && !this.parent()) {
21228 if (this.triggers === false) {
21233 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21234 var triggers = this.trigger ? this.trigger.split(' ') : [];
21235 Roo.each(triggers, function(trigger) {
21237 if (trigger == 'click') {
21238 on_el.on('click', this.toggle, this);
21239 } else if (trigger != 'manual') {
21240 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21241 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21243 on_el.on(eventIn ,this.enter, this);
21244 on_el.on(eventOut, this.leave, this);
21254 toggle : function () {
21255 this.hoverState == 'in' ? this.leave() : this.enter();
21258 enter : function () {
21260 clearTimeout(this.timeout);
21262 this.hoverState = 'in';
21264 if (!this.delay || !this.delay.show) {
21269 this.timeout = setTimeout(function () {
21270 if (_t.hoverState == 'in') {
21273 }, this.delay.show)
21276 leave : function() {
21277 clearTimeout(this.timeout);
21279 this.hoverState = 'out';
21281 if (!this.delay || !this.delay.hide) {
21286 this.timeout = setTimeout(function () {
21287 if (_t.hoverState == 'out') {
21290 }, this.delay.hide)
21294 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21295 * @param {string} (left|right|top|bottom) position
21297 show : function (on_el, placement)
21299 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21300 on_el = on_el || false; // default to false
21303 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21304 on_el = this.parent().el;
21305 } else if (this.over) {
21306 on_el = Roo.get(this.over);
21311 this.alignEl = Roo.get( on_el );
21314 this.render(document.body);
21320 if (this.title === false) {
21321 this.headerEl.hide();
21326 this.el.dom.style.display = 'block';
21329 if (this.alignEl) {
21330 this.updatePosition(this.placement, true);
21333 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21334 var es = this.el.getSize();
21335 var x = Roo.lib.Dom.getViewWidth()/2;
21336 var y = Roo.lib.Dom.getViewHeight()/2;
21337 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21342 //var arrow = this.el.select('.arrow',true).first();
21343 //arrow.set(align[2],
21345 this.el.addClass('in');
21349 this.hoverState = 'in';
21352 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21353 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21354 this.maskEl.dom.style.display = 'block';
21355 this.maskEl.addClass('show');
21357 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21359 this.fireEvent('show', this);
21363 * fire this manually after loading a grid in the table for example
21364 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21365 * @param {Boolean} try and move it if we cant get right position.
21367 updatePosition : function(placement, try_move)
21369 // allow for calling with no parameters
21370 placement = placement ? placement : this.placement;
21371 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21373 this.el.removeClass([
21374 'fade','top','bottom', 'left', 'right','in',
21375 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21377 this.el.addClass(placement + ' bs-popover-' + placement);
21379 if (!this.alignEl ) {
21383 switch (placement) {
21385 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21386 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21387 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21388 //normal display... or moved up/down.
21389 this.el.setXY(offset);
21390 var xy = this.alignEl.getAnchorXY('tr', false);
21392 this.arrowEl.setXY(xy);
21395 // continue through...
21396 return this.updatePosition('left', false);
21400 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21401 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21402 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21403 //normal display... or moved up/down.
21404 this.el.setXY(offset);
21405 var xy = this.alignEl.getAnchorXY('tl', false);
21406 xy[0]-=10;xy[1]+=5; // << fix me
21407 this.arrowEl.setXY(xy);
21411 return this.updatePosition('right', false);
21414 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21415 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21416 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21417 //normal display... or moved up/down.
21418 this.el.setXY(offset);
21419 var xy = this.alignEl.getAnchorXY('t', false);
21420 xy[1]-=10; // << fix me
21421 this.arrowEl.setXY(xy);
21425 return this.updatePosition('bottom', false);
21428 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21429 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21430 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21431 //normal display... or moved up/down.
21432 this.el.setXY(offset);
21433 var xy = this.alignEl.getAnchorXY('b', false);
21434 xy[1]+=2; // << fix me
21435 this.arrowEl.setXY(xy);
21439 return this.updatePosition('top', false);
21450 this.el.setXY([0,0]);
21451 this.el.removeClass('in');
21453 this.hoverState = null;
21454 this.maskEl.hide(); // always..
21455 this.fireEvent('hide', this);
21461 Roo.apply(Roo.bootstrap.Popover, {
21464 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21465 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21466 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21467 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21472 clickHander : false,
21476 onMouseDown : function(e)
21478 if (this.popups.length && !e.getTarget(".roo-popover")) {
21479 /// what is nothing is showing..
21488 register : function(popup)
21490 if (!Roo.bootstrap.Popover.clickHandler) {
21491 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21493 // hide other popups.
21494 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21495 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21496 this.hideAll(); //<< why?
21497 //this.popups.push(popup);
21499 hideAll : function()
21501 this.popups.forEach(function(p) {
21505 onShow : function() {
21506 Roo.bootstrap.Popover.popups.push(this);
21508 onHide : function() {
21509 Roo.bootstrap.Popover.popups.remove(this);
21515 * Card header - holder for the card header elements.
21520 * @class Roo.bootstrap.PopoverNav
21521 * @extends Roo.bootstrap.NavGroup
21522 * Bootstrap Popover header navigation class
21524 * Create a new Popover Header Navigation
21525 * @param {Object} config The config object
21528 Roo.bootstrap.PopoverNav = function(config){
21529 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21532 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21535 container_method : 'getPopoverHeader'
21553 * @class Roo.bootstrap.Progress
21554 * @extends Roo.bootstrap.Component
21555 * Bootstrap Progress class
21556 * @cfg {Boolean} striped striped of the progress bar
21557 * @cfg {Boolean} active animated of the progress bar
21561 * Create a new Progress
21562 * @param {Object} config The config object
21565 Roo.bootstrap.Progress = function(config){
21566 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21569 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21574 getAutoCreate : function(){
21582 cfg.cls += ' progress-striped';
21586 cfg.cls += ' active';
21605 * @class Roo.bootstrap.ProgressBar
21606 * @extends Roo.bootstrap.Component
21607 * Bootstrap ProgressBar class
21608 * @cfg {Number} aria_valuenow aria-value now
21609 * @cfg {Number} aria_valuemin aria-value min
21610 * @cfg {Number} aria_valuemax aria-value max
21611 * @cfg {String} label label for the progress bar
21612 * @cfg {String} panel (success | info | warning | danger )
21613 * @cfg {String} role role of the progress bar
21614 * @cfg {String} sr_only text
21618 * Create a new ProgressBar
21619 * @param {Object} config The config object
21622 Roo.bootstrap.ProgressBar = function(config){
21623 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21626 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21630 aria_valuemax : 100,
21636 getAutoCreate : function()
21641 cls: 'progress-bar',
21642 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21654 cfg.role = this.role;
21657 if(this.aria_valuenow){
21658 cfg['aria-valuenow'] = this.aria_valuenow;
21661 if(this.aria_valuemin){
21662 cfg['aria-valuemin'] = this.aria_valuemin;
21665 if(this.aria_valuemax){
21666 cfg['aria-valuemax'] = this.aria_valuemax;
21669 if(this.label && !this.sr_only){
21670 cfg.html = this.label;
21674 cfg.cls += ' progress-bar-' + this.panel;
21680 update : function(aria_valuenow)
21682 this.aria_valuenow = aria_valuenow;
21684 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21699 * @class Roo.bootstrap.TabGroup
21700 * @extends Roo.bootstrap.Column
21701 * Bootstrap Column class
21702 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21703 * @cfg {Boolean} carousel true to make the group behave like a carousel
21704 * @cfg {Boolean} bullets show bullets for the panels
21705 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21706 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21707 * @cfg {Boolean} showarrow (true|false) show arrow default true
21710 * Create a new TabGroup
21711 * @param {Object} config The config object
21714 Roo.bootstrap.TabGroup = function(config){
21715 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21717 this.navId = Roo.id();
21720 Roo.bootstrap.TabGroup.register(this);
21724 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21727 transition : false,
21732 slideOnTouch : false,
21735 getAutoCreate : function()
21737 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21739 cfg.cls += ' tab-content';
21741 if (this.carousel) {
21742 cfg.cls += ' carousel slide';
21745 cls : 'carousel-inner',
21749 if(this.bullets && !Roo.isTouch){
21752 cls : 'carousel-bullets',
21756 if(this.bullets_cls){
21757 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21764 cfg.cn[0].cn.push(bullets);
21767 if(this.showarrow){
21768 cfg.cn[0].cn.push({
21770 class : 'carousel-arrow',
21774 class : 'carousel-prev',
21778 class : 'fa fa-chevron-left'
21784 class : 'carousel-next',
21788 class : 'fa fa-chevron-right'
21801 initEvents: function()
21803 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21804 // this.el.on("touchstart", this.onTouchStart, this);
21807 if(this.autoslide){
21810 this.slideFn = window.setInterval(function() {
21811 _this.showPanelNext();
21815 if(this.showarrow){
21816 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21817 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21823 // onTouchStart : function(e, el, o)
21825 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21829 // this.showPanelNext();
21833 getChildContainer : function()
21835 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21839 * register a Navigation item
21840 * @param {Roo.bootstrap.NavItem} the navitem to add
21842 register : function(item)
21844 this.tabs.push( item);
21845 item.navId = this.navId; // not really needed..
21850 getActivePanel : function()
21853 Roo.each(this.tabs, function(t) {
21863 getPanelByName : function(n)
21866 Roo.each(this.tabs, function(t) {
21867 if (t.tabId == n) {
21875 indexOfPanel : function(p)
21878 Roo.each(this.tabs, function(t,i) {
21879 if (t.tabId == p.tabId) {
21888 * show a specific panel
21889 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21890 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21892 showPanel : function (pan)
21894 if(this.transition || typeof(pan) == 'undefined'){
21895 Roo.log("waiting for the transitionend");
21899 if (typeof(pan) == 'number') {
21900 pan = this.tabs[pan];
21903 if (typeof(pan) == 'string') {
21904 pan = this.getPanelByName(pan);
21907 var cur = this.getActivePanel();
21910 Roo.log('pan or acitve pan is undefined');
21914 if (pan.tabId == this.getActivePanel().tabId) {
21918 if (false === cur.fireEvent('beforedeactivate')) {
21922 if(this.bullets > 0 && !Roo.isTouch){
21923 this.setActiveBullet(this.indexOfPanel(pan));
21926 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21928 //class="carousel-item carousel-item-next carousel-item-left"
21930 this.transition = true;
21931 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21932 var lr = dir == 'next' ? 'left' : 'right';
21933 pan.el.addClass(dir); // or prev
21934 pan.el.addClass('carousel-item-' + dir); // or prev
21935 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21936 cur.el.addClass(lr); // or right
21937 pan.el.addClass(lr);
21938 cur.el.addClass('carousel-item-' +lr); // or right
21939 pan.el.addClass('carousel-item-' +lr);
21943 cur.el.on('transitionend', function() {
21944 Roo.log("trans end?");
21946 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21947 pan.setActive(true);
21949 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21950 cur.setActive(false);
21952 _this.transition = false;
21954 }, this, { single: true } );
21959 cur.setActive(false);
21960 pan.setActive(true);
21965 showPanelNext : function()
21967 var i = this.indexOfPanel(this.getActivePanel());
21969 if (i >= this.tabs.length - 1 && !this.autoslide) {
21973 if (i >= this.tabs.length - 1 && this.autoslide) {
21977 this.showPanel(this.tabs[i+1]);
21980 showPanelPrev : function()
21982 var i = this.indexOfPanel(this.getActivePanel());
21984 if (i < 1 && !this.autoslide) {
21988 if (i < 1 && this.autoslide) {
21989 i = this.tabs.length;
21992 this.showPanel(this.tabs[i-1]);
21996 addBullet: function()
21998 if(!this.bullets || Roo.isTouch){
22001 var ctr = this.el.select('.carousel-bullets',true).first();
22002 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22003 var bullet = ctr.createChild({
22004 cls : 'bullet bullet-' + i
22005 },ctr.dom.lastChild);
22010 bullet.on('click', (function(e, el, o, ii, t){
22012 e.preventDefault();
22014 this.showPanel(ii);
22016 if(this.autoslide && this.slideFn){
22017 clearInterval(this.slideFn);
22018 this.slideFn = window.setInterval(function() {
22019 _this.showPanelNext();
22023 }).createDelegate(this, [i, bullet], true));
22028 setActiveBullet : function(i)
22034 Roo.each(this.el.select('.bullet', true).elements, function(el){
22035 el.removeClass('selected');
22038 var bullet = this.el.select('.bullet-' + i, true).first();
22044 bullet.addClass('selected');
22055 Roo.apply(Roo.bootstrap.TabGroup, {
22059 * register a Navigation Group
22060 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22062 register : function(navgrp)
22064 this.groups[navgrp.navId] = navgrp;
22068 * fetch a Navigation Group based on the navigation ID
22069 * if one does not exist , it will get created.
22070 * @param {string} the navgroup to add
22071 * @returns {Roo.bootstrap.NavGroup} the navgroup
22073 get: function(navId) {
22074 if (typeof(this.groups[navId]) == 'undefined') {
22075 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22077 return this.groups[navId] ;
22092 * @class Roo.bootstrap.TabPanel
22093 * @extends Roo.bootstrap.Component
22094 * Bootstrap TabPanel class
22095 * @cfg {Boolean} active panel active
22096 * @cfg {String} html panel content
22097 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22098 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22099 * @cfg {String} href click to link..
22100 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22104 * Create a new TabPanel
22105 * @param {Object} config The config object
22108 Roo.bootstrap.TabPanel = function(config){
22109 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22113 * Fires when the active status changes
22114 * @param {Roo.bootstrap.TabPanel} this
22115 * @param {Boolean} state the new state
22120 * @event beforedeactivate
22121 * Fires before a tab is de-activated - can be used to do validation on a form.
22122 * @param {Roo.bootstrap.TabPanel} this
22123 * @return {Boolean} false if there is an error
22126 'beforedeactivate': true
22129 this.tabId = this.tabId || Roo.id();
22133 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22140 touchSlide : false,
22141 getAutoCreate : function(){
22146 // item is needed for carousel - not sure if it has any effect otherwise
22147 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22148 html: this.html || ''
22152 cfg.cls += ' active';
22156 cfg.tabId = this.tabId;
22164 initEvents: function()
22166 var p = this.parent();
22168 this.navId = this.navId || p.navId;
22170 if (typeof(this.navId) != 'undefined') {
22171 // not really needed.. but just in case.. parent should be a NavGroup.
22172 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22176 var i = tg.tabs.length - 1;
22178 if(this.active && tg.bullets > 0 && i < tg.bullets){
22179 tg.setActiveBullet(i);
22183 this.el.on('click', this.onClick, this);
22185 if(Roo.isTouch && this.touchSlide){
22186 this.el.on("touchstart", this.onTouchStart, this);
22187 this.el.on("touchmove", this.onTouchMove, this);
22188 this.el.on("touchend", this.onTouchEnd, this);
22193 onRender : function(ct, position)
22195 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22198 setActive : function(state)
22200 Roo.log("panel - set active " + this.tabId + "=" + state);
22202 this.active = state;
22204 this.el.removeClass('active');
22206 } else if (!this.el.hasClass('active')) {
22207 this.el.addClass('active');
22210 this.fireEvent('changed', this, state);
22213 onClick : function(e)
22215 e.preventDefault();
22217 if(!this.href.length){
22221 window.location.href = this.href;
22230 onTouchStart : function(e)
22232 this.swiping = false;
22234 this.startX = e.browserEvent.touches[0].clientX;
22235 this.startY = e.browserEvent.touches[0].clientY;
22238 onTouchMove : function(e)
22240 this.swiping = true;
22242 this.endX = e.browserEvent.touches[0].clientX;
22243 this.endY = e.browserEvent.touches[0].clientY;
22246 onTouchEnd : function(e)
22253 var tabGroup = this.parent();
22255 if(this.endX > this.startX){ // swiping right
22256 tabGroup.showPanelPrev();
22260 if(this.startX > this.endX){ // swiping left
22261 tabGroup.showPanelNext();
22280 * @class Roo.bootstrap.DateField
22281 * @extends Roo.bootstrap.Input
22282 * Bootstrap DateField class
22283 * @cfg {Number} weekStart default 0
22284 * @cfg {String} viewMode default empty, (months|years)
22285 * @cfg {String} minViewMode default empty, (months|years)
22286 * @cfg {Number} startDate default -Infinity
22287 * @cfg {Number} endDate default Infinity
22288 * @cfg {Boolean} todayHighlight default false
22289 * @cfg {Boolean} todayBtn default false
22290 * @cfg {Boolean} calendarWeeks default false
22291 * @cfg {Object} daysOfWeekDisabled default empty
22292 * @cfg {Boolean} singleMode default false (true | false)
22294 * @cfg {Boolean} keyboardNavigation default true
22295 * @cfg {String} language default en
22298 * Create a new DateField
22299 * @param {Object} config The config object
22302 Roo.bootstrap.DateField = function(config){
22303 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22307 * Fires when this field show.
22308 * @param {Roo.bootstrap.DateField} this
22309 * @param {Mixed} date The date value
22314 * Fires when this field hide.
22315 * @param {Roo.bootstrap.DateField} this
22316 * @param {Mixed} date The date value
22321 * Fires when select a date.
22322 * @param {Roo.bootstrap.DateField} this
22323 * @param {Mixed} date The date value
22327 * @event beforeselect
22328 * Fires when before select a date.
22329 * @param {Roo.bootstrap.DateField} this
22330 * @param {Mixed} date The date value
22332 beforeselect : true
22336 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22339 * @cfg {String} format
22340 * The default date format string which can be overriden for localization support. The format must be
22341 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22345 * @cfg {String} altFormats
22346 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22347 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22349 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22357 todayHighlight : false,
22363 keyboardNavigation: true,
22365 calendarWeeks: false,
22367 startDate: -Infinity,
22371 daysOfWeekDisabled: [],
22375 singleMode : false,
22377 UTCDate: function()
22379 return new Date(Date.UTC.apply(Date, arguments));
22382 UTCToday: function()
22384 var today = new Date();
22385 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22388 getDate: function() {
22389 var d = this.getUTCDate();
22390 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22393 getUTCDate: function() {
22397 setDate: function(d) {
22398 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22401 setUTCDate: function(d) {
22403 this.setValue(this.formatDate(this.date));
22406 onRender: function(ct, position)
22409 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22411 this.language = this.language || 'en';
22412 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22413 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22415 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22416 this.format = this.format || 'm/d/y';
22417 this.isInline = false;
22418 this.isInput = true;
22419 this.component = this.el.select('.add-on', true).first() || false;
22420 this.component = (this.component && this.component.length === 0) ? false : this.component;
22421 this.hasInput = this.component && this.inputEl().length;
22423 if (typeof(this.minViewMode === 'string')) {
22424 switch (this.minViewMode) {
22426 this.minViewMode = 1;
22429 this.minViewMode = 2;
22432 this.minViewMode = 0;
22437 if (typeof(this.viewMode === 'string')) {
22438 switch (this.viewMode) {
22451 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22453 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22455 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22457 this.picker().on('mousedown', this.onMousedown, this);
22458 this.picker().on('click', this.onClick, this);
22460 this.picker().addClass('datepicker-dropdown');
22462 this.startViewMode = this.viewMode;
22464 if(this.singleMode){
22465 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22466 v.setVisibilityMode(Roo.Element.DISPLAY);
22470 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22471 v.setStyle('width', '189px');
22475 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22476 if(!this.calendarWeeks){
22481 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22482 v.attr('colspan', function(i, val){
22483 return parseInt(val) + 1;
22488 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22490 this.setStartDate(this.startDate);
22491 this.setEndDate(this.endDate);
22493 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22500 if(this.isInline) {
22505 picker : function()
22507 return this.pickerEl;
22508 // return this.el.select('.datepicker', true).first();
22511 fillDow: function()
22513 var dowCnt = this.weekStart;
22522 if(this.calendarWeeks){
22530 while (dowCnt < this.weekStart + 7) {
22534 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22538 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22541 fillMonths: function()
22544 var months = this.picker().select('>.datepicker-months td', true).first();
22546 months.dom.innerHTML = '';
22552 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22555 months.createChild(month);
22562 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;
22564 if (this.date < this.startDate) {
22565 this.viewDate = new Date(this.startDate);
22566 } else if (this.date > this.endDate) {
22567 this.viewDate = new Date(this.endDate);
22569 this.viewDate = new Date(this.date);
22577 var d = new Date(this.viewDate),
22578 year = d.getUTCFullYear(),
22579 month = d.getUTCMonth(),
22580 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22581 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22582 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22583 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22584 currentDate = this.date && this.date.valueOf(),
22585 today = this.UTCToday();
22587 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22589 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22591 // this.picker.select('>tfoot th.today').
22592 // .text(dates[this.language].today)
22593 // .toggle(this.todayBtn !== false);
22595 this.updateNavArrows();
22598 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22600 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22602 prevMonth.setUTCDate(day);
22604 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22606 var nextMonth = new Date(prevMonth);
22608 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22610 nextMonth = nextMonth.valueOf();
22612 var fillMonths = false;
22614 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22616 while(prevMonth.valueOf() <= nextMonth) {
22619 if (prevMonth.getUTCDay() === this.weekStart) {
22621 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22629 if(this.calendarWeeks){
22630 // ISO 8601: First week contains first thursday.
22631 // ISO also states week starts on Monday, but we can be more abstract here.
22633 // Start of current week: based on weekstart/current date
22634 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22635 // Thursday of this week
22636 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22637 // First Thursday of year, year from thursday
22638 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22639 // Calendar week: ms between thursdays, div ms per day, div 7 days
22640 calWeek = (th - yth) / 864e5 / 7 + 1;
22642 fillMonths.cn.push({
22650 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22652 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22655 if (this.todayHighlight &&
22656 prevMonth.getUTCFullYear() == today.getFullYear() &&
22657 prevMonth.getUTCMonth() == today.getMonth() &&
22658 prevMonth.getUTCDate() == today.getDate()) {
22659 clsName += ' today';
22662 if (currentDate && prevMonth.valueOf() === currentDate) {
22663 clsName += ' active';
22666 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22667 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22668 clsName += ' disabled';
22671 fillMonths.cn.push({
22673 cls: 'day ' + clsName,
22674 html: prevMonth.getDate()
22677 prevMonth.setDate(prevMonth.getDate()+1);
22680 var currentYear = this.date && this.date.getUTCFullYear();
22681 var currentMonth = this.date && this.date.getUTCMonth();
22683 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22685 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22686 v.removeClass('active');
22688 if(currentYear === year && k === currentMonth){
22689 v.addClass('active');
22692 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22693 v.addClass('disabled');
22699 year = parseInt(year/10, 10) * 10;
22701 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22703 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22706 for (var i = -1; i < 11; i++) {
22707 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22709 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22717 showMode: function(dir)
22720 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22723 Roo.each(this.picker().select('>div',true).elements, function(v){
22724 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22727 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22732 if(this.isInline) {
22736 this.picker().removeClass(['bottom', 'top']);
22738 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22740 * place to the top of element!
22744 this.picker().addClass('top');
22745 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22750 this.picker().addClass('bottom');
22752 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22755 parseDate : function(value)
22757 if(!value || value instanceof Date){
22760 var v = Date.parseDate(value, this.format);
22761 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22762 v = Date.parseDate(value, 'Y-m-d');
22764 if(!v && this.altFormats){
22765 if(!this.altFormatsArray){
22766 this.altFormatsArray = this.altFormats.split("|");
22768 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22769 v = Date.parseDate(value, this.altFormatsArray[i]);
22775 formatDate : function(date, fmt)
22777 return (!date || !(date instanceof Date)) ?
22778 date : date.dateFormat(fmt || this.format);
22781 onFocus : function()
22783 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22787 onBlur : function()
22789 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22791 var d = this.inputEl().getValue();
22798 showPopup : function()
22800 this.picker().show();
22804 this.fireEvent('showpopup', this, this.date);
22807 hidePopup : function()
22809 if(this.isInline) {
22812 this.picker().hide();
22813 this.viewMode = this.startViewMode;
22816 this.fireEvent('hidepopup', this, this.date);
22820 onMousedown: function(e)
22822 e.stopPropagation();
22823 e.preventDefault();
22828 Roo.bootstrap.DateField.superclass.keyup.call(this);
22832 setValue: function(v)
22834 if(this.fireEvent('beforeselect', this, v) !== false){
22835 var d = new Date(this.parseDate(v) ).clearTime();
22837 if(isNaN(d.getTime())){
22838 this.date = this.viewDate = '';
22839 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22843 v = this.formatDate(d);
22845 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22847 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22851 this.fireEvent('select', this, this.date);
22855 getValue: function()
22857 return this.formatDate(this.date);
22860 fireKey: function(e)
22862 if (!this.picker().isVisible()){
22863 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22869 var dateChanged = false,
22871 newDate, newViewDate;
22876 e.preventDefault();
22880 if (!this.keyboardNavigation) {
22883 dir = e.keyCode == 37 ? -1 : 1;
22886 newDate = this.moveYear(this.date, dir);
22887 newViewDate = this.moveYear(this.viewDate, dir);
22888 } else if (e.shiftKey){
22889 newDate = this.moveMonth(this.date, dir);
22890 newViewDate = this.moveMonth(this.viewDate, dir);
22892 newDate = new Date(this.date);
22893 newDate.setUTCDate(this.date.getUTCDate() + dir);
22894 newViewDate = new Date(this.viewDate);
22895 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22897 if (this.dateWithinRange(newDate)){
22898 this.date = newDate;
22899 this.viewDate = newViewDate;
22900 this.setValue(this.formatDate(this.date));
22902 e.preventDefault();
22903 dateChanged = true;
22908 if (!this.keyboardNavigation) {
22911 dir = e.keyCode == 38 ? -1 : 1;
22913 newDate = this.moveYear(this.date, dir);
22914 newViewDate = this.moveYear(this.viewDate, dir);
22915 } else if (e.shiftKey){
22916 newDate = this.moveMonth(this.date, dir);
22917 newViewDate = this.moveMonth(this.viewDate, dir);
22919 newDate = new Date(this.date);
22920 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22921 newViewDate = new Date(this.viewDate);
22922 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22924 if (this.dateWithinRange(newDate)){
22925 this.date = newDate;
22926 this.viewDate = newViewDate;
22927 this.setValue(this.formatDate(this.date));
22929 e.preventDefault();
22930 dateChanged = true;
22934 this.setValue(this.formatDate(this.date));
22936 e.preventDefault();
22939 this.setValue(this.formatDate(this.date));
22953 onClick: function(e)
22955 e.stopPropagation();
22956 e.preventDefault();
22958 var target = e.getTarget();
22960 if(target.nodeName.toLowerCase() === 'i'){
22961 target = Roo.get(target).dom.parentNode;
22964 var nodeName = target.nodeName;
22965 var className = target.className;
22966 var html = target.innerHTML;
22967 //Roo.log(nodeName);
22969 switch(nodeName.toLowerCase()) {
22971 switch(className) {
22977 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22978 switch(this.viewMode){
22980 this.viewDate = this.moveMonth(this.viewDate, dir);
22984 this.viewDate = this.moveYear(this.viewDate, dir);
22990 var date = new Date();
22991 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
22993 this.setValue(this.formatDate(this.date));
23000 if (className.indexOf('disabled') < 0) {
23001 if (!this.viewDate) {
23002 this.viewDate = new Date();
23004 this.viewDate.setUTCDate(1);
23005 if (className.indexOf('month') > -1) {
23006 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23008 var year = parseInt(html, 10) || 0;
23009 this.viewDate.setUTCFullYear(year);
23013 if(this.singleMode){
23014 this.setValue(this.formatDate(this.viewDate));
23025 //Roo.log(className);
23026 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23027 var day = parseInt(html, 10) || 1;
23028 var year = (this.viewDate || new Date()).getUTCFullYear(),
23029 month = (this.viewDate || new Date()).getUTCMonth();
23031 if (className.indexOf('old') > -1) {
23038 } else if (className.indexOf('new') > -1) {
23046 //Roo.log([year,month,day]);
23047 this.date = this.UTCDate(year, month, day,0,0,0,0);
23048 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23050 //Roo.log(this.formatDate(this.date));
23051 this.setValue(this.formatDate(this.date));
23058 setStartDate: function(startDate)
23060 this.startDate = startDate || -Infinity;
23061 if (this.startDate !== -Infinity) {
23062 this.startDate = this.parseDate(this.startDate);
23065 this.updateNavArrows();
23068 setEndDate: function(endDate)
23070 this.endDate = endDate || Infinity;
23071 if (this.endDate !== Infinity) {
23072 this.endDate = this.parseDate(this.endDate);
23075 this.updateNavArrows();
23078 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23080 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23081 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23082 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23084 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23085 return parseInt(d, 10);
23088 this.updateNavArrows();
23091 updateNavArrows: function()
23093 if(this.singleMode){
23097 var d = new Date(this.viewDate),
23098 year = d.getUTCFullYear(),
23099 month = d.getUTCMonth();
23101 Roo.each(this.picker().select('.prev', true).elements, function(v){
23103 switch (this.viewMode) {
23106 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23112 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23119 Roo.each(this.picker().select('.next', true).elements, function(v){
23121 switch (this.viewMode) {
23124 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23130 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23138 moveMonth: function(date, dir)
23143 var new_date = new Date(date.valueOf()),
23144 day = new_date.getUTCDate(),
23145 month = new_date.getUTCMonth(),
23146 mag = Math.abs(dir),
23148 dir = dir > 0 ? 1 : -1;
23151 // If going back one month, make sure month is not current month
23152 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23154 return new_date.getUTCMonth() == month;
23156 // If going forward one month, make sure month is as expected
23157 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23159 return new_date.getUTCMonth() != new_month;
23161 new_month = month + dir;
23162 new_date.setUTCMonth(new_month);
23163 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23164 if (new_month < 0 || new_month > 11) {
23165 new_month = (new_month + 12) % 12;
23168 // For magnitudes >1, move one month at a time...
23169 for (var i=0; i<mag; i++) {
23170 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23171 new_date = this.moveMonth(new_date, dir);
23173 // ...then reset the day, keeping it in the new month
23174 new_month = new_date.getUTCMonth();
23175 new_date.setUTCDate(day);
23177 return new_month != new_date.getUTCMonth();
23180 // Common date-resetting loop -- if date is beyond end of month, make it
23183 new_date.setUTCDate(--day);
23184 new_date.setUTCMonth(new_month);
23189 moveYear: function(date, dir)
23191 return this.moveMonth(date, dir*12);
23194 dateWithinRange: function(date)
23196 return date >= this.startDate && date <= this.endDate;
23202 this.picker().remove();
23205 validateValue : function(value)
23207 if(this.getVisibilityEl().hasClass('hidden')){
23211 if(value.length < 1) {
23212 if(this.allowBlank){
23218 if(value.length < this.minLength){
23221 if(value.length > this.maxLength){
23225 var vt = Roo.form.VTypes;
23226 if(!vt[this.vtype](value, this)){
23230 if(typeof this.validator == "function"){
23231 var msg = this.validator(value);
23237 if(this.regex && !this.regex.test(value)){
23241 if(typeof(this.parseDate(value)) == 'undefined'){
23245 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23249 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23259 this.date = this.viewDate = '';
23261 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23266 Roo.apply(Roo.bootstrap.DateField, {
23277 html: '<i class="fa fa-arrow-left"/>'
23287 html: '<i class="fa fa-arrow-right"/>'
23329 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23330 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23331 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23332 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23333 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23346 navFnc: 'FullYear',
23351 navFnc: 'FullYear',
23356 Roo.apply(Roo.bootstrap.DateField, {
23360 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23364 cls: 'datepicker-days',
23368 cls: 'table-condensed',
23370 Roo.bootstrap.DateField.head,
23374 Roo.bootstrap.DateField.footer
23381 cls: 'datepicker-months',
23385 cls: 'table-condensed',
23387 Roo.bootstrap.DateField.head,
23388 Roo.bootstrap.DateField.content,
23389 Roo.bootstrap.DateField.footer
23396 cls: 'datepicker-years',
23400 cls: 'table-condensed',
23402 Roo.bootstrap.DateField.head,
23403 Roo.bootstrap.DateField.content,
23404 Roo.bootstrap.DateField.footer
23423 * @class Roo.bootstrap.TimeField
23424 * @extends Roo.bootstrap.Input
23425 * Bootstrap DateField class
23429 * Create a new TimeField
23430 * @param {Object} config The config object
23433 Roo.bootstrap.TimeField = function(config){
23434 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23438 * Fires when this field show.
23439 * @param {Roo.bootstrap.DateField} thisthis
23440 * @param {Mixed} date The date value
23445 * Fires when this field hide.
23446 * @param {Roo.bootstrap.DateField} this
23447 * @param {Mixed} date The date value
23452 * Fires when select a date.
23453 * @param {Roo.bootstrap.DateField} this
23454 * @param {Mixed} date The date value
23460 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23463 * @cfg {String} format
23464 * The default time format string which can be overriden for localization support. The format must be
23465 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23469 getAutoCreate : function()
23471 this.after = '<i class="fa far fa-clock"></i>';
23472 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23476 onRender: function(ct, position)
23479 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23481 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23483 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23485 this.pop = this.picker().select('>.datepicker-time',true).first();
23486 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23488 this.picker().on('mousedown', this.onMousedown, this);
23489 this.picker().on('click', this.onClick, this);
23491 this.picker().addClass('datepicker-dropdown');
23496 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23497 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23498 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23499 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23500 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23501 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23505 fireKey: function(e){
23506 if (!this.picker().isVisible()){
23507 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23513 e.preventDefault();
23521 this.onTogglePeriod();
23524 this.onIncrementMinutes();
23527 this.onDecrementMinutes();
23536 onClick: function(e) {
23537 e.stopPropagation();
23538 e.preventDefault();
23541 picker : function()
23543 return this.pickerEl;
23546 fillTime: function()
23548 var time = this.pop.select('tbody', true).first();
23550 time.dom.innerHTML = '';
23565 cls: 'hours-up fa fas fa-chevron-up'
23585 cls: 'minutes-up fa fas fa-chevron-up'
23606 cls: 'timepicker-hour',
23621 cls: 'timepicker-minute',
23636 cls: 'btn btn-primary period',
23658 cls: 'hours-down fa fas fa-chevron-down'
23678 cls: 'minutes-down fa fas fa-chevron-down'
23696 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23703 var hours = this.time.getHours();
23704 var minutes = this.time.getMinutes();
23717 hours = hours - 12;
23721 hours = '0' + hours;
23725 minutes = '0' + minutes;
23728 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23729 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23730 this.pop.select('button', true).first().dom.innerHTML = period;
23736 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23738 var cls = ['bottom'];
23740 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23747 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23751 //this.picker().setXY(20000,20000);
23752 this.picker().addClass(cls.join('-'));
23756 Roo.each(cls, function(c){
23761 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23762 //_this.picker().setTop(_this.inputEl().getHeight());
23766 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23768 //_this.picker().setTop(0 - _this.picker().getHeight());
23773 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23777 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23785 onFocus : function()
23787 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23791 onBlur : function()
23793 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23799 this.picker().show();
23804 this.fireEvent('show', this, this.date);
23809 this.picker().hide();
23812 this.fireEvent('hide', this, this.date);
23815 setTime : function()
23818 this.setValue(this.time.format(this.format));
23820 this.fireEvent('select', this, this.date);
23825 onMousedown: function(e){
23826 e.stopPropagation();
23827 e.preventDefault();
23830 onIncrementHours: function()
23832 Roo.log('onIncrementHours');
23833 this.time = this.time.add(Date.HOUR, 1);
23838 onDecrementHours: function()
23840 Roo.log('onDecrementHours');
23841 this.time = this.time.add(Date.HOUR, -1);
23845 onIncrementMinutes: function()
23847 Roo.log('onIncrementMinutes');
23848 this.time = this.time.add(Date.MINUTE, 1);
23852 onDecrementMinutes: function()
23854 Roo.log('onDecrementMinutes');
23855 this.time = this.time.add(Date.MINUTE, -1);
23859 onTogglePeriod: function()
23861 Roo.log('onTogglePeriod');
23862 this.time = this.time.add(Date.HOUR, 12);
23870 Roo.apply(Roo.bootstrap.TimeField, {
23874 cls: 'datepicker dropdown-menu',
23878 cls: 'datepicker-time',
23882 cls: 'table-condensed',
23911 cls: 'btn btn-info ok',
23939 * @class Roo.bootstrap.MonthField
23940 * @extends Roo.bootstrap.Input
23941 * Bootstrap MonthField class
23943 * @cfg {String} language default en
23946 * Create a new MonthField
23947 * @param {Object} config The config object
23950 Roo.bootstrap.MonthField = function(config){
23951 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23956 * Fires when this field show.
23957 * @param {Roo.bootstrap.MonthField} this
23958 * @param {Mixed} date The date value
23963 * Fires when this field hide.
23964 * @param {Roo.bootstrap.MonthField} this
23965 * @param {Mixed} date The date value
23970 * Fires when select a date.
23971 * @param {Roo.bootstrap.MonthField} this
23972 * @param {String} oldvalue The old value
23973 * @param {String} newvalue The new value
23979 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
23981 onRender: function(ct, position)
23984 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23986 this.language = this.language || 'en';
23987 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23988 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23990 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
23991 this.isInline = false;
23992 this.isInput = true;
23993 this.component = this.el.select('.add-on', true).first() || false;
23994 this.component = (this.component && this.component.length === 0) ? false : this.component;
23995 this.hasInput = this.component && this.inputEL().length;
23997 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
23999 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24001 this.picker().on('mousedown', this.onMousedown, this);
24002 this.picker().on('click', this.onClick, this);
24004 this.picker().addClass('datepicker-dropdown');
24006 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24007 v.setStyle('width', '189px');
24014 if(this.isInline) {
24020 setValue: function(v, suppressEvent)
24022 var o = this.getValue();
24024 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24028 if(suppressEvent !== true){
24029 this.fireEvent('select', this, o, v);
24034 getValue: function()
24039 onClick: function(e)
24041 e.stopPropagation();
24042 e.preventDefault();
24044 var target = e.getTarget();
24046 if(target.nodeName.toLowerCase() === 'i'){
24047 target = Roo.get(target).dom.parentNode;
24050 var nodeName = target.nodeName;
24051 var className = target.className;
24052 var html = target.innerHTML;
24054 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24058 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24060 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24066 picker : function()
24068 return this.pickerEl;
24071 fillMonths: function()
24074 var months = this.picker().select('>.datepicker-months td', true).first();
24076 months.dom.innerHTML = '';
24082 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24085 months.createChild(month);
24094 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24095 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24098 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24099 e.removeClass('active');
24101 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24102 e.addClass('active');
24109 if(this.isInline) {
24113 this.picker().removeClass(['bottom', 'top']);
24115 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24117 * place to the top of element!
24121 this.picker().addClass('top');
24122 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24127 this.picker().addClass('bottom');
24129 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24132 onFocus : function()
24134 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24138 onBlur : function()
24140 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24142 var d = this.inputEl().getValue();
24151 this.picker().show();
24152 this.picker().select('>.datepicker-months', true).first().show();
24156 this.fireEvent('show', this, this.date);
24161 if(this.isInline) {
24164 this.picker().hide();
24165 this.fireEvent('hide', this, this.date);
24169 onMousedown: function(e)
24171 e.stopPropagation();
24172 e.preventDefault();
24177 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24181 fireKey: function(e)
24183 if (!this.picker().isVisible()){
24184 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24195 e.preventDefault();
24199 dir = e.keyCode == 37 ? -1 : 1;
24201 this.vIndex = this.vIndex + dir;
24203 if(this.vIndex < 0){
24207 if(this.vIndex > 11){
24211 if(isNaN(this.vIndex)){
24215 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24221 dir = e.keyCode == 38 ? -1 : 1;
24223 this.vIndex = this.vIndex + dir * 4;
24225 if(this.vIndex < 0){
24229 if(this.vIndex > 11){
24233 if(isNaN(this.vIndex)){
24237 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24242 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24243 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24247 e.preventDefault();
24250 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24251 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24267 this.picker().remove();
24272 Roo.apply(Roo.bootstrap.MonthField, {
24291 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24292 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24297 Roo.apply(Roo.bootstrap.MonthField, {
24301 cls: 'datepicker dropdown-menu roo-dynamic',
24305 cls: 'datepicker-months',
24309 cls: 'table-condensed',
24311 Roo.bootstrap.DateField.content
24331 * @class Roo.bootstrap.CheckBox
24332 * @extends Roo.bootstrap.Input
24333 * Bootstrap CheckBox class
24335 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24336 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24337 * @cfg {String} boxLabel The text that appears beside the checkbox
24338 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24339 * @cfg {Boolean} checked initnal the element
24340 * @cfg {Boolean} inline inline the element (default false)
24341 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24342 * @cfg {String} tooltip label tooltip
24345 * Create a new CheckBox
24346 * @param {Object} config The config object
24349 Roo.bootstrap.CheckBox = function(config){
24350 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24355 * Fires when the element is checked or unchecked.
24356 * @param {Roo.bootstrap.CheckBox} this This input
24357 * @param {Boolean} checked The new checked value
24362 * Fires when the element is click.
24363 * @param {Roo.bootstrap.CheckBox} this This input
24370 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24372 inputType: 'checkbox',
24381 // checkbox success does not make any sense really..
24386 getAutoCreate : function()
24388 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24394 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24397 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24403 type : this.inputType,
24404 value : this.inputValue,
24405 cls : 'roo-' + this.inputType, //'form-box',
24406 placeholder : this.placeholder || ''
24410 if(this.inputType != 'radio'){
24414 cls : 'roo-hidden-value',
24415 value : this.checked ? this.inputValue : this.valueOff
24420 if (this.weight) { // Validity check?
24421 cfg.cls += " " + this.inputType + "-" + this.weight;
24424 if (this.disabled) {
24425 input.disabled=true;
24429 input.checked = this.checked;
24434 input.name = this.name;
24436 if(this.inputType != 'radio'){
24437 hidden.name = this.name;
24438 input.name = '_hidden_' + this.name;
24443 input.cls += ' input-' + this.size;
24448 ['xs','sm','md','lg'].map(function(size){
24449 if (settings[size]) {
24450 cfg.cls += ' col-' + size + '-' + settings[size];
24454 var inputblock = input;
24456 if (this.before || this.after) {
24459 cls : 'input-group',
24464 inputblock.cn.push({
24466 cls : 'input-group-addon',
24471 inputblock.cn.push(input);
24473 if(this.inputType != 'radio'){
24474 inputblock.cn.push(hidden);
24478 inputblock.cn.push({
24480 cls : 'input-group-addon',
24486 var boxLabelCfg = false;
24492 //'for': id, // box label is handled by onclick - so no for...
24494 html: this.boxLabel
24497 boxLabelCfg.tooltip = this.tooltip;
24503 if (align ==='left' && this.fieldLabel.length) {
24504 // Roo.log("left and has label");
24509 cls : 'control-label',
24510 html : this.fieldLabel
24521 cfg.cn[1].cn.push(boxLabelCfg);
24524 if(this.labelWidth > 12){
24525 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24528 if(this.labelWidth < 13 && this.labelmd == 0){
24529 this.labelmd = this.labelWidth;
24532 if(this.labellg > 0){
24533 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24534 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24537 if(this.labelmd > 0){
24538 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24539 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24542 if(this.labelsm > 0){
24543 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24544 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24547 if(this.labelxs > 0){
24548 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24549 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24552 } else if ( this.fieldLabel.length) {
24553 // Roo.log(" label");
24557 tag: this.boxLabel ? 'span' : 'label',
24559 cls: 'control-label box-input-label',
24560 //cls : 'input-group-addon',
24561 html : this.fieldLabel
24568 cfg.cn.push(boxLabelCfg);
24573 // Roo.log(" no label && no align");
24574 cfg.cn = [ inputblock ] ;
24576 cfg.cn.push(boxLabelCfg);
24584 if(this.inputType != 'radio'){
24585 cfg.cn.push(hidden);
24593 * return the real input element.
24595 inputEl: function ()
24597 return this.el.select('input.roo-' + this.inputType,true).first();
24599 hiddenEl: function ()
24601 return this.el.select('input.roo-hidden-value',true).first();
24604 labelEl: function()
24606 return this.el.select('label.control-label',true).first();
24608 /* depricated... */
24612 return this.labelEl();
24615 boxLabelEl: function()
24617 return this.el.select('label.box-label',true).first();
24620 initEvents : function()
24622 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24624 this.inputEl().on('click', this.onClick, this);
24626 if (this.boxLabel) {
24627 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24630 this.startValue = this.getValue();
24633 Roo.bootstrap.CheckBox.register(this);
24637 onClick : function(e)
24639 if(this.fireEvent('click', this, e) !== false){
24640 this.setChecked(!this.checked);
24645 setChecked : function(state,suppressEvent)
24647 this.startValue = this.getValue();
24649 if(this.inputType == 'radio'){
24651 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24652 e.dom.checked = false;
24655 this.inputEl().dom.checked = true;
24657 this.inputEl().dom.value = this.inputValue;
24659 if(suppressEvent !== true){
24660 this.fireEvent('check', this, true);
24668 this.checked = state;
24670 this.inputEl().dom.checked = state;
24673 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24675 if(suppressEvent !== true){
24676 this.fireEvent('check', this, state);
24682 getValue : function()
24684 if(this.inputType == 'radio'){
24685 return this.getGroupValue();
24688 return this.hiddenEl().dom.value;
24692 getGroupValue : function()
24694 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24698 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24701 setValue : function(v,suppressEvent)
24703 if(this.inputType == 'radio'){
24704 this.setGroupValue(v, suppressEvent);
24708 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24713 setGroupValue : function(v, suppressEvent)
24715 this.startValue = this.getValue();
24717 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24718 e.dom.checked = false;
24720 if(e.dom.value == v){
24721 e.dom.checked = true;
24725 if(suppressEvent !== true){
24726 this.fireEvent('check', this, true);
24734 validate : function()
24736 if(this.getVisibilityEl().hasClass('hidden')){
24742 (this.inputType == 'radio' && this.validateRadio()) ||
24743 (this.inputType == 'checkbox' && this.validateCheckbox())
24749 this.markInvalid();
24753 validateRadio : function()
24755 if(this.getVisibilityEl().hasClass('hidden')){
24759 if(this.allowBlank){
24765 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24766 if(!e.dom.checked){
24778 validateCheckbox : function()
24781 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24782 //return (this.getValue() == this.inputValue) ? true : false;
24785 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24793 for(var i in group){
24794 if(group[i].el.isVisible(true)){
24802 for(var i in group){
24807 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24814 * Mark this field as valid
24816 markValid : function()
24820 this.fireEvent('valid', this);
24822 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24825 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24832 if(this.inputType == 'radio'){
24833 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24834 var fg = e.findParent('.form-group', false, true);
24835 if (Roo.bootstrap.version == 3) {
24836 fg.removeClass([_this.invalidClass, _this.validClass]);
24837 fg.addClass(_this.validClass);
24839 fg.removeClass(['is-valid', 'is-invalid']);
24840 fg.addClass('is-valid');
24848 var fg = this.el.findParent('.form-group', false, true);
24849 if (Roo.bootstrap.version == 3) {
24850 fg.removeClass([this.invalidClass, this.validClass]);
24851 fg.addClass(this.validClass);
24853 fg.removeClass(['is-valid', 'is-invalid']);
24854 fg.addClass('is-valid');
24859 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24865 for(var i in group){
24866 var fg = group[i].el.findParent('.form-group', false, true);
24867 if (Roo.bootstrap.version == 3) {
24868 fg.removeClass([this.invalidClass, this.validClass]);
24869 fg.addClass(this.validClass);
24871 fg.removeClass(['is-valid', 'is-invalid']);
24872 fg.addClass('is-valid');
24878 * Mark this field as invalid
24879 * @param {String} msg The validation message
24881 markInvalid : function(msg)
24883 if(this.allowBlank){
24889 this.fireEvent('invalid', this, msg);
24891 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24894 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24898 label.markInvalid();
24901 if(this.inputType == 'radio'){
24903 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24904 var fg = e.findParent('.form-group', false, true);
24905 if (Roo.bootstrap.version == 3) {
24906 fg.removeClass([_this.invalidClass, _this.validClass]);
24907 fg.addClass(_this.invalidClass);
24909 fg.removeClass(['is-invalid', 'is-valid']);
24910 fg.addClass('is-invalid');
24918 var fg = this.el.findParent('.form-group', false, true);
24919 if (Roo.bootstrap.version == 3) {
24920 fg.removeClass([_this.invalidClass, _this.validClass]);
24921 fg.addClass(_this.invalidClass);
24923 fg.removeClass(['is-invalid', 'is-valid']);
24924 fg.addClass('is-invalid');
24929 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24935 for(var i in group){
24936 var fg = group[i].el.findParent('.form-group', false, true);
24937 if (Roo.bootstrap.version == 3) {
24938 fg.removeClass([_this.invalidClass, _this.validClass]);
24939 fg.addClass(_this.invalidClass);
24941 fg.removeClass(['is-invalid', 'is-valid']);
24942 fg.addClass('is-invalid');
24948 clearInvalid : function()
24950 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24952 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24954 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24956 if (label && label.iconEl) {
24957 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24958 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24962 disable : function()
24964 if(this.inputType != 'radio'){
24965 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24972 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24973 _this.getActionEl().addClass(this.disabledClass);
24974 e.dom.disabled = true;
24978 this.disabled = true;
24979 this.fireEvent("disable", this);
24983 enable : function()
24985 if(this.inputType != 'radio'){
24986 Roo.bootstrap.CheckBox.superclass.enable.call(this);
24993 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24994 _this.getActionEl().removeClass(this.disabledClass);
24995 e.dom.disabled = false;
24999 this.disabled = false;
25000 this.fireEvent("enable", this);
25004 setBoxLabel : function(v)
25009 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25015 Roo.apply(Roo.bootstrap.CheckBox, {
25020 * register a CheckBox Group
25021 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25023 register : function(checkbox)
25025 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25026 this.groups[checkbox.groupId] = {};
25029 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25033 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25037 * fetch a CheckBox Group based on the group ID
25038 * @param {string} the group ID
25039 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25041 get: function(groupId) {
25042 if (typeof(this.groups[groupId]) == 'undefined') {
25046 return this.groups[groupId] ;
25059 * @class Roo.bootstrap.Radio
25060 * @extends Roo.bootstrap.Component
25061 * Bootstrap Radio class
25062 * @cfg {String} boxLabel - the label associated
25063 * @cfg {String} value - the value of radio
25066 * Create a new Radio
25067 * @param {Object} config The config object
25069 Roo.bootstrap.Radio = function(config){
25070 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25074 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25080 getAutoCreate : function()
25084 cls : 'form-group radio',
25089 html : this.boxLabel
25097 initEvents : function()
25099 this.parent().register(this);
25101 this.el.on('click', this.onClick, this);
25105 onClick : function(e)
25107 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25108 this.setChecked(true);
25112 setChecked : function(state, suppressEvent)
25114 this.parent().setValue(this.value, suppressEvent);
25118 setBoxLabel : function(v)
25123 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25138 * @class Roo.bootstrap.SecurePass
25139 * @extends Roo.bootstrap.Input
25140 * Bootstrap SecurePass class
25144 * Create a new SecurePass
25145 * @param {Object} config The config object
25148 Roo.bootstrap.SecurePass = function (config) {
25149 // these go here, so the translation tool can replace them..
25151 PwdEmpty: "Please type a password, and then retype it to confirm.",
25152 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25153 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25154 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25155 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25156 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25157 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25158 TooWeak: "Your password is Too Weak."
25160 this.meterLabel = "Password strength:";
25161 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25162 this.meterClass = [
25163 "roo-password-meter-tooweak",
25164 "roo-password-meter-weak",
25165 "roo-password-meter-medium",
25166 "roo-password-meter-strong",
25167 "roo-password-meter-grey"
25172 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25175 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25177 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25179 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25180 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25181 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25182 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25183 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25184 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25185 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25195 * @cfg {String/Object} Label for the strength meter (defaults to
25196 * 'Password strength:')
25201 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25202 * ['Weak', 'Medium', 'Strong'])
25205 pwdStrengths: false,
25218 initEvents: function ()
25220 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25222 if (this.el.is('input[type=password]') && Roo.isSafari) {
25223 this.el.on('keydown', this.SafariOnKeyDown, this);
25226 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25229 onRender: function (ct, position)
25231 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25232 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25233 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25235 this.trigger.createChild({
25240 cls: 'roo-password-meter-grey col-xs-12',
25243 //width: this.meterWidth + 'px'
25247 cls: 'roo-password-meter-text'
25253 if (this.hideTrigger) {
25254 this.trigger.setDisplayed(false);
25256 this.setSize(this.width || '', this.height || '');
25259 onDestroy: function ()
25261 if (this.trigger) {
25262 this.trigger.removeAllListeners();
25263 this.trigger.remove();
25266 this.wrap.remove();
25268 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25271 checkStrength: function ()
25273 var pwd = this.inputEl().getValue();
25274 if (pwd == this._lastPwd) {
25279 if (this.ClientSideStrongPassword(pwd)) {
25281 } else if (this.ClientSideMediumPassword(pwd)) {
25283 } else if (this.ClientSideWeakPassword(pwd)) {
25289 Roo.log('strength1: ' + strength);
25291 //var pm = this.trigger.child('div/div/div').dom;
25292 var pm = this.trigger.child('div/div');
25293 pm.removeClass(this.meterClass);
25294 pm.addClass(this.meterClass[strength]);
25297 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25299 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25301 this._lastPwd = pwd;
25305 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25307 this._lastPwd = '';
25309 var pm = this.trigger.child('div/div');
25310 pm.removeClass(this.meterClass);
25311 pm.addClass('roo-password-meter-grey');
25314 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25317 this.inputEl().dom.type='password';
25320 validateValue: function (value)
25322 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25325 if (value.length == 0) {
25326 if (this.allowBlank) {
25327 this.clearInvalid();
25331 this.markInvalid(this.errors.PwdEmpty);
25332 this.errorMsg = this.errors.PwdEmpty;
25340 if (!value.match(/[\x21-\x7e]+/)) {
25341 this.markInvalid(this.errors.PwdBadChar);
25342 this.errorMsg = this.errors.PwdBadChar;
25345 if (value.length < 6) {
25346 this.markInvalid(this.errors.PwdShort);
25347 this.errorMsg = this.errors.PwdShort;
25350 if (value.length > 16) {
25351 this.markInvalid(this.errors.PwdLong);
25352 this.errorMsg = this.errors.PwdLong;
25356 if (this.ClientSideStrongPassword(value)) {
25358 } else if (this.ClientSideMediumPassword(value)) {
25360 } else if (this.ClientSideWeakPassword(value)) {
25367 if (strength < 2) {
25368 //this.markInvalid(this.errors.TooWeak);
25369 this.errorMsg = this.errors.TooWeak;
25374 console.log('strength2: ' + strength);
25376 //var pm = this.trigger.child('div/div/div').dom;
25378 var pm = this.trigger.child('div/div');
25379 pm.removeClass(this.meterClass);
25380 pm.addClass(this.meterClass[strength]);
25382 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25384 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25386 this.errorMsg = '';
25390 CharacterSetChecks: function (type)
25393 this.fResult = false;
25396 isctype: function (character, type)
25399 case this.kCapitalLetter:
25400 if (character >= 'A' && character <= 'Z') {
25405 case this.kSmallLetter:
25406 if (character >= 'a' && character <= 'z') {
25412 if (character >= '0' && character <= '9') {
25417 case this.kPunctuation:
25418 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25429 IsLongEnough: function (pwd, size)
25431 return !(pwd == null || isNaN(size) || pwd.length < size);
25434 SpansEnoughCharacterSets: function (word, nb)
25436 if (!this.IsLongEnough(word, nb))
25441 var characterSetChecks = new Array(
25442 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25443 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25446 for (var index = 0; index < word.length; ++index) {
25447 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25448 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25449 characterSetChecks[nCharSet].fResult = true;
25456 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25457 if (characterSetChecks[nCharSet].fResult) {
25462 if (nCharSets < nb) {
25468 ClientSideStrongPassword: function (pwd)
25470 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25473 ClientSideMediumPassword: function (pwd)
25475 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25478 ClientSideWeakPassword: function (pwd)
25480 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25483 })//<script type="text/javascript">
25486 * Based Ext JS Library 1.1.1
25487 * Copyright(c) 2006-2007, Ext JS, LLC.
25493 * @class Roo.HtmlEditorCore
25494 * @extends Roo.Component
25495 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25497 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25500 Roo.HtmlEditorCore = function(config){
25503 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25508 * @event initialize
25509 * Fires when the editor is fully initialized (including the iframe)
25510 * @param {Roo.HtmlEditorCore} this
25515 * Fires when the editor is first receives the focus. Any insertion must wait
25516 * until after this event.
25517 * @param {Roo.HtmlEditorCore} this
25521 * @event beforesync
25522 * Fires before the textarea is updated with content from the editor iframe. Return false
25523 * to cancel the sync.
25524 * @param {Roo.HtmlEditorCore} this
25525 * @param {String} html
25529 * @event beforepush
25530 * Fires before the iframe editor is updated with content from the textarea. Return false
25531 * to cancel the push.
25532 * @param {Roo.HtmlEditorCore} this
25533 * @param {String} html
25538 * Fires when the textarea is updated with content from the editor iframe.
25539 * @param {Roo.HtmlEditorCore} this
25540 * @param {String} html
25545 * Fires when the iframe editor is updated with content from the textarea.
25546 * @param {Roo.HtmlEditorCore} this
25547 * @param {String} html
25552 * @event editorevent
25553 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25554 * @param {Roo.HtmlEditorCore} this
25560 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25562 // defaults : white / black...
25563 this.applyBlacklists();
25570 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25574 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25580 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25585 * @cfg {Number} height (in pixels)
25589 * @cfg {Number} width (in pixels)
25594 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25597 stylesheets: false,
25602 // private properties
25603 validationEvent : false,
25605 initialized : false,
25607 sourceEditMode : false,
25608 onFocus : Roo.emptyFn,
25610 hideMode:'offsets',
25614 // blacklist + whitelisted elements..
25621 * Protected method that will not generally be called directly. It
25622 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25623 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25625 getDocMarkup : function(){
25629 // inherit styels from page...??
25630 if (this.stylesheets === false) {
25632 Roo.get(document.head).select('style').each(function(node) {
25633 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25636 Roo.get(document.head).select('link').each(function(node) {
25637 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25640 } else if (!this.stylesheets.length) {
25642 st = '<style type="text/css">' +
25643 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25646 for (var i in this.stylesheets) {
25647 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25652 st += '<style type="text/css">' +
25653 'IMG { cursor: pointer } ' +
25656 var cls = 'roo-htmleditor-body';
25658 if(this.bodyCls.length){
25659 cls += ' ' + this.bodyCls;
25662 return '<html><head>' + st +
25663 //<style type="text/css">' +
25664 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25666 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25670 onRender : function(ct, position)
25673 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25674 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25677 this.el.dom.style.border = '0 none';
25678 this.el.dom.setAttribute('tabIndex', -1);
25679 this.el.addClass('x-hidden hide');
25683 if(Roo.isIE){ // fix IE 1px bogus margin
25684 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25688 this.frameId = Roo.id();
25692 var iframe = this.owner.wrap.createChild({
25694 cls: 'form-control', // bootstrap..
25696 name: this.frameId,
25697 frameBorder : 'no',
25698 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25703 this.iframe = iframe.dom;
25705 this.assignDocWin();
25707 this.doc.designMode = 'on';
25710 this.doc.write(this.getDocMarkup());
25714 var task = { // must defer to wait for browser to be ready
25716 //console.log("run task?" + this.doc.readyState);
25717 this.assignDocWin();
25718 if(this.doc.body || this.doc.readyState == 'complete'){
25720 this.doc.designMode="on";
25724 Roo.TaskMgr.stop(task);
25725 this.initEditor.defer(10, this);
25732 Roo.TaskMgr.start(task);
25737 onResize : function(w, h)
25739 Roo.log('resize: ' +w + ',' + h );
25740 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25744 if(typeof w == 'number'){
25746 this.iframe.style.width = w + 'px';
25748 if(typeof h == 'number'){
25750 this.iframe.style.height = h + 'px';
25752 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25759 * Toggles the editor between standard and source edit mode.
25760 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25762 toggleSourceEdit : function(sourceEditMode){
25764 this.sourceEditMode = sourceEditMode === true;
25766 if(this.sourceEditMode){
25768 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25771 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25772 //this.iframe.className = '';
25775 //this.setSize(this.owner.wrap.getSize());
25776 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25783 * Protected method that will not generally be called directly. If you need/want
25784 * custom HTML cleanup, this is the method you should override.
25785 * @param {String} html The HTML to be cleaned
25786 * return {String} The cleaned HTML
25788 cleanHtml : function(html){
25789 html = String(html);
25790 if(html.length > 5){
25791 if(Roo.isSafari){ // strip safari nonsense
25792 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25795 if(html == ' '){
25802 * HTML Editor -> Textarea
25803 * Protected method that will not generally be called directly. Syncs the contents
25804 * of the editor iframe with the textarea.
25806 syncValue : function(){
25807 if(this.initialized){
25808 var bd = (this.doc.body || this.doc.documentElement);
25809 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25810 var html = bd.innerHTML;
25812 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25813 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25815 html = '<div style="'+m[0]+'">' + html + '</div>';
25818 html = this.cleanHtml(html);
25819 // fix up the special chars.. normaly like back quotes in word...
25820 // however we do not want to do this with chinese..
25821 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25823 var cc = match.charCodeAt();
25825 // Get the character value, handling surrogate pairs
25826 if (match.length == 2) {
25827 // It's a surrogate pair, calculate the Unicode code point
25828 var high = match.charCodeAt(0) - 0xD800;
25829 var low = match.charCodeAt(1) - 0xDC00;
25830 cc = (high * 0x400) + low + 0x10000;
25832 (cc >= 0x4E00 && cc < 0xA000 ) ||
25833 (cc >= 0x3400 && cc < 0x4E00 ) ||
25834 (cc >= 0xf900 && cc < 0xfb00 )
25839 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25840 return "&#" + cc + ";";
25847 if(this.owner.fireEvent('beforesync', this, html) !== false){
25848 this.el.dom.value = html;
25849 this.owner.fireEvent('sync', this, html);
25855 * Protected method that will not generally be called directly. Pushes the value of the textarea
25856 * into the iframe editor.
25858 pushValue : function(){
25859 if(this.initialized){
25860 var v = this.el.dom.value.trim();
25862 // if(v.length < 1){
25866 if(this.owner.fireEvent('beforepush', this, v) !== false){
25867 var d = (this.doc.body || this.doc.documentElement);
25869 this.cleanUpPaste();
25870 this.el.dom.value = d.innerHTML;
25871 this.owner.fireEvent('push', this, v);
25877 deferFocus : function(){
25878 this.focus.defer(10, this);
25882 focus : function(){
25883 if(this.win && !this.sourceEditMode){
25890 assignDocWin: function()
25892 var iframe = this.iframe;
25895 this.doc = iframe.contentWindow.document;
25896 this.win = iframe.contentWindow;
25898 // if (!Roo.get(this.frameId)) {
25901 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25902 // this.win = Roo.get(this.frameId).dom.contentWindow;
25904 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25908 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25909 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25914 initEditor : function(){
25915 //console.log("INIT EDITOR");
25916 this.assignDocWin();
25920 this.doc.designMode="on";
25922 this.doc.write(this.getDocMarkup());
25925 var dbody = (this.doc.body || this.doc.documentElement);
25926 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25927 // this copies styles from the containing element into thsi one..
25928 // not sure why we need all of this..
25929 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25931 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25932 //ss['background-attachment'] = 'fixed'; // w3c
25933 dbody.bgProperties = 'fixed'; // ie
25934 //Roo.DomHelper.applyStyles(dbody, ss);
25935 Roo.EventManager.on(this.doc, {
25936 //'mousedown': this.onEditorEvent,
25937 'mouseup': this.onEditorEvent,
25938 'dblclick': this.onEditorEvent,
25939 'click': this.onEditorEvent,
25940 'keyup': this.onEditorEvent,
25945 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25947 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25948 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25950 this.initialized = true;
25952 this.owner.fireEvent('initialize', this);
25957 onDestroy : function(){
25963 //for (var i =0; i < this.toolbars.length;i++) {
25964 // // fixme - ask toolbars for heights?
25965 // this.toolbars[i].onDestroy();
25968 //this.wrap.dom.innerHTML = '';
25969 //this.wrap.remove();
25974 onFirstFocus : function(){
25976 this.assignDocWin();
25979 this.activated = true;
25982 if(Roo.isGecko){ // prevent silly gecko errors
25984 var s = this.win.getSelection();
25985 if(!s.focusNode || s.focusNode.nodeType != 3){
25986 var r = s.getRangeAt(0);
25987 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25992 this.execCmd('useCSS', true);
25993 this.execCmd('styleWithCSS', false);
25996 this.owner.fireEvent('activate', this);
26000 adjustFont: function(btn){
26001 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26002 //if(Roo.isSafari){ // safari
26005 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26006 if(Roo.isSafari){ // safari
26007 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26008 v = (v < 10) ? 10 : v;
26009 v = (v > 48) ? 48 : v;
26010 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26015 v = Math.max(1, v+adjust);
26017 this.execCmd('FontSize', v );
26020 onEditorEvent : function(e)
26022 this.owner.fireEvent('editorevent', this, e);
26023 // this.updateToolbar();
26024 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26027 insertTag : function(tg)
26029 // could be a bit smarter... -> wrap the current selected tRoo..
26030 if (tg.toLowerCase() == 'span' ||
26031 tg.toLowerCase() == 'code' ||
26032 tg.toLowerCase() == 'sup' ||
26033 tg.toLowerCase() == 'sub'
26036 range = this.createRange(this.getSelection());
26037 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26038 wrappingNode.appendChild(range.extractContents());
26039 range.insertNode(wrappingNode);
26046 this.execCmd("formatblock", tg);
26050 insertText : function(txt)
26054 var range = this.createRange();
26055 range.deleteContents();
26056 //alert(Sender.getAttribute('label'));
26058 range.insertNode(this.doc.createTextNode(txt));
26064 * Executes a Midas editor command on the editor document and performs necessary focus and
26065 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26066 * @param {String} cmd The Midas command
26067 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26069 relayCmd : function(cmd, value){
26071 this.execCmd(cmd, value);
26072 this.owner.fireEvent('editorevent', this);
26073 //this.updateToolbar();
26074 this.owner.deferFocus();
26078 * Executes a Midas editor command directly on the editor document.
26079 * For visual commands, you should use {@link #relayCmd} instead.
26080 * <b>This should only be called after the editor is initialized.</b>
26081 * @param {String} cmd The Midas command
26082 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26084 execCmd : function(cmd, value){
26085 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26092 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26094 * @param {String} text | dom node..
26096 insertAtCursor : function(text)
26099 if(!this.activated){
26105 var r = this.doc.selection.createRange();
26116 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26120 // from jquery ui (MIT licenced)
26122 var win = this.win;
26124 if (win.getSelection && win.getSelection().getRangeAt) {
26125 range = win.getSelection().getRangeAt(0);
26126 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26127 range.insertNode(node);
26128 } else if (win.document.selection && win.document.selection.createRange) {
26129 // no firefox support
26130 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26131 win.document.selection.createRange().pasteHTML(txt);
26133 // no firefox support
26134 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26135 this.execCmd('InsertHTML', txt);
26144 mozKeyPress : function(e){
26146 var c = e.getCharCode(), cmd;
26149 c = String.fromCharCode(c).toLowerCase();
26163 this.cleanUpPaste.defer(100, this);
26171 e.preventDefault();
26179 fixKeys : function(){ // load time branching for fastest keydown performance
26181 return function(e){
26182 var k = e.getKey(), r;
26185 r = this.doc.selection.createRange();
26188 r.pasteHTML('    ');
26195 r = this.doc.selection.createRange();
26197 var target = r.parentElement();
26198 if(!target || target.tagName.toLowerCase() != 'li'){
26200 r.pasteHTML('<br />');
26206 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26207 this.cleanUpPaste.defer(100, this);
26213 }else if(Roo.isOpera){
26214 return function(e){
26215 var k = e.getKey();
26219 this.execCmd('InsertHTML','    ');
26222 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26223 this.cleanUpPaste.defer(100, this);
26228 }else if(Roo.isSafari){
26229 return function(e){
26230 var k = e.getKey();
26234 this.execCmd('InsertText','\t');
26238 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26239 this.cleanUpPaste.defer(100, this);
26247 getAllAncestors: function()
26249 var p = this.getSelectedNode();
26252 a.push(p); // push blank onto stack..
26253 p = this.getParentElement();
26257 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26261 a.push(this.doc.body);
26265 lastSelNode : false,
26268 getSelection : function()
26270 this.assignDocWin();
26271 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26274 getSelectedNode: function()
26276 // this may only work on Gecko!!!
26278 // should we cache this!!!!
26283 var range = this.createRange(this.getSelection()).cloneRange();
26286 var parent = range.parentElement();
26288 var testRange = range.duplicate();
26289 testRange.moveToElementText(parent);
26290 if (testRange.inRange(range)) {
26293 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26296 parent = parent.parentElement;
26301 // is ancestor a text element.
26302 var ac = range.commonAncestorContainer;
26303 if (ac.nodeType == 3) {
26304 ac = ac.parentNode;
26307 var ar = ac.childNodes;
26310 var other_nodes = [];
26311 var has_other_nodes = false;
26312 for (var i=0;i<ar.length;i++) {
26313 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26316 // fullly contained node.
26318 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26323 // probably selected..
26324 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26325 other_nodes.push(ar[i]);
26329 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26334 has_other_nodes = true;
26336 if (!nodes.length && other_nodes.length) {
26337 nodes= other_nodes;
26339 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26345 createRange: function(sel)
26347 // this has strange effects when using with
26348 // top toolbar - not sure if it's a great idea.
26349 //this.editor.contentWindow.focus();
26350 if (typeof sel != "undefined") {
26352 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26354 return this.doc.createRange();
26357 return this.doc.createRange();
26360 getParentElement: function()
26363 this.assignDocWin();
26364 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26366 var range = this.createRange(sel);
26369 var p = range.commonAncestorContainer;
26370 while (p.nodeType == 3) { // text node
26381 * Range intersection.. the hard stuff...
26385 * [ -- selected range --- ]
26389 * if end is before start or hits it. fail.
26390 * if start is after end or hits it fail.
26392 * if either hits (but other is outside. - then it's not
26398 // @see http://www.thismuchiknow.co.uk/?p=64.
26399 rangeIntersectsNode : function(range, node)
26401 var nodeRange = node.ownerDocument.createRange();
26403 nodeRange.selectNode(node);
26405 nodeRange.selectNodeContents(node);
26408 var rangeStartRange = range.cloneRange();
26409 rangeStartRange.collapse(true);
26411 var rangeEndRange = range.cloneRange();
26412 rangeEndRange.collapse(false);
26414 var nodeStartRange = nodeRange.cloneRange();
26415 nodeStartRange.collapse(true);
26417 var nodeEndRange = nodeRange.cloneRange();
26418 nodeEndRange.collapse(false);
26420 return rangeStartRange.compareBoundaryPoints(
26421 Range.START_TO_START, nodeEndRange) == -1 &&
26422 rangeEndRange.compareBoundaryPoints(
26423 Range.START_TO_START, nodeStartRange) == 1;
26427 rangeCompareNode : function(range, node)
26429 var nodeRange = node.ownerDocument.createRange();
26431 nodeRange.selectNode(node);
26433 nodeRange.selectNodeContents(node);
26437 range.collapse(true);
26439 nodeRange.collapse(true);
26441 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26442 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26444 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26446 var nodeIsBefore = ss == 1;
26447 var nodeIsAfter = ee == -1;
26449 if (nodeIsBefore && nodeIsAfter) {
26452 if (!nodeIsBefore && nodeIsAfter) {
26453 return 1; //right trailed.
26456 if (nodeIsBefore && !nodeIsAfter) {
26457 return 2; // left trailed.
26463 // private? - in a new class?
26464 cleanUpPaste : function()
26466 // cleans up the whole document..
26467 Roo.log('cleanuppaste');
26469 this.cleanUpChildren(this.doc.body);
26470 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26471 if (clean != this.doc.body.innerHTML) {
26472 this.doc.body.innerHTML = clean;
26477 cleanWordChars : function(input) {// change the chars to hex code
26478 var he = Roo.HtmlEditorCore;
26480 var output = input;
26481 Roo.each(he.swapCodes, function(sw) {
26482 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26484 output = output.replace(swapper, sw[1]);
26491 cleanUpChildren : function (n)
26493 if (!n.childNodes.length) {
26496 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26497 this.cleanUpChild(n.childNodes[i]);
26504 cleanUpChild : function (node)
26507 //console.log(node);
26508 if (node.nodeName == "#text") {
26509 // clean up silly Windows -- stuff?
26512 if (node.nodeName == "#comment") {
26513 node.parentNode.removeChild(node);
26514 // clean up silly Windows -- stuff?
26517 var lcname = node.tagName.toLowerCase();
26518 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26519 // whitelist of tags..
26521 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26523 node.parentNode.removeChild(node);
26528 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26530 // spans with no attributes - just remove them..
26531 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26532 remove_keep_children = true;
26535 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26536 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26538 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26539 // remove_keep_children = true;
26542 if (remove_keep_children) {
26543 this.cleanUpChildren(node);
26544 // inserts everything just before this node...
26545 while (node.childNodes.length) {
26546 var cn = node.childNodes[0];
26547 node.removeChild(cn);
26548 node.parentNode.insertBefore(cn, node);
26550 node.parentNode.removeChild(node);
26554 if (!node.attributes || !node.attributes.length) {
26559 this.cleanUpChildren(node);
26563 function cleanAttr(n,v)
26566 if (v.match(/^\./) || v.match(/^\//)) {
26569 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26572 if (v.match(/^#/)) {
26575 if (v.match(/^\{/)) { // allow template editing.
26578 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26579 node.removeAttribute(n);
26583 var cwhite = this.cwhite;
26584 var cblack = this.cblack;
26586 function cleanStyle(n,v)
26588 if (v.match(/expression/)) { //XSS?? should we even bother..
26589 node.removeAttribute(n);
26593 var parts = v.split(/;/);
26596 Roo.each(parts, function(p) {
26597 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26601 var l = p.split(':').shift().replace(/\s+/g,'');
26602 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26604 if ( cwhite.length && cblack.indexOf(l) > -1) {
26605 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26606 //node.removeAttribute(n);
26610 // only allow 'c whitelisted system attributes'
26611 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26612 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26613 //node.removeAttribute(n);
26623 if (clean.length) {
26624 node.setAttribute(n, clean.join(';'));
26626 node.removeAttribute(n);
26632 for (var i = node.attributes.length-1; i > -1 ; i--) {
26633 var a = node.attributes[i];
26636 if (a.name.toLowerCase().substr(0,2)=='on') {
26637 node.removeAttribute(a.name);
26640 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26641 node.removeAttribute(a.name);
26644 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26645 cleanAttr(a.name,a.value); // fixme..
26648 if (a.name == 'style') {
26649 cleanStyle(a.name,a.value);
26652 /// clean up MS crap..
26653 // tecnically this should be a list of valid class'es..
26656 if (a.name == 'class') {
26657 if (a.value.match(/^Mso/)) {
26658 node.removeAttribute('class');
26661 if (a.value.match(/^body$/)) {
26662 node.removeAttribute('class');
26673 this.cleanUpChildren(node);
26679 * Clean up MS wordisms...
26681 cleanWord : function(node)
26684 this.cleanWord(this.doc.body);
26689 node.nodeName == 'SPAN' &&
26690 !node.hasAttributes() &&
26691 node.childNodes.length == 1 &&
26692 node.firstChild.nodeName == "#text"
26694 var textNode = node.firstChild;
26695 node.removeChild(textNode);
26696 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26697 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26699 node.parentNode.insertBefore(textNode, node);
26700 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26701 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26703 node.parentNode.removeChild(node);
26706 if (node.nodeName == "#text") {
26707 // clean up silly Windows -- stuff?
26710 if (node.nodeName == "#comment") {
26711 node.parentNode.removeChild(node);
26712 // clean up silly Windows -- stuff?
26716 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26717 node.parentNode.removeChild(node);
26720 //Roo.log(node.tagName);
26721 // remove - but keep children..
26722 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26723 //Roo.log('-- removed');
26724 while (node.childNodes.length) {
26725 var cn = node.childNodes[0];
26726 node.removeChild(cn);
26727 node.parentNode.insertBefore(cn, node);
26728 // move node to parent - and clean it..
26729 this.cleanWord(cn);
26731 node.parentNode.removeChild(node);
26732 /// no need to iterate chidlren = it's got none..
26733 //this.iterateChildren(node, this.cleanWord);
26737 if (node.className.length) {
26739 var cn = node.className.split(/\W+/);
26741 Roo.each(cn, function(cls) {
26742 if (cls.match(/Mso[a-zA-Z]+/)) {
26747 node.className = cna.length ? cna.join(' ') : '';
26749 node.removeAttribute("class");
26753 if (node.hasAttribute("lang")) {
26754 node.removeAttribute("lang");
26757 if (node.hasAttribute("style")) {
26759 var styles = node.getAttribute("style").split(";");
26761 Roo.each(styles, function(s) {
26762 if (!s.match(/:/)) {
26765 var kv = s.split(":");
26766 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26769 // what ever is left... we allow.
26772 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26773 if (!nstyle.length) {
26774 node.removeAttribute('style');
26777 this.iterateChildren(node, this.cleanWord);
26783 * iterateChildren of a Node, calling fn each time, using this as the scole..
26784 * @param {DomNode} node node to iterate children of.
26785 * @param {Function} fn method of this class to call on each item.
26787 iterateChildren : function(node, fn)
26789 if (!node.childNodes.length) {
26792 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26793 fn.call(this, node.childNodes[i])
26799 * cleanTableWidths.
26801 * Quite often pasting from word etc.. results in tables with column and widths.
26802 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26805 cleanTableWidths : function(node)
26810 this.cleanTableWidths(this.doc.body);
26815 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26818 Roo.log(node.tagName);
26819 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26820 this.iterateChildren(node, this.cleanTableWidths);
26823 if (node.hasAttribute('width')) {
26824 node.removeAttribute('width');
26828 if (node.hasAttribute("style")) {
26831 var styles = node.getAttribute("style").split(";");
26833 Roo.each(styles, function(s) {
26834 if (!s.match(/:/)) {
26837 var kv = s.split(":");
26838 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26841 // what ever is left... we allow.
26844 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26845 if (!nstyle.length) {
26846 node.removeAttribute('style');
26850 this.iterateChildren(node, this.cleanTableWidths);
26858 domToHTML : function(currentElement, depth, nopadtext) {
26860 depth = depth || 0;
26861 nopadtext = nopadtext || false;
26863 if (!currentElement) {
26864 return this.domToHTML(this.doc.body);
26867 //Roo.log(currentElement);
26869 var allText = false;
26870 var nodeName = currentElement.nodeName;
26871 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26873 if (nodeName == '#text') {
26875 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26880 if (nodeName != 'BODY') {
26883 // Prints the node tagName, such as <A>, <IMG>, etc
26886 for(i = 0; i < currentElement.attributes.length;i++) {
26888 var aname = currentElement.attributes.item(i).name;
26889 if (!currentElement.attributes.item(i).value.length) {
26892 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26895 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26904 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26907 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26912 // Traverse the tree
26914 var currentElementChild = currentElement.childNodes.item(i);
26915 var allText = true;
26916 var innerHTML = '';
26918 while (currentElementChild) {
26919 // Formatting code (indent the tree so it looks nice on the screen)
26920 var nopad = nopadtext;
26921 if (lastnode == 'SPAN') {
26925 if (currentElementChild.nodeName == '#text') {
26926 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26927 toadd = nopadtext ? toadd : toadd.trim();
26928 if (!nopad && toadd.length > 80) {
26929 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26931 innerHTML += toadd;
26934 currentElementChild = currentElement.childNodes.item(i);
26940 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26942 // Recursively traverse the tree structure of the child node
26943 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26944 lastnode = currentElementChild.nodeName;
26946 currentElementChild=currentElement.childNodes.item(i);
26952 // The remaining code is mostly for formatting the tree
26953 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26958 ret+= "</"+tagName+">";
26964 applyBlacklists : function()
26966 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26967 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26971 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26972 if (b.indexOf(tag) > -1) {
26975 this.white.push(tag);
26979 Roo.each(w, function(tag) {
26980 if (b.indexOf(tag) > -1) {
26983 if (this.white.indexOf(tag) > -1) {
26986 this.white.push(tag);
26991 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26992 if (w.indexOf(tag) > -1) {
26995 this.black.push(tag);
26999 Roo.each(b, function(tag) {
27000 if (w.indexOf(tag) > -1) {
27003 if (this.black.indexOf(tag) > -1) {
27006 this.black.push(tag);
27011 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27012 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27016 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27017 if (b.indexOf(tag) > -1) {
27020 this.cwhite.push(tag);
27024 Roo.each(w, function(tag) {
27025 if (b.indexOf(tag) > -1) {
27028 if (this.cwhite.indexOf(tag) > -1) {
27031 this.cwhite.push(tag);
27036 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27037 if (w.indexOf(tag) > -1) {
27040 this.cblack.push(tag);
27044 Roo.each(b, function(tag) {
27045 if (w.indexOf(tag) > -1) {
27048 if (this.cblack.indexOf(tag) > -1) {
27051 this.cblack.push(tag);
27056 setStylesheets : function(stylesheets)
27058 if(typeof(stylesheets) == 'string'){
27059 Roo.get(this.iframe.contentDocument.head).createChild({
27061 rel : 'stylesheet',
27070 Roo.each(stylesheets, function(s) {
27075 Roo.get(_this.iframe.contentDocument.head).createChild({
27077 rel : 'stylesheet',
27086 removeStylesheets : function()
27090 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27095 setStyle : function(style)
27097 Roo.get(this.iframe.contentDocument.head).createChild({
27106 // hide stuff that is not compatible
27120 * @event specialkey
27124 * @cfg {String} fieldClass @hide
27127 * @cfg {String} focusClass @hide
27130 * @cfg {String} autoCreate @hide
27133 * @cfg {String} inputType @hide
27136 * @cfg {String} invalidClass @hide
27139 * @cfg {String} invalidText @hide
27142 * @cfg {String} msgFx @hide
27145 * @cfg {String} validateOnBlur @hide
27149 Roo.HtmlEditorCore.white = [
27150 'area', 'br', 'img', 'input', 'hr', 'wbr',
27152 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27153 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27154 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27155 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27156 'table', 'ul', 'xmp',
27158 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27161 'dir', 'menu', 'ol', 'ul', 'dl',
27167 Roo.HtmlEditorCore.black = [
27168 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27170 'base', 'basefont', 'bgsound', 'blink', 'body',
27171 'frame', 'frameset', 'head', 'html', 'ilayer',
27172 'iframe', 'layer', 'link', 'meta', 'object',
27173 'script', 'style' ,'title', 'xml' // clean later..
27175 Roo.HtmlEditorCore.clean = [
27176 'script', 'style', 'title', 'xml'
27178 Roo.HtmlEditorCore.remove = [
27183 Roo.HtmlEditorCore.ablack = [
27187 Roo.HtmlEditorCore.aclean = [
27188 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27192 Roo.HtmlEditorCore.pwhite= [
27193 'http', 'https', 'mailto'
27196 // white listed style attributes.
27197 Roo.HtmlEditorCore.cwhite= [
27198 // 'text-align', /// default is to allow most things..
27204 // black listed style attributes.
27205 Roo.HtmlEditorCore.cblack= [
27206 // 'font-size' -- this can be set by the project
27210 Roo.HtmlEditorCore.swapCodes =[
27211 [ 8211, "–" ],
27212 [ 8212, "—" ],
27229 * @class Roo.bootstrap.HtmlEditor
27230 * @extends Roo.bootstrap.TextArea
27231 * Bootstrap HtmlEditor class
27234 * Create a new HtmlEditor
27235 * @param {Object} config The config object
27238 Roo.bootstrap.HtmlEditor = function(config){
27239 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27240 if (!this.toolbars) {
27241 this.toolbars = [];
27244 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27247 * @event initialize
27248 * Fires when the editor is fully initialized (including the iframe)
27249 * @param {HtmlEditor} this
27254 * Fires when the editor is first receives the focus. Any insertion must wait
27255 * until after this event.
27256 * @param {HtmlEditor} this
27260 * @event beforesync
27261 * Fires before the textarea is updated with content from the editor iframe. Return false
27262 * to cancel the sync.
27263 * @param {HtmlEditor} this
27264 * @param {String} html
27268 * @event beforepush
27269 * Fires before the iframe editor is updated with content from the textarea. Return false
27270 * to cancel the push.
27271 * @param {HtmlEditor} this
27272 * @param {String} html
27277 * Fires when the textarea is updated with content from the editor iframe.
27278 * @param {HtmlEditor} this
27279 * @param {String} html
27284 * Fires when the iframe editor is updated with content from the textarea.
27285 * @param {HtmlEditor} this
27286 * @param {String} html
27290 * @event editmodechange
27291 * Fires when the editor switches edit modes
27292 * @param {HtmlEditor} this
27293 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27295 editmodechange: true,
27297 * @event editorevent
27298 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27299 * @param {HtmlEditor} this
27303 * @event firstfocus
27304 * Fires when on first focus - needed by toolbars..
27305 * @param {HtmlEditor} this
27310 * Auto save the htmlEditor value as a file into Events
27311 * @param {HtmlEditor} this
27315 * @event savedpreview
27316 * preview the saved version of htmlEditor
27317 * @param {HtmlEditor} this
27324 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27328 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27333 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27338 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27343 * @cfg {Number} height (in pixels)
27347 * @cfg {Number} width (in pixels)
27352 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27355 stylesheets: false,
27360 // private properties
27361 validationEvent : false,
27363 initialized : false,
27366 onFocus : Roo.emptyFn,
27368 hideMode:'offsets',
27370 tbContainer : false,
27374 toolbarContainer :function() {
27375 return this.wrap.select('.x-html-editor-tb',true).first();
27379 * Protected method that will not generally be called directly. It
27380 * is called when the editor creates its toolbar. Override this method if you need to
27381 * add custom toolbar buttons.
27382 * @param {HtmlEditor} editor
27384 createToolbar : function(){
27385 Roo.log('renewing');
27386 Roo.log("create toolbars");
27388 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27389 this.toolbars[0].render(this.toolbarContainer());
27393 // if (!editor.toolbars || !editor.toolbars.length) {
27394 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27397 // for (var i =0 ; i < editor.toolbars.length;i++) {
27398 // editor.toolbars[i] = Roo.factory(
27399 // typeof(editor.toolbars[i]) == 'string' ?
27400 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27401 // Roo.bootstrap.HtmlEditor);
27402 // editor.toolbars[i].init(editor);
27408 onRender : function(ct, position)
27410 // Roo.log("Call onRender: " + this.xtype);
27412 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27414 this.wrap = this.inputEl().wrap({
27415 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27418 this.editorcore.onRender(ct, position);
27420 if (this.resizable) {
27421 this.resizeEl = new Roo.Resizable(this.wrap, {
27425 minHeight : this.height,
27426 height: this.height,
27427 handles : this.resizable,
27430 resize : function(r, w, h) {
27431 _t.onResize(w,h); // -something
27437 this.createToolbar(this);
27440 if(!this.width && this.resizable){
27441 this.setSize(this.wrap.getSize());
27443 if (this.resizeEl) {
27444 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27445 // should trigger onReize..
27451 onResize : function(w, h)
27453 Roo.log('resize: ' +w + ',' + h );
27454 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27458 if(this.inputEl() ){
27459 if(typeof w == 'number'){
27460 var aw = w - this.wrap.getFrameWidth('lr');
27461 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27464 if(typeof h == 'number'){
27465 var tbh = -11; // fixme it needs to tool bar size!
27466 for (var i =0; i < this.toolbars.length;i++) {
27467 // fixme - ask toolbars for heights?
27468 tbh += this.toolbars[i].el.getHeight();
27469 //if (this.toolbars[i].footer) {
27470 // tbh += this.toolbars[i].footer.el.getHeight();
27478 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27479 ah -= 5; // knock a few pixes off for look..
27480 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27484 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27485 this.editorcore.onResize(ew,eh);
27490 * Toggles the editor between standard and source edit mode.
27491 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27493 toggleSourceEdit : function(sourceEditMode)
27495 this.editorcore.toggleSourceEdit(sourceEditMode);
27497 if(this.editorcore.sourceEditMode){
27498 Roo.log('editor - showing textarea');
27501 // Roo.log(this.syncValue());
27503 this.inputEl().removeClass(['hide', 'x-hidden']);
27504 this.inputEl().dom.removeAttribute('tabIndex');
27505 this.inputEl().focus();
27507 Roo.log('editor - hiding textarea');
27509 // Roo.log(this.pushValue());
27512 this.inputEl().addClass(['hide', 'x-hidden']);
27513 this.inputEl().dom.setAttribute('tabIndex', -1);
27514 //this.deferFocus();
27517 if(this.resizable){
27518 this.setSize(this.wrap.getSize());
27521 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27524 // private (for BoxComponent)
27525 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27527 // private (for BoxComponent)
27528 getResizeEl : function(){
27532 // private (for BoxComponent)
27533 getPositionEl : function(){
27538 initEvents : function(){
27539 this.originalValue = this.getValue();
27543 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27546 // markInvalid : Roo.emptyFn,
27548 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27551 // clearInvalid : Roo.emptyFn,
27553 setValue : function(v){
27554 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27555 this.editorcore.pushValue();
27560 deferFocus : function(){
27561 this.focus.defer(10, this);
27565 focus : function(){
27566 this.editorcore.focus();
27572 onDestroy : function(){
27578 for (var i =0; i < this.toolbars.length;i++) {
27579 // fixme - ask toolbars for heights?
27580 this.toolbars[i].onDestroy();
27583 this.wrap.dom.innerHTML = '';
27584 this.wrap.remove();
27589 onFirstFocus : function(){
27590 //Roo.log("onFirstFocus");
27591 this.editorcore.onFirstFocus();
27592 for (var i =0; i < this.toolbars.length;i++) {
27593 this.toolbars[i].onFirstFocus();
27599 syncValue : function()
27601 this.editorcore.syncValue();
27604 pushValue : function()
27606 this.editorcore.pushValue();
27610 // hide stuff that is not compatible
27624 * @event specialkey
27628 * @cfg {String} fieldClass @hide
27631 * @cfg {String} focusClass @hide
27634 * @cfg {String} autoCreate @hide
27637 * @cfg {String} inputType @hide
27641 * @cfg {String} invalidText @hide
27644 * @cfg {String} msgFx @hide
27647 * @cfg {String} validateOnBlur @hide
27656 Roo.namespace('Roo.bootstrap.htmleditor');
27658 * @class Roo.bootstrap.HtmlEditorToolbar1
27664 new Roo.bootstrap.HtmlEditor({
27667 new Roo.bootstrap.HtmlEditorToolbar1({
27668 disable : { fonts: 1 , format: 1, ..., ... , ...],
27674 * @cfg {Object} disable List of elements to disable..
27675 * @cfg {Array} btns List of additional buttons.
27679 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27682 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27685 Roo.apply(this, config);
27687 // default disabled, based on 'good practice'..
27688 this.disable = this.disable || {};
27689 Roo.applyIf(this.disable, {
27692 specialElements : true
27694 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27696 this.editor = config.editor;
27697 this.editorcore = config.editor.editorcore;
27699 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27701 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27702 // dont call parent... till later.
27704 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27709 editorcore : false,
27714 "h1","h2","h3","h4","h5","h6",
27716 "abbr", "acronym", "address", "cite", "samp", "var",
27720 onRender : function(ct, position)
27722 // Roo.log("Call onRender: " + this.xtype);
27724 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27726 this.el.dom.style.marginBottom = '0';
27728 var editorcore = this.editorcore;
27729 var editor= this.editor;
27732 var btn = function(id,cmd , toggle, handler, html){
27734 var event = toggle ? 'toggle' : 'click';
27739 xns: Roo.bootstrap,
27743 enableToggle:toggle !== false,
27745 pressed : toggle ? false : null,
27748 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27749 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27755 // var cb_box = function...
27760 xns: Roo.bootstrap,
27765 xns: Roo.bootstrap,
27769 Roo.each(this.formats, function(f) {
27770 style.menu.items.push({
27772 xns: Roo.bootstrap,
27773 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27778 editorcore.insertTag(this.tagname);
27785 children.push(style);
27787 btn('bold',false,true);
27788 btn('italic',false,true);
27789 btn('align-left', 'justifyleft',true);
27790 btn('align-center', 'justifycenter',true);
27791 btn('align-right' , 'justifyright',true);
27792 btn('link', false, false, function(btn) {
27793 //Roo.log("create link?");
27794 var url = prompt(this.createLinkText, this.defaultLinkValue);
27795 if(url && url != 'http:/'+'/'){
27796 this.editorcore.relayCmd('createlink', url);
27799 btn('list','insertunorderedlist',true);
27800 btn('pencil', false,true, function(btn){
27802 this.toggleSourceEdit(btn.pressed);
27805 if (this.editor.btns.length > 0) {
27806 for (var i = 0; i<this.editor.btns.length; i++) {
27807 children.push(this.editor.btns[i]);
27815 xns: Roo.bootstrap,
27820 xns: Roo.bootstrap,
27825 cog.menu.items.push({
27827 xns: Roo.bootstrap,
27828 html : Clean styles,
27833 editorcore.insertTag(this.tagname);
27842 this.xtype = 'NavSimplebar';
27844 for(var i=0;i< children.length;i++) {
27846 this.buttons.add(this.addxtypeChild(children[i]));
27850 editor.on('editorevent', this.updateToolbar, this);
27852 onBtnClick : function(id)
27854 this.editorcore.relayCmd(id);
27855 this.editorcore.focus();
27859 * Protected method that will not generally be called directly. It triggers
27860 * a toolbar update by reading the markup state of the current selection in the editor.
27862 updateToolbar: function(){
27864 if(!this.editorcore.activated){
27865 this.editor.onFirstFocus(); // is this neeed?
27869 var btns = this.buttons;
27870 var doc = this.editorcore.doc;
27871 btns.get('bold').setActive(doc.queryCommandState('bold'));
27872 btns.get('italic').setActive(doc.queryCommandState('italic'));
27873 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27875 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27876 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27877 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27879 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27880 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27883 var ans = this.editorcore.getAllAncestors();
27884 if (this.formatCombo) {
27887 var store = this.formatCombo.store;
27888 this.formatCombo.setValue("");
27889 for (var i =0; i < ans.length;i++) {
27890 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27892 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27900 // hides menus... - so this cant be on a menu...
27901 Roo.bootstrap.MenuMgr.hideAll();
27903 Roo.bootstrap.MenuMgr.hideAll();
27904 //this.editorsyncValue();
27906 onFirstFocus: function() {
27907 this.buttons.each(function(item){
27911 toggleSourceEdit : function(sourceEditMode){
27914 if(sourceEditMode){
27915 Roo.log("disabling buttons");
27916 this.buttons.each( function(item){
27917 if(item.cmd != 'pencil'){
27923 Roo.log("enabling buttons");
27924 if(this.editorcore.initialized){
27925 this.buttons.each( function(item){
27931 Roo.log("calling toggole on editor");
27932 // tell the editor that it's been pressed..
27933 this.editor.toggleSourceEdit(sourceEditMode);
27947 * @class Roo.bootstrap.Markdown
27948 * @extends Roo.bootstrap.TextArea
27949 * Bootstrap Showdown editable area
27950 * @cfg {string} content
27953 * Create a new Showdown
27956 Roo.bootstrap.Markdown = function(config){
27957 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27961 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27965 initEvents : function()
27968 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27969 this.markdownEl = this.el.createChild({
27970 cls : 'roo-markdown-area'
27972 this.inputEl().addClass('d-none');
27973 if (this.getValue() == '') {
27974 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27977 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27979 this.markdownEl.on('click', this.toggleTextEdit, this);
27980 this.on('blur', this.toggleTextEdit, this);
27981 this.on('specialkey', this.resizeTextArea, this);
27984 toggleTextEdit : function()
27986 var sh = this.markdownEl.getHeight();
27987 this.inputEl().addClass('d-none');
27988 this.markdownEl.addClass('d-none');
27989 if (!this.editing) {
27991 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
27992 this.inputEl().removeClass('d-none');
27993 this.inputEl().focus();
27994 this.editing = true;
27997 // show showdown...
27998 this.updateMarkdown();
27999 this.markdownEl.removeClass('d-none');
28000 this.editing = false;
28003 updateMarkdown : function()
28005 if (this.getValue() == '') {
28006 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28010 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28013 resizeTextArea: function () {
28016 Roo.log([sh, this.getValue().split("\n").length * 30]);
28017 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28019 setValue : function(val)
28021 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28022 if (!this.editing) {
28023 this.updateMarkdown();
28029 if (!this.editing) {
28030 this.toggleTextEdit();
28038 * Ext JS Library 1.1.1
28039 * Copyright(c) 2006-2007, Ext JS, LLC.
28041 * Originally Released Under LGPL - original licence link has changed is not relivant.
28044 * <script type="text/javascript">
28048 * @class Roo.bootstrap.PagingToolbar
28049 * @extends Roo.bootstrap.NavSimplebar
28050 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28052 * Create a new PagingToolbar
28053 * @param {Object} config The config object
28054 * @param {Roo.data.Store} store
28056 Roo.bootstrap.PagingToolbar = function(config)
28058 // old args format still supported... - xtype is prefered..
28059 // created from xtype...
28061 this.ds = config.dataSource;
28063 if (config.store && !this.ds) {
28064 this.store= Roo.factory(config.store, Roo.data);
28065 this.ds = this.store;
28066 this.ds.xmodule = this.xmodule || false;
28069 this.toolbarItems = [];
28070 if (config.items) {
28071 this.toolbarItems = config.items;
28074 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28079 this.bind(this.ds);
28082 if (Roo.bootstrap.version == 4) {
28083 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28085 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28090 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28092 * @cfg {Roo.data.Store} dataSource
28093 * The underlying data store providing the paged data
28096 * @cfg {String/HTMLElement/Element} container
28097 * container The id or element that will contain the toolbar
28100 * @cfg {Boolean} displayInfo
28101 * True to display the displayMsg (defaults to false)
28104 * @cfg {Number} pageSize
28105 * The number of records to display per page (defaults to 20)
28109 * @cfg {String} displayMsg
28110 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28112 displayMsg : 'Displaying {0} - {1} of {2}',
28114 * @cfg {String} emptyMsg
28115 * The message to display when no records are found (defaults to "No data to display")
28117 emptyMsg : 'No data to display',
28119 * Customizable piece of the default paging text (defaults to "Page")
28122 beforePageText : "Page",
28124 * Customizable piece of the default paging text (defaults to "of %0")
28127 afterPageText : "of {0}",
28129 * Customizable piece of the default paging text (defaults to "First Page")
28132 firstText : "First Page",
28134 * Customizable piece of the default paging text (defaults to "Previous Page")
28137 prevText : "Previous Page",
28139 * Customizable piece of the default paging text (defaults to "Next Page")
28142 nextText : "Next Page",
28144 * Customizable piece of the default paging text (defaults to "Last Page")
28147 lastText : "Last Page",
28149 * Customizable piece of the default paging text (defaults to "Refresh")
28152 refreshText : "Refresh",
28156 onRender : function(ct, position)
28158 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28159 this.navgroup.parentId = this.id;
28160 this.navgroup.onRender(this.el, null);
28161 // add the buttons to the navgroup
28163 if(this.displayInfo){
28164 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28165 this.displayEl = this.el.select('.x-paging-info', true).first();
28166 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28167 // this.displayEl = navel.el.select('span',true).first();
28173 Roo.each(_this.buttons, function(e){ // this might need to use render????
28174 Roo.factory(e).render(_this.el);
28178 Roo.each(_this.toolbarItems, function(e) {
28179 _this.navgroup.addItem(e);
28183 this.first = this.navgroup.addItem({
28184 tooltip: this.firstText,
28185 cls: "prev btn-outline-secondary",
28186 html : ' <i class="fa fa-step-backward"></i>',
28188 preventDefault: true,
28189 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28192 this.prev = this.navgroup.addItem({
28193 tooltip: this.prevText,
28194 cls: "prev btn-outline-secondary",
28195 html : ' <i class="fa fa-backward"></i>',
28197 preventDefault: true,
28198 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28200 //this.addSeparator();
28203 var field = this.navgroup.addItem( {
28205 cls : 'x-paging-position btn-outline-secondary',
28207 html : this.beforePageText +
28208 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28209 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28212 this.field = field.el.select('input', true).first();
28213 this.field.on("keydown", this.onPagingKeydown, this);
28214 this.field.on("focus", function(){this.dom.select();});
28217 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28218 //this.field.setHeight(18);
28219 //this.addSeparator();
28220 this.next = this.navgroup.addItem({
28221 tooltip: this.nextText,
28222 cls: "next btn-outline-secondary",
28223 html : ' <i class="fa fa-forward"></i>',
28225 preventDefault: true,
28226 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28228 this.last = this.navgroup.addItem({
28229 tooltip: this.lastText,
28230 html : ' <i class="fa fa-step-forward"></i>',
28231 cls: "next btn-outline-secondary",
28233 preventDefault: true,
28234 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28236 //this.addSeparator();
28237 this.loading = this.navgroup.addItem({
28238 tooltip: this.refreshText,
28239 cls: "btn-outline-secondary",
28240 html : ' <i class="fa fa-refresh"></i>',
28241 preventDefault: true,
28242 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28248 updateInfo : function(){
28249 if(this.displayEl){
28250 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28251 var msg = count == 0 ?
28255 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28257 this.displayEl.update(msg);
28262 onLoad : function(ds, r, o)
28264 this.cursor = o.params && o.params.start ? o.params.start : 0;
28266 var d = this.getPageData(),
28271 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28272 this.field.dom.value = ap;
28273 this.first.setDisabled(ap == 1);
28274 this.prev.setDisabled(ap == 1);
28275 this.next.setDisabled(ap == ps);
28276 this.last.setDisabled(ap == ps);
28277 this.loading.enable();
28282 getPageData : function(){
28283 var total = this.ds.getTotalCount();
28286 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28287 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28292 onLoadError : function(){
28293 this.loading.enable();
28297 onPagingKeydown : function(e){
28298 var k = e.getKey();
28299 var d = this.getPageData();
28301 var v = this.field.dom.value, pageNum;
28302 if(!v || isNaN(pageNum = parseInt(v, 10))){
28303 this.field.dom.value = d.activePage;
28306 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28307 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28310 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))
28312 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28313 this.field.dom.value = pageNum;
28314 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28317 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28319 var v = this.field.dom.value, pageNum;
28320 var increment = (e.shiftKey) ? 10 : 1;
28321 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28324 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28325 this.field.dom.value = d.activePage;
28328 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28330 this.field.dom.value = parseInt(v, 10) + increment;
28331 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28332 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28339 beforeLoad : function(){
28341 this.loading.disable();
28346 onClick : function(which){
28355 ds.load({params:{start: 0, limit: this.pageSize}});
28358 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28361 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28364 var total = ds.getTotalCount();
28365 var extra = total % this.pageSize;
28366 var lastStart = extra ? (total - extra) : total-this.pageSize;
28367 ds.load({params:{start: lastStart, limit: this.pageSize}});
28370 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28376 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28377 * @param {Roo.data.Store} store The data store to unbind
28379 unbind : function(ds){
28380 ds.un("beforeload", this.beforeLoad, this);
28381 ds.un("load", this.onLoad, this);
28382 ds.un("loadexception", this.onLoadError, this);
28383 ds.un("remove", this.updateInfo, this);
28384 ds.un("add", this.updateInfo, this);
28385 this.ds = undefined;
28389 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28390 * @param {Roo.data.Store} store The data store to bind
28392 bind : function(ds){
28393 ds.on("beforeload", this.beforeLoad, this);
28394 ds.on("load", this.onLoad, this);
28395 ds.on("loadexception", this.onLoadError, this);
28396 ds.on("remove", this.updateInfo, this);
28397 ds.on("add", this.updateInfo, this);
28408 * @class Roo.bootstrap.MessageBar
28409 * @extends Roo.bootstrap.Component
28410 * Bootstrap MessageBar class
28411 * @cfg {String} html contents of the MessageBar
28412 * @cfg {String} weight (info | success | warning | danger) default info
28413 * @cfg {String} beforeClass insert the bar before the given class
28414 * @cfg {Boolean} closable (true | false) default false
28415 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28418 * Create a new Element
28419 * @param {Object} config The config object
28422 Roo.bootstrap.MessageBar = function(config){
28423 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28426 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28432 beforeClass: 'bootstrap-sticky-wrap',
28434 getAutoCreate : function(){
28438 cls: 'alert alert-dismissable alert-' + this.weight,
28443 html: this.html || ''
28449 cfg.cls += ' alert-messages-fixed';
28463 onRender : function(ct, position)
28465 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28468 var cfg = Roo.apply({}, this.getAutoCreate());
28472 cfg.cls += ' ' + this.cls;
28475 cfg.style = this.style;
28477 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28479 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28482 this.el.select('>button.close').on('click', this.hide, this);
28488 if (!this.rendered) {
28494 this.fireEvent('show', this);
28500 if (!this.rendered) {
28506 this.fireEvent('hide', this);
28509 update : function()
28511 // var e = this.el.dom.firstChild;
28513 // if(this.closable){
28514 // e = e.nextSibling;
28517 // e.data = this.html || '';
28519 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28535 * @class Roo.bootstrap.Graph
28536 * @extends Roo.bootstrap.Component
28537 * Bootstrap Graph class
28541 @cfg {String} graphtype bar | vbar | pie
28542 @cfg {number} g_x coodinator | centre x (pie)
28543 @cfg {number} g_y coodinator | centre y (pie)
28544 @cfg {number} g_r radius (pie)
28545 @cfg {number} g_height height of the chart (respected by all elements in the set)
28546 @cfg {number} g_width width of the chart (respected by all elements in the set)
28547 @cfg {Object} title The title of the chart
28550 -opts (object) options for the chart
28552 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28553 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28555 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.
28556 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28558 o stretch (boolean)
28560 -opts (object) options for the pie
28563 o startAngle (number)
28564 o endAngle (number)
28568 * Create a new Input
28569 * @param {Object} config The config object
28572 Roo.bootstrap.Graph = function(config){
28573 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28579 * The img click event for the img.
28580 * @param {Roo.EventObject} e
28586 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28597 //g_colors: this.colors,
28604 getAutoCreate : function(){
28615 onRender : function(ct,position){
28618 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28620 if (typeof(Raphael) == 'undefined') {
28621 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28625 this.raphael = Raphael(this.el.dom);
28627 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28628 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28629 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28630 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28632 r.text(160, 10, "Single Series Chart").attr(txtattr);
28633 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28634 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28635 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28637 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28638 r.barchart(330, 10, 300, 220, data1);
28639 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28640 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28643 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28644 // r.barchart(30, 30, 560, 250, xdata, {
28645 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28646 // axis : "0 0 1 1",
28647 // axisxlabels : xdata
28648 // //yvalues : cols,
28651 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28653 // this.load(null,xdata,{
28654 // axis : "0 0 1 1",
28655 // axisxlabels : xdata
28660 load : function(graphtype,xdata,opts)
28662 this.raphael.clear();
28664 graphtype = this.graphtype;
28669 var r = this.raphael,
28670 fin = function () {
28671 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28673 fout = function () {
28674 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28676 pfin = function() {
28677 this.sector.stop();
28678 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28681 this.label[0].stop();
28682 this.label[0].attr({ r: 7.5 });
28683 this.label[1].attr({ "font-weight": 800 });
28686 pfout = function() {
28687 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28690 this.label[0].animate({ r: 5 }, 500, "bounce");
28691 this.label[1].attr({ "font-weight": 400 });
28697 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28700 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28703 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28704 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28706 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28713 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28718 setTitle: function(o)
28723 initEvents: function() {
28726 this.el.on('click', this.onClick, this);
28730 onClick : function(e)
28732 Roo.log('img onclick');
28733 this.fireEvent('click', this, e);
28745 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28748 * @class Roo.bootstrap.dash.NumberBox
28749 * @extends Roo.bootstrap.Component
28750 * Bootstrap NumberBox class
28751 * @cfg {String} headline Box headline
28752 * @cfg {String} content Box content
28753 * @cfg {String} icon Box icon
28754 * @cfg {String} footer Footer text
28755 * @cfg {String} fhref Footer href
28758 * Create a new NumberBox
28759 * @param {Object} config The config object
28763 Roo.bootstrap.dash.NumberBox = function(config){
28764 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28768 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28777 getAutoCreate : function(){
28781 cls : 'small-box ',
28789 cls : 'roo-headline',
28790 html : this.headline
28794 cls : 'roo-content',
28795 html : this.content
28809 cls : 'ion ' + this.icon
28818 cls : 'small-box-footer',
28819 href : this.fhref || '#',
28823 cfg.cn.push(footer);
28830 onRender : function(ct,position){
28831 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28838 setHeadline: function (value)
28840 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28843 setFooter: function (value, href)
28845 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28848 this.el.select('a.small-box-footer',true).first().attr('href', href);
28853 setContent: function (value)
28855 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28858 initEvents: function()
28872 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28875 * @class Roo.bootstrap.dash.TabBox
28876 * @extends Roo.bootstrap.Component
28877 * Bootstrap TabBox class
28878 * @cfg {String} title Title of the TabBox
28879 * @cfg {String} icon Icon of the TabBox
28880 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28881 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28884 * Create a new TabBox
28885 * @param {Object} config The config object
28889 Roo.bootstrap.dash.TabBox = function(config){
28890 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28895 * When a pane is added
28896 * @param {Roo.bootstrap.dash.TabPane} pane
28900 * @event activatepane
28901 * When a pane is activated
28902 * @param {Roo.bootstrap.dash.TabPane} pane
28904 "activatepane" : true
28912 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28917 tabScrollable : false,
28919 getChildContainer : function()
28921 return this.el.select('.tab-content', true).first();
28924 getAutoCreate : function(){
28928 cls: 'pull-left header',
28936 cls: 'fa ' + this.icon
28942 cls: 'nav nav-tabs pull-right',
28948 if(this.tabScrollable){
28955 cls: 'nav nav-tabs pull-right',
28966 cls: 'nav-tabs-custom',
28971 cls: 'tab-content no-padding',
28979 initEvents : function()
28981 //Roo.log('add add pane handler');
28982 this.on('addpane', this.onAddPane, this);
28985 * Updates the box title
28986 * @param {String} html to set the title to.
28988 setTitle : function(value)
28990 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28992 onAddPane : function(pane)
28994 this.panes.push(pane);
28995 //Roo.log('addpane');
28997 // tabs are rendere left to right..
28998 if(!this.showtabs){
29002 var ctr = this.el.select('.nav-tabs', true).first();
29005 var existing = ctr.select('.nav-tab',true);
29006 var qty = existing.getCount();;
29009 var tab = ctr.createChild({
29011 cls : 'nav-tab' + (qty ? '' : ' active'),
29019 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29022 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29024 pane.el.addClass('active');
29029 onTabClick : function(ev,un,ob,pane)
29031 //Roo.log('tab - prev default');
29032 ev.preventDefault();
29035 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29036 pane.tab.addClass('active');
29037 //Roo.log(pane.title);
29038 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29039 // technically we should have a deactivate event.. but maybe add later.
29040 // and it should not de-activate the selected tab...
29041 this.fireEvent('activatepane', pane);
29042 pane.el.addClass('active');
29043 pane.fireEvent('activate');
29048 getActivePane : function()
29051 Roo.each(this.panes, function(p) {
29052 if(p.el.hasClass('active')){
29073 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29075 * @class Roo.bootstrap.TabPane
29076 * @extends Roo.bootstrap.Component
29077 * Bootstrap TabPane class
29078 * @cfg {Boolean} active (false | true) Default false
29079 * @cfg {String} title title of panel
29083 * Create a new TabPane
29084 * @param {Object} config The config object
29087 Roo.bootstrap.dash.TabPane = function(config){
29088 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29094 * When a pane is activated
29095 * @param {Roo.bootstrap.dash.TabPane} pane
29102 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29107 // the tabBox that this is attached to.
29110 getAutoCreate : function()
29118 cfg.cls += ' active';
29123 initEvents : function()
29125 //Roo.log('trigger add pane handler');
29126 this.parent().fireEvent('addpane', this)
29130 * Updates the tab title
29131 * @param {String} html to set the title to.
29133 setTitle: function(str)
29139 this.tab.select('a', true).first().dom.innerHTML = str;
29156 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29159 * @class Roo.bootstrap.menu.Menu
29160 * @extends Roo.bootstrap.Component
29161 * Bootstrap Menu class - container for Menu
29162 * @cfg {String} html Text of the menu
29163 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29164 * @cfg {String} icon Font awesome icon
29165 * @cfg {String} pos Menu align to (top | bottom) default bottom
29169 * Create a new Menu
29170 * @param {Object} config The config object
29174 Roo.bootstrap.menu.Menu = function(config){
29175 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29179 * @event beforeshow
29180 * Fires before this menu is displayed
29181 * @param {Roo.bootstrap.menu.Menu} this
29185 * @event beforehide
29186 * Fires before this menu is hidden
29187 * @param {Roo.bootstrap.menu.Menu} this
29192 * Fires after this menu is displayed
29193 * @param {Roo.bootstrap.menu.Menu} this
29198 * Fires after this menu is hidden
29199 * @param {Roo.bootstrap.menu.Menu} this
29204 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29205 * @param {Roo.bootstrap.menu.Menu} this
29206 * @param {Roo.EventObject} e
29213 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29217 weight : 'default',
29222 getChildContainer : function() {
29223 if(this.isSubMenu){
29227 return this.el.select('ul.dropdown-menu', true).first();
29230 getAutoCreate : function()
29235 cls : 'roo-menu-text',
29243 cls : 'fa ' + this.icon
29254 cls : 'dropdown-button btn btn-' + this.weight,
29259 cls : 'dropdown-toggle btn btn-' + this.weight,
29269 cls : 'dropdown-menu'
29275 if(this.pos == 'top'){
29276 cfg.cls += ' dropup';
29279 if(this.isSubMenu){
29282 cls : 'dropdown-menu'
29289 onRender : function(ct, position)
29291 this.isSubMenu = ct.hasClass('dropdown-submenu');
29293 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29296 initEvents : function()
29298 if(this.isSubMenu){
29302 this.hidden = true;
29304 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29305 this.triggerEl.on('click', this.onTriggerPress, this);
29307 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29308 this.buttonEl.on('click', this.onClick, this);
29314 if(this.isSubMenu){
29318 return this.el.select('ul.dropdown-menu', true).first();
29321 onClick : function(e)
29323 this.fireEvent("click", this, e);
29326 onTriggerPress : function(e)
29328 if (this.isVisible()) {
29335 isVisible : function(){
29336 return !this.hidden;
29341 this.fireEvent("beforeshow", this);
29343 this.hidden = false;
29344 this.el.addClass('open');
29346 Roo.get(document).on("mouseup", this.onMouseUp, this);
29348 this.fireEvent("show", this);
29355 this.fireEvent("beforehide", this);
29357 this.hidden = true;
29358 this.el.removeClass('open');
29360 Roo.get(document).un("mouseup", this.onMouseUp);
29362 this.fireEvent("hide", this);
29365 onMouseUp : function()
29379 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29382 * @class Roo.bootstrap.menu.Item
29383 * @extends Roo.bootstrap.Component
29384 * Bootstrap MenuItem class
29385 * @cfg {Boolean} submenu (true | false) default false
29386 * @cfg {String} html text of the item
29387 * @cfg {String} href the link
29388 * @cfg {Boolean} disable (true | false) default false
29389 * @cfg {Boolean} preventDefault (true | false) default true
29390 * @cfg {String} icon Font awesome icon
29391 * @cfg {String} pos Submenu align to (left | right) default right
29395 * Create a new Item
29396 * @param {Object} config The config object
29400 Roo.bootstrap.menu.Item = function(config){
29401 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29405 * Fires when the mouse is hovering over this menu
29406 * @param {Roo.bootstrap.menu.Item} this
29407 * @param {Roo.EventObject} e
29412 * Fires when the mouse exits this menu
29413 * @param {Roo.bootstrap.menu.Item} this
29414 * @param {Roo.EventObject} e
29420 * The raw click event for the entire grid.
29421 * @param {Roo.EventObject} e
29427 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29432 preventDefault: true,
29437 getAutoCreate : function()
29442 cls : 'roo-menu-item-text',
29450 cls : 'fa ' + this.icon
29459 href : this.href || '#',
29466 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29470 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29472 if(this.pos == 'left'){
29473 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29480 initEvents : function()
29482 this.el.on('mouseover', this.onMouseOver, this);
29483 this.el.on('mouseout', this.onMouseOut, this);
29485 this.el.select('a', true).first().on('click', this.onClick, this);
29489 onClick : function(e)
29491 if(this.preventDefault){
29492 e.preventDefault();
29495 this.fireEvent("click", this, e);
29498 onMouseOver : function(e)
29500 if(this.submenu && this.pos == 'left'){
29501 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29504 this.fireEvent("mouseover", this, e);
29507 onMouseOut : function(e)
29509 this.fireEvent("mouseout", this, e);
29521 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29524 * @class Roo.bootstrap.menu.Separator
29525 * @extends Roo.bootstrap.Component
29526 * Bootstrap Separator class
29529 * Create a new Separator
29530 * @param {Object} config The config object
29534 Roo.bootstrap.menu.Separator = function(config){
29535 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29538 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29540 getAutoCreate : function(){
29543 cls: 'dropdown-divider divider'
29561 * @class Roo.bootstrap.Tooltip
29562 * Bootstrap Tooltip class
29563 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29564 * to determine which dom element triggers the tooltip.
29566 * It needs to add support for additional attributes like tooltip-position
29569 * Create a new Toolti
29570 * @param {Object} config The config object
29573 Roo.bootstrap.Tooltip = function(config){
29574 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29576 this.alignment = Roo.bootstrap.Tooltip.alignment;
29578 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29579 this.alignment = config.alignment;
29584 Roo.apply(Roo.bootstrap.Tooltip, {
29586 * @function init initialize tooltip monitoring.
29590 currentTip : false,
29591 currentRegion : false,
29597 Roo.get(document).on('mouseover', this.enter ,this);
29598 Roo.get(document).on('mouseout', this.leave, this);
29601 this.currentTip = new Roo.bootstrap.Tooltip();
29604 enter : function(ev)
29606 var dom = ev.getTarget();
29608 //Roo.log(['enter',dom]);
29609 var el = Roo.fly(dom);
29610 if (this.currentEl) {
29612 //Roo.log(this.currentEl);
29613 //Roo.log(this.currentEl.contains(dom));
29614 if (this.currentEl == el) {
29617 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29623 if (this.currentTip.el) {
29624 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29628 if(!el || el.dom == document){
29634 if (!el.attr('tooltip')) {
29635 pel = el.findParent("[tooltip]");
29637 bindEl = Roo.get(pel);
29643 // you can not look for children, as if el is the body.. then everythign is the child..
29644 if (!pel && !el.attr('tooltip')) { //
29645 if (!el.select("[tooltip]").elements.length) {
29648 // is the mouse over this child...?
29649 bindEl = el.select("[tooltip]").first();
29650 var xy = ev.getXY();
29651 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29652 //Roo.log("not in region.");
29655 //Roo.log("child element over..");
29658 this.currentEl = el;
29659 this.currentTip.bind(bindEl);
29660 this.currentRegion = Roo.lib.Region.getRegion(dom);
29661 this.currentTip.enter();
29664 leave : function(ev)
29666 var dom = ev.getTarget();
29667 //Roo.log(['leave',dom]);
29668 if (!this.currentEl) {
29673 if (dom != this.currentEl.dom) {
29676 var xy = ev.getXY();
29677 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29680 // only activate leave if mouse cursor is outside... bounding box..
29685 if (this.currentTip) {
29686 this.currentTip.leave();
29688 //Roo.log('clear currentEl');
29689 this.currentEl = false;
29694 'left' : ['r-l', [-2,0], 'right'],
29695 'right' : ['l-r', [2,0], 'left'],
29696 'bottom' : ['t-b', [0,2], 'top'],
29697 'top' : [ 'b-t', [0,-2], 'bottom']
29703 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29708 delay : null, // can be { show : 300 , hide: 500}
29712 hoverState : null, //???
29714 placement : 'bottom',
29718 getAutoCreate : function(){
29725 cls : 'tooltip-arrow arrow'
29728 cls : 'tooltip-inner'
29735 bind : function(el)
29740 initEvents : function()
29742 this.arrowEl = this.el.select('.arrow', true).first();
29743 this.innerEl = this.el.select('.tooltip-inner', true).first();
29746 enter : function () {
29748 if (this.timeout != null) {
29749 clearTimeout(this.timeout);
29752 this.hoverState = 'in';
29753 //Roo.log("enter - show");
29754 if (!this.delay || !this.delay.show) {
29759 this.timeout = setTimeout(function () {
29760 if (_t.hoverState == 'in') {
29763 }, this.delay.show);
29767 clearTimeout(this.timeout);
29769 this.hoverState = 'out';
29770 if (!this.delay || !this.delay.hide) {
29776 this.timeout = setTimeout(function () {
29777 //Roo.log("leave - timeout");
29779 if (_t.hoverState == 'out') {
29781 Roo.bootstrap.Tooltip.currentEl = false;
29786 show : function (msg)
29789 this.render(document.body);
29792 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29794 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29796 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29798 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29799 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29801 var placement = typeof this.placement == 'function' ?
29802 this.placement.call(this, this.el, on_el) :
29805 var autoToken = /\s?auto?\s?/i;
29806 var autoPlace = autoToken.test(placement);
29808 placement = placement.replace(autoToken, '') || 'top';
29812 //this.el.setXY([0,0]);
29814 //this.el.dom.style.display='block';
29816 //this.el.appendTo(on_el);
29818 var p = this.getPosition();
29819 var box = this.el.getBox();
29825 var align = this.alignment[placement];
29827 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29829 if(placement == 'top' || placement == 'bottom'){
29831 placement = 'right';
29834 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29835 placement = 'left';
29838 var scroll = Roo.select('body', true).first().getScroll();
29840 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29844 align = this.alignment[placement];
29846 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29850 var elems = document.getElementsByTagName('div');
29851 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29852 for (var i = 0; i < elems.length; i++) {
29853 var zindex = Number.parseInt(
29854 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29857 if (zindex > highest) {
29864 this.el.dom.style.zIndex = highest;
29866 this.el.alignTo(this.bindEl, align[0],align[1]);
29867 //var arrow = this.el.select('.arrow',true).first();
29868 //arrow.set(align[2],
29870 this.el.addClass(placement);
29871 this.el.addClass("bs-tooltip-"+ placement);
29873 this.el.addClass('in fade show');
29875 this.hoverState = null;
29877 if (this.el.hasClass('fade')) {
29892 //this.el.setXY([0,0]);
29893 this.el.removeClass(['show', 'in']);
29909 * @class Roo.bootstrap.LocationPicker
29910 * @extends Roo.bootstrap.Component
29911 * Bootstrap LocationPicker class
29912 * @cfg {Number} latitude Position when init default 0
29913 * @cfg {Number} longitude Position when init default 0
29914 * @cfg {Number} zoom default 15
29915 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29916 * @cfg {Boolean} mapTypeControl default false
29917 * @cfg {Boolean} disableDoubleClickZoom default false
29918 * @cfg {Boolean} scrollwheel default true
29919 * @cfg {Boolean} streetViewControl default false
29920 * @cfg {Number} radius default 0
29921 * @cfg {String} locationName
29922 * @cfg {Boolean} draggable default true
29923 * @cfg {Boolean} enableAutocomplete default false
29924 * @cfg {Boolean} enableReverseGeocode default true
29925 * @cfg {String} markerTitle
29928 * Create a new LocationPicker
29929 * @param {Object} config The config object
29933 Roo.bootstrap.LocationPicker = function(config){
29935 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29940 * Fires when the picker initialized.
29941 * @param {Roo.bootstrap.LocationPicker} this
29942 * @param {Google Location} location
29946 * @event positionchanged
29947 * Fires when the picker position changed.
29948 * @param {Roo.bootstrap.LocationPicker} this
29949 * @param {Google Location} location
29951 positionchanged : true,
29954 * Fires when the map resize.
29955 * @param {Roo.bootstrap.LocationPicker} this
29960 * Fires when the map show.
29961 * @param {Roo.bootstrap.LocationPicker} this
29966 * Fires when the map hide.
29967 * @param {Roo.bootstrap.LocationPicker} this
29972 * Fires when click the map.
29973 * @param {Roo.bootstrap.LocationPicker} this
29974 * @param {Map event} e
29978 * @event mapRightClick
29979 * Fires when right click the map.
29980 * @param {Roo.bootstrap.LocationPicker} this
29981 * @param {Map event} e
29983 mapRightClick : true,
29985 * @event markerClick
29986 * Fires when click the marker.
29987 * @param {Roo.bootstrap.LocationPicker} this
29988 * @param {Map event} e
29990 markerClick : true,
29992 * @event markerRightClick
29993 * Fires when right click the marker.
29994 * @param {Roo.bootstrap.LocationPicker} this
29995 * @param {Map event} e
29997 markerRightClick : true,
29999 * @event OverlayViewDraw
30000 * Fires when OverlayView Draw
30001 * @param {Roo.bootstrap.LocationPicker} this
30003 OverlayViewDraw : true,
30005 * @event OverlayViewOnAdd
30006 * Fires when OverlayView Draw
30007 * @param {Roo.bootstrap.LocationPicker} this
30009 OverlayViewOnAdd : true,
30011 * @event OverlayViewOnRemove
30012 * Fires when OverlayView Draw
30013 * @param {Roo.bootstrap.LocationPicker} this
30015 OverlayViewOnRemove : true,
30017 * @event OverlayViewShow
30018 * Fires when OverlayView Draw
30019 * @param {Roo.bootstrap.LocationPicker} this
30020 * @param {Pixel} cpx
30022 OverlayViewShow : true,
30024 * @event OverlayViewHide
30025 * Fires when OverlayView Draw
30026 * @param {Roo.bootstrap.LocationPicker} this
30028 OverlayViewHide : true,
30030 * @event loadexception
30031 * Fires when load google lib failed.
30032 * @param {Roo.bootstrap.LocationPicker} this
30034 loadexception : true
30039 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30041 gMapContext: false,
30047 mapTypeControl: false,
30048 disableDoubleClickZoom: false,
30050 streetViewControl: false,
30054 enableAutocomplete: false,
30055 enableReverseGeocode: true,
30058 getAutoCreate: function()
30063 cls: 'roo-location-picker'
30069 initEvents: function(ct, position)
30071 if(!this.el.getWidth() || this.isApplied()){
30075 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30080 initial: function()
30082 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30083 this.fireEvent('loadexception', this);
30087 if(!this.mapTypeId){
30088 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30091 this.gMapContext = this.GMapContext();
30093 this.initOverlayView();
30095 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30099 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30100 _this.setPosition(_this.gMapContext.marker.position);
30103 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30104 _this.fireEvent('mapClick', this, event);
30108 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30109 _this.fireEvent('mapRightClick', this, event);
30113 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30114 _this.fireEvent('markerClick', this, event);
30118 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30119 _this.fireEvent('markerRightClick', this, event);
30123 this.setPosition(this.gMapContext.location);
30125 this.fireEvent('initial', this, this.gMapContext.location);
30128 initOverlayView: function()
30132 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30136 _this.fireEvent('OverlayViewDraw', _this);
30141 _this.fireEvent('OverlayViewOnAdd', _this);
30144 onRemove: function()
30146 _this.fireEvent('OverlayViewOnRemove', _this);
30149 show: function(cpx)
30151 _this.fireEvent('OverlayViewShow', _this, cpx);
30156 _this.fireEvent('OverlayViewHide', _this);
30162 fromLatLngToContainerPixel: function(event)
30164 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30167 isApplied: function()
30169 return this.getGmapContext() == false ? false : true;
30172 getGmapContext: function()
30174 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30177 GMapContext: function()
30179 var position = new google.maps.LatLng(this.latitude, this.longitude);
30181 var _map = new google.maps.Map(this.el.dom, {
30184 mapTypeId: this.mapTypeId,
30185 mapTypeControl: this.mapTypeControl,
30186 disableDoubleClickZoom: this.disableDoubleClickZoom,
30187 scrollwheel: this.scrollwheel,
30188 streetViewControl: this.streetViewControl,
30189 locationName: this.locationName,
30190 draggable: this.draggable,
30191 enableAutocomplete: this.enableAutocomplete,
30192 enableReverseGeocode: this.enableReverseGeocode
30195 var _marker = new google.maps.Marker({
30196 position: position,
30198 title: this.markerTitle,
30199 draggable: this.draggable
30206 location: position,
30207 radius: this.radius,
30208 locationName: this.locationName,
30209 addressComponents: {
30210 formatted_address: null,
30211 addressLine1: null,
30212 addressLine2: null,
30214 streetNumber: null,
30218 stateOrProvince: null
30221 domContainer: this.el.dom,
30222 geodecoder: new google.maps.Geocoder()
30226 drawCircle: function(center, radius, options)
30228 if (this.gMapContext.circle != null) {
30229 this.gMapContext.circle.setMap(null);
30233 options = Roo.apply({}, options, {
30234 strokeColor: "#0000FF",
30235 strokeOpacity: .35,
30237 fillColor: "#0000FF",
30241 options.map = this.gMapContext.map;
30242 options.radius = radius;
30243 options.center = center;
30244 this.gMapContext.circle = new google.maps.Circle(options);
30245 return this.gMapContext.circle;
30251 setPosition: function(location)
30253 this.gMapContext.location = location;
30254 this.gMapContext.marker.setPosition(location);
30255 this.gMapContext.map.panTo(location);
30256 this.drawCircle(location, this.gMapContext.radius, {});
30260 if (this.gMapContext.settings.enableReverseGeocode) {
30261 this.gMapContext.geodecoder.geocode({
30262 latLng: this.gMapContext.location
30263 }, function(results, status) {
30265 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30266 _this.gMapContext.locationName = results[0].formatted_address;
30267 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30269 _this.fireEvent('positionchanged', this, location);
30276 this.fireEvent('positionchanged', this, location);
30281 google.maps.event.trigger(this.gMapContext.map, "resize");
30283 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30285 this.fireEvent('resize', this);
30288 setPositionByLatLng: function(latitude, longitude)
30290 this.setPosition(new google.maps.LatLng(latitude, longitude));
30293 getCurrentPosition: function()
30296 latitude: this.gMapContext.location.lat(),
30297 longitude: this.gMapContext.location.lng()
30301 getAddressName: function()
30303 return this.gMapContext.locationName;
30306 getAddressComponents: function()
30308 return this.gMapContext.addressComponents;
30311 address_component_from_google_geocode: function(address_components)
30315 for (var i = 0; i < address_components.length; i++) {
30316 var component = address_components[i];
30317 if (component.types.indexOf("postal_code") >= 0) {
30318 result.postalCode = component.short_name;
30319 } else if (component.types.indexOf("street_number") >= 0) {
30320 result.streetNumber = component.short_name;
30321 } else if (component.types.indexOf("route") >= 0) {
30322 result.streetName = component.short_name;
30323 } else if (component.types.indexOf("neighborhood") >= 0) {
30324 result.city = component.short_name;
30325 } else if (component.types.indexOf("locality") >= 0) {
30326 result.city = component.short_name;
30327 } else if (component.types.indexOf("sublocality") >= 0) {
30328 result.district = component.short_name;
30329 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30330 result.stateOrProvince = component.short_name;
30331 } else if (component.types.indexOf("country") >= 0) {
30332 result.country = component.short_name;
30336 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30337 result.addressLine2 = "";
30341 setZoomLevel: function(zoom)
30343 this.gMapContext.map.setZoom(zoom);
30356 this.fireEvent('show', this);
30367 this.fireEvent('hide', this);
30372 Roo.apply(Roo.bootstrap.LocationPicker, {
30374 OverlayView : function(map, options)
30376 options = options || {};
30383 * @class Roo.bootstrap.Alert
30384 * @extends Roo.bootstrap.Component
30385 * Bootstrap Alert class - shows an alert area box
30387 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30388 Enter a valid email address
30391 * @cfg {String} title The title of alert
30392 * @cfg {String} html The content of alert
30393 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30394 * @cfg {String} fa font-awesomeicon
30395 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30396 * @cfg {Boolean} close true to show a x closer
30400 * Create a new alert
30401 * @param {Object} config The config object
30405 Roo.bootstrap.Alert = function(config){
30406 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30410 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30416 faicon: false, // BC
30420 getAutoCreate : function()
30432 style : this.close ? '' : 'display:none'
30436 cls : 'roo-alert-icon'
30441 cls : 'roo-alert-title',
30446 cls : 'roo-alert-text',
30453 cfg.cn[0].cls += ' fa ' + this.faicon;
30456 cfg.cn[0].cls += ' fa ' + this.fa;
30460 cfg.cls += ' alert-' + this.weight;
30466 initEvents: function()
30468 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30469 this.titleEl = this.el.select('.roo-alert-title',true).first();
30470 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30471 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30472 if (this.seconds > 0) {
30473 this.hide.defer(this.seconds, this);
30477 * Set the Title Message HTML
30478 * @param {String} html
30480 setTitle : function(str)
30482 this.titleEl.dom.innerHTML = str;
30486 * Set the Body Message HTML
30487 * @param {String} html
30489 setHtml : function(str)
30491 this.htmlEl.dom.innerHTML = str;
30494 * Set the Weight of the alert
30495 * @param {String} (success|info|warning|danger) weight
30498 setWeight : function(weight)
30501 this.el.removeClass('alert-' + this.weight);
30504 this.weight = weight;
30506 this.el.addClass('alert-' + this.weight);
30509 * Set the Icon of the alert
30510 * @param {String} see fontawsome names (name without the 'fa-' bit)
30512 setIcon : function(icon)
30515 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30518 this.faicon = icon;
30520 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30545 * @class Roo.bootstrap.UploadCropbox
30546 * @extends Roo.bootstrap.Component
30547 * Bootstrap UploadCropbox class
30548 * @cfg {String} emptyText show when image has been loaded
30549 * @cfg {String} rotateNotify show when image too small to rotate
30550 * @cfg {Number} errorTimeout default 3000
30551 * @cfg {Number} minWidth default 300
30552 * @cfg {Number} minHeight default 300
30553 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30554 * @cfg {Boolean} isDocument (true|false) default false
30555 * @cfg {String} url action url
30556 * @cfg {String} paramName default 'imageUpload'
30557 * @cfg {String} method default POST
30558 * @cfg {Boolean} loadMask (true|false) default true
30559 * @cfg {Boolean} loadingText default 'Loading...'
30562 * Create a new UploadCropbox
30563 * @param {Object} config The config object
30566 Roo.bootstrap.UploadCropbox = function(config){
30567 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30571 * @event beforeselectfile
30572 * Fire before select file
30573 * @param {Roo.bootstrap.UploadCropbox} this
30575 "beforeselectfile" : true,
30578 * Fire after initEvent
30579 * @param {Roo.bootstrap.UploadCropbox} this
30584 * Fire after initEvent
30585 * @param {Roo.bootstrap.UploadCropbox} this
30586 * @param {String} data
30591 * Fire when preparing the file data
30592 * @param {Roo.bootstrap.UploadCropbox} this
30593 * @param {Object} file
30598 * Fire when get exception
30599 * @param {Roo.bootstrap.UploadCropbox} this
30600 * @param {XMLHttpRequest} xhr
30602 "exception" : true,
30604 * @event beforeloadcanvas
30605 * Fire before load the canvas
30606 * @param {Roo.bootstrap.UploadCropbox} this
30607 * @param {String} src
30609 "beforeloadcanvas" : true,
30612 * Fire when trash image
30613 * @param {Roo.bootstrap.UploadCropbox} this
30618 * Fire when download the image
30619 * @param {Roo.bootstrap.UploadCropbox} this
30623 * @event footerbuttonclick
30624 * Fire when footerbuttonclick
30625 * @param {Roo.bootstrap.UploadCropbox} this
30626 * @param {String} type
30628 "footerbuttonclick" : true,
30632 * @param {Roo.bootstrap.UploadCropbox} this
30637 * Fire when rotate the image
30638 * @param {Roo.bootstrap.UploadCropbox} this
30639 * @param {String} pos
30644 * Fire when inspect the file
30645 * @param {Roo.bootstrap.UploadCropbox} this
30646 * @param {Object} file
30651 * Fire when xhr upload the file
30652 * @param {Roo.bootstrap.UploadCropbox} this
30653 * @param {Object} data
30658 * Fire when arrange the file data
30659 * @param {Roo.bootstrap.UploadCropbox} this
30660 * @param {Object} formData
30665 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30668 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30670 emptyText : 'Click to upload image',
30671 rotateNotify : 'Image is too small to rotate',
30672 errorTimeout : 3000,
30686 cropType : 'image/jpeg',
30688 canvasLoaded : false,
30689 isDocument : false,
30691 paramName : 'imageUpload',
30693 loadingText : 'Loading...',
30696 getAutoCreate : function()
30700 cls : 'roo-upload-cropbox',
30704 cls : 'roo-upload-cropbox-selector',
30709 cls : 'roo-upload-cropbox-body',
30710 style : 'cursor:pointer',
30714 cls : 'roo-upload-cropbox-preview'
30718 cls : 'roo-upload-cropbox-thumb'
30722 cls : 'roo-upload-cropbox-empty-notify',
30723 html : this.emptyText
30727 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30728 html : this.rotateNotify
30734 cls : 'roo-upload-cropbox-footer',
30737 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30747 onRender : function(ct, position)
30749 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30751 if (this.buttons.length) {
30753 Roo.each(this.buttons, function(bb) {
30755 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30757 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30763 this.maskEl = this.el;
30767 initEvents : function()
30769 this.urlAPI = (window.createObjectURL && window) ||
30770 (window.URL && URL.revokeObjectURL && URL) ||
30771 (window.webkitURL && webkitURL);
30773 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30774 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30776 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30777 this.selectorEl.hide();
30779 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30780 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30782 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30783 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30784 this.thumbEl.hide();
30786 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30787 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30789 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30790 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30791 this.errorEl.hide();
30793 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30794 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30795 this.footerEl.hide();
30797 this.setThumbBoxSize();
30803 this.fireEvent('initial', this);
30810 window.addEventListener("resize", function() { _this.resize(); } );
30812 this.bodyEl.on('click', this.beforeSelectFile, this);
30815 this.bodyEl.on('touchstart', this.onTouchStart, this);
30816 this.bodyEl.on('touchmove', this.onTouchMove, this);
30817 this.bodyEl.on('touchend', this.onTouchEnd, this);
30821 this.bodyEl.on('mousedown', this.onMouseDown, this);
30822 this.bodyEl.on('mousemove', this.onMouseMove, this);
30823 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30824 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30825 Roo.get(document).on('mouseup', this.onMouseUp, this);
30828 this.selectorEl.on('change', this.onFileSelected, this);
30834 this.baseScale = 1;
30836 this.baseRotate = 1;
30837 this.dragable = false;
30838 this.pinching = false;
30841 this.cropData = false;
30842 this.notifyEl.dom.innerHTML = this.emptyText;
30844 this.selectorEl.dom.value = '';
30848 resize : function()
30850 if(this.fireEvent('resize', this) != false){
30851 this.setThumbBoxPosition();
30852 this.setCanvasPosition();
30856 onFooterButtonClick : function(e, el, o, type)
30859 case 'rotate-left' :
30860 this.onRotateLeft(e);
30862 case 'rotate-right' :
30863 this.onRotateRight(e);
30866 this.beforeSelectFile(e);
30881 this.fireEvent('footerbuttonclick', this, type);
30884 beforeSelectFile : function(e)
30886 e.preventDefault();
30888 if(this.fireEvent('beforeselectfile', this) != false){
30889 this.selectorEl.dom.click();
30893 onFileSelected : function(e)
30895 e.preventDefault();
30897 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30901 var file = this.selectorEl.dom.files[0];
30903 if(this.fireEvent('inspect', this, file) != false){
30904 this.prepare(file);
30909 trash : function(e)
30911 this.fireEvent('trash', this);
30914 download : function(e)
30916 this.fireEvent('download', this);
30919 loadCanvas : function(src)
30921 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30925 this.imageEl = document.createElement('img');
30929 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30931 this.imageEl.src = src;
30935 onLoadCanvas : function()
30937 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30938 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30940 this.bodyEl.un('click', this.beforeSelectFile, this);
30942 this.notifyEl.hide();
30943 this.thumbEl.show();
30944 this.footerEl.show();
30946 this.baseRotateLevel();
30948 if(this.isDocument){
30949 this.setThumbBoxSize();
30952 this.setThumbBoxPosition();
30954 this.baseScaleLevel();
30960 this.canvasLoaded = true;
30963 this.maskEl.unmask();
30968 setCanvasPosition : function()
30970 if(!this.canvasEl){
30974 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30975 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30977 this.previewEl.setLeft(pw);
30978 this.previewEl.setTop(ph);
30982 onMouseDown : function(e)
30986 this.dragable = true;
30987 this.pinching = false;
30989 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30990 this.dragable = false;
30994 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30995 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30999 onMouseMove : function(e)
31003 if(!this.canvasLoaded){
31007 if (!this.dragable){
31011 var minX = Math.ceil(this.thumbEl.getLeft(true));
31012 var minY = Math.ceil(this.thumbEl.getTop(true));
31014 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31015 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31017 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31018 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31020 x = x - this.mouseX;
31021 y = y - this.mouseY;
31023 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31024 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31026 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31027 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31029 this.previewEl.setLeft(bgX);
31030 this.previewEl.setTop(bgY);
31032 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31033 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31036 onMouseUp : function(e)
31040 this.dragable = false;
31043 onMouseWheel : function(e)
31047 this.startScale = this.scale;
31049 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31051 if(!this.zoomable()){
31052 this.scale = this.startScale;
31061 zoomable : function()
31063 var minScale = this.thumbEl.getWidth() / this.minWidth;
31065 if(this.minWidth < this.minHeight){
31066 minScale = this.thumbEl.getHeight() / this.minHeight;
31069 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31070 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31074 (this.rotate == 0 || this.rotate == 180) &&
31076 width > this.imageEl.OriginWidth ||
31077 height > this.imageEl.OriginHeight ||
31078 (width < this.minWidth && height < this.minHeight)
31086 (this.rotate == 90 || this.rotate == 270) &&
31088 width > this.imageEl.OriginWidth ||
31089 height > this.imageEl.OriginHeight ||
31090 (width < this.minHeight && height < this.minWidth)
31097 !this.isDocument &&
31098 (this.rotate == 0 || this.rotate == 180) &&
31100 width < this.minWidth ||
31101 width > this.imageEl.OriginWidth ||
31102 height < this.minHeight ||
31103 height > this.imageEl.OriginHeight
31110 !this.isDocument &&
31111 (this.rotate == 90 || this.rotate == 270) &&
31113 width < this.minHeight ||
31114 width > this.imageEl.OriginWidth ||
31115 height < this.minWidth ||
31116 height > this.imageEl.OriginHeight
31126 onRotateLeft : function(e)
31128 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31130 var minScale = this.thumbEl.getWidth() / this.minWidth;
31132 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31133 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31135 this.startScale = this.scale;
31137 while (this.getScaleLevel() < minScale){
31139 this.scale = this.scale + 1;
31141 if(!this.zoomable()){
31146 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31147 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31152 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31159 this.scale = this.startScale;
31161 this.onRotateFail();
31166 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31168 if(this.isDocument){
31169 this.setThumbBoxSize();
31170 this.setThumbBoxPosition();
31171 this.setCanvasPosition();
31176 this.fireEvent('rotate', this, 'left');
31180 onRotateRight : function(e)
31182 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31184 var minScale = this.thumbEl.getWidth() / this.minWidth;
31186 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31187 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31189 this.startScale = this.scale;
31191 while (this.getScaleLevel() < minScale){
31193 this.scale = this.scale + 1;
31195 if(!this.zoomable()){
31200 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31201 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31206 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31213 this.scale = this.startScale;
31215 this.onRotateFail();
31220 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31222 if(this.isDocument){
31223 this.setThumbBoxSize();
31224 this.setThumbBoxPosition();
31225 this.setCanvasPosition();
31230 this.fireEvent('rotate', this, 'right');
31233 onRotateFail : function()
31235 this.errorEl.show(true);
31239 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31244 this.previewEl.dom.innerHTML = '';
31246 var canvasEl = document.createElement("canvas");
31248 var contextEl = canvasEl.getContext("2d");
31250 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31251 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31252 var center = this.imageEl.OriginWidth / 2;
31254 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31255 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31256 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31257 center = this.imageEl.OriginHeight / 2;
31260 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31262 contextEl.translate(center, center);
31263 contextEl.rotate(this.rotate * Math.PI / 180);
31265 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31267 this.canvasEl = document.createElement("canvas");
31269 this.contextEl = this.canvasEl.getContext("2d");
31271 switch (this.rotate) {
31274 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31275 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31277 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31282 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31283 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31285 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31286 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);
31290 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31295 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31296 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31298 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31299 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);
31303 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);
31308 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31309 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31311 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31312 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31316 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);
31323 this.previewEl.appendChild(this.canvasEl);
31325 this.setCanvasPosition();
31330 if(!this.canvasLoaded){
31334 var imageCanvas = document.createElement("canvas");
31336 var imageContext = imageCanvas.getContext("2d");
31338 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31339 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31341 var center = imageCanvas.width / 2;
31343 imageContext.translate(center, center);
31345 imageContext.rotate(this.rotate * Math.PI / 180);
31347 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31349 var canvas = document.createElement("canvas");
31351 var context = canvas.getContext("2d");
31353 canvas.width = this.minWidth;
31354 canvas.height = this.minHeight;
31356 switch (this.rotate) {
31359 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31360 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31362 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31363 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31365 var targetWidth = this.minWidth - 2 * x;
31366 var targetHeight = this.minHeight - 2 * y;
31370 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31371 scale = targetWidth / width;
31374 if(x > 0 && y == 0){
31375 scale = targetHeight / height;
31378 if(x > 0 && y > 0){
31379 scale = targetWidth / width;
31381 if(width < height){
31382 scale = targetHeight / height;
31386 context.scale(scale, scale);
31388 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31389 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31391 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31392 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31394 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31399 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31400 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31402 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31403 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31405 var targetWidth = this.minWidth - 2 * x;
31406 var targetHeight = this.minHeight - 2 * y;
31410 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31411 scale = targetWidth / width;
31414 if(x > 0 && y == 0){
31415 scale = targetHeight / height;
31418 if(x > 0 && y > 0){
31419 scale = targetWidth / width;
31421 if(width < height){
31422 scale = targetHeight / height;
31426 context.scale(scale, scale);
31428 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31429 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31431 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31432 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31434 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31436 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31441 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31442 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31444 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31445 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31447 var targetWidth = this.minWidth - 2 * x;
31448 var targetHeight = this.minHeight - 2 * y;
31452 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31453 scale = targetWidth / width;
31456 if(x > 0 && y == 0){
31457 scale = targetHeight / height;
31460 if(x > 0 && y > 0){
31461 scale = targetWidth / width;
31463 if(width < height){
31464 scale = targetHeight / height;
31468 context.scale(scale, scale);
31470 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31471 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31473 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31474 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31476 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31477 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31479 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31484 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31485 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31487 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31488 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31490 var targetWidth = this.minWidth - 2 * x;
31491 var targetHeight = this.minHeight - 2 * y;
31495 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31496 scale = targetWidth / width;
31499 if(x > 0 && y == 0){
31500 scale = targetHeight / height;
31503 if(x > 0 && y > 0){
31504 scale = targetWidth / width;
31506 if(width < height){
31507 scale = targetHeight / height;
31511 context.scale(scale, scale);
31513 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31514 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31516 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31517 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31519 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31521 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31528 this.cropData = canvas.toDataURL(this.cropType);
31530 if(this.fireEvent('crop', this, this.cropData) !== false){
31531 this.process(this.file, this.cropData);
31538 setThumbBoxSize : function()
31542 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31543 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31544 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31546 this.minWidth = width;
31547 this.minHeight = height;
31549 if(this.rotate == 90 || this.rotate == 270){
31550 this.minWidth = height;
31551 this.minHeight = width;
31556 width = Math.ceil(this.minWidth * height / this.minHeight);
31558 if(this.minWidth > this.minHeight){
31560 height = Math.ceil(this.minHeight * width / this.minWidth);
31563 this.thumbEl.setStyle({
31564 width : width + 'px',
31565 height : height + 'px'
31572 setThumbBoxPosition : function()
31574 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31575 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31577 this.thumbEl.setLeft(x);
31578 this.thumbEl.setTop(y);
31582 baseRotateLevel : function()
31584 this.baseRotate = 1;
31587 typeof(this.exif) != 'undefined' &&
31588 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31589 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31591 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31594 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31598 baseScaleLevel : function()
31602 if(this.isDocument){
31604 if(this.baseRotate == 6 || this.baseRotate == 8){
31606 height = this.thumbEl.getHeight();
31607 this.baseScale = height / this.imageEl.OriginWidth;
31609 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31610 width = this.thumbEl.getWidth();
31611 this.baseScale = width / this.imageEl.OriginHeight;
31617 height = this.thumbEl.getHeight();
31618 this.baseScale = height / this.imageEl.OriginHeight;
31620 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31621 width = this.thumbEl.getWidth();
31622 this.baseScale = width / this.imageEl.OriginWidth;
31628 if(this.baseRotate == 6 || this.baseRotate == 8){
31630 width = this.thumbEl.getHeight();
31631 this.baseScale = width / this.imageEl.OriginHeight;
31633 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31634 height = this.thumbEl.getWidth();
31635 this.baseScale = height / this.imageEl.OriginHeight;
31638 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31639 height = this.thumbEl.getWidth();
31640 this.baseScale = height / this.imageEl.OriginHeight;
31642 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31643 width = this.thumbEl.getHeight();
31644 this.baseScale = width / this.imageEl.OriginWidth;
31651 width = this.thumbEl.getWidth();
31652 this.baseScale = width / this.imageEl.OriginWidth;
31654 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31655 height = this.thumbEl.getHeight();
31656 this.baseScale = height / this.imageEl.OriginHeight;
31659 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31661 height = this.thumbEl.getHeight();
31662 this.baseScale = height / this.imageEl.OriginHeight;
31664 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31665 width = this.thumbEl.getWidth();
31666 this.baseScale = width / this.imageEl.OriginWidth;
31674 getScaleLevel : function()
31676 return this.baseScale * Math.pow(1.1, this.scale);
31679 onTouchStart : function(e)
31681 if(!this.canvasLoaded){
31682 this.beforeSelectFile(e);
31686 var touches = e.browserEvent.touches;
31692 if(touches.length == 1){
31693 this.onMouseDown(e);
31697 if(touches.length != 2){
31703 for(var i = 0, finger; finger = touches[i]; i++){
31704 coords.push(finger.pageX, finger.pageY);
31707 var x = Math.pow(coords[0] - coords[2], 2);
31708 var y = Math.pow(coords[1] - coords[3], 2);
31710 this.startDistance = Math.sqrt(x + y);
31712 this.startScale = this.scale;
31714 this.pinching = true;
31715 this.dragable = false;
31719 onTouchMove : function(e)
31721 if(!this.pinching && !this.dragable){
31725 var touches = e.browserEvent.touches;
31732 this.onMouseMove(e);
31738 for(var i = 0, finger; finger = touches[i]; i++){
31739 coords.push(finger.pageX, finger.pageY);
31742 var x = Math.pow(coords[0] - coords[2], 2);
31743 var y = Math.pow(coords[1] - coords[3], 2);
31745 this.endDistance = Math.sqrt(x + y);
31747 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31749 if(!this.zoomable()){
31750 this.scale = this.startScale;
31758 onTouchEnd : function(e)
31760 this.pinching = false;
31761 this.dragable = false;
31765 process : function(file, crop)
31768 this.maskEl.mask(this.loadingText);
31771 this.xhr = new XMLHttpRequest();
31773 file.xhr = this.xhr;
31775 this.xhr.open(this.method, this.url, true);
31778 "Accept": "application/json",
31779 "Cache-Control": "no-cache",
31780 "X-Requested-With": "XMLHttpRequest"
31783 for (var headerName in headers) {
31784 var headerValue = headers[headerName];
31786 this.xhr.setRequestHeader(headerName, headerValue);
31792 this.xhr.onload = function()
31794 _this.xhrOnLoad(_this.xhr);
31797 this.xhr.onerror = function()
31799 _this.xhrOnError(_this.xhr);
31802 var formData = new FormData();
31804 formData.append('returnHTML', 'NO');
31807 formData.append('crop', crop);
31810 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31811 formData.append(this.paramName, file, file.name);
31814 if(typeof(file.filename) != 'undefined'){
31815 formData.append('filename', file.filename);
31818 if(typeof(file.mimetype) != 'undefined'){
31819 formData.append('mimetype', file.mimetype);
31822 if(this.fireEvent('arrange', this, formData) != false){
31823 this.xhr.send(formData);
31827 xhrOnLoad : function(xhr)
31830 this.maskEl.unmask();
31833 if (xhr.readyState !== 4) {
31834 this.fireEvent('exception', this, xhr);
31838 var response = Roo.decode(xhr.responseText);
31840 if(!response.success){
31841 this.fireEvent('exception', this, xhr);
31845 var response = Roo.decode(xhr.responseText);
31847 this.fireEvent('upload', this, response);
31851 xhrOnError : function()
31854 this.maskEl.unmask();
31857 Roo.log('xhr on error');
31859 var response = Roo.decode(xhr.responseText);
31865 prepare : function(file)
31868 this.maskEl.mask(this.loadingText);
31874 if(typeof(file) === 'string'){
31875 this.loadCanvas(file);
31879 if(!file || !this.urlAPI){
31884 this.cropType = file.type;
31888 if(this.fireEvent('prepare', this, this.file) != false){
31890 var reader = new FileReader();
31892 reader.onload = function (e) {
31893 if (e.target.error) {
31894 Roo.log(e.target.error);
31898 var buffer = e.target.result,
31899 dataView = new DataView(buffer),
31901 maxOffset = dataView.byteLength - 4,
31905 if (dataView.getUint16(0) === 0xffd8) {
31906 while (offset < maxOffset) {
31907 markerBytes = dataView.getUint16(offset);
31909 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31910 markerLength = dataView.getUint16(offset + 2) + 2;
31911 if (offset + markerLength > dataView.byteLength) {
31912 Roo.log('Invalid meta data: Invalid segment size.');
31916 if(markerBytes == 0xffe1){
31917 _this.parseExifData(
31924 offset += markerLength;
31934 var url = _this.urlAPI.createObjectURL(_this.file);
31936 _this.loadCanvas(url);
31941 reader.readAsArrayBuffer(this.file);
31947 parseExifData : function(dataView, offset, length)
31949 var tiffOffset = offset + 10,
31953 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31954 // No Exif data, might be XMP data instead
31958 // Check for the ASCII code for "Exif" (0x45786966):
31959 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31960 // No Exif data, might be XMP data instead
31963 if (tiffOffset + 8 > dataView.byteLength) {
31964 Roo.log('Invalid Exif data: Invalid segment size.');
31967 // Check for the two null bytes:
31968 if (dataView.getUint16(offset + 8) !== 0x0000) {
31969 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31972 // Check the byte alignment:
31973 switch (dataView.getUint16(tiffOffset)) {
31975 littleEndian = true;
31978 littleEndian = false;
31981 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31984 // Check for the TIFF tag marker (0x002A):
31985 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31986 Roo.log('Invalid Exif data: Missing TIFF marker.');
31989 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31990 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31992 this.parseExifTags(
31995 tiffOffset + dirOffset,
32000 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32005 if (dirOffset + 6 > dataView.byteLength) {
32006 Roo.log('Invalid Exif data: Invalid directory offset.');
32009 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32010 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32011 if (dirEndOffset + 4 > dataView.byteLength) {
32012 Roo.log('Invalid Exif data: Invalid directory size.');
32015 for (i = 0; i < tagsNumber; i += 1) {
32019 dirOffset + 2 + 12 * i, // tag offset
32023 // Return the offset to the next directory:
32024 return dataView.getUint32(dirEndOffset, littleEndian);
32027 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32029 var tag = dataView.getUint16(offset, littleEndian);
32031 this.exif[tag] = this.getExifValue(
32035 dataView.getUint16(offset + 2, littleEndian), // tag type
32036 dataView.getUint32(offset + 4, littleEndian), // tag length
32041 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32043 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32052 Roo.log('Invalid Exif data: Invalid tag type.');
32056 tagSize = tagType.size * length;
32057 // Determine if the value is contained in the dataOffset bytes,
32058 // or if the value at the dataOffset is a pointer to the actual data:
32059 dataOffset = tagSize > 4 ?
32060 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32061 if (dataOffset + tagSize > dataView.byteLength) {
32062 Roo.log('Invalid Exif data: Invalid data offset.');
32065 if (length === 1) {
32066 return tagType.getValue(dataView, dataOffset, littleEndian);
32069 for (i = 0; i < length; i += 1) {
32070 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32073 if (tagType.ascii) {
32075 // Concatenate the chars:
32076 for (i = 0; i < values.length; i += 1) {
32078 // Ignore the terminating NULL byte(s):
32079 if (c === '\u0000') {
32091 Roo.apply(Roo.bootstrap.UploadCropbox, {
32093 'Orientation': 0x0112
32097 1: 0, //'top-left',
32099 3: 180, //'bottom-right',
32100 // 4: 'bottom-left',
32102 6: 90, //'right-top',
32103 // 7: 'right-bottom',
32104 8: 270 //'left-bottom'
32108 // byte, 8-bit unsigned int:
32110 getValue: function (dataView, dataOffset) {
32111 return dataView.getUint8(dataOffset);
32115 // ascii, 8-bit byte:
32117 getValue: function (dataView, dataOffset) {
32118 return String.fromCharCode(dataView.getUint8(dataOffset));
32123 // short, 16 bit int:
32125 getValue: function (dataView, dataOffset, littleEndian) {
32126 return dataView.getUint16(dataOffset, littleEndian);
32130 // long, 32 bit int:
32132 getValue: function (dataView, dataOffset, littleEndian) {
32133 return dataView.getUint32(dataOffset, littleEndian);
32137 // rational = two long values, first is numerator, second is denominator:
32139 getValue: function (dataView, dataOffset, littleEndian) {
32140 return dataView.getUint32(dataOffset, littleEndian) /
32141 dataView.getUint32(dataOffset + 4, littleEndian);
32145 // slong, 32 bit signed int:
32147 getValue: function (dataView, dataOffset, littleEndian) {
32148 return dataView.getInt32(dataOffset, littleEndian);
32152 // srational, two slongs, first is numerator, second is denominator:
32154 getValue: function (dataView, dataOffset, littleEndian) {
32155 return dataView.getInt32(dataOffset, littleEndian) /
32156 dataView.getInt32(dataOffset + 4, littleEndian);
32166 cls : 'btn-group roo-upload-cropbox-rotate-left',
32167 action : 'rotate-left',
32171 cls : 'btn btn-default',
32172 html : '<i class="fa fa-undo"></i>'
32178 cls : 'btn-group roo-upload-cropbox-picture',
32179 action : 'picture',
32183 cls : 'btn btn-default',
32184 html : '<i class="fa fa-picture-o"></i>'
32190 cls : 'btn-group roo-upload-cropbox-rotate-right',
32191 action : 'rotate-right',
32195 cls : 'btn btn-default',
32196 html : '<i class="fa fa-repeat"></i>'
32204 cls : 'btn-group roo-upload-cropbox-rotate-left',
32205 action : 'rotate-left',
32209 cls : 'btn btn-default',
32210 html : '<i class="fa fa-undo"></i>'
32216 cls : 'btn-group roo-upload-cropbox-download',
32217 action : 'download',
32221 cls : 'btn btn-default',
32222 html : '<i class="fa fa-download"></i>'
32228 cls : 'btn-group roo-upload-cropbox-crop',
32233 cls : 'btn btn-default',
32234 html : '<i class="fa fa-crop"></i>'
32240 cls : 'btn-group roo-upload-cropbox-trash',
32245 cls : 'btn btn-default',
32246 html : '<i class="fa fa-trash"></i>'
32252 cls : 'btn-group roo-upload-cropbox-rotate-right',
32253 action : 'rotate-right',
32257 cls : 'btn btn-default',
32258 html : '<i class="fa fa-repeat"></i>'
32266 cls : 'btn-group roo-upload-cropbox-rotate-left',
32267 action : 'rotate-left',
32271 cls : 'btn btn-default',
32272 html : '<i class="fa fa-undo"></i>'
32278 cls : 'btn-group roo-upload-cropbox-rotate-right',
32279 action : 'rotate-right',
32283 cls : 'btn btn-default',
32284 html : '<i class="fa fa-repeat"></i>'
32297 * @class Roo.bootstrap.DocumentManager
32298 * @extends Roo.bootstrap.Component
32299 * Bootstrap DocumentManager class
32300 * @cfg {String} paramName default 'imageUpload'
32301 * @cfg {String} toolTipName default 'filename'
32302 * @cfg {String} method default POST
32303 * @cfg {String} url action url
32304 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32305 * @cfg {Boolean} multiple multiple upload default true
32306 * @cfg {Number} thumbSize default 300
32307 * @cfg {String} fieldLabel
32308 * @cfg {Number} labelWidth default 4
32309 * @cfg {String} labelAlign (left|top) default left
32310 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32311 * @cfg {Number} labellg set the width of label (1-12)
32312 * @cfg {Number} labelmd set the width of label (1-12)
32313 * @cfg {Number} labelsm set the width of label (1-12)
32314 * @cfg {Number} labelxs set the width of label (1-12)
32317 * Create a new DocumentManager
32318 * @param {Object} config The config object
32321 Roo.bootstrap.DocumentManager = function(config){
32322 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32325 this.delegates = [];
32330 * Fire when initial the DocumentManager
32331 * @param {Roo.bootstrap.DocumentManager} this
32336 * inspect selected file
32337 * @param {Roo.bootstrap.DocumentManager} this
32338 * @param {File} file
32343 * Fire when xhr load exception
32344 * @param {Roo.bootstrap.DocumentManager} this
32345 * @param {XMLHttpRequest} xhr
32347 "exception" : true,
32349 * @event afterupload
32350 * Fire when xhr load exception
32351 * @param {Roo.bootstrap.DocumentManager} this
32352 * @param {XMLHttpRequest} xhr
32354 "afterupload" : true,
32357 * prepare the form data
32358 * @param {Roo.bootstrap.DocumentManager} this
32359 * @param {Object} formData
32364 * Fire when remove the file
32365 * @param {Roo.bootstrap.DocumentManager} this
32366 * @param {Object} file
32371 * Fire after refresh the file
32372 * @param {Roo.bootstrap.DocumentManager} this
32377 * Fire after click the image
32378 * @param {Roo.bootstrap.DocumentManager} this
32379 * @param {Object} file
32384 * Fire when upload a image and editable set to true
32385 * @param {Roo.bootstrap.DocumentManager} this
32386 * @param {Object} file
32390 * @event beforeselectfile
32391 * Fire before select file
32392 * @param {Roo.bootstrap.DocumentManager} this
32394 "beforeselectfile" : true,
32397 * Fire before process file
32398 * @param {Roo.bootstrap.DocumentManager} this
32399 * @param {Object} file
32403 * @event previewrendered
32404 * Fire when preview rendered
32405 * @param {Roo.bootstrap.DocumentManager} this
32406 * @param {Object} file
32408 "previewrendered" : true,
32411 "previewResize" : true
32416 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32425 paramName : 'imageUpload',
32426 toolTipName : 'filename',
32429 labelAlign : 'left',
32439 getAutoCreate : function()
32441 var managerWidget = {
32443 cls : 'roo-document-manager',
32447 cls : 'roo-document-manager-selector',
32452 cls : 'roo-document-manager-uploader',
32456 cls : 'roo-document-manager-upload-btn',
32457 html : '<i class="fa fa-plus"></i>'
32468 cls : 'column col-md-12',
32473 if(this.fieldLabel.length){
32478 cls : 'column col-md-12',
32479 html : this.fieldLabel
32483 cls : 'column col-md-12',
32488 if(this.labelAlign == 'left'){
32493 html : this.fieldLabel
32502 if(this.labelWidth > 12){
32503 content[0].style = "width: " + this.labelWidth + 'px';
32506 if(this.labelWidth < 13 && this.labelmd == 0){
32507 this.labelmd = this.labelWidth;
32510 if(this.labellg > 0){
32511 content[0].cls += ' col-lg-' + this.labellg;
32512 content[1].cls += ' col-lg-' + (12 - this.labellg);
32515 if(this.labelmd > 0){
32516 content[0].cls += ' col-md-' + this.labelmd;
32517 content[1].cls += ' col-md-' + (12 - this.labelmd);
32520 if(this.labelsm > 0){
32521 content[0].cls += ' col-sm-' + this.labelsm;
32522 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32525 if(this.labelxs > 0){
32526 content[0].cls += ' col-xs-' + this.labelxs;
32527 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32535 cls : 'row clearfix',
32543 initEvents : function()
32545 this.managerEl = this.el.select('.roo-document-manager', true).first();
32546 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32548 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32549 this.selectorEl.hide();
32552 this.selectorEl.attr('multiple', 'multiple');
32555 this.selectorEl.on('change', this.onFileSelected, this);
32557 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32558 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32560 this.uploader.on('click', this.onUploaderClick, this);
32562 this.renderProgressDialog();
32566 window.addEventListener("resize", function() { _this.refresh(); } );
32568 this.fireEvent('initial', this);
32571 renderProgressDialog : function()
32575 this.progressDialog = new Roo.bootstrap.Modal({
32576 cls : 'roo-document-manager-progress-dialog',
32577 allow_close : false,
32588 btnclick : function() {
32589 _this.uploadCancel();
32595 this.progressDialog.render(Roo.get(document.body));
32597 this.progress = new Roo.bootstrap.Progress({
32598 cls : 'roo-document-manager-progress',
32603 this.progress.render(this.progressDialog.getChildContainer());
32605 this.progressBar = new Roo.bootstrap.ProgressBar({
32606 cls : 'roo-document-manager-progress-bar',
32609 aria_valuemax : 12,
32613 this.progressBar.render(this.progress.getChildContainer());
32616 onUploaderClick : function(e)
32618 e.preventDefault();
32620 if(this.fireEvent('beforeselectfile', this) != false){
32621 this.selectorEl.dom.click();
32626 onFileSelected : function(e)
32628 e.preventDefault();
32630 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32634 Roo.each(this.selectorEl.dom.files, function(file){
32635 if(this.fireEvent('inspect', this, file) != false){
32636 this.files.push(file);
32646 this.selectorEl.dom.value = '';
32648 if(!this.files || !this.files.length){
32652 if(this.boxes > 0 && this.files.length > this.boxes){
32653 this.files = this.files.slice(0, this.boxes);
32656 this.uploader.show();
32658 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32659 this.uploader.hide();
32668 Roo.each(this.files, function(file){
32670 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32671 var f = this.renderPreview(file);
32676 if(file.type.indexOf('image') != -1){
32677 this.delegates.push(
32679 _this.process(file);
32680 }).createDelegate(this)
32688 _this.process(file);
32689 }).createDelegate(this)
32694 this.files = files;
32696 this.delegates = this.delegates.concat(docs);
32698 if(!this.delegates.length){
32703 this.progressBar.aria_valuemax = this.delegates.length;
32710 arrange : function()
32712 if(!this.delegates.length){
32713 this.progressDialog.hide();
32718 var delegate = this.delegates.shift();
32720 this.progressDialog.show();
32722 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32724 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32729 refresh : function()
32731 this.uploader.show();
32733 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32734 this.uploader.hide();
32737 Roo.isTouch ? this.closable(false) : this.closable(true);
32739 this.fireEvent('refresh', this);
32742 onRemove : function(e, el, o)
32744 e.preventDefault();
32746 this.fireEvent('remove', this, o);
32750 remove : function(o)
32754 Roo.each(this.files, function(file){
32755 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32764 this.files = files;
32771 Roo.each(this.files, function(file){
32776 file.target.remove();
32785 onClick : function(e, el, o)
32787 e.preventDefault();
32789 this.fireEvent('click', this, o);
32793 closable : function(closable)
32795 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32797 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32809 xhrOnLoad : function(xhr)
32811 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32815 if (xhr.readyState !== 4) {
32817 this.fireEvent('exception', this, xhr);
32821 var response = Roo.decode(xhr.responseText);
32823 if(!response.success){
32825 this.fireEvent('exception', this, xhr);
32829 var file = this.renderPreview(response.data);
32831 this.files.push(file);
32835 this.fireEvent('afterupload', this, xhr);
32839 xhrOnError : function(xhr)
32841 Roo.log('xhr on error');
32843 var response = Roo.decode(xhr.responseText);
32850 process : function(file)
32852 if(this.fireEvent('process', this, file) !== false){
32853 if(this.editable && file.type.indexOf('image') != -1){
32854 this.fireEvent('edit', this, file);
32858 this.uploadStart(file, false);
32865 uploadStart : function(file, crop)
32867 this.xhr = new XMLHttpRequest();
32869 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32874 file.xhr = this.xhr;
32876 this.managerEl.createChild({
32878 cls : 'roo-document-manager-loading',
32882 tooltip : file.name,
32883 cls : 'roo-document-manager-thumb',
32884 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32890 this.xhr.open(this.method, this.url, true);
32893 "Accept": "application/json",
32894 "Cache-Control": "no-cache",
32895 "X-Requested-With": "XMLHttpRequest"
32898 for (var headerName in headers) {
32899 var headerValue = headers[headerName];
32901 this.xhr.setRequestHeader(headerName, headerValue);
32907 this.xhr.onload = function()
32909 _this.xhrOnLoad(_this.xhr);
32912 this.xhr.onerror = function()
32914 _this.xhrOnError(_this.xhr);
32917 var formData = new FormData();
32919 formData.append('returnHTML', 'NO');
32922 formData.append('crop', crop);
32925 formData.append(this.paramName, file, file.name);
32932 if(this.fireEvent('prepare', this, formData, options) != false){
32934 if(options.manually){
32938 this.xhr.send(formData);
32942 this.uploadCancel();
32945 uploadCancel : function()
32951 this.delegates = [];
32953 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32960 renderPreview : function(file)
32962 if(typeof(file.target) != 'undefined' && file.target){
32966 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32968 var previewEl = this.managerEl.createChild({
32970 cls : 'roo-document-manager-preview',
32974 tooltip : file[this.toolTipName],
32975 cls : 'roo-document-manager-thumb',
32976 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32981 html : '<i class="fa fa-times-circle"></i>'
32986 var close = previewEl.select('button.close', true).first();
32988 close.on('click', this.onRemove, this, file);
32990 file.target = previewEl;
32992 var image = previewEl.select('img', true).first();
32996 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32998 image.on('click', this.onClick, this, file);
33000 this.fireEvent('previewrendered', this, file);
33006 onPreviewLoad : function(file, image)
33008 if(typeof(file.target) == 'undefined' || !file.target){
33012 var width = image.dom.naturalWidth || image.dom.width;
33013 var height = image.dom.naturalHeight || image.dom.height;
33015 if(!this.previewResize) {
33019 if(width > height){
33020 file.target.addClass('wide');
33024 file.target.addClass('tall');
33029 uploadFromSource : function(file, crop)
33031 this.xhr = new XMLHttpRequest();
33033 this.managerEl.createChild({
33035 cls : 'roo-document-manager-loading',
33039 tooltip : file.name,
33040 cls : 'roo-document-manager-thumb',
33041 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33047 this.xhr.open(this.method, this.url, true);
33050 "Accept": "application/json",
33051 "Cache-Control": "no-cache",
33052 "X-Requested-With": "XMLHttpRequest"
33055 for (var headerName in headers) {
33056 var headerValue = headers[headerName];
33058 this.xhr.setRequestHeader(headerName, headerValue);
33064 this.xhr.onload = function()
33066 _this.xhrOnLoad(_this.xhr);
33069 this.xhr.onerror = function()
33071 _this.xhrOnError(_this.xhr);
33074 var formData = new FormData();
33076 formData.append('returnHTML', 'NO');
33078 formData.append('crop', crop);
33080 if(typeof(file.filename) != 'undefined'){
33081 formData.append('filename', file.filename);
33084 if(typeof(file.mimetype) != 'undefined'){
33085 formData.append('mimetype', file.mimetype);
33090 if(this.fireEvent('prepare', this, formData) != false){
33091 this.xhr.send(formData);
33101 * @class Roo.bootstrap.DocumentViewer
33102 * @extends Roo.bootstrap.Component
33103 * Bootstrap DocumentViewer class
33104 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33105 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33108 * Create a new DocumentViewer
33109 * @param {Object} config The config object
33112 Roo.bootstrap.DocumentViewer = function(config){
33113 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33118 * Fire after initEvent
33119 * @param {Roo.bootstrap.DocumentViewer} this
33125 * @param {Roo.bootstrap.DocumentViewer} this
33130 * Fire after download button
33131 * @param {Roo.bootstrap.DocumentViewer} this
33136 * Fire after trash button
33137 * @param {Roo.bootstrap.DocumentViewer} this
33144 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33146 showDownload : true,
33150 getAutoCreate : function()
33154 cls : 'roo-document-viewer',
33158 cls : 'roo-document-viewer-body',
33162 cls : 'roo-document-viewer-thumb',
33166 cls : 'roo-document-viewer-image'
33174 cls : 'roo-document-viewer-footer',
33177 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33181 cls : 'btn-group roo-document-viewer-download',
33185 cls : 'btn btn-default',
33186 html : '<i class="fa fa-download"></i>'
33192 cls : 'btn-group roo-document-viewer-trash',
33196 cls : 'btn btn-default',
33197 html : '<i class="fa fa-trash"></i>'
33210 initEvents : function()
33212 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33213 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33215 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33216 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33218 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33219 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33221 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33222 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33224 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33225 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33227 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33228 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33230 this.bodyEl.on('click', this.onClick, this);
33231 this.downloadBtn.on('click', this.onDownload, this);
33232 this.trashBtn.on('click', this.onTrash, this);
33234 this.downloadBtn.hide();
33235 this.trashBtn.hide();
33237 if(this.showDownload){
33238 this.downloadBtn.show();
33241 if(this.showTrash){
33242 this.trashBtn.show();
33245 if(!this.showDownload && !this.showTrash) {
33246 this.footerEl.hide();
33251 initial : function()
33253 this.fireEvent('initial', this);
33257 onClick : function(e)
33259 e.preventDefault();
33261 this.fireEvent('click', this);
33264 onDownload : function(e)
33266 e.preventDefault();
33268 this.fireEvent('download', this);
33271 onTrash : function(e)
33273 e.preventDefault();
33275 this.fireEvent('trash', this);
33287 * @class Roo.bootstrap.NavProgressBar
33288 * @extends Roo.bootstrap.Component
33289 * Bootstrap NavProgressBar class
33292 * Create a new nav progress bar
33293 * @param {Object} config The config object
33296 Roo.bootstrap.NavProgressBar = function(config){
33297 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33299 this.bullets = this.bullets || [];
33301 // Roo.bootstrap.NavProgressBar.register(this);
33305 * Fires when the active item changes
33306 * @param {Roo.bootstrap.NavProgressBar} this
33307 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33308 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33315 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33320 getAutoCreate : function()
33322 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33326 cls : 'roo-navigation-bar-group',
33330 cls : 'roo-navigation-top-bar'
33334 cls : 'roo-navigation-bullets-bar',
33338 cls : 'roo-navigation-bar'
33345 cls : 'roo-navigation-bottom-bar'
33355 initEvents: function()
33360 onRender : function(ct, position)
33362 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33364 if(this.bullets.length){
33365 Roo.each(this.bullets, function(b){
33374 addItem : function(cfg)
33376 var item = new Roo.bootstrap.NavProgressItem(cfg);
33378 item.parentId = this.id;
33379 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33382 var top = new Roo.bootstrap.Element({
33384 cls : 'roo-navigation-bar-text'
33387 var bottom = new Roo.bootstrap.Element({
33389 cls : 'roo-navigation-bar-text'
33392 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33393 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33395 var topText = new Roo.bootstrap.Element({
33397 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33400 var bottomText = new Roo.bootstrap.Element({
33402 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33405 topText.onRender(top.el, null);
33406 bottomText.onRender(bottom.el, null);
33409 item.bottomEl = bottom;
33412 this.barItems.push(item);
33417 getActive : function()
33419 var active = false;
33421 Roo.each(this.barItems, function(v){
33423 if (!v.isActive()) {
33435 setActiveItem : function(item)
33439 Roo.each(this.barItems, function(v){
33440 if (v.rid == item.rid) {
33444 if (v.isActive()) {
33445 v.setActive(false);
33450 item.setActive(true);
33452 this.fireEvent('changed', this, item, prev);
33455 getBarItem: function(rid)
33459 Roo.each(this.barItems, function(e) {
33460 if (e.rid != rid) {
33471 indexOfItem : function(item)
33475 Roo.each(this.barItems, function(v, i){
33477 if (v.rid != item.rid) {
33488 setActiveNext : function()
33490 var i = this.indexOfItem(this.getActive());
33492 if (i > this.barItems.length) {
33496 this.setActiveItem(this.barItems[i+1]);
33499 setActivePrev : function()
33501 var i = this.indexOfItem(this.getActive());
33507 this.setActiveItem(this.barItems[i-1]);
33510 format : function()
33512 if(!this.barItems.length){
33516 var width = 100 / this.barItems.length;
33518 Roo.each(this.barItems, function(i){
33519 i.el.setStyle('width', width + '%');
33520 i.topEl.el.setStyle('width', width + '%');
33521 i.bottomEl.el.setStyle('width', width + '%');
33530 * Nav Progress Item
33535 * @class Roo.bootstrap.NavProgressItem
33536 * @extends Roo.bootstrap.Component
33537 * Bootstrap NavProgressItem class
33538 * @cfg {String} rid the reference id
33539 * @cfg {Boolean} active (true|false) Is item active default false
33540 * @cfg {Boolean} disabled (true|false) Is item active default false
33541 * @cfg {String} html
33542 * @cfg {String} position (top|bottom) text position default bottom
33543 * @cfg {String} icon show icon instead of number
33546 * Create a new NavProgressItem
33547 * @param {Object} config The config object
33549 Roo.bootstrap.NavProgressItem = function(config){
33550 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33555 * The raw click event for the entire grid.
33556 * @param {Roo.bootstrap.NavProgressItem} this
33557 * @param {Roo.EventObject} e
33564 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33570 position : 'bottom',
33573 getAutoCreate : function()
33575 var iconCls = 'roo-navigation-bar-item-icon';
33577 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33581 cls: 'roo-navigation-bar-item',
33591 cfg.cls += ' active';
33594 cfg.cls += ' disabled';
33600 disable : function()
33602 this.setDisabled(true);
33605 enable : function()
33607 this.setDisabled(false);
33610 initEvents: function()
33612 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33614 this.iconEl.on('click', this.onClick, this);
33617 onClick : function(e)
33619 e.preventDefault();
33625 if(this.fireEvent('click', this, e) === false){
33629 this.parent().setActiveItem(this);
33632 isActive: function ()
33634 return this.active;
33637 setActive : function(state)
33639 if(this.active == state){
33643 this.active = state;
33646 this.el.addClass('active');
33650 this.el.removeClass('active');
33655 setDisabled : function(state)
33657 if(this.disabled == state){
33661 this.disabled = state;
33664 this.el.addClass('disabled');
33668 this.el.removeClass('disabled');
33671 tooltipEl : function()
33673 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33686 * @class Roo.bootstrap.FieldLabel
33687 * @extends Roo.bootstrap.Component
33688 * Bootstrap FieldLabel class
33689 * @cfg {String} html contents of the element
33690 * @cfg {String} tag tag of the element default label
33691 * @cfg {String} cls class of the element
33692 * @cfg {String} target label target
33693 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33694 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33695 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33696 * @cfg {String} iconTooltip default "This field is required"
33697 * @cfg {String} indicatorpos (left|right) default left
33700 * Create a new FieldLabel
33701 * @param {Object} config The config object
33704 Roo.bootstrap.FieldLabel = function(config){
33705 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33710 * Fires after the field has been marked as invalid.
33711 * @param {Roo.form.FieldLabel} this
33712 * @param {String} msg The validation message
33717 * Fires after the field has been validated with no errors.
33718 * @param {Roo.form.FieldLabel} this
33724 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33731 invalidClass : 'has-warning',
33732 validClass : 'has-success',
33733 iconTooltip : 'This field is required',
33734 indicatorpos : 'left',
33736 getAutoCreate : function(){
33739 if (!this.allowBlank) {
33745 cls : 'roo-bootstrap-field-label ' + this.cls,
33750 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33751 tooltip : this.iconTooltip
33760 if(this.indicatorpos == 'right'){
33763 cls : 'roo-bootstrap-field-label ' + this.cls,
33772 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33773 tooltip : this.iconTooltip
33782 initEvents: function()
33784 Roo.bootstrap.Element.superclass.initEvents.call(this);
33786 this.indicator = this.indicatorEl();
33788 if(this.indicator){
33789 this.indicator.removeClass('visible');
33790 this.indicator.addClass('invisible');
33793 Roo.bootstrap.FieldLabel.register(this);
33796 indicatorEl : function()
33798 var indicator = this.el.select('i.roo-required-indicator',true).first();
33809 * Mark this field as valid
33811 markValid : function()
33813 if(this.indicator){
33814 this.indicator.removeClass('visible');
33815 this.indicator.addClass('invisible');
33817 if (Roo.bootstrap.version == 3) {
33818 this.el.removeClass(this.invalidClass);
33819 this.el.addClass(this.validClass);
33821 this.el.removeClass('is-invalid');
33822 this.el.addClass('is-valid');
33826 this.fireEvent('valid', this);
33830 * Mark this field as invalid
33831 * @param {String} msg The validation message
33833 markInvalid : function(msg)
33835 if(this.indicator){
33836 this.indicator.removeClass('invisible');
33837 this.indicator.addClass('visible');
33839 if (Roo.bootstrap.version == 3) {
33840 this.el.removeClass(this.validClass);
33841 this.el.addClass(this.invalidClass);
33843 this.el.removeClass('is-valid');
33844 this.el.addClass('is-invalid');
33848 this.fireEvent('invalid', this, msg);
33854 Roo.apply(Roo.bootstrap.FieldLabel, {
33859 * register a FieldLabel Group
33860 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33862 register : function(label)
33864 if(this.groups.hasOwnProperty(label.target)){
33868 this.groups[label.target] = label;
33872 * fetch a FieldLabel Group based on the target
33873 * @param {string} target
33874 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33876 get: function(target) {
33877 if (typeof(this.groups[target]) == 'undefined') {
33881 return this.groups[target] ;
33890 * page DateSplitField.
33896 * @class Roo.bootstrap.DateSplitField
33897 * @extends Roo.bootstrap.Component
33898 * Bootstrap DateSplitField class
33899 * @cfg {string} fieldLabel - the label associated
33900 * @cfg {Number} labelWidth set the width of label (0-12)
33901 * @cfg {String} labelAlign (top|left)
33902 * @cfg {Boolean} dayAllowBlank (true|false) default false
33903 * @cfg {Boolean} monthAllowBlank (true|false) default false
33904 * @cfg {Boolean} yearAllowBlank (true|false) default false
33905 * @cfg {string} dayPlaceholder
33906 * @cfg {string} monthPlaceholder
33907 * @cfg {string} yearPlaceholder
33908 * @cfg {string} dayFormat default 'd'
33909 * @cfg {string} monthFormat default 'm'
33910 * @cfg {string} yearFormat default 'Y'
33911 * @cfg {Number} labellg set the width of label (1-12)
33912 * @cfg {Number} labelmd set the width of label (1-12)
33913 * @cfg {Number} labelsm set the width of label (1-12)
33914 * @cfg {Number} labelxs set the width of label (1-12)
33918 * Create a new DateSplitField
33919 * @param {Object} config The config object
33922 Roo.bootstrap.DateSplitField = function(config){
33923 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33929 * getting the data of years
33930 * @param {Roo.bootstrap.DateSplitField} this
33931 * @param {Object} years
33936 * getting the data of days
33937 * @param {Roo.bootstrap.DateSplitField} this
33938 * @param {Object} days
33943 * Fires after the field has been marked as invalid.
33944 * @param {Roo.form.Field} this
33945 * @param {String} msg The validation message
33950 * Fires after the field has been validated with no errors.
33951 * @param {Roo.form.Field} this
33957 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33960 labelAlign : 'top',
33962 dayAllowBlank : false,
33963 monthAllowBlank : false,
33964 yearAllowBlank : false,
33965 dayPlaceholder : '',
33966 monthPlaceholder : '',
33967 yearPlaceholder : '',
33971 isFormField : true,
33977 getAutoCreate : function()
33981 cls : 'row roo-date-split-field-group',
33986 cls : 'form-hidden-field roo-date-split-field-group-value',
33992 var labelCls = 'col-md-12';
33993 var contentCls = 'col-md-4';
33995 if(this.fieldLabel){
33999 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34003 html : this.fieldLabel
34008 if(this.labelAlign == 'left'){
34010 if(this.labelWidth > 12){
34011 label.style = "width: " + this.labelWidth + 'px';
34014 if(this.labelWidth < 13 && this.labelmd == 0){
34015 this.labelmd = this.labelWidth;
34018 if(this.labellg > 0){
34019 labelCls = ' col-lg-' + this.labellg;
34020 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34023 if(this.labelmd > 0){
34024 labelCls = ' col-md-' + this.labelmd;
34025 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34028 if(this.labelsm > 0){
34029 labelCls = ' col-sm-' + this.labelsm;
34030 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34033 if(this.labelxs > 0){
34034 labelCls = ' col-xs-' + this.labelxs;
34035 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34039 label.cls += ' ' + labelCls;
34041 cfg.cn.push(label);
34044 Roo.each(['day', 'month', 'year'], function(t){
34047 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34054 inputEl: function ()
34056 return this.el.select('.roo-date-split-field-group-value', true).first();
34059 onRender : function(ct, position)
34063 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34065 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34067 this.dayField = new Roo.bootstrap.ComboBox({
34068 allowBlank : this.dayAllowBlank,
34069 alwaysQuery : true,
34070 displayField : 'value',
34073 forceSelection : true,
34075 placeholder : this.dayPlaceholder,
34076 selectOnFocus : true,
34077 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34078 triggerAction : 'all',
34080 valueField : 'value',
34081 store : new Roo.data.SimpleStore({
34082 data : (function() {
34084 _this.fireEvent('days', _this, days);
34087 fields : [ 'value' ]
34090 select : function (_self, record, index)
34092 _this.setValue(_this.getValue());
34097 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34099 this.monthField = new Roo.bootstrap.MonthField({
34100 after : '<i class=\"fa fa-calendar\"></i>',
34101 allowBlank : this.monthAllowBlank,
34102 placeholder : this.monthPlaceholder,
34105 render : function (_self)
34107 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34108 e.preventDefault();
34112 select : function (_self, oldvalue, newvalue)
34114 _this.setValue(_this.getValue());
34119 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34121 this.yearField = new Roo.bootstrap.ComboBox({
34122 allowBlank : this.yearAllowBlank,
34123 alwaysQuery : true,
34124 displayField : 'value',
34127 forceSelection : true,
34129 placeholder : this.yearPlaceholder,
34130 selectOnFocus : true,
34131 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34132 triggerAction : 'all',
34134 valueField : 'value',
34135 store : new Roo.data.SimpleStore({
34136 data : (function() {
34138 _this.fireEvent('years', _this, years);
34141 fields : [ 'value' ]
34144 select : function (_self, record, index)
34146 _this.setValue(_this.getValue());
34151 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34154 setValue : function(v, format)
34156 this.inputEl.dom.value = v;
34158 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34160 var d = Date.parseDate(v, f);
34167 this.setDay(d.format(this.dayFormat));
34168 this.setMonth(d.format(this.monthFormat));
34169 this.setYear(d.format(this.yearFormat));
34176 setDay : function(v)
34178 this.dayField.setValue(v);
34179 this.inputEl.dom.value = this.getValue();
34184 setMonth : function(v)
34186 this.monthField.setValue(v, true);
34187 this.inputEl.dom.value = this.getValue();
34192 setYear : function(v)
34194 this.yearField.setValue(v);
34195 this.inputEl.dom.value = this.getValue();
34200 getDay : function()
34202 return this.dayField.getValue();
34205 getMonth : function()
34207 return this.monthField.getValue();
34210 getYear : function()
34212 return this.yearField.getValue();
34215 getValue : function()
34217 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34219 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34229 this.inputEl.dom.value = '';
34234 validate : function()
34236 var d = this.dayField.validate();
34237 var m = this.monthField.validate();
34238 var y = this.yearField.validate();
34243 (!this.dayAllowBlank && !d) ||
34244 (!this.monthAllowBlank && !m) ||
34245 (!this.yearAllowBlank && !y)
34250 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34259 this.markInvalid();
34264 markValid : function()
34267 var label = this.el.select('label', true).first();
34268 var icon = this.el.select('i.fa-star', true).first();
34274 this.fireEvent('valid', this);
34278 * Mark this field as invalid
34279 * @param {String} msg The validation message
34281 markInvalid : function(msg)
34284 var label = this.el.select('label', true).first();
34285 var icon = this.el.select('i.fa-star', true).first();
34287 if(label && !icon){
34288 this.el.select('.roo-date-split-field-label', true).createChild({
34290 cls : 'text-danger fa fa-lg fa-star',
34291 tooltip : 'This field is required',
34292 style : 'margin-right:5px;'
34296 this.fireEvent('invalid', this, msg);
34299 clearInvalid : function()
34301 var label = this.el.select('label', true).first();
34302 var icon = this.el.select('i.fa-star', true).first();
34308 this.fireEvent('valid', this);
34311 getName: function()
34321 * http://masonry.desandro.com
34323 * The idea is to render all the bricks based on vertical width...
34325 * The original code extends 'outlayer' - we might need to use that....
34331 * @class Roo.bootstrap.LayoutMasonry
34332 * @extends Roo.bootstrap.Component
34333 * Bootstrap Layout Masonry class
34336 * Create a new Element
34337 * @param {Object} config The config object
34340 Roo.bootstrap.LayoutMasonry = function(config){
34342 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34346 Roo.bootstrap.LayoutMasonry.register(this);
34352 * Fire after layout the items
34353 * @param {Roo.bootstrap.LayoutMasonry} this
34354 * @param {Roo.EventObject} e
34361 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34364 * @cfg {Boolean} isLayoutInstant = no animation?
34366 isLayoutInstant : false, // needed?
34369 * @cfg {Number} boxWidth width of the columns
34374 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34379 * @cfg {Number} padWidth padding below box..
34384 * @cfg {Number} gutter gutter width..
34389 * @cfg {Number} maxCols maximum number of columns
34395 * @cfg {Boolean} isAutoInitial defalut true
34397 isAutoInitial : true,
34402 * @cfg {Boolean} isHorizontal defalut false
34404 isHorizontal : false,
34406 currentSize : null,
34412 bricks: null, //CompositeElement
34416 _isLayoutInited : false,
34418 // isAlternative : false, // only use for vertical layout...
34421 * @cfg {Number} alternativePadWidth padding below box..
34423 alternativePadWidth : 50,
34425 selectedBrick : [],
34427 getAutoCreate : function(){
34429 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34433 cls: 'blog-masonary-wrapper ' + this.cls,
34435 cls : 'mas-boxes masonary'
34442 getChildContainer: function( )
34444 if (this.boxesEl) {
34445 return this.boxesEl;
34448 this.boxesEl = this.el.select('.mas-boxes').first();
34450 return this.boxesEl;
34454 initEvents : function()
34458 if(this.isAutoInitial){
34459 Roo.log('hook children rendered');
34460 this.on('childrenrendered', function() {
34461 Roo.log('children rendered');
34467 initial : function()
34469 this.selectedBrick = [];
34471 this.currentSize = this.el.getBox(true);
34473 Roo.EventManager.onWindowResize(this.resize, this);
34475 if(!this.isAutoInitial){
34483 //this.layout.defer(500,this);
34487 resize : function()
34489 var cs = this.el.getBox(true);
34492 this.currentSize.width == cs.width &&
34493 this.currentSize.x == cs.x &&
34494 this.currentSize.height == cs.height &&
34495 this.currentSize.y == cs.y
34497 Roo.log("no change in with or X or Y");
34501 this.currentSize = cs;
34507 layout : function()
34509 this._resetLayout();
34511 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34513 this.layoutItems( isInstant );
34515 this._isLayoutInited = true;
34517 this.fireEvent('layout', this);
34521 _resetLayout : function()
34523 if(this.isHorizontal){
34524 this.horizontalMeasureColumns();
34528 this.verticalMeasureColumns();
34532 verticalMeasureColumns : function()
34534 this.getContainerWidth();
34536 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34537 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34541 var boxWidth = this.boxWidth + this.padWidth;
34543 if(this.containerWidth < this.boxWidth){
34544 boxWidth = this.containerWidth
34547 var containerWidth = this.containerWidth;
34549 var cols = Math.floor(containerWidth / boxWidth);
34551 this.cols = Math.max( cols, 1 );
34553 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34555 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34557 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34559 this.colWidth = boxWidth + avail - this.padWidth;
34561 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34562 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34565 horizontalMeasureColumns : function()
34567 this.getContainerWidth();
34569 var boxWidth = this.boxWidth;
34571 if(this.containerWidth < boxWidth){
34572 boxWidth = this.containerWidth;
34575 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34577 this.el.setHeight(boxWidth);
34581 getContainerWidth : function()
34583 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34586 layoutItems : function( isInstant )
34588 Roo.log(this.bricks);
34590 var items = Roo.apply([], this.bricks);
34592 if(this.isHorizontal){
34593 this._horizontalLayoutItems( items , isInstant );
34597 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34598 // this._verticalAlternativeLayoutItems( items , isInstant );
34602 this._verticalLayoutItems( items , isInstant );
34606 _verticalLayoutItems : function ( items , isInstant)
34608 if ( !items || !items.length ) {
34613 ['xs', 'xs', 'xs', 'tall'],
34614 ['xs', 'xs', 'tall'],
34615 ['xs', 'xs', 'sm'],
34616 ['xs', 'xs', 'xs'],
34622 ['sm', 'xs', 'xs'],
34626 ['tall', 'xs', 'xs', 'xs'],
34627 ['tall', 'xs', 'xs'],
34639 Roo.each(items, function(item, k){
34641 switch (item.size) {
34642 // these layouts take up a full box,
34653 boxes.push([item]);
34676 var filterPattern = function(box, length)
34684 var pattern = box.slice(0, length);
34688 Roo.each(pattern, function(i){
34689 format.push(i.size);
34692 Roo.each(standard, function(s){
34694 if(String(s) != String(format)){
34703 if(!match && length == 1){
34708 filterPattern(box, length - 1);
34712 queue.push(pattern);
34714 box = box.slice(length, box.length);
34716 filterPattern(box, 4);
34722 Roo.each(boxes, function(box, k){
34728 if(box.length == 1){
34733 filterPattern(box, 4);
34737 this._processVerticalLayoutQueue( queue, isInstant );
34741 // _verticalAlternativeLayoutItems : function( items , isInstant )
34743 // if ( !items || !items.length ) {
34747 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34751 _horizontalLayoutItems : function ( items , isInstant)
34753 if ( !items || !items.length || items.length < 3) {
34759 var eItems = items.slice(0, 3);
34761 items = items.slice(3, items.length);
34764 ['xs', 'xs', 'xs', 'wide'],
34765 ['xs', 'xs', 'wide'],
34766 ['xs', 'xs', 'sm'],
34767 ['xs', 'xs', 'xs'],
34773 ['sm', 'xs', 'xs'],
34777 ['wide', 'xs', 'xs', 'xs'],
34778 ['wide', 'xs', 'xs'],
34791 Roo.each(items, function(item, k){
34793 switch (item.size) {
34804 boxes.push([item]);
34828 var filterPattern = function(box, length)
34836 var pattern = box.slice(0, length);
34840 Roo.each(pattern, function(i){
34841 format.push(i.size);
34844 Roo.each(standard, function(s){
34846 if(String(s) != String(format)){
34855 if(!match && length == 1){
34860 filterPattern(box, length - 1);
34864 queue.push(pattern);
34866 box = box.slice(length, box.length);
34868 filterPattern(box, 4);
34874 Roo.each(boxes, function(box, k){
34880 if(box.length == 1){
34885 filterPattern(box, 4);
34892 var pos = this.el.getBox(true);
34896 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34898 var hit_end = false;
34900 Roo.each(queue, function(box){
34904 Roo.each(box, function(b){
34906 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34916 Roo.each(box, function(b){
34918 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34921 mx = Math.max(mx, b.x);
34925 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34929 Roo.each(box, function(b){
34931 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34945 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34948 /** Sets position of item in DOM
34949 * @param {Element} item
34950 * @param {Number} x - horizontal position
34951 * @param {Number} y - vertical position
34952 * @param {Boolean} isInstant - disables transitions
34954 _processVerticalLayoutQueue : function( queue, isInstant )
34956 var pos = this.el.getBox(true);
34961 for (var i = 0; i < this.cols; i++){
34965 Roo.each(queue, function(box, k){
34967 var col = k % this.cols;
34969 Roo.each(box, function(b,kk){
34971 b.el.position('absolute');
34973 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34974 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34976 if(b.size == 'md-left' || b.size == 'md-right'){
34977 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34978 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34981 b.el.setWidth(width);
34982 b.el.setHeight(height);
34984 b.el.select('iframe',true).setSize(width,height);
34988 for (var i = 0; i < this.cols; i++){
34990 if(maxY[i] < maxY[col]){
34995 col = Math.min(col, i);
34999 x = pos.x + col * (this.colWidth + this.padWidth);
35003 var positions = [];
35005 switch (box.length){
35007 positions = this.getVerticalOneBoxColPositions(x, y, box);
35010 positions = this.getVerticalTwoBoxColPositions(x, y, box);
35013 positions = this.getVerticalThreeBoxColPositions(x, y, box);
35016 positions = this.getVerticalFourBoxColPositions(x, y, box);
35022 Roo.each(box, function(b,kk){
35024 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35026 var sz = b.el.getSize();
35028 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35036 for (var i = 0; i < this.cols; i++){
35037 mY = Math.max(mY, maxY[i]);
35040 this.el.setHeight(mY - pos.y);
35044 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35046 // var pos = this.el.getBox(true);
35049 // var maxX = pos.right;
35051 // var maxHeight = 0;
35053 // Roo.each(items, function(item, k){
35057 // item.el.position('absolute');
35059 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35061 // item.el.setWidth(width);
35063 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35065 // item.el.setHeight(height);
35068 // item.el.setXY([x, y], isInstant ? false : true);
35070 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35073 // y = y + height + this.alternativePadWidth;
35075 // maxHeight = maxHeight + height + this.alternativePadWidth;
35079 // this.el.setHeight(maxHeight);
35083 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35085 var pos = this.el.getBox(true);
35090 var maxX = pos.right;
35092 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35094 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35096 Roo.each(queue, function(box, k){
35098 Roo.each(box, function(b, kk){
35100 b.el.position('absolute');
35102 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35103 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35105 if(b.size == 'md-left' || b.size == 'md-right'){
35106 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35107 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35110 b.el.setWidth(width);
35111 b.el.setHeight(height);
35119 var positions = [];
35121 switch (box.length){
35123 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35126 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35129 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35132 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35138 Roo.each(box, function(b,kk){
35140 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35142 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35150 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35152 Roo.each(eItems, function(b,k){
35154 b.size = (k == 0) ? 'sm' : 'xs';
35155 b.x = (k == 0) ? 2 : 1;
35156 b.y = (k == 0) ? 2 : 1;
35158 b.el.position('absolute');
35160 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35162 b.el.setWidth(width);
35164 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35166 b.el.setHeight(height);
35170 var positions = [];
35173 x : maxX - this.unitWidth * 2 - this.gutter,
35178 x : maxX - this.unitWidth,
35179 y : minY + (this.unitWidth + this.gutter) * 2
35183 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35187 Roo.each(eItems, function(b,k){
35189 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35195 getVerticalOneBoxColPositions : function(x, y, box)
35199 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35201 if(box[0].size == 'md-left'){
35205 if(box[0].size == 'md-right'){
35210 x : x + (this.unitWidth + this.gutter) * rand,
35217 getVerticalTwoBoxColPositions : function(x, y, box)
35221 if(box[0].size == 'xs'){
35225 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35229 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35243 x : x + (this.unitWidth + this.gutter) * 2,
35244 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35251 getVerticalThreeBoxColPositions : function(x, y, box)
35255 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35263 x : x + (this.unitWidth + this.gutter) * 1,
35268 x : x + (this.unitWidth + this.gutter) * 2,
35276 if(box[0].size == 'xs' && box[1].size == 'xs'){
35285 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35289 x : x + (this.unitWidth + this.gutter) * 1,
35303 x : x + (this.unitWidth + this.gutter) * 2,
35308 x : x + (this.unitWidth + this.gutter) * 2,
35309 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35316 getVerticalFourBoxColPositions : function(x, y, box)
35320 if(box[0].size == 'xs'){
35329 y : y + (this.unitHeight + this.gutter) * 1
35334 y : y + (this.unitHeight + this.gutter) * 2
35338 x : x + (this.unitWidth + this.gutter) * 1,
35352 x : x + (this.unitWidth + this.gutter) * 2,
35357 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35358 y : y + (this.unitHeight + this.gutter) * 1
35362 x : x + (this.unitWidth + this.gutter) * 2,
35363 y : y + (this.unitWidth + this.gutter) * 2
35370 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35374 if(box[0].size == 'md-left'){
35376 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35383 if(box[0].size == 'md-right'){
35385 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35386 y : minY + (this.unitWidth + this.gutter) * 1
35392 var rand = Math.floor(Math.random() * (4 - box[0].y));
35395 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35396 y : minY + (this.unitWidth + this.gutter) * rand
35403 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35407 if(box[0].size == 'xs'){
35410 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35415 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35416 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35424 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35429 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35430 y : minY + (this.unitWidth + this.gutter) * 2
35437 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35441 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35444 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35449 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35450 y : minY + (this.unitWidth + this.gutter) * 1
35454 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35455 y : minY + (this.unitWidth + this.gutter) * 2
35462 if(box[0].size == 'xs' && box[1].size == 'xs'){
35465 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35470 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35475 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35476 y : minY + (this.unitWidth + this.gutter) * 1
35484 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35489 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35490 y : minY + (this.unitWidth + this.gutter) * 2
35494 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35495 y : minY + (this.unitWidth + this.gutter) * 2
35502 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35506 if(box[0].size == 'xs'){
35509 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35514 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35519 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),
35524 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35525 y : minY + (this.unitWidth + this.gutter) * 1
35533 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35538 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35539 y : minY + (this.unitWidth + this.gutter) * 2
35543 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35544 y : minY + (this.unitWidth + this.gutter) * 2
35548 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),
35549 y : minY + (this.unitWidth + this.gutter) * 2
35557 * remove a Masonry Brick
35558 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35560 removeBrick : function(brick_id)
35566 for (var i = 0; i<this.bricks.length; i++) {
35567 if (this.bricks[i].id == brick_id) {
35568 this.bricks.splice(i,1);
35569 this.el.dom.removeChild(Roo.get(brick_id).dom);
35576 * adds a Masonry Brick
35577 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35579 addBrick : function(cfg)
35581 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35582 //this.register(cn);
35583 cn.parentId = this.id;
35584 cn.render(this.el);
35589 * register a Masonry Brick
35590 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35593 register : function(brick)
35595 this.bricks.push(brick);
35596 brick.masonryId = this.id;
35600 * clear all the Masonry Brick
35602 clearAll : function()
35605 //this.getChildContainer().dom.innerHTML = "";
35606 this.el.dom.innerHTML = '';
35609 getSelected : function()
35611 if (!this.selectedBrick) {
35615 return this.selectedBrick;
35619 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35623 * register a Masonry Layout
35624 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35627 register : function(layout)
35629 this.groups[layout.id] = layout;
35632 * fetch a Masonry Layout based on the masonry layout ID
35633 * @param {string} the masonry layout to add
35634 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35637 get: function(layout_id) {
35638 if (typeof(this.groups[layout_id]) == 'undefined') {
35641 return this.groups[layout_id] ;
35653 * http://masonry.desandro.com
35655 * The idea is to render all the bricks based on vertical width...
35657 * The original code extends 'outlayer' - we might need to use that....
35663 * @class Roo.bootstrap.LayoutMasonryAuto
35664 * @extends Roo.bootstrap.Component
35665 * Bootstrap Layout Masonry class
35668 * Create a new Element
35669 * @param {Object} config The config object
35672 Roo.bootstrap.LayoutMasonryAuto = function(config){
35673 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35676 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35679 * @cfg {Boolean} isFitWidth - resize the width..
35681 isFitWidth : false, // options..
35683 * @cfg {Boolean} isOriginLeft = left align?
35685 isOriginLeft : true,
35687 * @cfg {Boolean} isOriginTop = top align?
35689 isOriginTop : false,
35691 * @cfg {Boolean} isLayoutInstant = no animation?
35693 isLayoutInstant : false, // needed?
35695 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35697 isResizingContainer : true,
35699 * @cfg {Number} columnWidth width of the columns
35705 * @cfg {Number} maxCols maximum number of columns
35710 * @cfg {Number} padHeight padding below box..
35716 * @cfg {Boolean} isAutoInitial defalut true
35719 isAutoInitial : true,
35725 initialColumnWidth : 0,
35726 currentSize : null,
35728 colYs : null, // array.
35735 bricks: null, //CompositeElement
35736 cols : 0, // array?
35737 // element : null, // wrapped now this.el
35738 _isLayoutInited : null,
35741 getAutoCreate : function(){
35745 cls: 'blog-masonary-wrapper ' + this.cls,
35747 cls : 'mas-boxes masonary'
35754 getChildContainer: function( )
35756 if (this.boxesEl) {
35757 return this.boxesEl;
35760 this.boxesEl = this.el.select('.mas-boxes').first();
35762 return this.boxesEl;
35766 initEvents : function()
35770 if(this.isAutoInitial){
35771 Roo.log('hook children rendered');
35772 this.on('childrenrendered', function() {
35773 Roo.log('children rendered');
35780 initial : function()
35782 this.reloadItems();
35784 this.currentSize = this.el.getBox(true);
35786 /// was window resize... - let's see if this works..
35787 Roo.EventManager.onWindowResize(this.resize, this);
35789 if(!this.isAutoInitial){
35794 this.layout.defer(500,this);
35797 reloadItems: function()
35799 this.bricks = this.el.select('.masonry-brick', true);
35801 this.bricks.each(function(b) {
35802 //Roo.log(b.getSize());
35803 if (!b.attr('originalwidth')) {
35804 b.attr('originalwidth', b.getSize().width);
35809 Roo.log(this.bricks.elements.length);
35812 resize : function()
35815 var cs = this.el.getBox(true);
35817 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35818 Roo.log("no change in with or X");
35821 this.currentSize = cs;
35825 layout : function()
35828 this._resetLayout();
35829 //this._manageStamps();
35831 // don't animate first layout
35832 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35833 this.layoutItems( isInstant );
35835 // flag for initalized
35836 this._isLayoutInited = true;
35839 layoutItems : function( isInstant )
35841 //var items = this._getItemsForLayout( this.items );
35842 // original code supports filtering layout items.. we just ignore it..
35844 this._layoutItems( this.bricks , isInstant );
35846 this._postLayout();
35848 _layoutItems : function ( items , isInstant)
35850 //this.fireEvent( 'layout', this, items );
35853 if ( !items || !items.elements.length ) {
35854 // no items, emit event with empty array
35859 items.each(function(item) {
35860 Roo.log("layout item");
35862 // get x/y object from method
35863 var position = this._getItemLayoutPosition( item );
35865 position.item = item;
35866 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35867 queue.push( position );
35870 this._processLayoutQueue( queue );
35872 /** Sets position of item in DOM
35873 * @param {Element} item
35874 * @param {Number} x - horizontal position
35875 * @param {Number} y - vertical position
35876 * @param {Boolean} isInstant - disables transitions
35878 _processLayoutQueue : function( queue )
35880 for ( var i=0, len = queue.length; i < len; i++ ) {
35881 var obj = queue[i];
35882 obj.item.position('absolute');
35883 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35889 * Any logic you want to do after each layout,
35890 * i.e. size the container
35892 _postLayout : function()
35894 this.resizeContainer();
35897 resizeContainer : function()
35899 if ( !this.isResizingContainer ) {
35902 var size = this._getContainerSize();
35904 this.el.setSize(size.width,size.height);
35905 this.boxesEl.setSize(size.width,size.height);
35911 _resetLayout : function()
35913 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35914 this.colWidth = this.el.getWidth();
35915 //this.gutter = this.el.getWidth();
35917 this.measureColumns();
35923 this.colYs.push( 0 );
35929 measureColumns : function()
35931 this.getContainerWidth();
35932 // if columnWidth is 0, default to outerWidth of first item
35933 if ( !this.columnWidth ) {
35934 var firstItem = this.bricks.first();
35935 Roo.log(firstItem);
35936 this.columnWidth = this.containerWidth;
35937 if (firstItem && firstItem.attr('originalwidth') ) {
35938 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35940 // columnWidth fall back to item of first element
35941 Roo.log("set column width?");
35942 this.initialColumnWidth = this.columnWidth ;
35944 // if first elem has no width, default to size of container
35949 if (this.initialColumnWidth) {
35950 this.columnWidth = this.initialColumnWidth;
35955 // column width is fixed at the top - however if container width get's smaller we should
35958 // this bit calcs how man columns..
35960 var columnWidth = this.columnWidth += this.gutter;
35962 // calculate columns
35963 var containerWidth = this.containerWidth + this.gutter;
35965 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35966 // fix rounding errors, typically with gutters
35967 var excess = columnWidth - containerWidth % columnWidth;
35970 // if overshoot is less than a pixel, round up, otherwise floor it
35971 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35972 cols = Math[ mathMethod ]( cols );
35973 this.cols = Math.max( cols, 1 );
35974 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35976 // padding positioning..
35977 var totalColWidth = this.cols * this.columnWidth;
35978 var padavail = this.containerWidth - totalColWidth;
35979 // so for 2 columns - we need 3 'pads'
35981 var padNeeded = (1+this.cols) * this.padWidth;
35983 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35985 this.columnWidth += padExtra
35986 //this.padWidth = Math.floor(padavail / ( this.cols));
35988 // adjust colum width so that padding is fixed??
35990 // we have 3 columns ... total = width * 3
35991 // we have X left over... that should be used by
35993 //if (this.expandC) {
36001 getContainerWidth : function()
36003 /* // container is parent if fit width
36004 var container = this.isFitWidth ? this.element.parentNode : this.element;
36005 // check that this.size and size are there
36006 // IE8 triggers resize on body size change, so they might not be
36008 var size = getSize( container ); //FIXME
36009 this.containerWidth = size && size.innerWidth; //FIXME
36012 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36016 _getItemLayoutPosition : function( item ) // what is item?
36018 // we resize the item to our columnWidth..
36020 item.setWidth(this.columnWidth);
36021 item.autoBoxAdjust = false;
36023 var sz = item.getSize();
36025 // how many columns does this brick span
36026 var remainder = this.containerWidth % this.columnWidth;
36028 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36029 // round if off by 1 pixel, otherwise use ceil
36030 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
36031 colSpan = Math.min( colSpan, this.cols );
36033 // normally this should be '1' as we dont' currently allow multi width columns..
36035 var colGroup = this._getColGroup( colSpan );
36036 // get the minimum Y value from the columns
36037 var minimumY = Math.min.apply( Math, colGroup );
36038 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36040 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
36042 // position the brick
36044 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36045 y: this.currentSize.y + minimumY + this.padHeight
36049 // apply setHeight to necessary columns
36050 var setHeight = minimumY + sz.height + this.padHeight;
36051 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36053 var setSpan = this.cols + 1 - colGroup.length;
36054 for ( var i = 0; i < setSpan; i++ ) {
36055 this.colYs[ shortColIndex + i ] = setHeight ;
36062 * @param {Number} colSpan - number of columns the element spans
36063 * @returns {Array} colGroup
36065 _getColGroup : function( colSpan )
36067 if ( colSpan < 2 ) {
36068 // if brick spans only one column, use all the column Ys
36073 // how many different places could this brick fit horizontally
36074 var groupCount = this.cols + 1 - colSpan;
36075 // for each group potential horizontal position
36076 for ( var i = 0; i < groupCount; i++ ) {
36077 // make an array of colY values for that one group
36078 var groupColYs = this.colYs.slice( i, i + colSpan );
36079 // and get the max value of the array
36080 colGroup[i] = Math.max.apply( Math, groupColYs );
36085 _manageStamp : function( stamp )
36087 var stampSize = stamp.getSize();
36088 var offset = stamp.getBox();
36089 // get the columns that this stamp affects
36090 var firstX = this.isOriginLeft ? offset.x : offset.right;
36091 var lastX = firstX + stampSize.width;
36092 var firstCol = Math.floor( firstX / this.columnWidth );
36093 firstCol = Math.max( 0, firstCol );
36095 var lastCol = Math.floor( lastX / this.columnWidth );
36096 // lastCol should not go over if multiple of columnWidth #425
36097 lastCol -= lastX % this.columnWidth ? 0 : 1;
36098 lastCol = Math.min( this.cols - 1, lastCol );
36100 // set colYs to bottom of the stamp
36101 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36104 for ( var i = firstCol; i <= lastCol; i++ ) {
36105 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36110 _getContainerSize : function()
36112 this.maxY = Math.max.apply( Math, this.colYs );
36117 if ( this.isFitWidth ) {
36118 size.width = this._getContainerFitWidth();
36124 _getContainerFitWidth : function()
36126 var unusedCols = 0;
36127 // count unused columns
36130 if ( this.colYs[i] !== 0 ) {
36135 // fit container to columns that have been used
36136 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36139 needsResizeLayout : function()
36141 var previousWidth = this.containerWidth;
36142 this.getContainerWidth();
36143 return previousWidth !== this.containerWidth;
36158 * @class Roo.bootstrap.MasonryBrick
36159 * @extends Roo.bootstrap.Component
36160 * Bootstrap MasonryBrick class
36163 * Create a new MasonryBrick
36164 * @param {Object} config The config object
36167 Roo.bootstrap.MasonryBrick = function(config){
36169 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36171 Roo.bootstrap.MasonryBrick.register(this);
36177 * When a MasonryBrick is clcik
36178 * @param {Roo.bootstrap.MasonryBrick} this
36179 * @param {Roo.EventObject} e
36185 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36188 * @cfg {String} title
36192 * @cfg {String} html
36196 * @cfg {String} bgimage
36200 * @cfg {String} videourl
36204 * @cfg {String} cls
36208 * @cfg {String} href
36212 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36217 * @cfg {String} placetitle (center|bottom)
36222 * @cfg {Boolean} isFitContainer defalut true
36224 isFitContainer : true,
36227 * @cfg {Boolean} preventDefault defalut false
36229 preventDefault : false,
36232 * @cfg {Boolean} inverse defalut false
36234 maskInverse : false,
36236 getAutoCreate : function()
36238 if(!this.isFitContainer){
36239 return this.getSplitAutoCreate();
36242 var cls = 'masonry-brick masonry-brick-full';
36244 if(this.href.length){
36245 cls += ' masonry-brick-link';
36248 if(this.bgimage.length){
36249 cls += ' masonry-brick-image';
36252 if(this.maskInverse){
36253 cls += ' mask-inverse';
36256 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36257 cls += ' enable-mask';
36261 cls += ' masonry-' + this.size + '-brick';
36264 if(this.placetitle.length){
36266 switch (this.placetitle) {
36268 cls += ' masonry-center-title';
36271 cls += ' masonry-bottom-title';
36278 if(!this.html.length && !this.bgimage.length){
36279 cls += ' masonry-center-title';
36282 if(!this.html.length && this.bgimage.length){
36283 cls += ' masonry-bottom-title';
36288 cls += ' ' + this.cls;
36292 tag: (this.href.length) ? 'a' : 'div',
36297 cls: 'masonry-brick-mask'
36301 cls: 'masonry-brick-paragraph',
36307 if(this.href.length){
36308 cfg.href = this.href;
36311 var cn = cfg.cn[1].cn;
36313 if(this.title.length){
36316 cls: 'masonry-brick-title',
36321 if(this.html.length){
36324 cls: 'masonry-brick-text',
36329 if (!this.title.length && !this.html.length) {
36330 cfg.cn[1].cls += ' hide';
36333 if(this.bgimage.length){
36336 cls: 'masonry-brick-image-view',
36341 if(this.videourl.length){
36342 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36343 // youtube support only?
36346 cls: 'masonry-brick-image-view',
36349 allowfullscreen : true
36357 getSplitAutoCreate : function()
36359 var cls = 'masonry-brick masonry-brick-split';
36361 if(this.href.length){
36362 cls += ' masonry-brick-link';
36365 if(this.bgimage.length){
36366 cls += ' masonry-brick-image';
36370 cls += ' masonry-' + this.size + '-brick';
36373 switch (this.placetitle) {
36375 cls += ' masonry-center-title';
36378 cls += ' masonry-bottom-title';
36381 if(!this.bgimage.length){
36382 cls += ' masonry-center-title';
36385 if(this.bgimage.length){
36386 cls += ' masonry-bottom-title';
36392 cls += ' ' + this.cls;
36396 tag: (this.href.length) ? 'a' : 'div',
36401 cls: 'masonry-brick-split-head',
36405 cls: 'masonry-brick-paragraph',
36412 cls: 'masonry-brick-split-body',
36418 if(this.href.length){
36419 cfg.href = this.href;
36422 if(this.title.length){
36423 cfg.cn[0].cn[0].cn.push({
36425 cls: 'masonry-brick-title',
36430 if(this.html.length){
36431 cfg.cn[1].cn.push({
36433 cls: 'masonry-brick-text',
36438 if(this.bgimage.length){
36439 cfg.cn[0].cn.push({
36441 cls: 'masonry-brick-image-view',
36446 if(this.videourl.length){
36447 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36448 // youtube support only?
36449 cfg.cn[0].cn.cn.push({
36451 cls: 'masonry-brick-image-view',
36454 allowfullscreen : true
36461 initEvents: function()
36463 switch (this.size) {
36496 this.el.on('touchstart', this.onTouchStart, this);
36497 this.el.on('touchmove', this.onTouchMove, this);
36498 this.el.on('touchend', this.onTouchEnd, this);
36499 this.el.on('contextmenu', this.onContextMenu, this);
36501 this.el.on('mouseenter' ,this.enter, this);
36502 this.el.on('mouseleave', this.leave, this);
36503 this.el.on('click', this.onClick, this);
36506 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36507 this.parent().bricks.push(this);
36512 onClick: function(e, el)
36514 var time = this.endTimer - this.startTimer;
36515 // Roo.log(e.preventDefault());
36518 e.preventDefault();
36523 if(!this.preventDefault){
36527 e.preventDefault();
36529 if (this.activeClass != '') {
36530 this.selectBrick();
36533 this.fireEvent('click', this, e);
36536 enter: function(e, el)
36538 e.preventDefault();
36540 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36544 if(this.bgimage.length && this.html.length){
36545 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36549 leave: function(e, el)
36551 e.preventDefault();
36553 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36557 if(this.bgimage.length && this.html.length){
36558 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36562 onTouchStart: function(e, el)
36564 // e.preventDefault();
36566 this.touchmoved = false;
36568 if(!this.isFitContainer){
36572 if(!this.bgimage.length || !this.html.length){
36576 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36578 this.timer = new Date().getTime();
36582 onTouchMove: function(e, el)
36584 this.touchmoved = true;
36587 onContextMenu : function(e,el)
36589 e.preventDefault();
36590 e.stopPropagation();
36594 onTouchEnd: function(e, el)
36596 // e.preventDefault();
36598 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36605 if(!this.bgimage.length || !this.html.length){
36607 if(this.href.length){
36608 window.location.href = this.href;
36614 if(!this.isFitContainer){
36618 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36620 window.location.href = this.href;
36623 //selection on single brick only
36624 selectBrick : function() {
36626 if (!this.parentId) {
36630 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36631 var index = m.selectedBrick.indexOf(this.id);
36634 m.selectedBrick.splice(index,1);
36635 this.el.removeClass(this.activeClass);
36639 for(var i = 0; i < m.selectedBrick.length; i++) {
36640 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36641 b.el.removeClass(b.activeClass);
36644 m.selectedBrick = [];
36646 m.selectedBrick.push(this.id);
36647 this.el.addClass(this.activeClass);
36651 isSelected : function(){
36652 return this.el.hasClass(this.activeClass);
36657 Roo.apply(Roo.bootstrap.MasonryBrick, {
36660 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36662 * register a Masonry Brick
36663 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36666 register : function(brick)
36668 //this.groups[brick.id] = brick;
36669 this.groups.add(brick.id, brick);
36672 * fetch a masonry brick based on the masonry brick ID
36673 * @param {string} the masonry brick to add
36674 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36677 get: function(brick_id)
36679 // if (typeof(this.groups[brick_id]) == 'undefined') {
36682 // return this.groups[brick_id] ;
36684 if(this.groups.key(brick_id)) {
36685 return this.groups.key(brick_id);
36703 * @class Roo.bootstrap.Brick
36704 * @extends Roo.bootstrap.Component
36705 * Bootstrap Brick class
36708 * Create a new Brick
36709 * @param {Object} config The config object
36712 Roo.bootstrap.Brick = function(config){
36713 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36719 * When a Brick is click
36720 * @param {Roo.bootstrap.Brick} this
36721 * @param {Roo.EventObject} e
36727 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36730 * @cfg {String} title
36734 * @cfg {String} html
36738 * @cfg {String} bgimage
36742 * @cfg {String} cls
36746 * @cfg {String} href
36750 * @cfg {String} video
36754 * @cfg {Boolean} square
36758 getAutoCreate : function()
36760 var cls = 'roo-brick';
36762 if(this.href.length){
36763 cls += ' roo-brick-link';
36766 if(this.bgimage.length){
36767 cls += ' roo-brick-image';
36770 if(!this.html.length && !this.bgimage.length){
36771 cls += ' roo-brick-center-title';
36774 if(!this.html.length && this.bgimage.length){
36775 cls += ' roo-brick-bottom-title';
36779 cls += ' ' + this.cls;
36783 tag: (this.href.length) ? 'a' : 'div',
36788 cls: 'roo-brick-paragraph',
36794 if(this.href.length){
36795 cfg.href = this.href;
36798 var cn = cfg.cn[0].cn;
36800 if(this.title.length){
36803 cls: 'roo-brick-title',
36808 if(this.html.length){
36811 cls: 'roo-brick-text',
36818 if(this.bgimage.length){
36821 cls: 'roo-brick-image-view',
36829 initEvents: function()
36831 if(this.title.length || this.html.length){
36832 this.el.on('mouseenter' ,this.enter, this);
36833 this.el.on('mouseleave', this.leave, this);
36836 Roo.EventManager.onWindowResize(this.resize, this);
36838 if(this.bgimage.length){
36839 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36840 this.imageEl.on('load', this.onImageLoad, this);
36847 onImageLoad : function()
36852 resize : function()
36854 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36856 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36858 if(this.bgimage.length){
36859 var image = this.el.select('.roo-brick-image-view', true).first();
36861 image.setWidth(paragraph.getWidth());
36864 image.setHeight(paragraph.getWidth());
36867 this.el.setHeight(image.getHeight());
36868 paragraph.setHeight(image.getHeight());
36874 enter: function(e, el)
36876 e.preventDefault();
36878 if(this.bgimage.length){
36879 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36880 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36884 leave: function(e, el)
36886 e.preventDefault();
36888 if(this.bgimage.length){
36889 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36890 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36905 * @class Roo.bootstrap.NumberField
36906 * @extends Roo.bootstrap.Input
36907 * Bootstrap NumberField class
36913 * Create a new NumberField
36914 * @param {Object} config The config object
36917 Roo.bootstrap.NumberField = function(config){
36918 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36921 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36924 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36926 allowDecimals : true,
36928 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36930 decimalSeparator : ".",
36932 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36934 decimalPrecision : 2,
36936 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36938 allowNegative : true,
36941 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36945 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36947 minValue : Number.NEGATIVE_INFINITY,
36949 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36951 maxValue : Number.MAX_VALUE,
36953 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36955 minText : "The minimum value for this field is {0}",
36957 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36959 maxText : "The maximum value for this field is {0}",
36961 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36962 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36964 nanText : "{0} is not a valid number",
36966 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36968 thousandsDelimiter : false,
36970 * @cfg {String} valueAlign alignment of value
36972 valueAlign : "left",
36974 getAutoCreate : function()
36976 var hiddenInput = {
36980 cls: 'hidden-number-input'
36984 hiddenInput.name = this.name;
36989 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36991 this.name = hiddenInput.name;
36993 if(cfg.cn.length > 0) {
36994 cfg.cn.push(hiddenInput);
37001 initEvents : function()
37003 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37005 var allowed = "0123456789";
37007 if(this.allowDecimals){
37008 allowed += this.decimalSeparator;
37011 if(this.allowNegative){
37015 if(this.thousandsDelimiter) {
37019 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37021 var keyPress = function(e){
37023 var k = e.getKey();
37025 var c = e.getCharCode();
37028 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37029 allowed.indexOf(String.fromCharCode(c)) === -1
37035 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37039 if(allowed.indexOf(String.fromCharCode(c)) === -1){
37044 this.el.on("keypress", keyPress, this);
37047 validateValue : function(value)
37050 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37054 var num = this.parseValue(value);
37057 this.markInvalid(String.format(this.nanText, value));
37061 if(num < this.minValue){
37062 this.markInvalid(String.format(this.minText, this.minValue));
37066 if(num > this.maxValue){
37067 this.markInvalid(String.format(this.maxText, this.maxValue));
37074 getValue : function()
37076 var v = this.hiddenEl().getValue();
37078 return this.fixPrecision(this.parseValue(v));
37081 parseValue : function(value)
37083 if(this.thousandsDelimiter) {
37085 r = new RegExp(",", "g");
37086 value = value.replace(r, "");
37089 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37090 return isNaN(value) ? '' : value;
37093 fixPrecision : function(value)
37095 if(this.thousandsDelimiter) {
37097 r = new RegExp(",", "g");
37098 value = value.replace(r, "");
37101 var nan = isNaN(value);
37103 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37104 return nan ? '' : value;
37106 return parseFloat(value).toFixed(this.decimalPrecision);
37109 setValue : function(v)
37111 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37117 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37119 this.inputEl().dom.value = (v == '') ? '' :
37120 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37122 if(!this.allowZero && v === '0') {
37123 this.hiddenEl().dom.value = '';
37124 this.inputEl().dom.value = '';
37131 decimalPrecisionFcn : function(v)
37133 return Math.floor(v);
37136 beforeBlur : function()
37138 var v = this.parseValue(this.getRawValue());
37140 if(v || v === 0 || v === ''){
37145 hiddenEl : function()
37147 return this.el.select('input.hidden-number-input',true).first();
37159 * @class Roo.bootstrap.DocumentSlider
37160 * @extends Roo.bootstrap.Component
37161 * Bootstrap DocumentSlider class
37164 * Create a new DocumentViewer
37165 * @param {Object} config The config object
37168 Roo.bootstrap.DocumentSlider = function(config){
37169 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37176 * Fire after initEvent
37177 * @param {Roo.bootstrap.DocumentSlider} this
37182 * Fire after update
37183 * @param {Roo.bootstrap.DocumentSlider} this
37189 * @param {Roo.bootstrap.DocumentSlider} this
37195 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37201 getAutoCreate : function()
37205 cls : 'roo-document-slider',
37209 cls : 'roo-document-slider-header',
37213 cls : 'roo-document-slider-header-title'
37219 cls : 'roo-document-slider-body',
37223 cls : 'roo-document-slider-prev',
37227 cls : 'fa fa-chevron-left'
37233 cls : 'roo-document-slider-thumb',
37237 cls : 'roo-document-slider-image'
37243 cls : 'roo-document-slider-next',
37247 cls : 'fa fa-chevron-right'
37259 initEvents : function()
37261 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37262 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37264 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37265 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37267 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37268 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37270 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37271 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37273 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37274 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37276 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37277 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37279 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37280 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37282 this.thumbEl.on('click', this.onClick, this);
37284 this.prevIndicator.on('click', this.prev, this);
37286 this.nextIndicator.on('click', this.next, this);
37290 initial : function()
37292 if(this.files.length){
37293 this.indicator = 1;
37297 this.fireEvent('initial', this);
37300 update : function()
37302 this.imageEl.attr('src', this.files[this.indicator - 1]);
37304 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37306 this.prevIndicator.show();
37308 if(this.indicator == 1){
37309 this.prevIndicator.hide();
37312 this.nextIndicator.show();
37314 if(this.indicator == this.files.length){
37315 this.nextIndicator.hide();
37318 this.thumbEl.scrollTo('top');
37320 this.fireEvent('update', this);
37323 onClick : function(e)
37325 e.preventDefault();
37327 this.fireEvent('click', this);
37332 e.preventDefault();
37334 this.indicator = Math.max(1, this.indicator - 1);
37341 e.preventDefault();
37343 this.indicator = Math.min(this.files.length, this.indicator + 1);
37357 * @class Roo.bootstrap.RadioSet
37358 * @extends Roo.bootstrap.Input
37359 * Bootstrap RadioSet class
37360 * @cfg {String} indicatorpos (left|right) default left
37361 * @cfg {Boolean} inline (true|false) inline the element (default true)
37362 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37364 * Create a new RadioSet
37365 * @param {Object} config The config object
37368 Roo.bootstrap.RadioSet = function(config){
37370 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37374 Roo.bootstrap.RadioSet.register(this);
37379 * Fires when the element is checked or unchecked.
37380 * @param {Roo.bootstrap.RadioSet} this This radio
37381 * @param {Roo.bootstrap.Radio} item The checked item
37386 * Fires when the element is click.
37387 * @param {Roo.bootstrap.RadioSet} this This radio set
37388 * @param {Roo.bootstrap.Radio} item The checked item
37389 * @param {Roo.EventObject} e The event object
37396 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37404 indicatorpos : 'left',
37406 getAutoCreate : function()
37410 cls : 'roo-radio-set-label',
37414 html : this.fieldLabel
37418 if (Roo.bootstrap.version == 3) {
37421 if(this.indicatorpos == 'left'){
37424 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37425 tooltip : 'This field is required'
37430 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37431 tooltip : 'This field is required'
37437 cls : 'roo-radio-set-items'
37440 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37442 if (align === 'left' && this.fieldLabel.length) {
37445 cls : "roo-radio-set-right",
37451 if(this.labelWidth > 12){
37452 label.style = "width: " + this.labelWidth + 'px';
37455 if(this.labelWidth < 13 && this.labelmd == 0){
37456 this.labelmd = this.labelWidth;
37459 if(this.labellg > 0){
37460 label.cls += ' col-lg-' + this.labellg;
37461 items.cls += ' col-lg-' + (12 - this.labellg);
37464 if(this.labelmd > 0){
37465 label.cls += ' col-md-' + this.labelmd;
37466 items.cls += ' col-md-' + (12 - this.labelmd);
37469 if(this.labelsm > 0){
37470 label.cls += ' col-sm-' + this.labelsm;
37471 items.cls += ' col-sm-' + (12 - this.labelsm);
37474 if(this.labelxs > 0){
37475 label.cls += ' col-xs-' + this.labelxs;
37476 items.cls += ' col-xs-' + (12 - this.labelxs);
37482 cls : 'roo-radio-set',
37486 cls : 'roo-radio-set-input',
37489 value : this.value ? this.value : ''
37496 if(this.weight.length){
37497 cfg.cls += ' roo-radio-' + this.weight;
37501 cfg.cls += ' roo-radio-set-inline';
37505 ['xs','sm','md','lg'].map(function(size){
37506 if (settings[size]) {
37507 cfg.cls += ' col-' + size + '-' + settings[size];
37515 initEvents : function()
37517 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37518 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37520 if(!this.fieldLabel.length){
37521 this.labelEl.hide();
37524 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37525 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37527 this.indicator = this.indicatorEl();
37529 if(this.indicator){
37530 this.indicator.addClass('invisible');
37533 this.originalValue = this.getValue();
37537 inputEl: function ()
37539 return this.el.select('.roo-radio-set-input', true).first();
37542 getChildContainer : function()
37544 return this.itemsEl;
37547 register : function(item)
37549 this.radioes.push(item);
37553 validate : function()
37555 if(this.getVisibilityEl().hasClass('hidden')){
37561 Roo.each(this.radioes, function(i){
37570 if(this.allowBlank) {
37574 if(this.disabled || valid){
37579 this.markInvalid();
37584 markValid : function()
37586 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37587 this.indicatorEl().removeClass('visible');
37588 this.indicatorEl().addClass('invisible');
37592 if (Roo.bootstrap.version == 3) {
37593 this.el.removeClass([this.invalidClass, this.validClass]);
37594 this.el.addClass(this.validClass);
37596 this.el.removeClass(['is-invalid','is-valid']);
37597 this.el.addClass(['is-valid']);
37599 this.fireEvent('valid', this);
37602 markInvalid : function(msg)
37604 if(this.allowBlank || this.disabled){
37608 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37609 this.indicatorEl().removeClass('invisible');
37610 this.indicatorEl().addClass('visible');
37612 if (Roo.bootstrap.version == 3) {
37613 this.el.removeClass([this.invalidClass, this.validClass]);
37614 this.el.addClass(this.invalidClass);
37616 this.el.removeClass(['is-invalid','is-valid']);
37617 this.el.addClass(['is-invalid']);
37620 this.fireEvent('invalid', this, msg);
37624 setValue : function(v, suppressEvent)
37626 if(this.value === v){
37633 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37636 Roo.each(this.radioes, function(i){
37638 i.el.removeClass('checked');
37641 Roo.each(this.radioes, function(i){
37643 if(i.value === v || i.value.toString() === v.toString()){
37645 i.el.addClass('checked');
37647 if(suppressEvent !== true){
37648 this.fireEvent('check', this, i);
37659 clearInvalid : function(){
37661 if(!this.el || this.preventMark){
37665 this.el.removeClass([this.invalidClass]);
37667 this.fireEvent('valid', this);
37672 Roo.apply(Roo.bootstrap.RadioSet, {
37676 register : function(set)
37678 this.groups[set.name] = set;
37681 get: function(name)
37683 if (typeof(this.groups[name]) == 'undefined') {
37687 return this.groups[name] ;
37693 * Ext JS Library 1.1.1
37694 * Copyright(c) 2006-2007, Ext JS, LLC.
37696 * Originally Released Under LGPL - original licence link has changed is not relivant.
37699 * <script type="text/javascript">
37704 * @class Roo.bootstrap.SplitBar
37705 * @extends Roo.util.Observable
37706 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37710 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37711 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37712 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37713 split.minSize = 100;
37714 split.maxSize = 600;
37715 split.animate = true;
37716 split.on('moved', splitterMoved);
37719 * Create a new SplitBar
37720 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37721 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37722 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37723 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37724 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37725 position of the SplitBar).
37727 Roo.bootstrap.SplitBar = function(cfg){
37732 // dragElement : elm
37733 // resizingElement: el,
37735 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37736 // placement : Roo.bootstrap.SplitBar.LEFT ,
37737 // existingProxy ???
37740 this.el = Roo.get(cfg.dragElement, true);
37741 this.el.dom.unselectable = "on";
37743 this.resizingEl = Roo.get(cfg.resizingElement, true);
37747 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37748 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37751 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37754 * The minimum size of the resizing element. (Defaults to 0)
37760 * The maximum size of the resizing element. (Defaults to 2000)
37763 this.maxSize = 2000;
37766 * Whether to animate the transition to the new size
37769 this.animate = false;
37772 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37775 this.useShim = false;
37780 if(!cfg.existingProxy){
37782 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37784 this.proxy = Roo.get(cfg.existingProxy).dom;
37787 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37790 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37793 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37796 this.dragSpecs = {};
37799 * @private The adapter to use to positon and resize elements
37801 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37802 this.adapter.init(this);
37804 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37806 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37807 this.el.addClass("roo-splitbar-h");
37810 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37811 this.el.addClass("roo-splitbar-v");
37817 * Fires when the splitter is moved (alias for {@link #event-moved})
37818 * @param {Roo.bootstrap.SplitBar} this
37819 * @param {Number} newSize the new width or height
37824 * Fires when the splitter is moved
37825 * @param {Roo.bootstrap.SplitBar} this
37826 * @param {Number} newSize the new width or height
37830 * @event beforeresize
37831 * Fires before the splitter is dragged
37832 * @param {Roo.bootstrap.SplitBar} this
37834 "beforeresize" : true,
37836 "beforeapply" : true
37839 Roo.util.Observable.call(this);
37842 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37843 onStartProxyDrag : function(x, y){
37844 this.fireEvent("beforeresize", this);
37846 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37848 o.enableDisplayMode("block");
37849 // all splitbars share the same overlay
37850 Roo.bootstrap.SplitBar.prototype.overlay = o;
37852 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37853 this.overlay.show();
37854 Roo.get(this.proxy).setDisplayed("block");
37855 var size = this.adapter.getElementSize(this);
37856 this.activeMinSize = this.getMinimumSize();;
37857 this.activeMaxSize = this.getMaximumSize();;
37858 var c1 = size - this.activeMinSize;
37859 var c2 = Math.max(this.activeMaxSize - size, 0);
37860 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37861 this.dd.resetConstraints();
37862 this.dd.setXConstraint(
37863 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37864 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37866 this.dd.setYConstraint(0, 0);
37868 this.dd.resetConstraints();
37869 this.dd.setXConstraint(0, 0);
37870 this.dd.setYConstraint(
37871 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37872 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37875 this.dragSpecs.startSize = size;
37876 this.dragSpecs.startPoint = [x, y];
37877 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37881 * @private Called after the drag operation by the DDProxy
37883 onEndProxyDrag : function(e){
37884 Roo.get(this.proxy).setDisplayed(false);
37885 var endPoint = Roo.lib.Event.getXY(e);
37887 this.overlay.hide();
37890 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37891 newSize = this.dragSpecs.startSize +
37892 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37893 endPoint[0] - this.dragSpecs.startPoint[0] :
37894 this.dragSpecs.startPoint[0] - endPoint[0]
37897 newSize = this.dragSpecs.startSize +
37898 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37899 endPoint[1] - this.dragSpecs.startPoint[1] :
37900 this.dragSpecs.startPoint[1] - endPoint[1]
37903 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37904 if(newSize != this.dragSpecs.startSize){
37905 if(this.fireEvent('beforeapply', this, newSize) !== false){
37906 this.adapter.setElementSize(this, newSize);
37907 this.fireEvent("moved", this, newSize);
37908 this.fireEvent("resize", this, newSize);
37914 * Get the adapter this SplitBar uses
37915 * @return The adapter object
37917 getAdapter : function(){
37918 return this.adapter;
37922 * Set the adapter this SplitBar uses
37923 * @param {Object} adapter A SplitBar adapter object
37925 setAdapter : function(adapter){
37926 this.adapter = adapter;
37927 this.adapter.init(this);
37931 * Gets the minimum size for the resizing element
37932 * @return {Number} The minimum size
37934 getMinimumSize : function(){
37935 return this.minSize;
37939 * Sets the minimum size for the resizing element
37940 * @param {Number} minSize The minimum size
37942 setMinimumSize : function(minSize){
37943 this.minSize = minSize;
37947 * Gets the maximum size for the resizing element
37948 * @return {Number} The maximum size
37950 getMaximumSize : function(){
37951 return this.maxSize;
37955 * Sets the maximum size for the resizing element
37956 * @param {Number} maxSize The maximum size
37958 setMaximumSize : function(maxSize){
37959 this.maxSize = maxSize;
37963 * Sets the initialize size for the resizing element
37964 * @param {Number} size The initial size
37966 setCurrentSize : function(size){
37967 var oldAnimate = this.animate;
37968 this.animate = false;
37969 this.adapter.setElementSize(this, size);
37970 this.animate = oldAnimate;
37974 * Destroy this splitbar.
37975 * @param {Boolean} removeEl True to remove the element
37977 destroy : function(removeEl){
37979 this.shim.remove();
37982 this.proxy.parentNode.removeChild(this.proxy);
37990 * @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.
37992 Roo.bootstrap.SplitBar.createProxy = function(dir){
37993 var proxy = new Roo.Element(document.createElement("div"));
37994 proxy.unselectable();
37995 var cls = 'roo-splitbar-proxy';
37996 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37997 document.body.appendChild(proxy.dom);
38002 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38003 * Default Adapter. It assumes the splitter and resizing element are not positioned
38004 * elements and only gets/sets the width of the element. Generally used for table based layouts.
38006 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38009 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38010 // do nothing for now
38011 init : function(s){
38015 * Called before drag operations to get the current size of the resizing element.
38016 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38018 getElementSize : function(s){
38019 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38020 return s.resizingEl.getWidth();
38022 return s.resizingEl.getHeight();
38027 * Called after drag operations to set the size of the resizing element.
38028 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38029 * @param {Number} newSize The new size to set
38030 * @param {Function} onComplete A function to be invoked when resizing is complete
38032 setElementSize : function(s, newSize, onComplete){
38033 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38035 s.resizingEl.setWidth(newSize);
38037 onComplete(s, newSize);
38040 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38045 s.resizingEl.setHeight(newSize);
38047 onComplete(s, newSize);
38050 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38057 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38058 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38059 * Adapter that moves the splitter element to align with the resized sizing element.
38060 * Used with an absolute positioned SplitBar.
38061 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38062 * document.body, make sure you assign an id to the body element.
38064 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38065 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38066 this.container = Roo.get(container);
38069 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38070 init : function(s){
38071 this.basic.init(s);
38074 getElementSize : function(s){
38075 return this.basic.getElementSize(s);
38078 setElementSize : function(s, newSize, onComplete){
38079 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38082 moveSplitter : function(s){
38083 var yes = Roo.bootstrap.SplitBar;
38084 switch(s.placement){
38086 s.el.setX(s.resizingEl.getRight());
38089 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38092 s.el.setY(s.resizingEl.getBottom());
38095 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38102 * Orientation constant - Create a vertical SplitBar
38106 Roo.bootstrap.SplitBar.VERTICAL = 1;
38109 * Orientation constant - Create a horizontal SplitBar
38113 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38116 * Placement constant - The resizing element is to the left of the splitter element
38120 Roo.bootstrap.SplitBar.LEFT = 1;
38123 * Placement constant - The resizing element is to the right of the splitter element
38127 Roo.bootstrap.SplitBar.RIGHT = 2;
38130 * Placement constant - The resizing element is positioned above the splitter element
38134 Roo.bootstrap.SplitBar.TOP = 3;
38137 * Placement constant - The resizing element is positioned under splitter element
38141 Roo.bootstrap.SplitBar.BOTTOM = 4;
38142 Roo.namespace("Roo.bootstrap.layout");/*
38144 * Ext JS Library 1.1.1
38145 * Copyright(c) 2006-2007, Ext JS, LLC.
38147 * Originally Released Under LGPL - original licence link has changed is not relivant.
38150 * <script type="text/javascript">
38154 * @class Roo.bootstrap.layout.Manager
38155 * @extends Roo.bootstrap.Component
38156 * Base class for layout managers.
38158 Roo.bootstrap.layout.Manager = function(config)
38160 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38166 /** false to disable window resize monitoring @type Boolean */
38167 this.monitorWindowResize = true;
38172 * Fires when a layout is performed.
38173 * @param {Roo.LayoutManager} this
38177 * @event regionresized
38178 * Fires when the user resizes a region.
38179 * @param {Roo.LayoutRegion} region The resized region
38180 * @param {Number} newSize The new size (width for east/west, height for north/south)
38182 "regionresized" : true,
38184 * @event regioncollapsed
38185 * Fires when a region is collapsed.
38186 * @param {Roo.LayoutRegion} region The collapsed region
38188 "regioncollapsed" : true,
38190 * @event regionexpanded
38191 * Fires when a region is expanded.
38192 * @param {Roo.LayoutRegion} region The expanded region
38194 "regionexpanded" : true
38196 this.updating = false;
38199 this.el = Roo.get(config.el);
38205 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38210 monitorWindowResize : true,
38216 onRender : function(ct, position)
38219 this.el = Roo.get(ct);
38222 //this.fireEvent('render',this);
38226 initEvents: function()
38230 // ie scrollbar fix
38231 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38232 document.body.scroll = "no";
38233 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38234 this.el.position('relative');
38236 this.id = this.el.id;
38237 this.el.addClass("roo-layout-container");
38238 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38239 if(this.el.dom != document.body ) {
38240 this.el.on('resize', this.layout,this);
38241 this.el.on('show', this.layout,this);
38247 * Returns true if this layout is currently being updated
38248 * @return {Boolean}
38250 isUpdating : function(){
38251 return this.updating;
38255 * Suspend the LayoutManager from doing auto-layouts while
38256 * making multiple add or remove calls
38258 beginUpdate : function(){
38259 this.updating = true;
38263 * Restore auto-layouts and optionally disable the manager from performing a layout
38264 * @param {Boolean} noLayout true to disable a layout update
38266 endUpdate : function(noLayout){
38267 this.updating = false;
38273 layout: function(){
38277 onRegionResized : function(region, newSize){
38278 this.fireEvent("regionresized", region, newSize);
38282 onRegionCollapsed : function(region){
38283 this.fireEvent("regioncollapsed", region);
38286 onRegionExpanded : function(region){
38287 this.fireEvent("regionexpanded", region);
38291 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38292 * performs box-model adjustments.
38293 * @return {Object} The size as an object {width: (the width), height: (the height)}
38295 getViewSize : function()
38298 if(this.el.dom != document.body){
38299 size = this.el.getSize();
38301 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38303 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38304 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38309 * Returns the Element this layout is bound to.
38310 * @return {Roo.Element}
38312 getEl : function(){
38317 * Returns the specified region.
38318 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38319 * @return {Roo.LayoutRegion}
38321 getRegion : function(target){
38322 return this.regions[target.toLowerCase()];
38325 onWindowResize : function(){
38326 if(this.monitorWindowResize){
38333 * Ext JS Library 1.1.1
38334 * Copyright(c) 2006-2007, Ext JS, LLC.
38336 * Originally Released Under LGPL - original licence link has changed is not relivant.
38339 * <script type="text/javascript">
38342 * @class Roo.bootstrap.layout.Border
38343 * @extends Roo.bootstrap.layout.Manager
38344 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38345 * please see: examples/bootstrap/nested.html<br><br>
38347 <b>The container the layout is rendered into can be either the body element or any other element.
38348 If it is not the body element, the container needs to either be an absolute positioned element,
38349 or you will need to add "position:relative" to the css of the container. You will also need to specify
38350 the container size if it is not the body element.</b>
38353 * Create a new Border
38354 * @param {Object} config Configuration options
38356 Roo.bootstrap.layout.Border = function(config){
38357 config = config || {};
38358 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38362 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38363 if(config[region]){
38364 config[region].region = region;
38365 this.addRegion(config[region]);
38371 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38373 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38375 parent : false, // this might point to a 'nest' or a ???
38378 * Creates and adds a new region if it doesn't already exist.
38379 * @param {String} target The target region key (north, south, east, west or center).
38380 * @param {Object} config The regions config object
38381 * @return {BorderLayoutRegion} The new region
38383 addRegion : function(config)
38385 if(!this.regions[config.region]){
38386 var r = this.factory(config);
38387 this.bindRegion(r);
38389 return this.regions[config.region];
38393 bindRegion : function(r){
38394 this.regions[r.config.region] = r;
38396 r.on("visibilitychange", this.layout, this);
38397 r.on("paneladded", this.layout, this);
38398 r.on("panelremoved", this.layout, this);
38399 r.on("invalidated", this.layout, this);
38400 r.on("resized", this.onRegionResized, this);
38401 r.on("collapsed", this.onRegionCollapsed, this);
38402 r.on("expanded", this.onRegionExpanded, this);
38406 * Performs a layout update.
38408 layout : function()
38410 if(this.updating) {
38414 // render all the rebions if they have not been done alreayd?
38415 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38416 if(this.regions[region] && !this.regions[region].bodyEl){
38417 this.regions[region].onRender(this.el)
38421 var size = this.getViewSize();
38422 var w = size.width;
38423 var h = size.height;
38428 //var x = 0, y = 0;
38430 var rs = this.regions;
38431 var north = rs["north"];
38432 var south = rs["south"];
38433 var west = rs["west"];
38434 var east = rs["east"];
38435 var center = rs["center"];
38436 //if(this.hideOnLayout){ // not supported anymore
38437 //c.el.setStyle("display", "none");
38439 if(north && north.isVisible()){
38440 var b = north.getBox();
38441 var m = north.getMargins();
38442 b.width = w - (m.left+m.right);
38445 centerY = b.height + b.y + m.bottom;
38446 centerH -= centerY;
38447 north.updateBox(this.safeBox(b));
38449 if(south && south.isVisible()){
38450 var b = south.getBox();
38451 var m = south.getMargins();
38452 b.width = w - (m.left+m.right);
38454 var totalHeight = (b.height + m.top + m.bottom);
38455 b.y = h - totalHeight + m.top;
38456 centerH -= totalHeight;
38457 south.updateBox(this.safeBox(b));
38459 if(west && west.isVisible()){
38460 var b = west.getBox();
38461 var m = west.getMargins();
38462 b.height = centerH - (m.top+m.bottom);
38464 b.y = centerY + m.top;
38465 var totalWidth = (b.width + m.left + m.right);
38466 centerX += totalWidth;
38467 centerW -= totalWidth;
38468 west.updateBox(this.safeBox(b));
38470 if(east && east.isVisible()){
38471 var b = east.getBox();
38472 var m = east.getMargins();
38473 b.height = centerH - (m.top+m.bottom);
38474 var totalWidth = (b.width + m.left + m.right);
38475 b.x = w - totalWidth + m.left;
38476 b.y = centerY + m.top;
38477 centerW -= totalWidth;
38478 east.updateBox(this.safeBox(b));
38481 var m = center.getMargins();
38483 x: centerX + m.left,
38484 y: centerY + m.top,
38485 width: centerW - (m.left+m.right),
38486 height: centerH - (m.top+m.bottom)
38488 //if(this.hideOnLayout){
38489 //center.el.setStyle("display", "block");
38491 center.updateBox(this.safeBox(centerBox));
38494 this.fireEvent("layout", this);
38498 safeBox : function(box){
38499 box.width = Math.max(0, box.width);
38500 box.height = Math.max(0, box.height);
38505 * Adds a ContentPanel (or subclass) to this layout.
38506 * @param {String} target The target region key (north, south, east, west or center).
38507 * @param {Roo.ContentPanel} panel The panel to add
38508 * @return {Roo.ContentPanel} The added panel
38510 add : function(target, panel){
38512 target = target.toLowerCase();
38513 return this.regions[target].add(panel);
38517 * Remove a ContentPanel (or subclass) to this layout.
38518 * @param {String} target The target region key (north, south, east, west or center).
38519 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38520 * @return {Roo.ContentPanel} The removed panel
38522 remove : function(target, panel){
38523 target = target.toLowerCase();
38524 return this.regions[target].remove(panel);
38528 * Searches all regions for a panel with the specified id
38529 * @param {String} panelId
38530 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38532 findPanel : function(panelId){
38533 var rs = this.regions;
38534 for(var target in rs){
38535 if(typeof rs[target] != "function"){
38536 var p = rs[target].getPanel(panelId);
38546 * Searches all regions for a panel with the specified id and activates (shows) it.
38547 * @param {String/ContentPanel} panelId The panels id or the panel itself
38548 * @return {Roo.ContentPanel} The shown panel or null
38550 showPanel : function(panelId) {
38551 var rs = this.regions;
38552 for(var target in rs){
38553 var r = rs[target];
38554 if(typeof r != "function"){
38555 if(r.hasPanel(panelId)){
38556 return r.showPanel(panelId);
38564 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38565 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38568 restoreState : function(provider){
38570 provider = Roo.state.Manager;
38572 var sm = new Roo.LayoutStateManager();
38573 sm.init(this, provider);
38579 * Adds a xtype elements to the layout.
38583 xtype : 'ContentPanel',
38590 xtype : 'NestedLayoutPanel',
38596 items : [ ... list of content panels or nested layout panels.. ]
38600 * @param {Object} cfg Xtype definition of item to add.
38602 addxtype : function(cfg)
38604 // basically accepts a pannel...
38605 // can accept a layout region..!?!?
38606 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38609 // theory? children can only be panels??
38611 //if (!cfg.xtype.match(/Panel$/)) {
38616 if (typeof(cfg.region) == 'undefined') {
38617 Roo.log("Failed to add Panel, region was not set");
38621 var region = cfg.region;
38627 xitems = cfg.items;
38632 if ( region == 'center') {
38633 Roo.log("Center: " + cfg.title);
38639 case 'Content': // ContentPanel (el, cfg)
38640 case 'Scroll': // ContentPanel (el, cfg)
38642 cfg.autoCreate = cfg.autoCreate || true;
38643 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38645 // var el = this.el.createChild();
38646 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38649 this.add(region, ret);
38653 case 'TreePanel': // our new panel!
38654 cfg.el = this.el.createChild();
38655 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38656 this.add(region, ret);
38661 // create a new Layout (which is a Border Layout...
38663 var clayout = cfg.layout;
38664 clayout.el = this.el.createChild();
38665 clayout.items = clayout.items || [];
38669 // replace this exitems with the clayout ones..
38670 xitems = clayout.items;
38672 // force background off if it's in center...
38673 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38674 cfg.background = false;
38676 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38679 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38680 //console.log('adding nested layout panel ' + cfg.toSource());
38681 this.add(region, ret);
38682 nb = {}; /// find first...
38687 // needs grid and region
38689 //var el = this.getRegion(region).el.createChild();
38691 *var el = this.el.createChild();
38692 // create the grid first...
38693 cfg.grid.container = el;
38694 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38697 if (region == 'center' && this.active ) {
38698 cfg.background = false;
38701 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38703 this.add(region, ret);
38705 if (cfg.background) {
38706 // render grid on panel activation (if panel background)
38707 ret.on('activate', function(gp) {
38708 if (!gp.grid.rendered) {
38709 // gp.grid.render(el);
38713 // cfg.grid.render(el);
38719 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38720 // it was the old xcomponent building that caused this before.
38721 // espeically if border is the top element in the tree.
38731 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38733 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38734 this.add(region, ret);
38738 throw "Can not add '" + cfg.xtype + "' to Border";
38744 this.beginUpdate();
38748 Roo.each(xitems, function(i) {
38749 region = nb && i.region ? i.region : false;
38751 var add = ret.addxtype(i);
38754 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38755 if (!i.background) {
38756 abn[region] = nb[region] ;
38763 // make the last non-background panel active..
38764 //if (nb) { Roo.log(abn); }
38767 for(var r in abn) {
38768 region = this.getRegion(r);
38770 // tried using nb[r], but it does not work..
38772 region.showPanel(abn[r]);
38783 factory : function(cfg)
38786 var validRegions = Roo.bootstrap.layout.Border.regions;
38788 var target = cfg.region;
38791 var r = Roo.bootstrap.layout;
38795 return new r.North(cfg);
38797 return new r.South(cfg);
38799 return new r.East(cfg);
38801 return new r.West(cfg);
38803 return new r.Center(cfg);
38805 throw 'Layout region "'+target+'" not supported.';
38812 * Ext JS Library 1.1.1
38813 * Copyright(c) 2006-2007, Ext JS, LLC.
38815 * Originally Released Under LGPL - original licence link has changed is not relivant.
38818 * <script type="text/javascript">
38822 * @class Roo.bootstrap.layout.Basic
38823 * @extends Roo.util.Observable
38824 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38825 * and does not have a titlebar, tabs or any other features. All it does is size and position
38826 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38827 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38828 * @cfg {string} region the region that it inhabits..
38829 * @cfg {bool} skipConfig skip config?
38833 Roo.bootstrap.layout.Basic = function(config){
38835 this.mgr = config.mgr;
38837 this.position = config.region;
38839 var skipConfig = config.skipConfig;
38843 * @scope Roo.BasicLayoutRegion
38847 * @event beforeremove
38848 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38849 * @param {Roo.LayoutRegion} this
38850 * @param {Roo.ContentPanel} panel The panel
38851 * @param {Object} e The cancel event object
38853 "beforeremove" : true,
38855 * @event invalidated
38856 * Fires when the layout for this region is changed.
38857 * @param {Roo.LayoutRegion} this
38859 "invalidated" : true,
38861 * @event visibilitychange
38862 * Fires when this region is shown or hidden
38863 * @param {Roo.LayoutRegion} this
38864 * @param {Boolean} visibility true or false
38866 "visibilitychange" : true,
38868 * @event paneladded
38869 * Fires when a panel is added.
38870 * @param {Roo.LayoutRegion} this
38871 * @param {Roo.ContentPanel} panel The panel
38873 "paneladded" : true,
38875 * @event panelremoved
38876 * Fires when a panel is removed.
38877 * @param {Roo.LayoutRegion} this
38878 * @param {Roo.ContentPanel} panel The panel
38880 "panelremoved" : true,
38882 * @event beforecollapse
38883 * Fires when this region before collapse.
38884 * @param {Roo.LayoutRegion} this
38886 "beforecollapse" : true,
38889 * Fires when this region is collapsed.
38890 * @param {Roo.LayoutRegion} this
38892 "collapsed" : true,
38895 * Fires when this region is expanded.
38896 * @param {Roo.LayoutRegion} this
38901 * Fires when this region is slid into view.
38902 * @param {Roo.LayoutRegion} this
38904 "slideshow" : true,
38907 * Fires when this region slides out of view.
38908 * @param {Roo.LayoutRegion} this
38910 "slidehide" : true,
38912 * @event panelactivated
38913 * Fires when a panel is activated.
38914 * @param {Roo.LayoutRegion} this
38915 * @param {Roo.ContentPanel} panel The activated panel
38917 "panelactivated" : true,
38920 * Fires when the user resizes this region.
38921 * @param {Roo.LayoutRegion} this
38922 * @param {Number} newSize The new size (width for east/west, height for north/south)
38926 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38927 this.panels = new Roo.util.MixedCollection();
38928 this.panels.getKey = this.getPanelId.createDelegate(this);
38930 this.activePanel = null;
38931 // ensure listeners are added...
38933 if (config.listeners || config.events) {
38934 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38935 listeners : config.listeners || {},
38936 events : config.events || {}
38940 if(skipConfig !== true){
38941 this.applyConfig(config);
38945 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38947 getPanelId : function(p){
38951 applyConfig : function(config){
38952 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38953 this.config = config;
38958 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38959 * the width, for horizontal (north, south) the height.
38960 * @param {Number} newSize The new width or height
38962 resizeTo : function(newSize){
38963 var el = this.el ? this.el :
38964 (this.activePanel ? this.activePanel.getEl() : null);
38966 switch(this.position){
38969 el.setWidth(newSize);
38970 this.fireEvent("resized", this, newSize);
38974 el.setHeight(newSize);
38975 this.fireEvent("resized", this, newSize);
38981 getBox : function(){
38982 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38985 getMargins : function(){
38986 return this.margins;
38989 updateBox : function(box){
38991 var el = this.activePanel.getEl();
38992 el.dom.style.left = box.x + "px";
38993 el.dom.style.top = box.y + "px";
38994 this.activePanel.setSize(box.width, box.height);
38998 * Returns the container element for this region.
38999 * @return {Roo.Element}
39001 getEl : function(){
39002 return this.activePanel;
39006 * Returns true if this region is currently visible.
39007 * @return {Boolean}
39009 isVisible : function(){
39010 return this.activePanel ? true : false;
39013 setActivePanel : function(panel){
39014 panel = this.getPanel(panel);
39015 if(this.activePanel && this.activePanel != panel){
39016 this.activePanel.setActiveState(false);
39017 this.activePanel.getEl().setLeftTop(-10000,-10000);
39019 this.activePanel = panel;
39020 panel.setActiveState(true);
39022 panel.setSize(this.box.width, this.box.height);
39024 this.fireEvent("panelactivated", this, panel);
39025 this.fireEvent("invalidated");
39029 * Show the specified panel.
39030 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39031 * @return {Roo.ContentPanel} The shown panel or null
39033 showPanel : function(panel){
39034 panel = this.getPanel(panel);
39036 this.setActivePanel(panel);
39042 * Get the active panel for this region.
39043 * @return {Roo.ContentPanel} The active panel or null
39045 getActivePanel : function(){
39046 return this.activePanel;
39050 * Add the passed ContentPanel(s)
39051 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39052 * @return {Roo.ContentPanel} The panel added (if only one was added)
39054 add : function(panel){
39055 if(arguments.length > 1){
39056 for(var i = 0, len = arguments.length; i < len; i++) {
39057 this.add(arguments[i]);
39061 if(this.hasPanel(panel)){
39062 this.showPanel(panel);
39065 var el = panel.getEl();
39066 if(el.dom.parentNode != this.mgr.el.dom){
39067 this.mgr.el.dom.appendChild(el.dom);
39069 if(panel.setRegion){
39070 panel.setRegion(this);
39072 this.panels.add(panel);
39073 el.setStyle("position", "absolute");
39074 if(!panel.background){
39075 this.setActivePanel(panel);
39076 if(this.config.initialSize && this.panels.getCount()==1){
39077 this.resizeTo(this.config.initialSize);
39080 this.fireEvent("paneladded", this, panel);
39085 * Returns true if the panel is in this region.
39086 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39087 * @return {Boolean}
39089 hasPanel : function(panel){
39090 if(typeof panel == "object"){ // must be panel obj
39091 panel = panel.getId();
39093 return this.getPanel(panel) ? true : false;
39097 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39098 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39099 * @param {Boolean} preservePanel Overrides the config preservePanel option
39100 * @return {Roo.ContentPanel} The panel that was removed
39102 remove : function(panel, preservePanel){
39103 panel = this.getPanel(panel);
39108 this.fireEvent("beforeremove", this, panel, e);
39109 if(e.cancel === true){
39112 var panelId = panel.getId();
39113 this.panels.removeKey(panelId);
39118 * Returns the panel specified or null if it's not in this region.
39119 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39120 * @return {Roo.ContentPanel}
39122 getPanel : function(id){
39123 if(typeof id == "object"){ // must be panel obj
39126 return this.panels.get(id);
39130 * Returns this regions position (north/south/east/west/center).
39133 getPosition: function(){
39134 return this.position;
39138 * Ext JS Library 1.1.1
39139 * Copyright(c) 2006-2007, Ext JS, LLC.
39141 * Originally Released Under LGPL - original licence link has changed is not relivant.
39144 * <script type="text/javascript">
39148 * @class Roo.bootstrap.layout.Region
39149 * @extends Roo.bootstrap.layout.Basic
39150 * This class represents a region in a layout manager.
39152 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39153 * @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})
39154 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39155 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39156 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39157 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39158 * @cfg {String} title The title for the region (overrides panel titles)
39159 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39160 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39161 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39162 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39163 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39164 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39165 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39166 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39167 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39168 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39170 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39171 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39172 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39173 * @cfg {Number} width For East/West panels
39174 * @cfg {Number} height For North/South panels
39175 * @cfg {Boolean} split To show the splitter
39176 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39178 * @cfg {string} cls Extra CSS classes to add to region
39180 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39181 * @cfg {string} region the region that it inhabits..
39184 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39185 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39187 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39188 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39189 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39191 Roo.bootstrap.layout.Region = function(config)
39193 this.applyConfig(config);
39195 var mgr = config.mgr;
39196 var pos = config.region;
39197 config.skipConfig = true;
39198 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39201 this.onRender(mgr.el);
39204 this.visible = true;
39205 this.collapsed = false;
39206 this.unrendered_panels = [];
39209 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39211 position: '', // set by wrapper (eg. north/south etc..)
39212 unrendered_panels : null, // unrendered panels.
39214 tabPosition : false,
39216 mgr: false, // points to 'Border'
39219 createBody : function(){
39220 /** This region's body element
39221 * @type Roo.Element */
39222 this.bodyEl = this.el.createChild({
39224 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39228 onRender: function(ctr, pos)
39230 var dh = Roo.DomHelper;
39231 /** This region's container element
39232 * @type Roo.Element */
39233 this.el = dh.append(ctr.dom, {
39235 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39237 /** This region's title element
39238 * @type Roo.Element */
39240 this.titleEl = dh.append(this.el.dom, {
39242 unselectable: "on",
39243 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39245 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39246 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39250 this.titleEl.enableDisplayMode();
39251 /** This region's title text element
39252 * @type HTMLElement */
39253 this.titleTextEl = this.titleEl.dom.firstChild;
39254 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39256 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39257 this.closeBtn.enableDisplayMode();
39258 this.closeBtn.on("click", this.closeClicked, this);
39259 this.closeBtn.hide();
39261 this.createBody(this.config);
39262 if(this.config.hideWhenEmpty){
39264 this.on("paneladded", this.validateVisibility, this);
39265 this.on("panelremoved", this.validateVisibility, this);
39267 if(this.autoScroll){
39268 this.bodyEl.setStyle("overflow", "auto");
39270 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39272 //if(c.titlebar !== false){
39273 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39274 this.titleEl.hide();
39276 this.titleEl.show();
39277 if(this.config.title){
39278 this.titleTextEl.innerHTML = this.config.title;
39282 if(this.config.collapsed){
39283 this.collapse(true);
39285 if(this.config.hidden){
39289 if (this.unrendered_panels && this.unrendered_panels.length) {
39290 for (var i =0;i< this.unrendered_panels.length; i++) {
39291 this.add(this.unrendered_panels[i]);
39293 this.unrendered_panels = null;
39299 applyConfig : function(c)
39302 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39303 var dh = Roo.DomHelper;
39304 if(c.titlebar !== false){
39305 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39306 this.collapseBtn.on("click", this.collapse, this);
39307 this.collapseBtn.enableDisplayMode();
39309 if(c.showPin === true || this.showPin){
39310 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39311 this.stickBtn.enableDisplayMode();
39312 this.stickBtn.on("click", this.expand, this);
39313 this.stickBtn.hide();
39318 /** This region's collapsed element
39319 * @type Roo.Element */
39322 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39323 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39326 if(c.floatable !== false){
39327 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39328 this.collapsedEl.on("click", this.collapseClick, this);
39331 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39332 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39333 id: "message", unselectable: "on", style:{"float":"left"}});
39334 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39336 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39337 this.expandBtn.on("click", this.expand, this);
39341 if(this.collapseBtn){
39342 this.collapseBtn.setVisible(c.collapsible == true);
39345 this.cmargins = c.cmargins || this.cmargins ||
39346 (this.position == "west" || this.position == "east" ?
39347 {top: 0, left: 2, right:2, bottom: 0} :
39348 {top: 2, left: 0, right:0, bottom: 2});
39350 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39353 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39355 this.autoScroll = c.autoScroll || false;
39360 this.duration = c.duration || .30;
39361 this.slideDuration = c.slideDuration || .45;
39366 * Returns true if this region is currently visible.
39367 * @return {Boolean}
39369 isVisible : function(){
39370 return this.visible;
39374 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39375 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39377 //setCollapsedTitle : function(title){
39378 // title = title || " ";
39379 // if(this.collapsedTitleTextEl){
39380 // this.collapsedTitleTextEl.innerHTML = title;
39384 getBox : function(){
39386 // if(!this.collapsed){
39387 b = this.el.getBox(false, true);
39389 // b = this.collapsedEl.getBox(false, true);
39394 getMargins : function(){
39395 return this.margins;
39396 //return this.collapsed ? this.cmargins : this.margins;
39399 highlight : function(){
39400 this.el.addClass("x-layout-panel-dragover");
39403 unhighlight : function(){
39404 this.el.removeClass("x-layout-panel-dragover");
39407 updateBox : function(box)
39409 if (!this.bodyEl) {
39410 return; // not rendered yet..
39414 if(!this.collapsed){
39415 this.el.dom.style.left = box.x + "px";
39416 this.el.dom.style.top = box.y + "px";
39417 this.updateBody(box.width, box.height);
39419 this.collapsedEl.dom.style.left = box.x + "px";
39420 this.collapsedEl.dom.style.top = box.y + "px";
39421 this.collapsedEl.setSize(box.width, box.height);
39424 this.tabs.autoSizeTabs();
39428 updateBody : function(w, h)
39431 this.el.setWidth(w);
39432 w -= this.el.getBorderWidth("rl");
39433 if(this.config.adjustments){
39434 w += this.config.adjustments[0];
39437 if(h !== null && h > 0){
39438 this.el.setHeight(h);
39439 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39440 h -= this.el.getBorderWidth("tb");
39441 if(this.config.adjustments){
39442 h += this.config.adjustments[1];
39444 this.bodyEl.setHeight(h);
39446 h = this.tabs.syncHeight(h);
39449 if(this.panelSize){
39450 w = w !== null ? w : this.panelSize.width;
39451 h = h !== null ? h : this.panelSize.height;
39453 if(this.activePanel){
39454 var el = this.activePanel.getEl();
39455 w = w !== null ? w : el.getWidth();
39456 h = h !== null ? h : el.getHeight();
39457 this.panelSize = {width: w, height: h};
39458 this.activePanel.setSize(w, h);
39460 if(Roo.isIE && this.tabs){
39461 this.tabs.el.repaint();
39466 * Returns the container element for this region.
39467 * @return {Roo.Element}
39469 getEl : function(){
39474 * Hides this region.
39477 //if(!this.collapsed){
39478 this.el.dom.style.left = "-2000px";
39481 // this.collapsedEl.dom.style.left = "-2000px";
39482 // this.collapsedEl.hide();
39484 this.visible = false;
39485 this.fireEvent("visibilitychange", this, false);
39489 * Shows this region if it was previously hidden.
39492 //if(!this.collapsed){
39495 // this.collapsedEl.show();
39497 this.visible = true;
39498 this.fireEvent("visibilitychange", this, true);
39501 closeClicked : function(){
39502 if(this.activePanel){
39503 this.remove(this.activePanel);
39507 collapseClick : function(e){
39509 e.stopPropagation();
39512 e.stopPropagation();
39518 * Collapses this region.
39519 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39522 collapse : function(skipAnim, skipCheck = false){
39523 if(this.collapsed) {
39527 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39529 this.collapsed = true;
39531 this.split.el.hide();
39533 if(this.config.animate && skipAnim !== true){
39534 this.fireEvent("invalidated", this);
39535 this.animateCollapse();
39537 this.el.setLocation(-20000,-20000);
39539 this.collapsedEl.show();
39540 this.fireEvent("collapsed", this);
39541 this.fireEvent("invalidated", this);
39547 animateCollapse : function(){
39552 * Expands this region if it was previously collapsed.
39553 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39554 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39557 expand : function(e, skipAnim){
39559 e.stopPropagation();
39561 if(!this.collapsed || this.el.hasActiveFx()) {
39565 this.afterSlideIn();
39568 this.collapsed = false;
39569 if(this.config.animate && skipAnim !== true){
39570 this.animateExpand();
39574 this.split.el.show();
39576 this.collapsedEl.setLocation(-2000,-2000);
39577 this.collapsedEl.hide();
39578 this.fireEvent("invalidated", this);
39579 this.fireEvent("expanded", this);
39583 animateExpand : function(){
39587 initTabs : function()
39589 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39591 var ts = new Roo.bootstrap.panel.Tabs({
39592 el: this.bodyEl.dom,
39594 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39595 disableTooltips: this.config.disableTabTips,
39596 toolbar : this.config.toolbar
39599 if(this.config.hideTabs){
39600 ts.stripWrap.setDisplayed(false);
39603 ts.resizeTabs = this.config.resizeTabs === true;
39604 ts.minTabWidth = this.config.minTabWidth || 40;
39605 ts.maxTabWidth = this.config.maxTabWidth || 250;
39606 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39607 ts.monitorResize = false;
39608 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39609 ts.bodyEl.addClass('roo-layout-tabs-body');
39610 this.panels.each(this.initPanelAsTab, this);
39613 initPanelAsTab : function(panel){
39614 var ti = this.tabs.addTab(
39618 this.config.closeOnTab && panel.isClosable(),
39621 if(panel.tabTip !== undefined){
39622 ti.setTooltip(panel.tabTip);
39624 ti.on("activate", function(){
39625 this.setActivePanel(panel);
39628 if(this.config.closeOnTab){
39629 ti.on("beforeclose", function(t, e){
39631 this.remove(panel);
39635 panel.tabItem = ti;
39640 updatePanelTitle : function(panel, title)
39642 if(this.activePanel == panel){
39643 this.updateTitle(title);
39646 var ti = this.tabs.getTab(panel.getEl().id);
39648 if(panel.tabTip !== undefined){
39649 ti.setTooltip(panel.tabTip);
39654 updateTitle : function(title){
39655 if(this.titleTextEl && !this.config.title){
39656 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39660 setActivePanel : function(panel)
39662 panel = this.getPanel(panel);
39663 if(this.activePanel && this.activePanel != panel){
39664 if(this.activePanel.setActiveState(false) === false){
39668 this.activePanel = panel;
39669 panel.setActiveState(true);
39670 if(this.panelSize){
39671 panel.setSize(this.panelSize.width, this.panelSize.height);
39674 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39676 this.updateTitle(panel.getTitle());
39678 this.fireEvent("invalidated", this);
39680 this.fireEvent("panelactivated", this, panel);
39684 * Shows the specified panel.
39685 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39686 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39688 showPanel : function(panel)
39690 panel = this.getPanel(panel);
39693 var tab = this.tabs.getTab(panel.getEl().id);
39694 if(tab.isHidden()){
39695 this.tabs.unhideTab(tab.id);
39699 this.setActivePanel(panel);
39706 * Get the active panel for this region.
39707 * @return {Roo.ContentPanel} The active panel or null
39709 getActivePanel : function(){
39710 return this.activePanel;
39713 validateVisibility : function(){
39714 if(this.panels.getCount() < 1){
39715 this.updateTitle(" ");
39716 this.closeBtn.hide();
39719 if(!this.isVisible()){
39726 * Adds the passed ContentPanel(s) to this region.
39727 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39728 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39730 add : function(panel)
39732 if(arguments.length > 1){
39733 for(var i = 0, len = arguments.length; i < len; i++) {
39734 this.add(arguments[i]);
39739 // if we have not been rendered yet, then we can not really do much of this..
39740 if (!this.bodyEl) {
39741 this.unrendered_panels.push(panel);
39748 if(this.hasPanel(panel)){
39749 this.showPanel(panel);
39752 panel.setRegion(this);
39753 this.panels.add(panel);
39754 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39755 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39756 // and hide them... ???
39757 this.bodyEl.dom.appendChild(panel.getEl().dom);
39758 if(panel.background !== true){
39759 this.setActivePanel(panel);
39761 this.fireEvent("paneladded", this, panel);
39768 this.initPanelAsTab(panel);
39772 if(panel.background !== true){
39773 this.tabs.activate(panel.getEl().id);
39775 this.fireEvent("paneladded", this, panel);
39780 * Hides the tab for the specified panel.
39781 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39783 hidePanel : function(panel){
39784 if(this.tabs && (panel = this.getPanel(panel))){
39785 this.tabs.hideTab(panel.getEl().id);
39790 * Unhides the tab for a previously hidden panel.
39791 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39793 unhidePanel : function(panel){
39794 if(this.tabs && (panel = this.getPanel(panel))){
39795 this.tabs.unhideTab(panel.getEl().id);
39799 clearPanels : function(){
39800 while(this.panels.getCount() > 0){
39801 this.remove(this.panels.first());
39806 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39807 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39808 * @param {Boolean} preservePanel Overrides the config preservePanel option
39809 * @return {Roo.ContentPanel} The panel that was removed
39811 remove : function(panel, preservePanel)
39813 panel = this.getPanel(panel);
39818 this.fireEvent("beforeremove", this, panel, e);
39819 if(e.cancel === true){
39822 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39823 var panelId = panel.getId();
39824 this.panels.removeKey(panelId);
39826 document.body.appendChild(panel.getEl().dom);
39829 this.tabs.removeTab(panel.getEl().id);
39830 }else if (!preservePanel){
39831 this.bodyEl.dom.removeChild(panel.getEl().dom);
39833 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39834 var p = this.panels.first();
39835 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39836 tempEl.appendChild(p.getEl().dom);
39837 this.bodyEl.update("");
39838 this.bodyEl.dom.appendChild(p.getEl().dom);
39840 this.updateTitle(p.getTitle());
39842 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39843 this.setActivePanel(p);
39845 panel.setRegion(null);
39846 if(this.activePanel == panel){
39847 this.activePanel = null;
39849 if(this.config.autoDestroy !== false && preservePanel !== true){
39850 try{panel.destroy();}catch(e){}
39852 this.fireEvent("panelremoved", this, panel);
39857 * Returns the TabPanel component used by this region
39858 * @return {Roo.TabPanel}
39860 getTabs : function(){
39864 createTool : function(parentEl, className){
39865 var btn = Roo.DomHelper.append(parentEl, {
39867 cls: "x-layout-tools-button",
39870 cls: "roo-layout-tools-button-inner " + className,
39874 btn.addClassOnOver("roo-layout-tools-button-over");
39879 * Ext JS Library 1.1.1
39880 * Copyright(c) 2006-2007, Ext JS, LLC.
39882 * Originally Released Under LGPL - original licence link has changed is not relivant.
39885 * <script type="text/javascript">
39891 * @class Roo.SplitLayoutRegion
39892 * @extends Roo.LayoutRegion
39893 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39895 Roo.bootstrap.layout.Split = function(config){
39896 this.cursor = config.cursor;
39897 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39900 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39902 splitTip : "Drag to resize.",
39903 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39904 useSplitTips : false,
39906 applyConfig : function(config){
39907 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39910 onRender : function(ctr,pos) {
39912 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39913 if(!this.config.split){
39918 var splitEl = Roo.DomHelper.append(ctr.dom, {
39920 id: this.el.id + "-split",
39921 cls: "roo-layout-split roo-layout-split-"+this.position,
39924 /** The SplitBar for this region
39925 * @type Roo.SplitBar */
39926 // does not exist yet...
39927 Roo.log([this.position, this.orientation]);
39929 this.split = new Roo.bootstrap.SplitBar({
39930 dragElement : splitEl,
39931 resizingElement: this.el,
39932 orientation : this.orientation
39935 this.split.on("moved", this.onSplitMove, this);
39936 this.split.useShim = this.config.useShim === true;
39937 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39938 if(this.useSplitTips){
39939 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39941 //if(config.collapsible){
39942 // this.split.el.on("dblclick", this.collapse, this);
39945 if(typeof this.config.minSize != "undefined"){
39946 this.split.minSize = this.config.minSize;
39948 if(typeof this.config.maxSize != "undefined"){
39949 this.split.maxSize = this.config.maxSize;
39951 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39952 this.hideSplitter();
39957 getHMaxSize : function(){
39958 var cmax = this.config.maxSize || 10000;
39959 var center = this.mgr.getRegion("center");
39960 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39963 getVMaxSize : function(){
39964 var cmax = this.config.maxSize || 10000;
39965 var center = this.mgr.getRegion("center");
39966 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39969 onSplitMove : function(split, newSize){
39970 this.fireEvent("resized", this, newSize);
39974 * Returns the {@link Roo.SplitBar} for this region.
39975 * @return {Roo.SplitBar}
39977 getSplitBar : function(){
39982 this.hideSplitter();
39983 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39986 hideSplitter : function(){
39988 this.split.el.setLocation(-2000,-2000);
39989 this.split.el.hide();
39995 this.split.el.show();
39997 Roo.bootstrap.layout.Split.superclass.show.call(this);
40000 beforeSlide: function(){
40001 if(Roo.isGecko){// firefox overflow auto bug workaround
40002 this.bodyEl.clip();
40004 this.tabs.bodyEl.clip();
40006 if(this.activePanel){
40007 this.activePanel.getEl().clip();
40009 if(this.activePanel.beforeSlide){
40010 this.activePanel.beforeSlide();
40016 afterSlide : function(){
40017 if(Roo.isGecko){// firefox overflow auto bug workaround
40018 this.bodyEl.unclip();
40020 this.tabs.bodyEl.unclip();
40022 if(this.activePanel){
40023 this.activePanel.getEl().unclip();
40024 if(this.activePanel.afterSlide){
40025 this.activePanel.afterSlide();
40031 initAutoHide : function(){
40032 if(this.autoHide !== false){
40033 if(!this.autoHideHd){
40034 var st = new Roo.util.DelayedTask(this.slideIn, this);
40035 this.autoHideHd = {
40036 "mouseout": function(e){
40037 if(!e.within(this.el, true)){
40041 "mouseover" : function(e){
40047 this.el.on(this.autoHideHd);
40051 clearAutoHide : function(){
40052 if(this.autoHide !== false){
40053 this.el.un("mouseout", this.autoHideHd.mouseout);
40054 this.el.un("mouseover", this.autoHideHd.mouseover);
40058 clearMonitor : function(){
40059 Roo.get(document).un("click", this.slideInIf, this);
40062 // these names are backwards but not changed for compat
40063 slideOut : function(){
40064 if(this.isSlid || this.el.hasActiveFx()){
40067 this.isSlid = true;
40068 if(this.collapseBtn){
40069 this.collapseBtn.hide();
40071 this.closeBtnState = this.closeBtn.getStyle('display');
40072 this.closeBtn.hide();
40074 this.stickBtn.show();
40077 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40078 this.beforeSlide();
40079 this.el.setStyle("z-index", 10001);
40080 this.el.slideIn(this.getSlideAnchor(), {
40081 callback: function(){
40083 this.initAutoHide();
40084 Roo.get(document).on("click", this.slideInIf, this);
40085 this.fireEvent("slideshow", this);
40092 afterSlideIn : function(){
40093 this.clearAutoHide();
40094 this.isSlid = false;
40095 this.clearMonitor();
40096 this.el.setStyle("z-index", "");
40097 if(this.collapseBtn){
40098 this.collapseBtn.show();
40100 this.closeBtn.setStyle('display', this.closeBtnState);
40102 this.stickBtn.hide();
40104 this.fireEvent("slidehide", this);
40107 slideIn : function(cb){
40108 if(!this.isSlid || this.el.hasActiveFx()){
40112 this.isSlid = false;
40113 this.beforeSlide();
40114 this.el.slideOut(this.getSlideAnchor(), {
40115 callback: function(){
40116 this.el.setLeftTop(-10000, -10000);
40118 this.afterSlideIn();
40126 slideInIf : function(e){
40127 if(!e.within(this.el)){
40132 animateCollapse : function(){
40133 this.beforeSlide();
40134 this.el.setStyle("z-index", 20000);
40135 var anchor = this.getSlideAnchor();
40136 this.el.slideOut(anchor, {
40137 callback : function(){
40138 this.el.setStyle("z-index", "");
40139 this.collapsedEl.slideIn(anchor, {duration:.3});
40141 this.el.setLocation(-10000,-10000);
40143 this.fireEvent("collapsed", this);
40150 animateExpand : function(){
40151 this.beforeSlide();
40152 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40153 this.el.setStyle("z-index", 20000);
40154 this.collapsedEl.hide({
40157 this.el.slideIn(this.getSlideAnchor(), {
40158 callback : function(){
40159 this.el.setStyle("z-index", "");
40162 this.split.el.show();
40164 this.fireEvent("invalidated", this);
40165 this.fireEvent("expanded", this);
40193 getAnchor : function(){
40194 return this.anchors[this.position];
40197 getCollapseAnchor : function(){
40198 return this.canchors[this.position];
40201 getSlideAnchor : function(){
40202 return this.sanchors[this.position];
40205 getAlignAdj : function(){
40206 var cm = this.cmargins;
40207 switch(this.position){
40223 getExpandAdj : function(){
40224 var c = this.collapsedEl, cm = this.cmargins;
40225 switch(this.position){
40227 return [-(cm.right+c.getWidth()+cm.left), 0];
40230 return [cm.right+c.getWidth()+cm.left, 0];
40233 return [0, -(cm.top+cm.bottom+c.getHeight())];
40236 return [0, cm.top+cm.bottom+c.getHeight()];
40242 * Ext JS Library 1.1.1
40243 * Copyright(c) 2006-2007, Ext JS, LLC.
40245 * Originally Released Under LGPL - original licence link has changed is not relivant.
40248 * <script type="text/javascript">
40251 * These classes are private internal classes
40253 Roo.bootstrap.layout.Center = function(config){
40254 config.region = "center";
40255 Roo.bootstrap.layout.Region.call(this, config);
40256 this.visible = true;
40257 this.minWidth = config.minWidth || 20;
40258 this.minHeight = config.minHeight || 20;
40261 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40263 // center panel can't be hidden
40267 // center panel can't be hidden
40270 getMinWidth: function(){
40271 return this.minWidth;
40274 getMinHeight: function(){
40275 return this.minHeight;
40289 Roo.bootstrap.layout.North = function(config)
40291 config.region = 'north';
40292 config.cursor = 'n-resize';
40294 Roo.bootstrap.layout.Split.call(this, config);
40298 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40299 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40300 this.split.el.addClass("roo-layout-split-v");
40302 //var size = config.initialSize || config.height;
40303 //if(this.el && typeof size != "undefined"){
40304 // this.el.setHeight(size);
40307 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40309 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40312 onRender : function(ctr, pos)
40314 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40315 var size = this.config.initialSize || this.config.height;
40316 if(this.el && typeof size != "undefined"){
40317 this.el.setHeight(size);
40322 getBox : function(){
40323 if(this.collapsed){
40324 return this.collapsedEl.getBox();
40326 var box = this.el.getBox();
40328 box.height += this.split.el.getHeight();
40333 updateBox : function(box){
40334 if(this.split && !this.collapsed){
40335 box.height -= this.split.el.getHeight();
40336 this.split.el.setLeft(box.x);
40337 this.split.el.setTop(box.y+box.height);
40338 this.split.el.setWidth(box.width);
40340 if(this.collapsed){
40341 this.updateBody(box.width, null);
40343 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40351 Roo.bootstrap.layout.South = function(config){
40352 config.region = 'south';
40353 config.cursor = 's-resize';
40354 Roo.bootstrap.layout.Split.call(this, config);
40356 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40357 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40358 this.split.el.addClass("roo-layout-split-v");
40363 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40364 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40366 onRender : function(ctr, pos)
40368 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40369 var size = this.config.initialSize || this.config.height;
40370 if(this.el && typeof size != "undefined"){
40371 this.el.setHeight(size);
40376 getBox : function(){
40377 if(this.collapsed){
40378 return this.collapsedEl.getBox();
40380 var box = this.el.getBox();
40382 var sh = this.split.el.getHeight();
40389 updateBox : function(box){
40390 if(this.split && !this.collapsed){
40391 var sh = this.split.el.getHeight();
40394 this.split.el.setLeft(box.x);
40395 this.split.el.setTop(box.y-sh);
40396 this.split.el.setWidth(box.width);
40398 if(this.collapsed){
40399 this.updateBody(box.width, null);
40401 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40405 Roo.bootstrap.layout.East = function(config){
40406 config.region = "east";
40407 config.cursor = "e-resize";
40408 Roo.bootstrap.layout.Split.call(this, config);
40410 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40411 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40412 this.split.el.addClass("roo-layout-split-h");
40416 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40417 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40419 onRender : function(ctr, pos)
40421 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40422 var size = this.config.initialSize || this.config.width;
40423 if(this.el && typeof size != "undefined"){
40424 this.el.setWidth(size);
40429 getBox : function(){
40430 if(this.collapsed){
40431 return this.collapsedEl.getBox();
40433 var box = this.el.getBox();
40435 var sw = this.split.el.getWidth();
40442 updateBox : function(box){
40443 if(this.split && !this.collapsed){
40444 var sw = this.split.el.getWidth();
40446 this.split.el.setLeft(box.x);
40447 this.split.el.setTop(box.y);
40448 this.split.el.setHeight(box.height);
40451 if(this.collapsed){
40452 this.updateBody(null, box.height);
40454 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40458 Roo.bootstrap.layout.West = function(config){
40459 config.region = "west";
40460 config.cursor = "w-resize";
40462 Roo.bootstrap.layout.Split.call(this, config);
40464 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40465 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40466 this.split.el.addClass("roo-layout-split-h");
40470 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40471 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40473 onRender: function(ctr, pos)
40475 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40476 var size = this.config.initialSize || this.config.width;
40477 if(typeof size != "undefined"){
40478 this.el.setWidth(size);
40482 getBox : function(){
40483 if(this.collapsed){
40484 return this.collapsedEl.getBox();
40486 var box = this.el.getBox();
40487 if (box.width == 0) {
40488 box.width = this.config.width; // kludge?
40491 box.width += this.split.el.getWidth();
40496 updateBox : function(box){
40497 if(this.split && !this.collapsed){
40498 var sw = this.split.el.getWidth();
40500 this.split.el.setLeft(box.x+box.width);
40501 this.split.el.setTop(box.y);
40502 this.split.el.setHeight(box.height);
40504 if(this.collapsed){
40505 this.updateBody(null, box.height);
40507 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40509 });Roo.namespace("Roo.bootstrap.panel");/*
40511 * Ext JS Library 1.1.1
40512 * Copyright(c) 2006-2007, Ext JS, LLC.
40514 * Originally Released Under LGPL - original licence link has changed is not relivant.
40517 * <script type="text/javascript">
40520 * @class Roo.ContentPanel
40521 * @extends Roo.util.Observable
40522 * A basic ContentPanel element.
40523 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40524 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40525 * @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
40526 * @cfg {Boolean} closable True if the panel can be closed/removed
40527 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40528 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40529 * @cfg {Toolbar} toolbar A toolbar for this panel
40530 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40531 * @cfg {String} title The title for this panel
40532 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40533 * @cfg {String} url Calls {@link #setUrl} with this value
40534 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40535 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40536 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40537 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40538 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40539 * @cfg {Boolean} badges render the badges
40540 * @cfg {String} cls extra classes to use
40541 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40544 * Create a new ContentPanel.
40545 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40546 * @param {String/Object} config A string to set only the title or a config object
40547 * @param {String} content (optional) Set the HTML content for this panel
40548 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40550 Roo.bootstrap.panel.Content = function( config){
40552 this.tpl = config.tpl || false;
40554 var el = config.el;
40555 var content = config.content;
40557 if(config.autoCreate){ // xtype is available if this is called from factory
40560 this.el = Roo.get(el);
40561 if(!this.el && config && config.autoCreate){
40562 if(typeof config.autoCreate == "object"){
40563 if(!config.autoCreate.id){
40564 config.autoCreate.id = config.id||el;
40566 this.el = Roo.DomHelper.append(document.body,
40567 config.autoCreate, true);
40571 cls: (config.cls || '') +
40572 (config.background ? ' bg-' + config.background : '') +
40573 " roo-layout-inactive-content",
40576 if (config.iframe) {
40580 style : 'border: 0px',
40581 src : 'about:blank'
40587 elcfg.html = config.html;
40591 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40592 if (config.iframe) {
40593 this.iframeEl = this.el.select('iframe',true).first();
40598 this.closable = false;
40599 this.loaded = false;
40600 this.active = false;
40603 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40605 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40607 this.wrapEl = this.el; //this.el.wrap();
40609 if (config.toolbar.items) {
40610 ti = config.toolbar.items ;
40611 delete config.toolbar.items ;
40615 this.toolbar.render(this.wrapEl, 'before');
40616 for(var i =0;i < ti.length;i++) {
40617 // Roo.log(['add child', items[i]]);
40618 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40620 this.toolbar.items = nitems;
40621 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40622 delete config.toolbar;
40626 // xtype created footer. - not sure if will work as we normally have to render first..
40627 if (this.footer && !this.footer.el && this.footer.xtype) {
40628 if (!this.wrapEl) {
40629 this.wrapEl = this.el.wrap();
40632 this.footer.container = this.wrapEl.createChild();
40634 this.footer = Roo.factory(this.footer, Roo);
40639 if(typeof config == "string"){
40640 this.title = config;
40642 Roo.apply(this, config);
40646 this.resizeEl = Roo.get(this.resizeEl, true);
40648 this.resizeEl = this.el;
40650 // handle view.xtype
40658 * Fires when this panel is activated.
40659 * @param {Roo.ContentPanel} this
40663 * @event deactivate
40664 * Fires when this panel is activated.
40665 * @param {Roo.ContentPanel} this
40667 "deactivate" : true,
40671 * Fires when this panel is resized if fitToFrame is true.
40672 * @param {Roo.ContentPanel} this
40673 * @param {Number} width The width after any component adjustments
40674 * @param {Number} height The height after any component adjustments
40680 * Fires when this tab is created
40681 * @param {Roo.ContentPanel} this
40687 * Fires when this content is scrolled
40688 * @param {Roo.ContentPanel} this
40689 * @param {Event} scrollEvent
40700 if(this.autoScroll && !this.iframe){
40701 this.resizeEl.setStyle("overflow", "auto");
40702 this.resizeEl.on('scroll', this.onScroll, this);
40704 // fix randome scrolling
40705 //this.el.on('scroll', function() {
40706 // Roo.log('fix random scolling');
40707 // this.scrollTo('top',0);
40710 content = content || this.content;
40712 this.setContent(content);
40714 if(config && config.url){
40715 this.setUrl(this.url, this.params, this.loadOnce);
40720 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40722 if (this.view && typeof(this.view.xtype) != 'undefined') {
40723 this.view.el = this.el.appendChild(document.createElement("div"));
40724 this.view = Roo.factory(this.view);
40725 this.view.render && this.view.render(false, '');
40729 this.fireEvent('render', this);
40732 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40742 /* Resize Element - use this to work out scroll etc. */
40745 setRegion : function(region){
40746 this.region = region;
40747 this.setActiveClass(region && !this.background);
40751 setActiveClass: function(state)
40754 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40755 this.el.setStyle('position','relative');
40757 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40758 this.el.setStyle('position', 'absolute');
40763 * Returns the toolbar for this Panel if one was configured.
40764 * @return {Roo.Toolbar}
40766 getToolbar : function(){
40767 return this.toolbar;
40770 setActiveState : function(active)
40772 this.active = active;
40773 this.setActiveClass(active);
40775 if(this.fireEvent("deactivate", this) === false){
40780 this.fireEvent("activate", this);
40784 * Updates this panel's element (not for iframe)
40785 * @param {String} content The new content
40786 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40788 setContent : function(content, loadScripts){
40793 this.el.update(content, loadScripts);
40796 ignoreResize : function(w, h){
40797 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40800 this.lastSize = {width: w, height: h};
40805 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40806 * @return {Roo.UpdateManager} The UpdateManager
40808 getUpdateManager : function(){
40812 return this.el.getUpdateManager();
40815 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40816 * Does not work with IFRAME contents
40817 * @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:
40820 url: "your-url.php",
40821 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40822 callback: yourFunction,
40823 scope: yourObject, //(optional scope)
40826 text: "Loading...",
40832 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40833 * 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.
40834 * @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}
40835 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40836 * @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.
40837 * @return {Roo.ContentPanel} this
40845 var um = this.el.getUpdateManager();
40846 um.update.apply(um, arguments);
40852 * 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.
40853 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40854 * @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)
40855 * @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)
40856 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40858 setUrl : function(url, params, loadOnce){
40860 this.iframeEl.dom.src = url;
40864 if(this.refreshDelegate){
40865 this.removeListener("activate", this.refreshDelegate);
40867 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40868 this.on("activate", this.refreshDelegate);
40869 return this.el.getUpdateManager();
40872 _handleRefresh : function(url, params, loadOnce){
40873 if(!loadOnce || !this.loaded){
40874 var updater = this.el.getUpdateManager();
40875 updater.update(url, params, this._setLoaded.createDelegate(this));
40879 _setLoaded : function(){
40880 this.loaded = true;
40884 * Returns this panel's id
40887 getId : function(){
40892 * Returns this panel's element - used by regiosn to add.
40893 * @return {Roo.Element}
40895 getEl : function(){
40896 return this.wrapEl || this.el;
40901 adjustForComponents : function(width, height)
40903 //Roo.log('adjustForComponents ');
40904 if(this.resizeEl != this.el){
40905 width -= this.el.getFrameWidth('lr');
40906 height -= this.el.getFrameWidth('tb');
40909 var te = this.toolbar.getEl();
40910 te.setWidth(width);
40911 height -= te.getHeight();
40914 var te = this.footer.getEl();
40915 te.setWidth(width);
40916 height -= te.getHeight();
40920 if(this.adjustments){
40921 width += this.adjustments[0];
40922 height += this.adjustments[1];
40924 return {"width": width, "height": height};
40927 setSize : function(width, height){
40928 if(this.fitToFrame && !this.ignoreResize(width, height)){
40929 if(this.fitContainer && this.resizeEl != this.el){
40930 this.el.setSize(width, height);
40932 var size = this.adjustForComponents(width, height);
40934 this.iframeEl.setSize(width,height);
40937 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40938 this.fireEvent('resize', this, size.width, size.height);
40945 * Returns this panel's title
40948 getTitle : function(){
40950 if (typeof(this.title) != 'object') {
40955 for (var k in this.title) {
40956 if (!this.title.hasOwnProperty(k)) {
40960 if (k.indexOf('-') >= 0) {
40961 var s = k.split('-');
40962 for (var i = 0; i<s.length; i++) {
40963 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40966 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40973 * Set this panel's title
40974 * @param {String} title
40976 setTitle : function(title){
40977 this.title = title;
40979 this.region.updatePanelTitle(this, title);
40984 * Returns true is this panel was configured to be closable
40985 * @return {Boolean}
40987 isClosable : function(){
40988 return this.closable;
40991 beforeSlide : function(){
40993 this.resizeEl.clip();
40996 afterSlide : function(){
40998 this.resizeEl.unclip();
41002 * Force a content refresh from the URL specified in the {@link #setUrl} method.
41003 * Will fail silently if the {@link #setUrl} method has not been called.
41004 * This does not activate the panel, just updates its content.
41006 refresh : function(){
41007 if(this.refreshDelegate){
41008 this.loaded = false;
41009 this.refreshDelegate();
41014 * Destroys this panel
41016 destroy : function(){
41017 this.el.removeAllListeners();
41018 var tempEl = document.createElement("span");
41019 tempEl.appendChild(this.el.dom);
41020 tempEl.innerHTML = "";
41026 * form - if the content panel contains a form - this is a reference to it.
41027 * @type {Roo.form.Form}
41031 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41032 * This contains a reference to it.
41038 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41048 * @param {Object} cfg Xtype definition of item to add.
41052 getChildContainer: function () {
41053 return this.getEl();
41057 onScroll : function(e)
41059 this.fireEvent('scroll', this, e);
41064 var ret = new Roo.factory(cfg);
41069 if (cfg.xtype.match(/^Form$/)) {
41072 //if (this.footer) {
41073 // el = this.footer.container.insertSibling(false, 'before');
41075 el = this.el.createChild();
41078 this.form = new Roo.form.Form(cfg);
41081 if ( this.form.allItems.length) {
41082 this.form.render(el.dom);
41086 // should only have one of theses..
41087 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41088 // views.. should not be just added - used named prop 'view''
41090 cfg.el = this.el.appendChild(document.createElement("div"));
41093 var ret = new Roo.factory(cfg);
41095 ret.render && ret.render(false, ''); // render blank..
41105 * @class Roo.bootstrap.panel.Grid
41106 * @extends Roo.bootstrap.panel.Content
41108 * Create a new GridPanel.
41109 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41110 * @param {Object} config A the config object
41116 Roo.bootstrap.panel.Grid = function(config)
41120 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41121 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41123 config.el = this.wrapper;
41124 //this.el = this.wrapper;
41126 if (config.container) {
41127 // ctor'ed from a Border/panel.grid
41130 this.wrapper.setStyle("overflow", "hidden");
41131 this.wrapper.addClass('roo-grid-container');
41136 if(config.toolbar){
41137 var tool_el = this.wrapper.createChild();
41138 this.toolbar = Roo.factory(config.toolbar);
41140 if (config.toolbar.items) {
41141 ti = config.toolbar.items ;
41142 delete config.toolbar.items ;
41146 this.toolbar.render(tool_el);
41147 for(var i =0;i < ti.length;i++) {
41148 // Roo.log(['add child', items[i]]);
41149 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41151 this.toolbar.items = nitems;
41153 delete config.toolbar;
41156 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41157 config.grid.scrollBody = true;;
41158 config.grid.monitorWindowResize = false; // turn off autosizing
41159 config.grid.autoHeight = false;
41160 config.grid.autoWidth = false;
41162 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41164 if (config.background) {
41165 // render grid on panel activation (if panel background)
41166 this.on('activate', function(gp) {
41167 if (!gp.grid.rendered) {
41168 gp.grid.render(this.wrapper);
41169 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41174 this.grid.render(this.wrapper);
41175 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41178 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41179 // ??? needed ??? config.el = this.wrapper;
41184 // xtype created footer. - not sure if will work as we normally have to render first..
41185 if (this.footer && !this.footer.el && this.footer.xtype) {
41187 var ctr = this.grid.getView().getFooterPanel(true);
41188 this.footer.dataSource = this.grid.dataSource;
41189 this.footer = Roo.factory(this.footer, Roo);
41190 this.footer.render(ctr);
41200 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41201 getId : function(){
41202 return this.grid.id;
41206 * Returns the grid for this panel
41207 * @return {Roo.bootstrap.Table}
41209 getGrid : function(){
41213 setSize : function(width, height){
41214 if(!this.ignoreResize(width, height)){
41215 var grid = this.grid;
41216 var size = this.adjustForComponents(width, height);
41217 // tfoot is not a footer?
41220 var gridel = grid.getGridEl();
41221 gridel.setSize(size.width, size.height);
41223 var tbd = grid.getGridEl().select('tbody', true).first();
41224 var thd = grid.getGridEl().select('thead',true).first();
41225 var tbf= grid.getGridEl().select('tfoot', true).first();
41228 size.height -= tbf.getHeight();
41231 size.height -= thd.getHeight();
41234 tbd.setSize(size.width, size.height );
41235 // this is for the account management tab -seems to work there.
41236 var thd = grid.getGridEl().select('thead',true).first();
41238 // tbd.setSize(size.width, size.height - thd.getHeight());
41247 beforeSlide : function(){
41248 this.grid.getView().scroller.clip();
41251 afterSlide : function(){
41252 this.grid.getView().scroller.unclip();
41255 destroy : function(){
41256 this.grid.destroy();
41258 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41263 * @class Roo.bootstrap.panel.Nest
41264 * @extends Roo.bootstrap.panel.Content
41266 * Create a new Panel, that can contain a layout.Border.
41269 * @param {Roo.BorderLayout} layout The layout for this panel
41270 * @param {String/Object} config A string to set only the title or a config object
41272 Roo.bootstrap.panel.Nest = function(config)
41274 // construct with only one argument..
41275 /* FIXME - implement nicer consturctors
41276 if (layout.layout) {
41278 layout = config.layout;
41279 delete config.layout;
41281 if (layout.xtype && !layout.getEl) {
41282 // then layout needs constructing..
41283 layout = Roo.factory(layout, Roo);
41287 config.el = config.layout.getEl();
41289 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41291 config.layout.monitorWindowResize = false; // turn off autosizing
41292 this.layout = config.layout;
41293 this.layout.getEl().addClass("roo-layout-nested-layout");
41294 this.layout.parent = this;
41301 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41303 setSize : function(width, height){
41304 if(!this.ignoreResize(width, height)){
41305 var size = this.adjustForComponents(width, height);
41306 var el = this.layout.getEl();
41307 if (size.height < 1) {
41308 el.setWidth(size.width);
41310 el.setSize(size.width, size.height);
41312 var touch = el.dom.offsetWidth;
41313 this.layout.layout();
41314 // ie requires a double layout on the first pass
41315 if(Roo.isIE && !this.initialized){
41316 this.initialized = true;
41317 this.layout.layout();
41322 // activate all subpanels if not currently active..
41324 setActiveState : function(active){
41325 this.active = active;
41326 this.setActiveClass(active);
41329 this.fireEvent("deactivate", this);
41333 this.fireEvent("activate", this);
41334 // not sure if this should happen before or after..
41335 if (!this.layout) {
41336 return; // should not happen..
41339 for (var r in this.layout.regions) {
41340 reg = this.layout.getRegion(r);
41341 if (reg.getActivePanel()) {
41342 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41343 reg.setActivePanel(reg.getActivePanel());
41346 if (!reg.panels.length) {
41349 reg.showPanel(reg.getPanel(0));
41358 * Returns the nested BorderLayout for this panel
41359 * @return {Roo.BorderLayout}
41361 getLayout : function(){
41362 return this.layout;
41366 * Adds a xtype elements to the layout of the nested panel
41370 xtype : 'ContentPanel',
41377 xtype : 'NestedLayoutPanel',
41383 items : [ ... list of content panels or nested layout panels.. ]
41387 * @param {Object} cfg Xtype definition of item to add.
41389 addxtype : function(cfg) {
41390 return this.layout.addxtype(cfg);
41395 * Ext JS Library 1.1.1
41396 * Copyright(c) 2006-2007, Ext JS, LLC.
41398 * Originally Released Under LGPL - original licence link has changed is not relivant.
41401 * <script type="text/javascript">
41404 * @class Roo.TabPanel
41405 * @extends Roo.util.Observable
41406 * A lightweight tab container.
41410 // basic tabs 1, built from existing content
41411 var tabs = new Roo.TabPanel("tabs1");
41412 tabs.addTab("script", "View Script");
41413 tabs.addTab("markup", "View Markup");
41414 tabs.activate("script");
41416 // more advanced tabs, built from javascript
41417 var jtabs = new Roo.TabPanel("jtabs");
41418 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41420 // set up the UpdateManager
41421 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41422 var updater = tab2.getUpdateManager();
41423 updater.setDefaultUrl("ajax1.htm");
41424 tab2.on('activate', updater.refresh, updater, true);
41426 // Use setUrl for Ajax loading
41427 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41428 tab3.setUrl("ajax2.htm", null, true);
41431 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41434 jtabs.activate("jtabs-1");
41437 * Create a new TabPanel.
41438 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41439 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41441 Roo.bootstrap.panel.Tabs = function(config){
41443 * The container element for this TabPanel.
41444 * @type Roo.Element
41446 this.el = Roo.get(config.el);
41449 if(typeof config == "boolean"){
41450 this.tabPosition = config ? "bottom" : "top";
41452 Roo.apply(this, config);
41456 if(this.tabPosition == "bottom"){
41457 // if tabs are at the bottom = create the body first.
41458 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41459 this.el.addClass("roo-tabs-bottom");
41461 // next create the tabs holders
41463 if (this.tabPosition == "west"){
41465 var reg = this.region; // fake it..
41467 if (!reg.mgr.parent) {
41470 reg = reg.mgr.parent.region;
41472 Roo.log("got nest?");
41474 if (reg.mgr.getRegion('west')) {
41475 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41476 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41477 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41478 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41479 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41487 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41488 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41489 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41490 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41495 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41498 // finally - if tabs are at the top, then create the body last..
41499 if(this.tabPosition != "bottom"){
41500 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41501 * @type Roo.Element
41503 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41504 this.el.addClass("roo-tabs-top");
41508 this.bodyEl.setStyle("position", "relative");
41510 this.active = null;
41511 this.activateDelegate = this.activate.createDelegate(this);
41516 * Fires when the active tab changes
41517 * @param {Roo.TabPanel} this
41518 * @param {Roo.TabPanelItem} activePanel The new active tab
41522 * @event beforetabchange
41523 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41524 * @param {Roo.TabPanel} this
41525 * @param {Object} e Set cancel to true on this object to cancel the tab change
41526 * @param {Roo.TabPanelItem} tab The tab being changed to
41528 "beforetabchange" : true
41531 Roo.EventManager.onWindowResize(this.onResize, this);
41532 this.cpad = this.el.getPadding("lr");
41533 this.hiddenCount = 0;
41536 // toolbar on the tabbar support...
41537 if (this.toolbar) {
41538 alert("no toolbar support yet");
41539 this.toolbar = false;
41541 var tcfg = this.toolbar;
41542 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41543 this.toolbar = new Roo.Toolbar(tcfg);
41544 if (Roo.isSafari) {
41545 var tbl = tcfg.container.child('table', true);
41546 tbl.setAttribute('width', '100%');
41554 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41557 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41559 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41561 tabPosition : "top",
41563 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41565 currentTabWidth : 0,
41567 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41571 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41575 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41577 preferredTabWidth : 175,
41579 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41581 resizeTabs : false,
41583 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41585 monitorResize : true,
41587 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41589 toolbar : false, // set by caller..
41591 region : false, /// set by caller
41593 disableTooltips : true, // not used yet...
41596 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41597 * @param {String} id The id of the div to use <b>or create</b>
41598 * @param {String} text The text for the tab
41599 * @param {String} content (optional) Content to put in the TabPanelItem body
41600 * @param {Boolean} closable (optional) True to create a close icon on the tab
41601 * @return {Roo.TabPanelItem} The created TabPanelItem
41603 addTab : function(id, text, content, closable, tpl)
41605 var item = new Roo.bootstrap.panel.TabItem({
41609 closable : closable,
41612 this.addTabItem(item);
41614 item.setContent(content);
41620 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41621 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41622 * @return {Roo.TabPanelItem}
41624 getTab : function(id){
41625 return this.items[id];
41629 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41630 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41632 hideTab : function(id){
41633 var t = this.items[id];
41636 this.hiddenCount++;
41637 this.autoSizeTabs();
41642 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41643 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41645 unhideTab : function(id){
41646 var t = this.items[id];
41648 t.setHidden(false);
41649 this.hiddenCount--;
41650 this.autoSizeTabs();
41655 * Adds an existing {@link Roo.TabPanelItem}.
41656 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41658 addTabItem : function(item)
41660 this.items[item.id] = item;
41661 this.items.push(item);
41662 this.autoSizeTabs();
41663 // if(this.resizeTabs){
41664 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41665 // this.autoSizeTabs();
41667 // item.autoSize();
41672 * Removes a {@link Roo.TabPanelItem}.
41673 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41675 removeTab : function(id){
41676 var items = this.items;
41677 var tab = items[id];
41678 if(!tab) { return; }
41679 var index = items.indexOf(tab);
41680 if(this.active == tab && items.length > 1){
41681 var newTab = this.getNextAvailable(index);
41686 this.stripEl.dom.removeChild(tab.pnode.dom);
41687 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41688 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41690 items.splice(index, 1);
41691 delete this.items[tab.id];
41692 tab.fireEvent("close", tab);
41693 tab.purgeListeners();
41694 this.autoSizeTabs();
41697 getNextAvailable : function(start){
41698 var items = this.items;
41700 // look for a next tab that will slide over to
41701 // replace the one being removed
41702 while(index < items.length){
41703 var item = items[++index];
41704 if(item && !item.isHidden()){
41708 // if one isn't found select the previous tab (on the left)
41711 var item = items[--index];
41712 if(item && !item.isHidden()){
41720 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41721 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41723 disableTab : function(id){
41724 var tab = this.items[id];
41725 if(tab && this.active != tab){
41731 * Enables a {@link Roo.TabPanelItem} that is disabled.
41732 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41734 enableTab : function(id){
41735 var tab = this.items[id];
41740 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41741 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41742 * @return {Roo.TabPanelItem} The TabPanelItem.
41744 activate : function(id)
41746 //Roo.log('activite:' + id);
41748 var tab = this.items[id];
41752 if(tab == this.active || tab.disabled){
41756 this.fireEvent("beforetabchange", this, e, tab);
41757 if(e.cancel !== true && !tab.disabled){
41759 this.active.hide();
41761 this.active = this.items[id];
41762 this.active.show();
41763 this.fireEvent("tabchange", this, this.active);
41769 * Gets the active {@link Roo.TabPanelItem}.
41770 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41772 getActiveTab : function(){
41773 return this.active;
41777 * Updates the tab body element to fit the height of the container element
41778 * for overflow scrolling
41779 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41781 syncHeight : function(targetHeight){
41782 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41783 var bm = this.bodyEl.getMargins();
41784 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41785 this.bodyEl.setHeight(newHeight);
41789 onResize : function(){
41790 if(this.monitorResize){
41791 this.autoSizeTabs();
41796 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41798 beginUpdate : function(){
41799 this.updating = true;
41803 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41805 endUpdate : function(){
41806 this.updating = false;
41807 this.autoSizeTabs();
41811 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41813 autoSizeTabs : function()
41815 var count = this.items.length;
41816 var vcount = count - this.hiddenCount;
41819 this.stripEl.hide();
41821 this.stripEl.show();
41824 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41829 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41830 var availWidth = Math.floor(w / vcount);
41831 var b = this.stripBody;
41832 if(b.getWidth() > w){
41833 var tabs = this.items;
41834 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41835 if(availWidth < this.minTabWidth){
41836 /*if(!this.sleft){ // incomplete scrolling code
41837 this.createScrollButtons();
41840 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41843 if(this.currentTabWidth < this.preferredTabWidth){
41844 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41850 * Returns the number of tabs in this TabPanel.
41853 getCount : function(){
41854 return this.items.length;
41858 * Resizes all the tabs to the passed width
41859 * @param {Number} The new width
41861 setTabWidth : function(width){
41862 this.currentTabWidth = width;
41863 for(var i = 0, len = this.items.length; i < len; i++) {
41864 if(!this.items[i].isHidden()) {
41865 this.items[i].setWidth(width);
41871 * Destroys this TabPanel
41872 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41874 destroy : function(removeEl){
41875 Roo.EventManager.removeResizeListener(this.onResize, this);
41876 for(var i = 0, len = this.items.length; i < len; i++){
41877 this.items[i].purgeListeners();
41879 if(removeEl === true){
41880 this.el.update("");
41885 createStrip : function(container)
41887 var strip = document.createElement("nav");
41888 strip.className = Roo.bootstrap.version == 4 ?
41889 "navbar-light bg-light" :
41890 "navbar navbar-default"; //"x-tabs-wrap";
41891 container.appendChild(strip);
41895 createStripList : function(strip)
41897 // div wrapper for retard IE
41898 // returns the "tr" element.
41899 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41900 //'<div class="x-tabs-strip-wrap">'+
41901 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41902 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41903 return strip.firstChild; //.firstChild.firstChild.firstChild;
41905 createBody : function(container)
41907 var body = document.createElement("div");
41908 Roo.id(body, "tab-body");
41909 //Roo.fly(body).addClass("x-tabs-body");
41910 Roo.fly(body).addClass("tab-content");
41911 container.appendChild(body);
41914 createItemBody :function(bodyEl, id){
41915 var body = Roo.getDom(id);
41917 body = document.createElement("div");
41920 //Roo.fly(body).addClass("x-tabs-item-body");
41921 Roo.fly(body).addClass("tab-pane");
41922 bodyEl.insertBefore(body, bodyEl.firstChild);
41926 createStripElements : function(stripEl, text, closable, tpl)
41928 var td = document.createElement("li"); // was td..
41929 td.className = 'nav-item';
41931 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41934 stripEl.appendChild(td);
41936 td.className = "x-tabs-closable";
41937 if(!this.closeTpl){
41938 this.closeTpl = new Roo.Template(
41939 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41940 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41941 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41944 var el = this.closeTpl.overwrite(td, {"text": text});
41945 var close = el.getElementsByTagName("div")[0];
41946 var inner = el.getElementsByTagName("em")[0];
41947 return {"el": el, "close": close, "inner": inner};
41950 // not sure what this is..
41951 // if(!this.tabTpl){
41952 //this.tabTpl = new Roo.Template(
41953 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41954 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41956 // this.tabTpl = new Roo.Template(
41957 // '<a href="#">' +
41958 // '<span unselectable="on"' +
41959 // (this.disableTooltips ? '' : ' title="{text}"') +
41960 // ' >{text}</span></a>'
41966 var template = tpl || this.tabTpl || false;
41969 template = new Roo.Template(
41970 Roo.bootstrap.version == 4 ?
41972 '<a class="nav-link" href="#" unselectable="on"' +
41973 (this.disableTooltips ? '' : ' title="{text}"') +
41976 '<a class="nav-link" href="#">' +
41977 '<span unselectable="on"' +
41978 (this.disableTooltips ? '' : ' title="{text}"') +
41979 ' >{text}</span></a>'
41984 switch (typeof(template)) {
41988 template = new Roo.Template(template);
41994 var el = template.overwrite(td, {"text": text});
41996 var inner = el.getElementsByTagName("span")[0];
41998 return {"el": el, "inner": inner};
42006 * @class Roo.TabPanelItem
42007 * @extends Roo.util.Observable
42008 * Represents an individual item (tab plus body) in a TabPanel.
42009 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42010 * @param {String} id The id of this TabPanelItem
42011 * @param {String} text The text for the tab of this TabPanelItem
42012 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42014 Roo.bootstrap.panel.TabItem = function(config){
42016 * The {@link Roo.TabPanel} this TabPanelItem belongs to
42017 * @type Roo.TabPanel
42019 this.tabPanel = config.panel;
42021 * The id for this TabPanelItem
42024 this.id = config.id;
42026 this.disabled = false;
42028 this.text = config.text;
42030 this.loaded = false;
42031 this.closable = config.closable;
42034 * The body element for this TabPanelItem.
42035 * @type Roo.Element
42037 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42038 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42039 this.bodyEl.setStyle("display", "block");
42040 this.bodyEl.setStyle("zoom", "1");
42041 //this.hideAction();
42043 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42045 this.el = Roo.get(els.el);
42046 this.inner = Roo.get(els.inner, true);
42047 this.textEl = Roo.bootstrap.version == 4 ?
42048 this.el : Roo.get(this.el.dom.firstChild, true);
42050 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42051 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42054 // this.el.on("mousedown", this.onTabMouseDown, this);
42055 this.el.on("click", this.onTabClick, this);
42057 if(config.closable){
42058 var c = Roo.get(els.close, true);
42059 c.dom.title = this.closeText;
42060 c.addClassOnOver("close-over");
42061 c.on("click", this.closeClick, this);
42067 * Fires when this tab becomes the active tab.
42068 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42069 * @param {Roo.TabPanelItem} this
42073 * @event beforeclose
42074 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42075 * @param {Roo.TabPanelItem} this
42076 * @param {Object} e Set cancel to true on this object to cancel the close.
42078 "beforeclose": true,
42081 * Fires when this tab is closed.
42082 * @param {Roo.TabPanelItem} this
42086 * @event deactivate
42087 * Fires when this tab is no longer the active tab.
42088 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42089 * @param {Roo.TabPanelItem} this
42091 "deactivate" : true
42093 this.hidden = false;
42095 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42098 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42100 purgeListeners : function(){
42101 Roo.util.Observable.prototype.purgeListeners.call(this);
42102 this.el.removeAllListeners();
42105 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42108 this.status_node.addClass("active");
42111 this.tabPanel.stripWrap.repaint();
42113 this.fireEvent("activate", this.tabPanel, this);
42117 * Returns true if this tab is the active tab.
42118 * @return {Boolean}
42120 isActive : function(){
42121 return this.tabPanel.getActiveTab() == this;
42125 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42128 this.status_node.removeClass("active");
42130 this.fireEvent("deactivate", this.tabPanel, this);
42133 hideAction : function(){
42134 this.bodyEl.hide();
42135 this.bodyEl.setStyle("position", "absolute");
42136 this.bodyEl.setLeft("-20000px");
42137 this.bodyEl.setTop("-20000px");
42140 showAction : function(){
42141 this.bodyEl.setStyle("position", "relative");
42142 this.bodyEl.setTop("");
42143 this.bodyEl.setLeft("");
42144 this.bodyEl.show();
42148 * Set the tooltip for the tab.
42149 * @param {String} tooltip The tab's tooltip
42151 setTooltip : function(text){
42152 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42153 this.textEl.dom.qtip = text;
42154 this.textEl.dom.removeAttribute('title');
42156 this.textEl.dom.title = text;
42160 onTabClick : function(e){
42161 e.preventDefault();
42162 this.tabPanel.activate(this.id);
42165 onTabMouseDown : function(e){
42166 e.preventDefault();
42167 this.tabPanel.activate(this.id);
42170 getWidth : function(){
42171 return this.inner.getWidth();
42174 setWidth : function(width){
42175 var iwidth = width - this.linode.getPadding("lr");
42176 this.inner.setWidth(iwidth);
42177 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42178 this.linode.setWidth(width);
42182 * Show or hide the tab
42183 * @param {Boolean} hidden True to hide or false to show.
42185 setHidden : function(hidden){
42186 this.hidden = hidden;
42187 this.linode.setStyle("display", hidden ? "none" : "");
42191 * Returns true if this tab is "hidden"
42192 * @return {Boolean}
42194 isHidden : function(){
42195 return this.hidden;
42199 * Returns the text for this tab
42202 getText : function(){
42206 autoSize : function(){
42207 //this.el.beginMeasure();
42208 this.textEl.setWidth(1);
42210 * #2804 [new] Tabs in Roojs
42211 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42213 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42214 //this.el.endMeasure();
42218 * Sets the text for the tab (Note: this also sets the tooltip text)
42219 * @param {String} text The tab's text and tooltip
42221 setText : function(text){
42223 this.textEl.update(text);
42224 this.setTooltip(text);
42225 //if(!this.tabPanel.resizeTabs){
42226 // this.autoSize();
42230 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42232 activate : function(){
42233 this.tabPanel.activate(this.id);
42237 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42239 disable : function(){
42240 if(this.tabPanel.active != this){
42241 this.disabled = true;
42242 this.status_node.addClass("disabled");
42247 * Enables this TabPanelItem if it was previously disabled.
42249 enable : function(){
42250 this.disabled = false;
42251 this.status_node.removeClass("disabled");
42255 * Sets the content for this TabPanelItem.
42256 * @param {String} content The content
42257 * @param {Boolean} loadScripts true to look for and load scripts
42259 setContent : function(content, loadScripts){
42260 this.bodyEl.update(content, loadScripts);
42264 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42265 * @return {Roo.UpdateManager} The UpdateManager
42267 getUpdateManager : function(){
42268 return this.bodyEl.getUpdateManager();
42272 * Set a URL to be used to load the content for this TabPanelItem.
42273 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42274 * @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)
42275 * @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)
42276 * @return {Roo.UpdateManager} The UpdateManager
42278 setUrl : function(url, params, loadOnce){
42279 if(this.refreshDelegate){
42280 this.un('activate', this.refreshDelegate);
42282 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42283 this.on("activate", this.refreshDelegate);
42284 return this.bodyEl.getUpdateManager();
42288 _handleRefresh : function(url, params, loadOnce){
42289 if(!loadOnce || !this.loaded){
42290 var updater = this.bodyEl.getUpdateManager();
42291 updater.update(url, params, this._setLoaded.createDelegate(this));
42296 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42297 * Will fail silently if the setUrl method has not been called.
42298 * This does not activate the panel, just updates its content.
42300 refresh : function(){
42301 if(this.refreshDelegate){
42302 this.loaded = false;
42303 this.refreshDelegate();
42308 _setLoaded : function(){
42309 this.loaded = true;
42313 closeClick : function(e){
42316 this.fireEvent("beforeclose", this, o);
42317 if(o.cancel !== true){
42318 this.tabPanel.removeTab(this.id);
42322 * The text displayed in the tooltip for the close icon.
42325 closeText : "Close this tab"
42328 * This script refer to:
42329 * Title: International Telephone Input
42330 * Author: Jack O'Connor
42331 * Code version: v12.1.12
42332 * Availability: https://github.com/jackocnr/intl-tel-input.git
42335 Roo.bootstrap.PhoneInputData = function() {
42338 "Afghanistan (افغانستان)",
42343 "Albania (Shqipëri)",
42348 "Algeria (الجزائر)",
42373 "Antigua and Barbuda",
42383 "Armenia (Հայաստան)",
42399 "Austria (Österreich)",
42404 "Azerbaijan (Azərbaycan)",
42414 "Bahrain (البحرين)",
42419 "Bangladesh (বাংলাদেশ)",
42429 "Belarus (Беларусь)",
42434 "Belgium (België)",
42464 "Bosnia and Herzegovina (Босна и Херцеговина)",
42479 "British Indian Ocean Territory",
42484 "British Virgin Islands",
42494 "Bulgaria (България)",
42504 "Burundi (Uburundi)",
42509 "Cambodia (កម្ពុជា)",
42514 "Cameroon (Cameroun)",
42523 ["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"]
42526 "Cape Verde (Kabu Verdi)",
42531 "Caribbean Netherlands",
42542 "Central African Republic (République centrafricaine)",
42562 "Christmas Island",
42568 "Cocos (Keeling) Islands",
42579 "Comoros (جزر القمر)",
42584 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42589 "Congo (Republic) (Congo-Brazzaville)",
42609 "Croatia (Hrvatska)",
42630 "Czech Republic (Česká republika)",
42635 "Denmark (Danmark)",
42650 "Dominican Republic (República Dominicana)",
42654 ["809", "829", "849"]
42672 "Equatorial Guinea (Guinea Ecuatorial)",
42692 "Falkland Islands (Islas Malvinas)",
42697 "Faroe Islands (Føroyar)",
42718 "French Guiana (Guyane française)",
42723 "French Polynesia (Polynésie française)",
42738 "Georgia (საქართველო)",
42743 "Germany (Deutschland)",
42763 "Greenland (Kalaallit Nunaat)",
42800 "Guinea-Bissau (Guiné Bissau)",
42825 "Hungary (Magyarország)",
42830 "Iceland (Ísland)",
42850 "Iraq (العراق)",
42866 "Israel (ישראל)",
42893 "Jordan (الأردن)",
42898 "Kazakhstan (Казахстан)",
42919 "Kuwait (الكويت)",
42924 "Kyrgyzstan (Кыргызстан)",
42934 "Latvia (Latvija)",
42939 "Lebanon (لبنان)",
42954 "Libya (ليبيا)",
42964 "Lithuania (Lietuva)",
42979 "Macedonia (FYROM) (Македонија)",
42984 "Madagascar (Madagasikara)",
43014 "Marshall Islands",
43024 "Mauritania (موريتانيا)",
43029 "Mauritius (Moris)",
43050 "Moldova (Republica Moldova)",
43060 "Mongolia (Монгол)",
43065 "Montenegro (Crna Gora)",
43075 "Morocco (المغرب)",
43081 "Mozambique (Moçambique)",
43086 "Myanmar (Burma) (မြန်မာ)",
43091 "Namibia (Namibië)",
43106 "Netherlands (Nederland)",
43111 "New Caledonia (Nouvelle-Calédonie)",
43146 "North Korea (조선 민주주의 인민 공화국)",
43151 "Northern Mariana Islands",
43167 "Pakistan (پاکستان)",
43177 "Palestine (فلسطين)",
43187 "Papua New Guinea",
43229 "Réunion (La Réunion)",
43235 "Romania (România)",
43251 "Saint Barthélemy",
43262 "Saint Kitts and Nevis",
43272 "Saint Martin (Saint-Martin (partie française))",
43278 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43283 "Saint Vincent and the Grenadines",
43298 "São Tomé and Príncipe (São Tomé e Príncipe)",
43303 "Saudi Arabia (المملكة العربية السعودية)",
43308 "Senegal (Sénégal)",
43338 "Slovakia (Slovensko)",
43343 "Slovenia (Slovenija)",
43353 "Somalia (Soomaaliya)",
43363 "South Korea (대한민국)",
43368 "South Sudan (جنوب السودان)",
43378 "Sri Lanka (ශ්රී ලංකාව)",
43383 "Sudan (السودان)",
43393 "Svalbard and Jan Mayen",
43404 "Sweden (Sverige)",
43409 "Switzerland (Schweiz)",
43414 "Syria (سوريا)",
43459 "Trinidad and Tobago",
43464 "Tunisia (تونس)",
43469 "Turkey (Türkiye)",
43479 "Turks and Caicos Islands",
43489 "U.S. Virgin Islands",
43499 "Ukraine (Україна)",
43504 "United Arab Emirates (الإمارات العربية المتحدة)",
43526 "Uzbekistan (Oʻzbekiston)",
43536 "Vatican City (Città del Vaticano)",
43547 "Vietnam (Việt Nam)",
43552 "Wallis and Futuna (Wallis-et-Futuna)",
43557 "Western Sahara (الصحراء الغربية)",
43563 "Yemen (اليمن)",
43587 * This script refer to:
43588 * Title: International Telephone Input
43589 * Author: Jack O'Connor
43590 * Code version: v12.1.12
43591 * Availability: https://github.com/jackocnr/intl-tel-input.git
43595 * @class Roo.bootstrap.PhoneInput
43596 * @extends Roo.bootstrap.TriggerField
43597 * An input with International dial-code selection
43599 * @cfg {String} defaultDialCode default '+852'
43600 * @cfg {Array} preferedCountries default []
43603 * Create a new PhoneInput.
43604 * @param {Object} config Configuration options
43607 Roo.bootstrap.PhoneInput = function(config) {
43608 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43611 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43613 listWidth: undefined,
43615 selectedClass: 'active',
43617 invalidClass : "has-warning",
43619 validClass: 'has-success',
43621 allowed: '0123456789',
43626 * @cfg {String} defaultDialCode The default dial code when initializing the input
43628 defaultDialCode: '+852',
43631 * @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
43633 preferedCountries: false,
43635 getAutoCreate : function()
43637 var data = Roo.bootstrap.PhoneInputData();
43638 var align = this.labelAlign || this.parentLabelAlign();
43641 this.allCountries = [];
43642 this.dialCodeMapping = [];
43644 for (var i = 0; i < data.length; i++) {
43646 this.allCountries[i] = {
43650 priority: c[3] || 0,
43651 areaCodes: c[4] || null
43653 this.dialCodeMapping[c[2]] = {
43656 priority: c[3] || 0,
43657 areaCodes: c[4] || null
43669 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43670 maxlength: this.max_length,
43671 cls : 'form-control tel-input',
43672 autocomplete: 'new-password'
43675 var hiddenInput = {
43678 cls: 'hidden-tel-input'
43682 hiddenInput.name = this.name;
43685 if (this.disabled) {
43686 input.disabled = true;
43689 var flag_container = {
43706 cls: this.hasFeedback ? 'has-feedback' : '',
43712 cls: 'dial-code-holder',
43719 cls: 'roo-select2-container input-group',
43726 if (this.fieldLabel.length) {
43729 tooltip: 'This field is required'
43735 cls: 'control-label',
43741 html: this.fieldLabel
43744 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43750 if(this.indicatorpos == 'right') {
43751 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43758 if(align == 'left') {
43766 if(this.labelWidth > 12){
43767 label.style = "width: " + this.labelWidth + 'px';
43769 if(this.labelWidth < 13 && this.labelmd == 0){
43770 this.labelmd = this.labelWidth;
43772 if(this.labellg > 0){
43773 label.cls += ' col-lg-' + this.labellg;
43774 input.cls += ' col-lg-' + (12 - this.labellg);
43776 if(this.labelmd > 0){
43777 label.cls += ' col-md-' + this.labelmd;
43778 container.cls += ' col-md-' + (12 - this.labelmd);
43780 if(this.labelsm > 0){
43781 label.cls += ' col-sm-' + this.labelsm;
43782 container.cls += ' col-sm-' + (12 - this.labelsm);
43784 if(this.labelxs > 0){
43785 label.cls += ' col-xs-' + this.labelxs;
43786 container.cls += ' col-xs-' + (12 - this.labelxs);
43796 var settings = this;
43798 ['xs','sm','md','lg'].map(function(size){
43799 if (settings[size]) {
43800 cfg.cls += ' col-' + size + '-' + settings[size];
43804 this.store = new Roo.data.Store({
43805 proxy : new Roo.data.MemoryProxy({}),
43806 reader : new Roo.data.JsonReader({
43817 'name' : 'dialCode',
43821 'name' : 'priority',
43825 'name' : 'areaCodes',
43832 if(!this.preferedCountries) {
43833 this.preferedCountries = [
43840 var p = this.preferedCountries.reverse();
43843 for (var i = 0; i < p.length; i++) {
43844 for (var j = 0; j < this.allCountries.length; j++) {
43845 if(this.allCountries[j].iso2 == p[i]) {
43846 var t = this.allCountries[j];
43847 this.allCountries.splice(j,1);
43848 this.allCountries.unshift(t);
43854 this.store.proxy.data = {
43856 data: this.allCountries
43862 initEvents : function()
43865 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43867 this.indicator = this.indicatorEl();
43868 this.flag = this.flagEl();
43869 this.dialCodeHolder = this.dialCodeHolderEl();
43871 this.trigger = this.el.select('div.flag-box',true).first();
43872 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43877 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43878 _this.list.setWidth(lw);
43881 this.list.on('mouseover', this.onViewOver, this);
43882 this.list.on('mousemove', this.onViewMove, this);
43883 this.inputEl().on("keyup", this.onKeyUp, this);
43884 this.inputEl().on("keypress", this.onKeyPress, this);
43886 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43888 this.view = new Roo.View(this.list, this.tpl, {
43889 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43892 this.view.on('click', this.onViewClick, this);
43893 this.setValue(this.defaultDialCode);
43896 onTriggerClick : function(e)
43898 Roo.log('trigger click');
43903 if(this.isExpanded()){
43905 this.hasFocus = false;
43907 this.store.load({});
43908 this.hasFocus = true;
43913 isExpanded : function()
43915 return this.list.isVisible();
43918 collapse : function()
43920 if(!this.isExpanded()){
43924 Roo.get(document).un('mousedown', this.collapseIf, this);
43925 Roo.get(document).un('mousewheel', this.collapseIf, this);
43926 this.fireEvent('collapse', this);
43930 expand : function()
43934 if(this.isExpanded() || !this.hasFocus){
43938 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43939 this.list.setWidth(lw);
43942 this.restrictHeight();
43944 Roo.get(document).on('mousedown', this.collapseIf, this);
43945 Roo.get(document).on('mousewheel', this.collapseIf, this);
43947 this.fireEvent('expand', this);
43950 restrictHeight : function()
43952 this.list.alignTo(this.inputEl(), this.listAlign);
43953 this.list.alignTo(this.inputEl(), this.listAlign);
43956 onViewOver : function(e, t)
43958 if(this.inKeyMode){
43961 var item = this.view.findItemFromChild(t);
43964 var index = this.view.indexOf(item);
43965 this.select(index, false);
43970 onViewClick : function(view, doFocus, el, e)
43972 var index = this.view.getSelectedIndexes()[0];
43974 var r = this.store.getAt(index);
43977 this.onSelect(r, index);
43979 if(doFocus !== false && !this.blockFocus){
43980 this.inputEl().focus();
43984 onViewMove : function(e, t)
43986 this.inKeyMode = false;
43989 select : function(index, scrollIntoView)
43991 this.selectedIndex = index;
43992 this.view.select(index);
43993 if(scrollIntoView !== false){
43994 var el = this.view.getNode(index);
43996 this.list.scrollChildIntoView(el, false);
44001 createList : function()
44003 this.list = Roo.get(document.body).createChild({
44005 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44006 style: 'display:none'
44009 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44012 collapseIf : function(e)
44014 var in_combo = e.within(this.el);
44015 var in_list = e.within(this.list);
44016 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44018 if (in_combo || in_list || is_list) {
44024 onSelect : function(record, index)
44026 if(this.fireEvent('beforeselect', this, record, index) !== false){
44028 this.setFlagClass(record.data.iso2);
44029 this.setDialCode(record.data.dialCode);
44030 this.hasFocus = false;
44032 this.fireEvent('select', this, record, index);
44036 flagEl : function()
44038 var flag = this.el.select('div.flag',true).first();
44045 dialCodeHolderEl : function()
44047 var d = this.el.select('input.dial-code-holder',true).first();
44054 setDialCode : function(v)
44056 this.dialCodeHolder.dom.value = '+'+v;
44059 setFlagClass : function(n)
44061 this.flag.dom.className = 'flag '+n;
44064 getValue : function()
44066 var v = this.inputEl().getValue();
44067 if(this.dialCodeHolder) {
44068 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44073 setValue : function(v)
44075 var d = this.getDialCode(v);
44077 //invalid dial code
44078 if(v.length == 0 || !d || d.length == 0) {
44080 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44081 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44087 this.setFlagClass(this.dialCodeMapping[d].iso2);
44088 this.setDialCode(d);
44089 this.inputEl().dom.value = v.replace('+'+d,'');
44090 this.hiddenEl().dom.value = this.getValue();
44095 getDialCode : function(v)
44099 if (v.length == 0) {
44100 return this.dialCodeHolder.dom.value;
44104 if (v.charAt(0) != "+") {
44107 var numericChars = "";
44108 for (var i = 1; i < v.length; i++) {
44109 var c = v.charAt(i);
44112 if (this.dialCodeMapping[numericChars]) {
44113 dialCode = v.substr(1, i);
44115 if (numericChars.length == 4) {
44125 this.setValue(this.defaultDialCode);
44129 hiddenEl : function()
44131 return this.el.select('input.hidden-tel-input',true).first();
44134 // after setting val
44135 onKeyUp : function(e){
44136 this.setValue(this.getValue());
44139 onKeyPress : function(e){
44140 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44147 * @class Roo.bootstrap.MoneyField
44148 * @extends Roo.bootstrap.ComboBox
44149 * Bootstrap MoneyField class
44152 * Create a new MoneyField.
44153 * @param {Object} config Configuration options
44156 Roo.bootstrap.MoneyField = function(config) {
44158 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44162 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44165 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44167 allowDecimals : true,
44169 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44171 decimalSeparator : ".",
44173 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44175 decimalPrecision : 0,
44177 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44179 allowNegative : true,
44181 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44185 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44187 minValue : Number.NEGATIVE_INFINITY,
44189 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44191 maxValue : Number.MAX_VALUE,
44193 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44195 minText : "The minimum value for this field is {0}",
44197 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44199 maxText : "The maximum value for this field is {0}",
44201 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44202 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44204 nanText : "{0} is not a valid number",
44206 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44210 * @cfg {String} defaults currency of the MoneyField
44211 * value should be in lkey
44213 defaultCurrency : false,
44215 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44217 thousandsDelimiter : false,
44219 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44230 getAutoCreate : function()
44232 var align = this.labelAlign || this.parentLabelAlign();
44244 cls : 'form-control roo-money-amount-input',
44245 autocomplete: 'new-password'
44248 var hiddenInput = {
44252 cls: 'hidden-number-input'
44255 if(this.max_length) {
44256 input.maxlength = this.max_length;
44260 hiddenInput.name = this.name;
44263 if (this.disabled) {
44264 input.disabled = true;
44267 var clg = 12 - this.inputlg;
44268 var cmd = 12 - this.inputmd;
44269 var csm = 12 - this.inputsm;
44270 var cxs = 12 - this.inputxs;
44274 cls : 'row roo-money-field',
44278 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44282 cls: 'roo-select2-container input-group',
44286 cls : 'form-control roo-money-currency-input',
44287 autocomplete: 'new-password',
44289 name : this.currencyName
44293 cls : 'input-group-addon',
44307 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44311 cls: this.hasFeedback ? 'has-feedback' : '',
44322 if (this.fieldLabel.length) {
44325 tooltip: 'This field is required'
44331 cls: 'control-label',
44337 html: this.fieldLabel
44340 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44346 if(this.indicatorpos == 'right') {
44347 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44354 if(align == 'left') {
44362 if(this.labelWidth > 12){
44363 label.style = "width: " + this.labelWidth + 'px';
44365 if(this.labelWidth < 13 && this.labelmd == 0){
44366 this.labelmd = this.labelWidth;
44368 if(this.labellg > 0){
44369 label.cls += ' col-lg-' + this.labellg;
44370 input.cls += ' col-lg-' + (12 - this.labellg);
44372 if(this.labelmd > 0){
44373 label.cls += ' col-md-' + this.labelmd;
44374 container.cls += ' col-md-' + (12 - this.labelmd);
44376 if(this.labelsm > 0){
44377 label.cls += ' col-sm-' + this.labelsm;
44378 container.cls += ' col-sm-' + (12 - this.labelsm);
44380 if(this.labelxs > 0){
44381 label.cls += ' col-xs-' + this.labelxs;
44382 container.cls += ' col-xs-' + (12 - this.labelxs);
44393 var settings = this;
44395 ['xs','sm','md','lg'].map(function(size){
44396 if (settings[size]) {
44397 cfg.cls += ' col-' + size + '-' + settings[size];
44404 initEvents : function()
44406 this.indicator = this.indicatorEl();
44408 this.initCurrencyEvent();
44410 this.initNumberEvent();
44413 initCurrencyEvent : function()
44416 throw "can not find store for combo";
44419 this.store = Roo.factory(this.store, Roo.data);
44420 this.store.parent = this;
44424 this.triggerEl = this.el.select('.input-group-addon', true).first();
44426 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44431 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44432 _this.list.setWidth(lw);
44435 this.list.on('mouseover', this.onViewOver, this);
44436 this.list.on('mousemove', this.onViewMove, this);
44437 this.list.on('scroll', this.onViewScroll, this);
44440 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44443 this.view = new Roo.View(this.list, this.tpl, {
44444 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44447 this.view.on('click', this.onViewClick, this);
44449 this.store.on('beforeload', this.onBeforeLoad, this);
44450 this.store.on('load', this.onLoad, this);
44451 this.store.on('loadexception', this.onLoadException, this);
44453 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44454 "up" : function(e){
44455 this.inKeyMode = true;
44459 "down" : function(e){
44460 if(!this.isExpanded()){
44461 this.onTriggerClick();
44463 this.inKeyMode = true;
44468 "enter" : function(e){
44471 if(this.fireEvent("specialkey", this, e)){
44472 this.onViewClick(false);
44478 "esc" : function(e){
44482 "tab" : function(e){
44485 if(this.fireEvent("specialkey", this, e)){
44486 this.onViewClick(false);
44494 doRelay : function(foo, bar, hname){
44495 if(hname == 'down' || this.scope.isExpanded()){
44496 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44504 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44508 initNumberEvent : function(e)
44510 this.inputEl().on("keydown" , this.fireKey, this);
44511 this.inputEl().on("focus", this.onFocus, this);
44512 this.inputEl().on("blur", this.onBlur, this);
44514 this.inputEl().relayEvent('keyup', this);
44516 if(this.indicator){
44517 this.indicator.addClass('invisible');
44520 this.originalValue = this.getValue();
44522 if(this.validationEvent == 'keyup'){
44523 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44524 this.inputEl().on('keyup', this.filterValidation, this);
44526 else if(this.validationEvent !== false){
44527 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44530 if(this.selectOnFocus){
44531 this.on("focus", this.preFocus, this);
44534 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44535 this.inputEl().on("keypress", this.filterKeys, this);
44537 this.inputEl().relayEvent('keypress', this);
44540 var allowed = "0123456789";
44542 if(this.allowDecimals){
44543 allowed += this.decimalSeparator;
44546 if(this.allowNegative){
44550 if(this.thousandsDelimiter) {
44554 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44556 var keyPress = function(e){
44558 var k = e.getKey();
44560 var c = e.getCharCode();
44563 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44564 allowed.indexOf(String.fromCharCode(c)) === -1
44570 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44574 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44579 this.inputEl().on("keypress", keyPress, this);
44583 onTriggerClick : function(e)
44590 this.loadNext = false;
44592 if(this.isExpanded()){
44597 this.hasFocus = true;
44599 if(this.triggerAction == 'all') {
44600 this.doQuery(this.allQuery, true);
44604 this.doQuery(this.getRawValue());
44607 getCurrency : function()
44609 var v = this.currencyEl().getValue();
44614 restrictHeight : function()
44616 this.list.alignTo(this.currencyEl(), this.listAlign);
44617 this.list.alignTo(this.currencyEl(), this.listAlign);
44620 onViewClick : function(view, doFocus, el, e)
44622 var index = this.view.getSelectedIndexes()[0];
44624 var r = this.store.getAt(index);
44627 this.onSelect(r, index);
44631 onSelect : function(record, index){
44633 if(this.fireEvent('beforeselect', this, record, index) !== false){
44635 this.setFromCurrencyData(index > -1 ? record.data : false);
44639 this.fireEvent('select', this, record, index);
44643 setFromCurrencyData : function(o)
44647 this.lastCurrency = o;
44649 if (this.currencyField) {
44650 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44652 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44655 this.lastSelectionText = currency;
44657 //setting default currency
44658 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44659 this.setCurrency(this.defaultCurrency);
44663 this.setCurrency(currency);
44666 setFromData : function(o)
44670 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44672 this.setFromCurrencyData(c);
44677 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44679 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44682 this.setValue(value);
44686 setCurrency : function(v)
44688 this.currencyValue = v;
44691 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44696 setValue : function(v)
44698 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44704 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44706 this.inputEl().dom.value = (v == '') ? '' :
44707 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44709 if(!this.allowZero && v === '0') {
44710 this.hiddenEl().dom.value = '';
44711 this.inputEl().dom.value = '';
44718 getRawValue : function()
44720 var v = this.inputEl().getValue();
44725 getValue : function()
44727 return this.fixPrecision(this.parseValue(this.getRawValue()));
44730 parseValue : function(value)
44732 if(this.thousandsDelimiter) {
44734 r = new RegExp(",", "g");
44735 value = value.replace(r, "");
44738 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44739 return isNaN(value) ? '' : value;
44743 fixPrecision : function(value)
44745 if(this.thousandsDelimiter) {
44747 r = new RegExp(",", "g");
44748 value = value.replace(r, "");
44751 var nan = isNaN(value);
44753 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44754 return nan ? '' : value;
44756 return parseFloat(value).toFixed(this.decimalPrecision);
44759 decimalPrecisionFcn : function(v)
44761 return Math.floor(v);
44764 validateValue : function(value)
44766 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44770 var num = this.parseValue(value);
44773 this.markInvalid(String.format(this.nanText, value));
44777 if(num < this.minValue){
44778 this.markInvalid(String.format(this.minText, this.minValue));
44782 if(num > this.maxValue){
44783 this.markInvalid(String.format(this.maxText, this.maxValue));
44790 validate : function()
44792 if(this.disabled || this.allowBlank){
44797 var currency = this.getCurrency();
44799 if(this.validateValue(this.getRawValue()) && currency.length){
44804 this.markInvalid();
44808 getName: function()
44813 beforeBlur : function()
44819 var v = this.parseValue(this.getRawValue());
44826 onBlur : function()
44830 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44831 //this.el.removeClass(this.focusClass);
44834 this.hasFocus = false;
44836 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44840 var v = this.getValue();
44842 if(String(v) !== String(this.startValue)){
44843 this.fireEvent('change', this, v, this.startValue);
44846 this.fireEvent("blur", this);
44849 inputEl : function()
44851 return this.el.select('.roo-money-amount-input', true).first();
44854 currencyEl : function()
44856 return this.el.select('.roo-money-currency-input', true).first();
44859 hiddenEl : function()
44861 return this.el.select('input.hidden-number-input',true).first();
44865 * @class Roo.bootstrap.BezierSignature
44866 * @extends Roo.bootstrap.Component
44867 * Bootstrap BezierSignature class
44868 * This script refer to:
44869 * Title: Signature Pad
44871 * Availability: https://github.com/szimek/signature_pad
44874 * Create a new BezierSignature
44875 * @param {Object} config The config object
44878 Roo.bootstrap.BezierSignature = function(config){
44879 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44885 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44892 mouse_btn_down: true,
44895 * @cfg {int} canvas height
44897 canvas_height: '200px',
44900 * @cfg {float|function} Radius of a single dot.
44905 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44910 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44915 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44920 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44925 * @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.
44927 bg_color: 'rgba(0, 0, 0, 0)',
44930 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44932 dot_color: 'black',
44935 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44937 velocity_filter_weight: 0.7,
44940 * @cfg {function} Callback when stroke begin.
44945 * @cfg {function} Callback when stroke end.
44949 getAutoCreate : function()
44951 var cls = 'roo-signature column';
44954 cls += ' ' + this.cls;
44964 for(var i = 0; i < col_sizes.length; i++) {
44965 if(this[col_sizes[i]]) {
44966 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44976 cls: 'roo-signature-body',
44980 cls: 'roo-signature-body-canvas',
44981 height: this.canvas_height,
44982 width: this.canvas_width
44989 style: 'display: none'
44997 initEvents: function()
44999 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45001 var canvas = this.canvasEl();
45003 // mouse && touch event swapping...
45004 canvas.dom.style.touchAction = 'none';
45005 canvas.dom.style.msTouchAction = 'none';
45007 this.mouse_btn_down = false;
45008 canvas.on('mousedown', this._handleMouseDown, this);
45009 canvas.on('mousemove', this._handleMouseMove, this);
45010 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45012 if (window.PointerEvent) {
45013 canvas.on('pointerdown', this._handleMouseDown, this);
45014 canvas.on('pointermove', this._handleMouseMove, this);
45015 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45018 if ('ontouchstart' in window) {
45019 canvas.on('touchstart', this._handleTouchStart, this);
45020 canvas.on('touchmove', this._handleTouchMove, this);
45021 canvas.on('touchend', this._handleTouchEnd, this);
45024 Roo.EventManager.onWindowResize(this.resize, this, true);
45026 // file input event
45027 this.fileEl().on('change', this.uploadImage, this);
45034 resize: function(){
45036 var canvas = this.canvasEl().dom;
45037 var ctx = this.canvasElCtx();
45038 var img_data = false;
45040 if(canvas.width > 0) {
45041 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45043 // setting canvas width will clean img data
45046 var style = window.getComputedStyle ?
45047 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45049 var padding_left = parseInt(style.paddingLeft) || 0;
45050 var padding_right = parseInt(style.paddingRight) || 0;
45052 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45055 ctx.putImageData(img_data, 0, 0);
45059 _handleMouseDown: function(e)
45061 if (e.browserEvent.which === 1) {
45062 this.mouse_btn_down = true;
45063 this.strokeBegin(e);
45067 _handleMouseMove: function (e)
45069 if (this.mouse_btn_down) {
45070 this.strokeMoveUpdate(e);
45074 _handleMouseUp: function (e)
45076 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45077 this.mouse_btn_down = false;
45082 _handleTouchStart: function (e) {
45084 e.preventDefault();
45085 if (e.browserEvent.targetTouches.length === 1) {
45086 // var touch = e.browserEvent.changedTouches[0];
45087 // this.strokeBegin(touch);
45089 this.strokeBegin(e); // assume e catching the correct xy...
45093 _handleTouchMove: function (e) {
45094 e.preventDefault();
45095 // var touch = event.targetTouches[0];
45096 // _this._strokeMoveUpdate(touch);
45097 this.strokeMoveUpdate(e);
45100 _handleTouchEnd: function (e) {
45101 var wasCanvasTouched = e.target === this.canvasEl().dom;
45102 if (wasCanvasTouched) {
45103 e.preventDefault();
45104 // var touch = event.changedTouches[0];
45105 // _this._strokeEnd(touch);
45110 reset: function () {
45111 this._lastPoints = [];
45112 this._lastVelocity = 0;
45113 this._lastWidth = (this.min_width + this.max_width) / 2;
45114 this.canvasElCtx().fillStyle = this.dot_color;
45117 strokeMoveUpdate: function(e)
45119 this.strokeUpdate(e);
45121 if (this.throttle) {
45122 this.throttleStroke(this.strokeUpdate, this.throttle);
45125 this.strokeUpdate(e);
45129 strokeBegin: function(e)
45131 var newPointGroup = {
45132 color: this.dot_color,
45136 if (typeof this.onBegin === 'function') {
45140 this.curve_data.push(newPointGroup);
45142 this.strokeUpdate(e);
45145 strokeUpdate: function(e)
45147 var rect = this.canvasEl().dom.getBoundingClientRect();
45148 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45149 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45150 var lastPoints = lastPointGroup.points;
45151 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45152 var isLastPointTooClose = lastPoint
45153 ? point.distanceTo(lastPoint) <= this.min_distance
45155 var color = lastPointGroup.color;
45156 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45157 var curve = this.addPoint(point);
45159 this.drawDot({color: color, point: point});
45162 this.drawCurve({color: color, curve: curve});
45172 strokeEnd: function(e)
45174 this.strokeUpdate(e);
45175 if (typeof this.onEnd === 'function') {
45180 addPoint: function (point) {
45181 var _lastPoints = this._lastPoints;
45182 _lastPoints.push(point);
45183 if (_lastPoints.length > 2) {
45184 if (_lastPoints.length === 3) {
45185 _lastPoints.unshift(_lastPoints[0]);
45187 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45188 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45189 _lastPoints.shift();
45195 calculateCurveWidths: function (startPoint, endPoint) {
45196 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45197 (1 - this.velocity_filter_weight) * this._lastVelocity;
45199 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45202 start: this._lastWidth
45205 this._lastVelocity = velocity;
45206 this._lastWidth = newWidth;
45210 drawDot: function (_a) {
45211 var color = _a.color, point = _a.point;
45212 var ctx = this.canvasElCtx();
45213 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45215 this.drawCurveSegment(point.x, point.y, width);
45217 ctx.fillStyle = color;
45221 drawCurve: function (_a) {
45222 var color = _a.color, curve = _a.curve;
45223 var ctx = this.canvasElCtx();
45224 var widthDelta = curve.endWidth - curve.startWidth;
45225 var drawSteps = Math.floor(curve.length()) * 2;
45227 ctx.fillStyle = color;
45228 for (var i = 0; i < drawSteps; i += 1) {
45229 var t = i / drawSteps;
45235 var x = uuu * curve.startPoint.x;
45236 x += 3 * uu * t * curve.control1.x;
45237 x += 3 * u * tt * curve.control2.x;
45238 x += ttt * curve.endPoint.x;
45239 var y = uuu * curve.startPoint.y;
45240 y += 3 * uu * t * curve.control1.y;
45241 y += 3 * u * tt * curve.control2.y;
45242 y += ttt * curve.endPoint.y;
45243 var width = curve.startWidth + ttt * widthDelta;
45244 this.drawCurveSegment(x, y, width);
45250 drawCurveSegment: function (x, y, width) {
45251 var ctx = this.canvasElCtx();
45253 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45254 this.is_empty = false;
45259 var ctx = this.canvasElCtx();
45260 var canvas = this.canvasEl().dom;
45261 ctx.fillStyle = this.bg_color;
45262 ctx.clearRect(0, 0, canvas.width, canvas.height);
45263 ctx.fillRect(0, 0, canvas.width, canvas.height);
45264 this.curve_data = [];
45266 this.is_empty = true;
45271 return this.el.select('input',true).first();
45274 canvasEl: function()
45276 return this.el.select('canvas',true).first();
45279 canvasElCtx: function()
45281 return this.el.select('canvas',true).first().dom.getContext('2d');
45284 getImage: function(type)
45286 if(this.is_empty) {
45291 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45294 drawFromImage: function(img_src)
45296 var img = new Image();
45298 img.onload = function(){
45299 this.canvasElCtx().drawImage(img, 0, 0);
45304 this.is_empty = false;
45307 selectImage: function()
45309 this.fileEl().dom.click();
45312 uploadImage: function(e)
45314 var reader = new FileReader();
45316 reader.onload = function(e){
45317 var img = new Image();
45318 img.onload = function(){
45320 this.canvasElCtx().drawImage(img, 0, 0);
45322 img.src = e.target.result;
45325 reader.readAsDataURL(e.target.files[0]);
45328 // Bezier Point Constructor
45329 Point: (function () {
45330 function Point(x, y, time) {
45333 this.time = time || Date.now();
45335 Point.prototype.distanceTo = function (start) {
45336 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45338 Point.prototype.equals = function (other) {
45339 return this.x === other.x && this.y === other.y && this.time === other.time;
45341 Point.prototype.velocityFrom = function (start) {
45342 return this.time !== start.time
45343 ? this.distanceTo(start) / (this.time - start.time)
45350 // Bezier Constructor
45351 Bezier: (function () {
45352 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45353 this.startPoint = startPoint;
45354 this.control2 = control2;
45355 this.control1 = control1;
45356 this.endPoint = endPoint;
45357 this.startWidth = startWidth;
45358 this.endWidth = endWidth;
45360 Bezier.fromPoints = function (points, widths, scope) {
45361 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45362 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45363 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45365 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45366 var dx1 = s1.x - s2.x;
45367 var dy1 = s1.y - s2.y;
45368 var dx2 = s2.x - s3.x;
45369 var dy2 = s2.y - s3.y;
45370 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45371 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45372 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45373 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45374 var dxm = m1.x - m2.x;
45375 var dym = m1.y - m2.y;
45376 var k = l2 / (l1 + l2);
45377 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45378 var tx = s2.x - cm.x;
45379 var ty = s2.y - cm.y;
45381 c1: new scope.Point(m1.x + tx, m1.y + ty),
45382 c2: new scope.Point(m2.x + tx, m2.y + ty)
45385 Bezier.prototype.length = function () {
45390 for (var i = 0; i <= steps; i += 1) {
45392 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45393 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45395 var xdiff = cx - px;
45396 var ydiff = cy - py;
45397 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45404 Bezier.prototype.point = function (t, start, c1, c2, end) {
45405 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45406 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45407 + (3.0 * c2 * (1.0 - t) * t * t)
45408 + (end * t * t * t);
45413 throttleStroke: function(fn, wait) {
45414 if (wait === void 0) { wait = 250; }
45416 var timeout = null;
45420 var later = function () {
45421 previous = Date.now();
45423 result = fn.apply(storedContext, storedArgs);
45425 storedContext = null;
45429 return function wrapper() {
45431 for (var _i = 0; _i < arguments.length; _i++) {
45432 args[_i] = arguments[_i];
45434 var now = Date.now();
45435 var remaining = wait - (now - previous);
45436 storedContext = this;
45438 if (remaining <= 0 || remaining > wait) {
45440 clearTimeout(timeout);
45444 result = fn.apply(storedContext, storedArgs);
45446 storedContext = null;
45450 else if (!timeout) {
45451 timeout = window.setTimeout(later, remaining);