2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
365 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367 return Roo.get(document.body);
371 * Fetch the element to display the tooltip on.
372 * @return {Roo.Element} defaults to this.el
374 tooltipEl : function()
379 addxtype : function(tree,cntr)
383 cn = Roo.factory(tree);
384 //Roo.log(['addxtype', cn]);
386 cn.parentType = this.xtype; //??
387 cn.parentId = this.id;
389 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390 if (typeof(cn.container_method) == 'string') {
391 cntr = cn.container_method;
395 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
397 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
399 var build_from_html = Roo.XComponent.build_from_html;
401 var is_body = (tree.xtype == 'Body') ;
403 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405 var self_cntr_el = Roo.get(this[cntr](false));
407 // do not try and build conditional elements
408 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414 return this.addxtypeChild(tree,cntr, is_body);
417 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
420 return this.addxtypeChild(Roo.apply({}, tree),cntr);
423 Roo.log('skipping render');
429 if (!build_from_html) {
433 // this i think handles overlaying multiple children of the same type
434 // with the sam eelement.. - which might be buggy..
436 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
442 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453 addxtypeChild : function (tree, cntr, is_body)
455 Roo.debug && Roo.log('addxtypeChild:' + cntr);
457 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
460 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461 (typeof(tree['flexy:foreach']) != 'undefined');
465 skip_children = false;
466 // render the element if it's not BODY.
469 // if parent was disabled, then do not try and create the children..
470 if(!this[cntr](true)){
475 cn = Roo.factory(tree);
477 cn.parentType = this.xtype; //??
478 cn.parentId = this.id;
480 var build_from_html = Roo.XComponent.build_from_html;
483 // does the container contain child eleemnts with 'xtype' attributes.
484 // that match this xtype..
485 // note - when we render we create these as well..
486 // so we should check to see if body has xtype set.
487 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489 var self_cntr_el = Roo.get(this[cntr](false));
490 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492 //Roo.log(Roo.XComponent.build_from_html);
493 //Roo.log("got echild:");
496 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497 // and are not displayed -this causes this to use up the wrong element when matching.
498 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
501 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
508 //echild.dom.removeAttribute('xtype');
510 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511 Roo.debug && Roo.log(self_cntr_el);
512 Roo.debug && Roo.log(echild);
513 Roo.debug && Roo.log(cn);
519 // if object has flexy:if - then it may or may not be rendered.
520 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
521 // skip a flexy if element.
522 Roo.debug && Roo.log('skipping render');
523 Roo.debug && Roo.log(tree);
525 Roo.debug && Roo.log('skipping all children');
526 skip_children = true;
531 // actually if flexy:foreach is found, we really want to create
532 // multiple copies here...
534 //Roo.log(this[cntr]());
535 // some elements do not have render methods.. like the layouts...
537 if(this[cntr](true) === false){
542 cn.render && cn.render(this[cntr](true));
545 // then add the element..
552 if (typeof (tree.menu) != 'undefined') {
553 tree.menu.parentType = cn.xtype;
554 tree.menu.triggerEl = cn.el;
555 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559 if (!tree.items || !tree.items.length) {
561 //Roo.log(["no children", this]);
566 var items = tree.items;
569 //Roo.log(items.length);
571 if (!skip_children) {
572 for(var i =0;i < items.length;i++) {
573 // Roo.log(['add child', items[i]]);
574 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
580 //Roo.log("fire childrenrendered");
582 cn.fireEvent('childrenrendered', this);
588 * Set the element that will be used to show or hide
590 setVisibilityEl : function(el)
592 this.visibilityEl = el;
596 * Get the element that will be used to show or hide
598 getVisibilityEl : function()
600 if (typeof(this.visibilityEl) == 'object') {
601 return this.visibilityEl;
604 if (typeof(this.visibilityEl) == 'string') {
605 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612 * Show a component - removes 'hidden' class
616 if(!this.getVisibilityEl()){
620 this.getVisibilityEl().removeClass(['hidden','d-none']);
622 this.fireEvent('show', this);
627 * Hide a component - adds 'hidden' class
631 if(!this.getVisibilityEl()){
635 this.getVisibilityEl().addClass(['hidden','d-none']);
637 this.fireEvent('hide', this);
650 * @class Roo.bootstrap.Element
651 * @extends Roo.bootstrap.Component
652 * Bootstrap Element class
653 * @cfg {String} html contents of the element
654 * @cfg {String} tag tag of the element
655 * @cfg {String} cls class of the element
656 * @cfg {Boolean} preventDefault (true|false) default false
657 * @cfg {Boolean} clickable (true|false) default false
658 * @cfg {String} role default blank - set to button to force cursor pointer
662 * Create a new Element
663 * @param {Object} config The config object
666 Roo.bootstrap.Element = function(config){
667 Roo.bootstrap.Element.superclass.constructor.call(this, config);
673 * When a element is chick
674 * @param {Roo.bootstrap.Element} this
675 * @param {Roo.EventObject} e
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
688 preventDefault: false,
693 getAutoCreate : function(){
697 // cls: this.cls, double assign in parent class Component.js :: onRender
700 if (this.role !== false) {
701 cfg.role = this.role;
707 initEvents: function()
709 Roo.bootstrap.Element.superclass.initEvents.call(this);
712 this.el.on('click', this.onClick, this);
718 onClick : function(e)
720 if(this.preventDefault){
724 this.fireEvent('click', this, e); // why was this double click before?
732 getValue : function()
734 return this.el.dom.innerHTML;
737 setValue : function(value)
739 this.el.dom.innerHTML = value;
754 * @class Roo.bootstrap.DropTarget
755 * @extends Roo.bootstrap.Element
756 * Bootstrap DropTarget class
758 * @cfg {string} name dropable name
761 * Create a new Dropable Area
762 * @param {Object} config The config object
765 Roo.bootstrap.DropTarget = function(config){
766 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
772 * When a element is chick
773 * @param {Roo.bootstrap.Element} this
774 * @param {Roo.EventObject} e
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
783 getAutoCreate : function(){
788 initEvents: function()
790 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
794 drop : this.dragDrop.createDelegate(this),
795 enter : this.dragEnter.createDelegate(this),
796 out : this.dragOut.createDelegate(this),
797 over : this.dragOver.createDelegate(this)
801 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
804 dragDrop : function(source,e,data)
806 // user has to decide how to impliment this.
809 //this.fireEvent('drop', this, source, e ,data);
813 dragEnter : function(n, dd, e, data)
815 // probably want to resize the element to match the dropped element..
817 this.originalSize = this.el.getSize();
818 this.el.setSize( n.el.getSize());
819 this.dropZone.DDM.refreshCache(this.name);
820 Roo.log([n, dd, e, data]);
823 dragOut : function(value)
825 // resize back to normal
827 this.el.setSize(this.originalSize);
828 this.dropZone.resetConstraints();
831 dragOver : function()
848 * @class Roo.bootstrap.Body
849 * @extends Roo.bootstrap.Component
850 * Bootstrap Body class
854 * @param {Object} config The config object
857 Roo.bootstrap.Body = function(config){
859 config = config || {};
861 Roo.bootstrap.Body.superclass.constructor.call(this, config);
862 this.el = Roo.get(config.el ? config.el : document.body );
863 if (this.cls && this.cls.length) {
864 Roo.get(document.body).addClass(this.cls);
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
870 is_body : true,// just to make sure it's constructed?
875 onRender : function(ct, position)
877 /* Roo.log("Roo.bootstrap.Body - onRender");
878 if (this.cls && this.cls.length) {
879 Roo.get(document.body).addClass(this.cls);
898 * @class Roo.bootstrap.ButtonGroup
899 * @extends Roo.bootstrap.Component
900 * Bootstrap ButtonGroup class
901 * @cfg {String} size lg | sm | xs (default empty normal)
902 * @cfg {String} align vertical | justified (default none)
903 * @cfg {String} direction up | down (default down)
904 * @cfg {Boolean} toolbar false | true
905 * @cfg {Boolean} btn true | false
910 * @param {Object} config The config object
913 Roo.bootstrap.ButtonGroup = function(config){
914 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
925 getAutoCreate : function(){
931 cfg.html = this.html || cfg.html;
942 if (['vertical','justified'].indexOf(this.align)!==-1) {
943 cfg.cls = 'btn-group-' + this.align;
945 if (this.align == 'justified') {
946 console.log(this.items);
950 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951 cfg.cls += ' btn-group-' + this.size;
954 if (this.direction == 'up') {
955 cfg.cls += ' dropup' ;
961 * Add a button to the group (similar to NavItem API.)
963 addItem : function(cfg)
965 var cn = new Roo.bootstrap.Button(cfg);
967 cn.parentId = this.id;
968 cn.onRender(this.el, null);
982 * @class Roo.bootstrap.Button
983 * @extends Roo.bootstrap.Component
984 * Bootstrap Button class
985 * @cfg {String} html The button content
986 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989 * @cfg {String} size (lg|sm|xs)
990 * @cfg {String} tag (a|input|submit)
991 * @cfg {String} href empty or href
992 * @cfg {Boolean} disabled default false;
993 * @cfg {Boolean} isClose default false;
994 * @cfg {String} glyphicon depricated - use fa
995 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996 * @cfg {String} badge text for badge
997 * @cfg {String} theme (default|glow)
998 * @cfg {Boolean} inverse dark themed version
999 * @cfg {Boolean} toggle is it a slidy toggle button
1000 * @cfg {Boolean} pressed default null - if the button ahs active state
1001 * @cfg {String} ontext text for on slidy toggle state
1002 * @cfg {String} offtext text for off slidy toggle state
1003 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1004 * @cfg {Boolean} removeClass remove the standard class..
1005 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1006 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1009 * Create a new button
1010 * @param {Object} config The config object
1014 Roo.bootstrap.Button = function(config){
1015 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1021 * When a button is pressed
1022 * @param {Roo.bootstrap.Button} btn
1023 * @param {Roo.EventObject} e
1028 * When a button is double clicked
1029 * @param {Roo.bootstrap.Button} btn
1030 * @param {Roo.EventObject} e
1035 * After the button has been toggles
1036 * @param {Roo.bootstrap.Button} btn
1037 * @param {Roo.EventObject} e
1038 * @param {boolean} pressed (also available as button.pressed)
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1065 preventDefault: true,
1074 getAutoCreate : function(){
1082 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084 this.tag = 'button';
1088 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090 if (this.toggle == true) {
1093 cls: 'slider-frame roo-button',
1097 'data-on-text':'ON',
1098 'data-off-text':'OFF',
1099 cls: 'slider-button',
1104 // why are we validating the weights?
1105 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106 cfg.cls += ' ' + this.weight;
1113 cfg.cls += ' close';
1115 cfg["aria-hidden"] = true;
1117 cfg.html = "×";
1123 if (this.theme==='default') {
1124 cfg.cls = 'btn roo-button';
1126 //if (this.parentType != 'Navbar') {
1127 this.weight = this.weight.length ? this.weight : 'default';
1129 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133 cfg.cls += ' btn-' + outline + weight;
1134 if (this.weight == 'default') {
1136 cfg.cls += ' btn-' + this.weight;
1139 } else if (this.theme==='glow') {
1142 cfg.cls = 'btn-glow roo-button';
1144 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146 cfg.cls += ' ' + this.weight;
1152 this.cls += ' inverse';
1156 if (this.active || this.pressed === true) {
1157 cfg.cls += ' active';
1160 if (this.disabled) {
1161 cfg.disabled = 'disabled';
1165 Roo.log('changing to ul' );
1167 this.glyphicon = 'caret';
1168 if (Roo.bootstrap.version == 4) {
1169 this.fa = 'caret-down';
1174 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176 //gsRoo.log(this.parentType);
1177 if (this.parentType === 'Navbar' && !this.parent().bar) {
1178 Roo.log('changing to li?');
1187 href : this.href || '#'
1190 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1191 cfg.cls += ' dropdown';
1198 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1200 if (this.glyphicon) {
1201 cfg.html = ' ' + cfg.html;
1206 cls: 'glyphicon glyphicon-' + this.glyphicon
1211 cfg.html = ' ' + cfg.html;
1216 cls: 'fa fas fa-' + this.fa
1226 // cfg.cls='btn roo-button';
1230 var value = cfg.html;
1235 cls: 'glyphicon glyphicon-' + this.glyphicon,
1242 cls: 'fa fas fa-' + this.fa,
1247 var bw = this.badge_weight.length ? this.badge_weight :
1248 (this.weight.length ? this.weight : 'secondary');
1249 bw = bw == 'default' ? 'secondary' : bw;
1255 cls: 'badge badge-' + bw,
1264 cfg.cls += ' dropdown';
1265 cfg.html = typeof(cfg.html) != 'undefined' ?
1266 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1269 if (cfg.tag !== 'a' && this.href !== '') {
1270 throw "Tag must be a to set href.";
1271 } else if (this.href.length > 0) {
1272 cfg.href = this.href;
1275 if(this.removeClass){
1280 cfg.target = this.target;
1285 initEvents: function() {
1286 // Roo.log('init events?');
1287 // Roo.log(this.el.dom);
1290 if (typeof (this.menu) != 'undefined') {
1291 this.menu.parentType = this.xtype;
1292 this.menu.triggerEl = this.el;
1293 this.addxtype(Roo.apply({}, this.menu));
1297 if (this.el.hasClass('roo-button')) {
1298 this.el.on('click', this.onClick, this);
1299 this.el.on('dblclick', this.onDblClick, this);
1301 this.el.select('.roo-button').on('click', this.onClick, this);
1302 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1306 if(this.removeClass){
1307 this.el.on('click', this.onClick, this);
1310 if (this.group === true) {
1311 if (this.pressed === false || this.pressed === true) {
1314 this.pressed = false;
1315 this.setActive(this.pressed);
1320 this.el.enableDisplayMode();
1323 onClick : function(e)
1325 if (this.disabled) {
1329 Roo.log('button on click ');
1330 if(this.preventDefault){
1339 this.setActive(true);
1340 var pi = this.parent().items;
1341 for (var i = 0;i < pi.length;i++) {
1342 if (this == pi[i]) {
1345 if (pi[i].el.hasClass('roo-button')) {
1346 pi[i].setActive(false);
1349 this.fireEvent('click', this, e);
1353 if (this.pressed === true || this.pressed === false) {
1354 this.toggleActive(e);
1358 this.fireEvent('click', this, e);
1360 onDblClick: function(e)
1362 if (this.disabled) {
1365 if(this.preventDefault){
1368 this.fireEvent('dblclick', this, e);
1371 * Enables this button
1375 this.disabled = false;
1376 this.el.removeClass('disabled');
1377 this.el.dom.removeAttribute("disabled");
1381 * Disable this button
1383 disable : function()
1385 this.disabled = true;
1386 this.el.addClass('disabled');
1387 this.el.attr("disabled", "disabled")
1390 * sets the active state on/off,
1391 * @param {Boolean} state (optional) Force a particular state
1393 setActive : function(v) {
1395 this.el[v ? 'addClass' : 'removeClass']('active');
1399 * toggles the current active state
1401 toggleActive : function(e)
1403 this.setActive(!this.pressed); // this modifies pressed...
1404 this.fireEvent('toggle', this, e, this.pressed);
1407 * get the current active state
1408 * @return {boolean} true if it's active
1410 isActive : function()
1412 return this.el.hasClass('active');
1415 * set the text of the first selected button
1417 setText : function(str)
1419 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1422 * get the text of the first selected button
1424 getText : function()
1426 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1429 setWeight : function(str)
1431 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434 var outline = this.outline ? 'outline-' : '';
1435 if (str == 'default') {
1436 this.el.addClass('btn-default btn-outline-secondary');
1439 this.el.addClass('btn-' + outline + str);
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446 Roo.bootstrap.Button.weights = [
1466 * @class Roo.bootstrap.Column
1467 * @extends Roo.bootstrap.Component
1468 * Bootstrap Column class
1469 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1479 * @cfg {Boolean} hidden (true|false) hide the element
1480 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481 * @cfg {String} fa (ban|check|...) font awesome icon
1482 * @cfg {Number} fasize (1|2|....) font awsome size
1484 * @cfg {String} icon (info-sign|check|...) glyphicon name
1486 * @cfg {String} html content of column.
1489 * Create a new Column
1490 * @param {Object} config The config object
1493 Roo.bootstrap.Column = function(config){
1494 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1515 getAutoCreate : function(){
1516 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1524 var sizes = ['xs','sm','md','lg'];
1525 sizes.map(function(size ,ix){
1526 //Roo.log( size + ':' + settings[size]);
1528 if (settings[size+'off'] !== false) {
1529 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1532 if (settings[size] === false) {
1536 if (!settings[size]) { // 0 = hidden
1537 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539 for (var i = ix; i > -1; i--) {
1540 cfg.cls += ' d-' + sizes[i] + '-none';
1546 cfg.cls += ' col-' + size + '-' + settings[size] + (
1547 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1553 cfg.cls += ' hidden';
1556 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557 cfg.cls +=' alert alert-' + this.alert;
1561 if (this.html.length) {
1562 cfg.html = this.html;
1566 if (this.fasize > 1) {
1567 fasize = ' fa-' + this.fasize + 'x';
1569 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1574 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1593 * @class Roo.bootstrap.Container
1594 * @extends Roo.bootstrap.Component
1595 * Bootstrap Container class
1596 * @cfg {Boolean} jumbotron is it a jumbotron element
1597 * @cfg {String} html content of element
1598 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1600 * @cfg {String} header content of header (for panel)
1601 * @cfg {String} footer content of footer (for panel)
1602 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603 * @cfg {String} tag (header|aside|section) type of HTML tag.
1604 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605 * @cfg {String} fa font awesome icon
1606 * @cfg {String} icon (info-sign|check|...) glyphicon name
1607 * @cfg {Boolean} hidden (true|false) hide the element
1608 * @cfg {Boolean} expandable (true|false) default false
1609 * @cfg {Boolean} expanded (true|false) default true
1610 * @cfg {String} rheader contet on the right of header
1611 * @cfg {Boolean} clickable (true|false) default false
1615 * Create a new Container
1616 * @param {Object} config The config object
1619 Roo.bootstrap.Container = function(config){
1620 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1626 * After the panel has been expand
1628 * @param {Roo.bootstrap.Container} this
1633 * After the panel has been collapsed
1635 * @param {Roo.bootstrap.Container} this
1640 * When a element is chick
1641 * @param {Roo.bootstrap.Container} this
1642 * @param {Roo.EventObject} e
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1666 getChildContainer : function() {
1672 if (this.panel.length) {
1673 return this.el.select('.panel-body',true).first();
1680 getAutoCreate : function(){
1683 tag : this.tag || 'div',
1687 if (this.jumbotron) {
1688 cfg.cls = 'jumbotron';
1693 // - this is applied by the parent..
1695 // cfg.cls = this.cls + '';
1698 if (this.sticky.length) {
1700 var bd = Roo.get(document.body);
1701 if (!bd.hasClass('bootstrap-sticky')) {
1702 bd.addClass('bootstrap-sticky');
1703 Roo.select('html',true).setStyle('height', '100%');
1706 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1710 if (this.well.length) {
1711 switch (this.well) {
1714 cfg.cls +=' well well-' +this.well;
1723 cfg.cls += ' hidden';
1727 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728 cfg.cls +=' alert alert-' + this.alert;
1733 if (this.panel.length) {
1734 cfg.cls += ' panel panel-' + this.panel;
1736 if (this.header.length) {
1740 if(this.expandable){
1742 cfg.cls = cfg.cls + ' expandable';
1746 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1754 cls : 'panel-title',
1755 html : (this.expandable ? ' ' : '') + this.header
1759 cls: 'panel-header-right',
1765 cls : 'panel-heading',
1766 style : this.expandable ? 'cursor: pointer' : '',
1774 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1779 if (this.footer.length) {
1781 cls : 'panel-footer',
1790 body.html = this.html || cfg.html;
1791 // prefix with the icons..
1793 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1796 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1801 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802 cfg.cls = 'container';
1808 initEvents: function()
1810 if(this.expandable){
1811 var headerEl = this.headerEl();
1814 headerEl.on('click', this.onToggleClick, this);
1819 this.el.on('click', this.onClick, this);
1824 onToggleClick : function()
1826 var headerEl = this.headerEl();
1842 if(this.fireEvent('expand', this)) {
1844 this.expanded = true;
1846 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848 this.el.select('.panel-body',true).first().removeClass('hide');
1850 var toggleEl = this.toggleEl();
1856 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1861 collapse : function()
1863 if(this.fireEvent('collapse', this)) {
1865 this.expanded = false;
1867 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868 this.el.select('.panel-body',true).first().addClass('hide');
1870 var toggleEl = this.toggleEl();
1876 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1880 toggleEl : function()
1882 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1886 return this.el.select('.panel-heading .fa',true).first();
1889 headerEl : function()
1891 if(!this.el || !this.panel.length || !this.header.length){
1895 return this.el.select('.panel-heading',true).first()
1900 if(!this.el || !this.panel.length){
1904 return this.el.select('.panel-body',true).first()
1907 titleEl : function()
1909 if(!this.el || !this.panel.length || !this.header.length){
1913 return this.el.select('.panel-title',true).first();
1916 setTitle : function(v)
1918 var titleEl = this.titleEl();
1924 titleEl.dom.innerHTML = v;
1927 getTitle : function()
1930 var titleEl = this.titleEl();
1936 return titleEl.dom.innerHTML;
1939 setRightTitle : function(v)
1941 var t = this.el.select('.panel-header-right',true).first();
1947 t.dom.innerHTML = v;
1950 onClick : function(e)
1954 this.fireEvent('click', this, e);
1961 * This is BS4's Card element.. - similar to our containers probably..
1965 * @class Roo.bootstrap.Card
1966 * @extends Roo.bootstrap.Component
1967 * Bootstrap Card class
1970 * possible... may not be implemented..
1971 * @cfg {String} header_image src url of image.
1972 * @cfg {String|Object} header
1973 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1976 * @cfg {String} title
1977 * @cfg {String} subtitle
1978 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979 * @cfg {String} footer
1981 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983 * @cfg {String} margin (0|1|2|3|4|5|auto)
1984 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991 * @cfg {String} padding (0|1|2|3|4|5)
1992 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994 * @cfg {String} padding_left (0|1|2|3|4|5)
1995 * @cfg {String} padding_right (0|1|2|3|4|5)
1996 * @cfg {String} padding_x (0|1|2|3|4|5)
1997 * @cfg {String} padding_y (0|1|2|3|4|5)
1999 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005 * @config {Boolean} dragable if this card can be dragged.
2006 * @config {String} drag_group group for drag
2007 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2008 * @config {String} drop_group group for drag
2010 * @config {Boolean} collapsable can the body be collapsed.
2011 * @config {Boolean} collapsed is the body collapsed when rendered...
2012 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013 * @config {Boolean} rotated is the body rotated when rendered...
2016 * Create a new Container
2017 * @param {Object} config The config object
2020 Roo.bootstrap.Card = function(config){
2021 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2027 * When a element a card is dropped
2028 * @param {Roo.bootstrap.Card} this
2031 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032 * @param {String} position 'above' or 'below'
2033 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2039 * When a element a card is rotate
2040 * @param {Roo.bootstrap.Card} this
2041 * @param {Roo.Element} n the node being dropped?
2042 * @param {Boolean} rotate status
2047 * When a card element is dragged over ready to drop (return false to block dropable)
2048 * @param {Roo.bootstrap.Card} this
2049 * @param {Object} data from dragdrop
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2062 margin: '', /// may be better in component?
2092 collapsable : false,
2101 childContainer : false,
2102 dropEl : false, /// the dom placeholde element that indicates drop location.
2103 containerEl: false, // body container
2104 bodyEl: false, // card-body
2105 headerContainerEl : false, //
2107 header_imageEl : false,
2110 layoutCls : function()
2114 Roo.log(this.margin_bottom.length);
2115 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2121 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2126 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2132 // more generic support?
2140 // Roo.log("Call onRender: " + this.xtype);
2141 /* We are looking at something like this.
2143 <img src="..." class="card-img-top" alt="...">
2144 <div class="card-body">
2145 <h5 class="card-title">Card title</h5>
2146 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148 >> this bit is really the body...
2149 <div> << we will ad dthis in hopefully it will not break shit.
2151 ** card text does not actually have any styling...
2153 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2156 <a href="#" class="card-link">Card link</a>
2159 <div class="card-footer">
2160 <small class="text-muted">Last updated 3 mins ago</small>
2164 getAutoCreate : function(){
2172 if (this.weight.length && this.weight != 'light') {
2173 cfg.cls += ' text-white';
2175 cfg.cls += ' text-dark'; // need as it's nested..
2177 if (this.weight.length) {
2178 cfg.cls += ' bg-' + this.weight;
2181 cfg.cls += ' ' + this.layoutCls();
2184 var hdr_ctr = false;
2185 if (this.header.length) {
2187 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2196 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2202 if (this.collapsable) {
2205 cls : 'd-block user-select-none',
2209 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2214 hdr.cn.push(hdr_ctr);
2219 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2224 if (this.header_image.length) {
2227 cls : 'card-img-top',
2228 src: this.header_image // escape?
2233 cls : 'card-img-top d-none'
2239 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2243 if (this.collapsable || this.rotateable) {
2246 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2253 if (this.title.length) {
2257 src: this.title // escape?
2261 if (this.subtitle.length) {
2265 src: this.subtitle // escape?
2271 cls : 'roo-card-body-ctr'
2274 if (this.html.length) {
2280 // fixme ? handle objects?
2282 if (this.footer.length) {
2285 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2290 cfg.cn.push({cls : 'card-footer d-none'});
2299 getCardHeader : function()
2301 var ret = this.el.select('.card-header',true).first();
2302 if (ret.hasClass('d-none')) {
2303 ret.removeClass('d-none');
2308 getCardFooter : function()
2310 var ret = this.el.select('.card-footer',true).first();
2311 if (ret.hasClass('d-none')) {
2312 ret.removeClass('d-none');
2317 getCardImageTop : function()
2319 var ret = this.header_imageEl;
2320 if (ret.hasClass('d-none')) {
2321 ret.removeClass('d-none');
2327 getChildContainer : function()
2333 return this.el.select('.roo-card-body-ctr',true).first();
2336 initEvents: function()
2338 this.bodyEl = this.el.select('.card-body',true).first();
2339 this.containerEl = this.getChildContainer();
2341 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342 containerScroll: true,
2343 ddGroup: this.drag_group || 'default_card_drag_group'
2345 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347 if (this.dropable) {
2348 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349 containerScroll: true,
2350 ddGroup: this.drop_group || 'default_card_drag_group'
2352 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2359 if (this.collapsable) {
2360 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362 if (this.rotateable) {
2363 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367 this.footerEl = this.el.select('.card-footer',true).first();
2368 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370 this.headerEl = this.el.select('.card-header',true).first();
2373 this.el.addClass('roo-card-rotated');
2374 this.fireEvent('rotate', this, true);
2376 this.header_imageEl = this.el.select('.card-img-top',true).first();
2377 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2380 getDragData : function(e)
2382 var target = this.getEl();
2384 //this.handleSelection(e);
2389 nodes: this.getEl(),
2394 dragData.ddel = target.dom ; // the div element
2395 Roo.log(target.getWidth( ));
2396 dragData.ddel.style.width = target.getWidth() + 'px';
2403 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2404 * whole Element becomes the target, and this causes the drop gesture to append.
2406 * Returns an object:
2409 position : 'below' or 'above'
2410 card : relateive to card OBJECT (or true for no cards listed)
2411 items_n : relative to nth item in list
2412 card_n : relative to nth card in list
2417 getTargetFromEvent : function(e, dragged_card_el)
2419 var target = e.getTarget();
2420 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421 target = target.parentNode;
2432 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433 // see if target is one of the 'cards'...
2436 //Roo.log(this.items.length);
2439 var last_card_n = 0;
2441 for (var i = 0;i< this.items.length;i++) {
2443 if (!this.items[i].el.hasClass('card')) {
2446 pos = this.getDropPoint(e, this.items[i].el.dom);
2448 cards_len = ret.cards.length;
2449 //Roo.log(this.items[i].el.dom.id);
2450 ret.cards.push(this.items[i]);
2452 if (ret.card_n < 0 && pos == 'above') {
2453 ret.position = cards_len > 0 ? 'below' : pos;
2454 ret.items_n = i > 0 ? i - 1 : 0;
2455 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2456 ret.card = ret.cards[ret.card_n];
2459 if (!ret.cards.length) {
2461 ret.position = 'below';
2465 // could not find a card.. stick it at the end..
2466 if (ret.card_n < 0) {
2467 ret.card_n = last_card_n;
2468 ret.card = ret.cards[last_card_n];
2469 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470 ret.position = 'below';
2473 if (this.items[ret.items_n].el == dragged_card_el) {
2477 if (ret.position == 'below') {
2478 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480 if (card_after && card_after.el == dragged_card_el) {
2487 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489 if (card_before && card_before.el == dragged_card_el) {
2496 onNodeEnter : function(n, dd, e, data){
2499 onNodeOver : function(n, dd, e, data)
2502 var target_info = this.getTargetFromEvent(e,data.source.el);
2503 if (target_info === false) {
2504 this.dropPlaceHolder('hide');
2507 Roo.log(['getTargetFromEvent', target_info ]);
2510 if (this.fireEvent('cardover', this, [ data ]) === false) {
2514 this.dropPlaceHolder('show', target_info,data);
2518 onNodeOut : function(n, dd, e, data){
2519 this.dropPlaceHolder('hide');
2522 onNodeDrop : function(n, dd, e, data)
2525 // call drop - return false if
2527 // this could actually fail - if the Network drops..
2528 // we will ignore this at present..- client should probably reload
2529 // the whole set of cards if stuff like that fails.
2532 var info = this.getTargetFromEvent(e,data.source.el);
2533 if (info === false) {
2536 this.dropPlaceHolder('hide');
2540 this.acceptCard(data.source, info.position, info.card, info.items_n);
2544 firstChildCard : function()
2546 for (var i = 0;i< this.items.length;i++) {
2548 if (!this.items[i].el.hasClass('card')) {
2551 return this.items[i];
2553 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2558 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2560 acceptCard : function(move_card, position, next_to_card )
2562 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2566 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568 move_card.parent().removeCard(move_card);
2571 var dom = move_card.el.dom;
2572 dom.style.width = ''; // clear with - which is set by drag.
2574 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575 var cardel = next_to_card.el.dom;
2577 if (position == 'above' ) {
2578 cardel.parentNode.insertBefore(dom, cardel);
2579 } else if (cardel.nextSibling) {
2580 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582 cardel.parentNode.append(dom);
2585 // card container???
2586 this.containerEl.dom.append(dom);
2589 //FIXME HANDLE card = true
2591 // add this to the correct place in items.
2593 // remove Card from items.
2596 if (this.items.length) {
2598 //Roo.log([info.items_n, info.position, this.items.length]);
2599 for (var i =0; i < this.items.length; i++) {
2600 if (i == to_items_n && position == 'above') {
2601 nitems.push(move_card);
2603 nitems.push(this.items[i]);
2604 if (i == to_items_n && position == 'below') {
2605 nitems.push(move_card);
2608 this.items = nitems;
2609 Roo.log(this.items);
2611 this.items.push(move_card);
2614 move_card.parentId = this.id;
2620 removeCard : function(c)
2622 this.items = this.items.filter(function(e) { return e != c });
2625 dom.parentNode.removeChild(dom);
2626 dom.style.width = ''; // clear with - which is set by drag.
2631 /** Decide whether to drop above or below a View node. */
2632 getDropPoint : function(e, n, dd)
2637 if (n == this.containerEl.dom) {
2640 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641 var c = t + (b - t) / 2;
2642 var y = Roo.lib.Event.getPageY(e);
2649 onToggleCollapse : function(e)
2651 if (this.collapsed) {
2652 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653 this.collapsableEl.addClass('show');
2654 this.collapsed = false;
2657 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658 this.collapsableEl.removeClass('show');
2659 this.collapsed = true;
2664 onToggleRotate : function(e)
2666 this.collapsableEl.removeClass('show');
2667 this.footerEl.removeClass('d-none');
2668 this.el.removeClass('roo-card-rotated');
2669 this.el.removeClass('d-none');
2672 this.collapsableEl.addClass('show');
2673 this.rotated = false;
2674 this.fireEvent('rotate', this, this.rotated);
2677 this.el.addClass('roo-card-rotated');
2678 this.footerEl.addClass('d-none');
2679 this.el.select('.roo-collapsable').removeClass('show');
2681 this.rotated = true;
2682 this.fireEvent('rotate', this, this.rotated);
2686 dropPlaceHolder: function (action, info, data)
2688 if (this.dropEl === false) {
2689 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2693 this.dropEl.removeClass(['d-none', 'd-block']);
2694 if (action == 'hide') {
2696 this.dropEl.addClass('d-none');
2699 // FIXME - info.card == true!!!
2700 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702 if (info.card !== true) {
2703 var cardel = info.card.el.dom;
2705 if (info.position == 'above') {
2706 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707 } else if (cardel.nextSibling) {
2708 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710 cardel.parentNode.append(this.dropEl.dom);
2713 // card container???
2714 this.containerEl.dom.append(this.dropEl.dom);
2717 this.dropEl.addClass('d-block roo-card-dropzone');
2719 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2726 setHeaderText: function(html)
2729 if (this.headerContainerEl) {
2730 this.headerContainerEl.dom.innerHTML = html;
2733 onHeaderImageLoad : function(ev, he)
2735 if (!this.header_image_fit_square) {
2739 var hw = he.naturalHeight / he.naturalWidth;
2742 //var w = he.dom.naturalWidth;
2745 he.style.position = 'relative';
2747 var nw = (ww * (1/hw));
2748 Roo.get(he).setSize( ww * (1/hw), ww);
2749 he.style.left = ((ww - nw)/ 2) + 'px';
2750 he.style.position = 'relative';
2761 * Card header - holder for the card header elements.
2766 * @class Roo.bootstrap.CardHeader
2767 * @extends Roo.bootstrap.Element
2768 * Bootstrap CardHeader class
2770 * Create a new Card Header - that you can embed children into
2771 * @param {Object} config The config object
2774 Roo.bootstrap.CardHeader = function(config){
2775 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2781 container_method : 'getCardHeader'
2794 * Card footer - holder for the card footer elements.
2799 * @class Roo.bootstrap.CardFooter
2800 * @extends Roo.bootstrap.Element
2801 * Bootstrap CardFooter class
2803 * Create a new Card Footer - that you can embed children into
2804 * @param {Object} config The config object
2807 Roo.bootstrap.CardFooter = function(config){
2808 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2814 container_method : 'getCardFooter'
2827 * Card header - holder for the card header elements.
2832 * @class Roo.bootstrap.CardImageTop
2833 * @extends Roo.bootstrap.Element
2834 * Bootstrap CardImageTop class
2836 * Create a new Card Image Top container
2837 * @param {Object} config The config object
2840 Roo.bootstrap.CardImageTop = function(config){
2841 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2847 container_method : 'getCardImageTop'
2862 * @class Roo.bootstrap.ButtonUploader
2863 * @extends Roo.bootstrap.Button
2864 * Bootstrap Button Uploader class - it's a button which when you add files to it
2867 * @cfg {Number} errorTimeout default 3000
2868 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2869 * @cfg {Array} html The button text.
2870 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2873 * Create a new CardUploader
2874 * @param {Object} config The config object
2877 Roo.bootstrap.ButtonUploader = function(config){
2881 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2887 * @event beforeselect
2888 * When button is pressed, before show upload files dialog is shown
2889 * @param {Roo.bootstrap.UploaderButton} this
2892 'beforeselect' : true,
2894 * @event fired when files have been selected,
2895 * When a the download link is clicked
2896 * @param {Roo.bootstrap.UploaderButton} this
2897 * @param {Array} Array of files that have been uploaded
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2907 errorTimeout : 3000,
2911 fileCollection : false,
2916 getAutoCreate : function()
2921 cls : 'd-none roo-card-upload-selector'
2924 if (this.multiple) {
2925 im.multiple = 'multiple';
2931 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2941 initEvents : function()
2944 Roo.bootstrap.Button.prototype.initEvents.call(this);
2950 this.urlAPI = (window.createObjectURL && window) ||
2951 (window.URL && URL.revokeObjectURL && URL) ||
2952 (window.webkitURL && webkitURL);
2957 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959 this.selectorEl.on('change', this.onFileSelected, this);
2966 onClick : function(e)
2970 if ( this.fireEvent('beforeselect', this) === false) {
2974 this.selectorEl.dom.click();
2978 onFileSelected : function(e)
2982 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2985 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986 this.selectorEl.dom.value = '';// hopefully reset..
2988 this.fireEvent('uploaded', this, files );
2996 * addCard - add an Attachment to the uploader
2997 * @param data - the data about the image to upload
3001 title : "Title of file",
3002 is_uploaded : false,
3003 src : "http://.....",
3004 srcfile : { the File upload object },
3005 mimetype : file.type,
3008 .. any other data...
3033 * @class Roo.bootstrap.Img
3034 * @extends Roo.bootstrap.Component
3035 * Bootstrap Img class
3036 * @cfg {Boolean} imgResponsive false | true
3037 * @cfg {String} border rounded | circle | thumbnail
3038 * @cfg {String} src image source
3039 * @cfg {String} alt image alternative text
3040 * @cfg {String} href a tag href
3041 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042 * @cfg {String} xsUrl xs image source
3043 * @cfg {String} smUrl sm image source
3044 * @cfg {String} mdUrl md image source
3045 * @cfg {String} lgUrl lg image source
3046 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3049 * Create a new Input
3050 * @param {Object} config The config object
3053 Roo.bootstrap.Img = function(config){
3054 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3060 * The img click event for the img.
3061 * @param {Roo.EventObject} e
3066 * The when any image loads
3067 * @param {Roo.EventObject} e
3073 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3075 imgResponsive: true,
3084 backgroundContain : false,
3086 getAutoCreate : function()
3088 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3089 return this.createSingleImg();
3094 cls: 'roo-image-responsive-group',
3099 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3101 if(!_this[size + 'Url']){
3107 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3108 html: _this.html || cfg.html,
3109 src: _this[size + 'Url']
3112 img.cls += ' roo-image-responsive-' + size;
3114 var s = ['xs', 'sm', 'md', 'lg'];
3116 s.splice(s.indexOf(size), 1);
3118 Roo.each(s, function(ss){
3119 img.cls += ' hidden-' + ss;
3122 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3123 cfg.cls += ' img-' + _this.border;
3127 cfg.alt = _this.alt;
3140 a.target = _this.target;
3144 cfg.cn.push((_this.href) ? a : img);
3151 createSingleImg : function()
3155 cls: (this.imgResponsive) ? 'img-responsive' : '',
3157 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3160 if (this.backgroundContain) {
3161 cfg.cls += ' background-contain';
3164 cfg.html = this.html || cfg.html;
3166 if (this.backgroundContain) {
3167 cfg.style="background-image: url(" + this.src + ')';
3169 cfg.src = this.src || cfg.src;
3172 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3173 cfg.cls += ' img-' + this.border;
3190 a.target = this.target;
3195 return (this.href) ? a : cfg;
3198 initEvents: function()
3201 this.el.on('click', this.onClick, this);
3203 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3204 this.el.on('load', this.onImageLoad, this);
3206 // not sure if this works.. not tested
3207 this.el.select('img', true).on('load', this.onImageLoad, this);
3212 onClick : function(e)
3214 Roo.log('img onclick');
3215 this.fireEvent('click', this, e);
3217 onImageLoad: function(e)
3219 Roo.log('img load');
3220 this.fireEvent('load', this, e);
3224 * Sets the url of the image - used to update it
3225 * @param {String} url the url of the image
3228 setSrc : function(url)
3232 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3233 if (this.backgroundContain) {
3234 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3236 this.el.dom.src = url;
3241 this.el.select('img', true).first().dom.src = url;
3257 * @class Roo.bootstrap.Link
3258 * @extends Roo.bootstrap.Component
3259 * Bootstrap Link Class
3260 * @cfg {String} alt image alternative text
3261 * @cfg {String} href a tag href
3262 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3263 * @cfg {String} html the content of the link.
3264 * @cfg {String} anchor name for the anchor link
3265 * @cfg {String} fa - favicon
3267 * @cfg {Boolean} preventDefault (true | false) default false
3271 * Create a new Input
3272 * @param {Object} config The config object
3275 Roo.bootstrap.Link = function(config){
3276 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3282 * The img click event for the img.
3283 * @param {Roo.EventObject} e
3289 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3293 preventDefault: false,
3299 getAutoCreate : function()
3301 var html = this.html || '';
3303 if (this.fa !== false) {
3304 html = '<i class="fa fa-' + this.fa + '"></i>';
3309 // anchor's do not require html/href...
3310 if (this.anchor === false) {
3312 cfg.href = this.href || '#';
3314 cfg.name = this.anchor;
3315 if (this.html !== false || this.fa !== false) {
3318 if (this.href !== false) {
3319 cfg.href = this.href;
3323 if(this.alt !== false){
3328 if(this.target !== false) {
3329 cfg.target = this.target;
3335 initEvents: function() {
3337 if(!this.href || this.preventDefault){
3338 this.el.on('click', this.onClick, this);
3342 onClick : function(e)
3344 if(this.preventDefault){
3347 //Roo.log('img onclick');
3348 this.fireEvent('click', this, e);
3361 * @class Roo.bootstrap.Header
3362 * @extends Roo.bootstrap.Component
3363 * Bootstrap Header class
3364 * @cfg {String} html content of header
3365 * @cfg {Number} level (1|2|3|4|5|6) default 1
3368 * Create a new Header
3369 * @param {Object} config The config object
3373 Roo.bootstrap.Header = function(config){
3374 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3377 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3385 getAutoCreate : function(){
3390 tag: 'h' + (1 *this.level),
3391 html: this.html || ''
3403 * Ext JS Library 1.1.1
3404 * Copyright(c) 2006-2007, Ext JS, LLC.
3406 * Originally Released Under LGPL - original licence link has changed is not relivant.
3409 * <script type="text/javascript">
3413 * @class Roo.bootstrap.MenuMgr
3414 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3417 Roo.bootstrap.MenuMgr = function(){
3418 var menus, active, groups = {}, attached = false, lastShow = new Date();
3420 // private - called when first menu is created
3423 active = new Roo.util.MixedCollection();
3424 Roo.get(document).addKeyListener(27, function(){
3425 if(active.length > 0){
3433 if(active && active.length > 0){
3434 var c = active.clone();
3444 if(active.length < 1){
3445 Roo.get(document).un("mouseup", onMouseDown);
3453 var last = active.last();
3454 lastShow = new Date();
3457 Roo.get(document).on("mouseup", onMouseDown);
3462 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3463 m.parentMenu.activeChild = m;
3464 }else if(last && last.isVisible()){
3465 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3470 function onBeforeHide(m){
3472 m.activeChild.hide();
3474 if(m.autoHideTimer){
3475 clearTimeout(m.autoHideTimer);
3476 delete m.autoHideTimer;
3481 function onBeforeShow(m){
3482 var pm = m.parentMenu;
3483 if(!pm && !m.allowOtherMenus){
3485 }else if(pm && pm.activeChild && active != m){
3486 pm.activeChild.hide();
3490 // private this should really trigger on mouseup..
3491 function onMouseDown(e){
3492 Roo.log("on Mouse Up");
3494 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3495 Roo.log("MenuManager hideAll");
3504 function onBeforeCheck(mi, state){
3506 var g = groups[mi.group];
3507 for(var i = 0, l = g.length; i < l; i++){
3509 g[i].setChecked(false);
3518 * Hides all menus that are currently visible
3520 hideAll : function(){
3525 register : function(menu){
3529 menus[menu.id] = menu;
3530 menu.on("beforehide", onBeforeHide);
3531 menu.on("hide", onHide);
3532 menu.on("beforeshow", onBeforeShow);
3533 menu.on("show", onShow);
3535 if(g && menu.events["checkchange"]){
3539 groups[g].push(menu);
3540 menu.on("checkchange", onCheck);
3545 * Returns a {@link Roo.menu.Menu} object
3546 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3547 * be used to generate and return a new Menu instance.
3549 get : function(menu){
3550 if(typeof menu == "string"){ // menu id
3552 }else if(menu.events){ // menu instance
3555 /*else if(typeof menu.length == 'number'){ // array of menu items?
3556 return new Roo.bootstrap.Menu({items:menu});
3557 }else{ // otherwise, must be a config
3558 return new Roo.bootstrap.Menu(menu);
3565 unregister : function(menu){
3566 delete menus[menu.id];
3567 menu.un("beforehide", onBeforeHide);
3568 menu.un("hide", onHide);
3569 menu.un("beforeshow", onBeforeShow);
3570 menu.un("show", onShow);
3572 if(g && menu.events["checkchange"]){
3573 groups[g].remove(menu);
3574 menu.un("checkchange", onCheck);
3579 registerCheckable : function(menuItem){
3580 var g = menuItem.group;
3585 groups[g].push(menuItem);
3586 menuItem.on("beforecheckchange", onBeforeCheck);
3591 unregisterCheckable : function(menuItem){
3592 var g = menuItem.group;
3594 groups[g].remove(menuItem);
3595 menuItem.un("beforecheckchange", onBeforeCheck);
3607 * @class Roo.bootstrap.Menu
3608 * @extends Roo.bootstrap.Component
3609 * Bootstrap Menu class - container for MenuItems
3610 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3611 * @cfg {bool} hidden if the menu should be hidden when rendered.
3612 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3613 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3614 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3615 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3619 * @param {Object} config The config object
3623 Roo.bootstrap.Menu = function(config){
3625 if (config.type == 'treeview') {
3626 // normally menu's are drawn attached to the document to handle layering etc..
3627 // however treeview (used by the docs menu is drawn into the parent element)
3628 this.container_method = 'getChildContainer';
3631 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3632 if (this.registerMenu && this.type != 'treeview') {
3633 Roo.bootstrap.MenuMgr.register(this);
3640 * Fires before this menu is displayed (return false to block)
3641 * @param {Roo.menu.Menu} this
3646 * Fires before this menu is hidden (return false to block)
3647 * @param {Roo.menu.Menu} this
3652 * Fires after this menu is displayed
3653 * @param {Roo.menu.Menu} this
3658 * Fires after this menu is hidden
3659 * @param {Roo.menu.Menu} this
3664 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3665 * @param {Roo.menu.Menu} this
3666 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3667 * @param {Roo.EventObject} e
3672 * Fires when the mouse is hovering over this menu
3673 * @param {Roo.menu.Menu} this
3674 * @param {Roo.EventObject} e
3675 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3680 * Fires when the mouse exits this menu
3681 * @param {Roo.menu.Menu} this
3682 * @param {Roo.EventObject} e
3683 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3688 * Fires when a menu item contained in this menu is clicked
3689 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3690 * @param {Roo.EventObject} e
3694 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3697 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3701 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3704 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3706 registerMenu : true,
3708 menuItems :false, // stores the menu items..
3718 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3720 hideTrigger : false,
3725 getChildContainer : function() {
3729 getAutoCreate : function(){
3731 //if (['right'].indexOf(this.align)!==-1) {
3732 // cfg.cn[1].cls += ' pull-right'
3737 cls : 'dropdown-menu shadow' ,
3738 style : 'z-index:1000'
3742 if (this.type === 'submenu') {
3743 cfg.cls = 'submenu active';
3745 if (this.type === 'treeview') {
3746 cfg.cls = 'treeview-menu';
3751 initEvents : function() {
3753 // Roo.log("ADD event");
3754 // Roo.log(this.triggerEl.dom);
3755 if (this.triggerEl) {
3757 this.triggerEl.on('click', this.onTriggerClick, this);
3759 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3761 if (!this.hideTrigger) {
3762 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3763 // dropdown toggle on the 'a' in BS4?
3764 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3766 this.triggerEl.addClass('dropdown-toggle');
3772 this.el.on('touchstart' , this.onTouch, this);
3774 this.el.on('click' , this.onClick, this);
3776 this.el.on("mouseover", this.onMouseOver, this);
3777 this.el.on("mouseout", this.onMouseOut, this);
3781 findTargetItem : function(e)
3783 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3787 //Roo.log(t); Roo.log(t.id);
3789 //Roo.log(this.menuitems);
3790 return this.menuitems.get(t.id);
3792 //return this.items.get(t.menuItemId);
3798 onTouch : function(e)
3800 Roo.log("menu.onTouch");
3801 //e.stopEvent(); this make the user popdown broken
3805 onClick : function(e)
3807 Roo.log("menu.onClick");
3809 var t = this.findTargetItem(e);
3810 if(!t || t.isContainer){
3815 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3816 if(t == this.activeItem && t.shouldDeactivate(e)){
3817 this.activeItem.deactivate();
3818 delete this.activeItem;
3822 this.setActiveItem(t, true);
3830 Roo.log('pass click event');
3834 this.fireEvent("click", this, t, e);
3838 if(!t.href.length || t.href == '#'){
3839 (function() { _this.hide(); }).defer(100);
3844 onMouseOver : function(e){
3845 var t = this.findTargetItem(e);
3848 // if(t.canActivate && !t.disabled){
3849 // this.setActiveItem(t, true);
3853 this.fireEvent("mouseover", this, e, t);
3855 isVisible : function(){
3856 return !this.hidden;
3858 onMouseOut : function(e){
3859 var t = this.findTargetItem(e);
3862 // if(t == this.activeItem && t.shouldDeactivate(e)){
3863 // this.activeItem.deactivate();
3864 // delete this.activeItem;
3867 this.fireEvent("mouseout", this, e, t);
3872 * Displays this menu relative to another element
3873 * @param {String/HTMLElement/Roo.Element} element The element to align to
3874 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3875 * the element (defaults to this.defaultAlign)
3876 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3878 show : function(el, pos, parentMenu)
3880 if (false === this.fireEvent("beforeshow", this)) {
3881 Roo.log("show canceled");
3884 this.parentMenu = parentMenu;
3888 this.el.addClass('show'); // show otherwise we do not know how big we are..
3890 var xy = this.el.getAlignToXY(el, pos);
3892 // bl-tl << left align below
3893 // tl-bl << left align
3895 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3896 // if it goes to far to the right.. -> align left.
3897 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3900 // was left align - go right?
3901 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3904 // goes down the bottom
3905 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3907 var a = this.align.replace('?', '').split('-');
3908 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3912 this.showAt( xy , parentMenu, false);
3915 * Displays this menu at a specific xy position
3916 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3917 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3919 showAt : function(xy, parentMenu, /* private: */_e){
3920 this.parentMenu = parentMenu;
3925 this.fireEvent("beforeshow", this);
3926 //xy = this.el.adjustForConstraints(xy);
3930 this.hideMenuItems();
3931 this.hidden = false;
3932 if (this.triggerEl) {
3933 this.triggerEl.addClass('open');
3936 this.el.addClass('show');
3940 // reassign x when hitting right
3942 // reassign y when hitting bottom
3944 // but the list may align on trigger left or trigger top... should it be a properity?
3946 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3951 this.fireEvent("show", this);
3957 this.doFocus.defer(50, this);
3961 doFocus : function(){
3963 this.focusEl.focus();
3968 * Hides this menu and optionally all parent menus
3969 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3971 hide : function(deep)
3973 if (false === this.fireEvent("beforehide", this)) {
3974 Roo.log("hide canceled");
3977 this.hideMenuItems();
3978 if(this.el && this.isVisible()){
3980 if(this.activeItem){
3981 this.activeItem.deactivate();
3982 this.activeItem = null;
3984 if (this.triggerEl) {
3985 this.triggerEl.removeClass('open');
3988 this.el.removeClass('show');
3990 this.fireEvent("hide", this);
3992 if(deep === true && this.parentMenu){
3993 this.parentMenu.hide(true);
3997 onTriggerClick : function(e)
3999 Roo.log('trigger click');
4001 var target = e.getTarget();
4003 Roo.log(target.nodeName.toLowerCase());
4005 if(target.nodeName.toLowerCase() === 'i'){
4011 onTriggerPress : function(e)
4013 Roo.log('trigger press');
4014 //Roo.log(e.getTarget());
4015 // Roo.log(this.triggerEl.dom);
4017 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4018 var pel = Roo.get(e.getTarget());
4019 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4020 Roo.log('is treeview or dropdown?');
4024 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4028 if (this.isVisible()) {
4034 this.show(this.triggerEl, this.align, false);
4037 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4044 hideMenuItems : function()
4046 Roo.log("hide Menu Items");
4051 this.el.select('.open',true).each(function(aa) {
4053 aa.removeClass('open');
4057 addxtypeChild : function (tree, cntr) {
4058 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4060 this.menuitems.add(comp);
4072 this.getEl().dom.innerHTML = '';
4073 this.menuitems.clear();
4087 * @class Roo.bootstrap.MenuItem
4088 * @extends Roo.bootstrap.Component
4089 * Bootstrap MenuItem class
4090 * @cfg {String} html the menu label
4091 * @cfg {String} href the link
4092 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4093 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4094 * @cfg {Boolean} active used on sidebars to highlight active itesm
4095 * @cfg {String} fa favicon to show on left of menu item.
4096 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4100 * Create a new MenuItem
4101 * @param {Object} config The config object
4105 Roo.bootstrap.MenuItem = function(config){
4106 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4111 * The raw click event for the entire grid.
4112 * @param {Roo.bootstrap.MenuItem} this
4113 * @param {Roo.EventObject} e
4119 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4123 preventDefault: false,
4124 isContainer : false,
4128 getAutoCreate : function(){
4130 if(this.isContainer){
4133 cls: 'dropdown-menu-item '
4143 cls : 'dropdown-item',
4148 if (this.fa !== false) {
4151 cls : 'fa fa-' + this.fa
4160 cls: 'dropdown-menu-item',
4163 if (this.parent().type == 'treeview') {
4164 cfg.cls = 'treeview-menu';
4167 cfg.cls += ' active';
4172 anc.href = this.href || cfg.cn[0].href ;
4173 ctag.html = this.html || cfg.cn[0].html ;
4177 initEvents: function()
4179 if (this.parent().type == 'treeview') {
4180 this.el.select('a').on('click', this.onClick, this);
4184 this.menu.parentType = this.xtype;
4185 this.menu.triggerEl = this.el;
4186 this.menu = this.addxtype(Roo.apply({}, this.menu));
4190 onClick : function(e)
4192 Roo.log('item on click ');
4194 if(this.preventDefault){
4197 //this.parent().hideMenuItems();
4199 this.fireEvent('click', this, e);
4218 * @class Roo.bootstrap.MenuSeparator
4219 * @extends Roo.bootstrap.Component
4220 * Bootstrap MenuSeparator class
4223 * Create a new MenuItem
4224 * @param {Object} config The config object
4228 Roo.bootstrap.MenuSeparator = function(config){
4229 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4232 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4234 getAutoCreate : function(){
4253 * @class Roo.bootstrap.Modal
4254 * @extends Roo.bootstrap.Component
4255 * Bootstrap Modal class
4256 * @cfg {String} title Title of dialog
4257 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4258 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4259 * @cfg {Boolean} specificTitle default false
4260 * @cfg {Array} buttons Array of buttons or standard button set..
4261 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4262 * @cfg {Boolean} animate default true
4263 * @cfg {Boolean} allow_close default true
4264 * @cfg {Boolean} fitwindow default false
4265 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4266 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4267 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4268 * @cfg {String} size (sm|lg|xl) default empty
4269 * @cfg {Number} max_width set the max width of modal
4270 * @cfg {Boolean} editableTitle can the title be edited
4275 * Create a new Modal Dialog
4276 * @param {Object} config The config object
4279 Roo.bootstrap.Modal = function(config){
4280 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4285 * The raw btnclick event for the button
4286 * @param {Roo.EventObject} e
4291 * Fire when dialog resize
4292 * @param {Roo.bootstrap.Modal} this
4293 * @param {Roo.EventObject} e
4297 * @event titlechanged
4298 * Fire when the editable title has been changed
4299 * @param {Roo.bootstrap.Modal} this
4300 * @param {Roo.EventObject} value
4302 "titlechanged" : true
4305 this.buttons = this.buttons || [];
4308 this.tmpl = Roo.factory(this.tmpl);
4313 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4315 title : 'test dialog',
4325 specificTitle: false,
4327 buttonPosition: 'right',
4349 editableTitle : false,
4351 onRender : function(ct, position)
4353 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4356 var cfg = Roo.apply({}, this.getAutoCreate());
4359 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4361 //if (!cfg.name.length) {
4365 cfg.cls += ' ' + this.cls;
4368 cfg.style = this.style;
4370 this.el = Roo.get(document.body).createChild(cfg, position);
4372 //var type = this.el.dom.type;
4375 if(this.tabIndex !== undefined){
4376 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4379 this.dialogEl = this.el.select('.modal-dialog',true).first();
4380 this.bodyEl = this.el.select('.modal-body',true).first();
4381 this.closeEl = this.el.select('.modal-header .close', true).first();
4382 this.headerEl = this.el.select('.modal-header',true).first();
4383 this.titleEl = this.el.select('.modal-title',true).first();
4384 this.footerEl = this.el.select('.modal-footer',true).first();
4386 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4388 //this.el.addClass("x-dlg-modal");
4390 if (this.buttons.length) {
4391 Roo.each(this.buttons, function(bb) {
4392 var b = Roo.apply({}, bb);
4393 b.xns = b.xns || Roo.bootstrap;
4394 b.xtype = b.xtype || 'Button';
4395 if (typeof(b.listeners) == 'undefined') {
4396 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4399 var btn = Roo.factory(b);
4401 btn.render(this.getButtonContainer());
4405 // render the children.
4408 if(typeof(this.items) != 'undefined'){
4409 var items = this.items;
4412 for(var i =0;i < items.length;i++) {
4413 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4417 this.items = nitems;
4419 // where are these used - they used to be body/close/footer
4423 //this.el.addClass([this.fieldClass, this.cls]);
4427 getAutoCreate : function()
4429 // we will default to modal-body-overflow - might need to remove or make optional later.
4431 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4432 html : this.html || ''
4437 cls : 'modal-title',
4441 if(this.specificTitle){ // WTF is this?
4446 if (this.allow_close && Roo.bootstrap.version == 3) {
4456 if (this.editableTitle) {
4458 cls: 'form-control roo-editable-title d-none',
4464 if (this.allow_close && Roo.bootstrap.version == 4) {
4474 if(this.size.length){
4475 size = 'modal-' + this.size;
4478 var footer = Roo.bootstrap.version == 3 ?
4480 cls : 'modal-footer',
4484 cls: 'btn-' + this.buttonPosition
4489 { // BS4 uses mr-auto on left buttons....
4490 cls : 'modal-footer'
4501 cls: "modal-dialog " + size,
4504 cls : "modal-content",
4507 cls : 'modal-header',
4522 modal.cls += ' fade';
4528 getChildContainer : function() {
4533 getButtonContainer : function() {
4535 return Roo.bootstrap.version == 4 ?
4536 this.el.select('.modal-footer',true).first()
4537 : this.el.select('.modal-footer div',true).first();
4540 initEvents : function()
4542 if (this.allow_close) {
4543 this.closeEl.on('click', this.hide, this);
4545 Roo.EventManager.onWindowResize(this.resize, this, true);
4546 if (this.editableTitle) {
4547 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4548 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4549 this.headerEditEl.on('keyup', function(e) {
4550 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4551 this.toggleHeaderInput(false)
4554 this.headerEditEl.on('blur', function(e) {
4555 this.toggleHeaderInput(false)
4564 this.maskEl.setSize(
4565 Roo.lib.Dom.getViewWidth(true),
4566 Roo.lib.Dom.getViewHeight(true)
4569 if (this.fitwindow) {
4571 this.dialogEl.setStyle( { 'max-width' : '100%' });
4573 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4574 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4579 if(this.max_width !== 0) {
4581 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4584 this.setSize(w, this.height);
4588 if(this.max_height) {
4589 this.setSize(w,Math.min(
4591 Roo.lib.Dom.getViewportHeight(true) - 60
4597 if(!this.fit_content) {
4598 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4602 this.setSize(w, Math.min(
4604 this.headerEl.getHeight() +
4605 this.footerEl.getHeight() +
4606 this.getChildHeight(this.bodyEl.dom.childNodes),
4607 Roo.lib.Dom.getViewportHeight(true) - 60)
4613 setSize : function(w,h)
4624 if (!this.rendered) {
4627 this.toggleHeaderInput(false);
4628 //this.el.setStyle('display', 'block');
4629 this.el.removeClass('hideing');
4630 this.el.dom.style.display='block';
4632 Roo.get(document.body).addClass('modal-open');
4634 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4637 this.el.addClass('show');
4638 this.el.addClass('in');
4641 this.el.addClass('show');
4642 this.el.addClass('in');
4645 // not sure how we can show data in here..
4647 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4650 Roo.get(document.body).addClass("x-body-masked");
4652 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4653 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4654 this.maskEl.dom.style.display = 'block';
4655 this.maskEl.addClass('show');
4660 this.fireEvent('show', this);
4662 // set zindex here - otherwise it appears to be ignored...
4663 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4666 this.items.forEach( function(e) {
4667 e.layout ? e.layout() : false;
4675 if(this.fireEvent("beforehide", this) !== false){
4677 this.maskEl.removeClass('show');
4679 this.maskEl.dom.style.display = '';
4680 Roo.get(document.body).removeClass("x-body-masked");
4681 this.el.removeClass('in');
4682 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4684 if(this.animate){ // why
4685 this.el.addClass('hideing');
4686 this.el.removeClass('show');
4688 if (!this.el.hasClass('hideing')) {
4689 return; // it's been shown again...
4692 this.el.dom.style.display='';
4694 Roo.get(document.body).removeClass('modal-open');
4695 this.el.removeClass('hideing');
4699 this.el.removeClass('show');
4700 this.el.dom.style.display='';
4701 Roo.get(document.body).removeClass('modal-open');
4704 this.fireEvent('hide', this);
4707 isVisible : function()
4710 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4714 addButton : function(str, cb)
4718 var b = Roo.apply({}, { html : str } );
4719 b.xns = b.xns || Roo.bootstrap;
4720 b.xtype = b.xtype || 'Button';
4721 if (typeof(b.listeners) == 'undefined') {
4722 b.listeners = { click : cb.createDelegate(this) };
4725 var btn = Roo.factory(b);
4727 btn.render(this.getButtonContainer());
4733 setDefaultButton : function(btn)
4735 //this.el.select('.modal-footer').()
4738 resizeTo: function(w,h)
4740 this.dialogEl.setWidth(w);
4742 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4744 this.bodyEl.setHeight(h - diff);
4746 this.fireEvent('resize', this);
4749 setContentSize : function(w, h)
4753 onButtonClick: function(btn,e)
4756 this.fireEvent('btnclick', btn.name, e);
4759 * Set the title of the Dialog
4760 * @param {String} str new Title
4762 setTitle: function(str) {
4763 this.titleEl.dom.innerHTML = str;
4767 * Set the body of the Dialog
4768 * @param {String} str new Title
4770 setBody: function(str) {
4771 this.bodyEl.dom.innerHTML = str;
4774 * Set the body of the Dialog using the template
4775 * @param {Obj} data - apply this data to the template and replace the body contents.
4777 applyBody: function(obj)
4780 Roo.log("Error - using apply Body without a template");
4783 this.tmpl.overwrite(this.bodyEl, obj);
4786 getChildHeight : function(child_nodes)
4790 child_nodes.length == 0
4795 var child_height = 0;
4797 for(var i = 0; i < child_nodes.length; i++) {
4800 * for modal with tabs...
4801 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4803 var layout_childs = child_nodes[i].childNodes;
4805 for(var j = 0; j < layout_childs.length; j++) {
4807 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4809 var layout_body_childs = layout_childs[j].childNodes;
4811 for(var k = 0; k < layout_body_childs.length; k++) {
4813 if(layout_body_childs[k].classList.contains('navbar')) {
4814 child_height += layout_body_childs[k].offsetHeight;
4818 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4820 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4822 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4824 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4825 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4840 child_height += child_nodes[i].offsetHeight;
4841 // Roo.log(child_nodes[i].offsetHeight);
4844 return child_height;
4846 toggleHeaderInput : function(is_edit)
4848 if (!this.editableTitle) {
4849 return; // not editable.
4851 if (is_edit && this.is_header_editing) {
4852 return; // already editing..
4856 this.headerEditEl.dom.value = this.title;
4857 this.headerEditEl.removeClass('d-none');
4858 this.headerEditEl.dom.focus();
4859 this.titleEl.addClass('d-none');
4861 this.is_header_editing = true;
4864 // flip back to not editing.
4865 this.title = this.headerEditEl.dom.value;
4866 this.headerEditEl.addClass('d-none');
4867 this.titleEl.removeClass('d-none');
4868 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4869 this.is_header_editing = false;
4870 this.fireEvent('titlechanged', this, this.title);
4879 Roo.apply(Roo.bootstrap.Modal, {
4881 * Button config that displays a single OK button
4890 * Button config that displays Yes and No buttons
4906 * Button config that displays OK and Cancel buttons
4921 * Button config that displays Yes, No and Cancel buttons
4946 * messagebox - can be used as a replace
4950 * @class Roo.MessageBox
4951 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4955 Roo.Msg.alert('Status', 'Changes saved successfully.');
4957 // Prompt for user data:
4958 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4960 // process text value...
4964 // Show a dialog using config options:
4966 title:'Save Changes?',
4967 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4968 buttons: Roo.Msg.YESNOCANCEL,
4975 Roo.bootstrap.MessageBox = function(){
4976 var dlg, opt, mask, waitTimer;
4977 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4978 var buttons, activeTextEl, bwidth;
4982 var handleButton = function(button){
4984 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4988 var handleHide = function(){
4990 dlg.el.removeClass(opt.cls);
4993 // Roo.TaskMgr.stop(waitTimer);
4994 // waitTimer = null;
4999 var updateButtons = function(b){
5002 buttons["ok"].hide();
5003 buttons["cancel"].hide();
5004 buttons["yes"].hide();
5005 buttons["no"].hide();
5006 dlg.footerEl.hide();
5010 dlg.footerEl.show();
5011 for(var k in buttons){
5012 if(typeof buttons[k] != "function"){
5015 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5016 width += buttons[k].el.getWidth()+15;
5026 var handleEsc = function(d, k, e){
5027 if(opt && opt.closable !== false){
5037 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5038 * @return {Roo.BasicDialog} The BasicDialog element
5040 getDialog : function(){
5042 dlg = new Roo.bootstrap.Modal( {
5045 //constraintoviewport:false,
5047 //collapsible : false,
5052 //buttonAlign:"center",
5053 closeClick : function(){
5054 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5057 handleButton("cancel");
5062 dlg.on("hide", handleHide);
5064 //dlg.addKeyListener(27, handleEsc);
5066 this.buttons = buttons;
5067 var bt = this.buttonText;
5068 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5069 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5070 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5071 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5073 bodyEl = dlg.bodyEl.createChild({
5075 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5076 '<textarea class="roo-mb-textarea"></textarea>' +
5077 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5079 msgEl = bodyEl.dom.firstChild;
5080 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5081 textboxEl.enableDisplayMode();
5082 textboxEl.addKeyListener([10,13], function(){
5083 if(dlg.isVisible() && opt && opt.buttons){
5086 }else if(opt.buttons.yes){
5087 handleButton("yes");
5091 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5092 textareaEl.enableDisplayMode();
5093 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5094 progressEl.enableDisplayMode();
5096 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5097 var pf = progressEl.dom.firstChild;
5099 pp = Roo.get(pf.firstChild);
5100 pp.setHeight(pf.offsetHeight);
5108 * Updates the message box body text
5109 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5110 * the XHTML-compliant non-breaking space character '&#160;')
5111 * @return {Roo.MessageBox} This message box
5113 updateText : function(text)
5115 if(!dlg.isVisible() && !opt.width){
5116 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5117 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5119 msgEl.innerHTML = text || ' ';
5121 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5122 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5124 Math.min(opt.width || cw , this.maxWidth),
5125 Math.max(opt.minWidth || this.minWidth, bwidth)
5128 activeTextEl.setWidth(w);
5130 if(dlg.isVisible()){
5131 dlg.fixedcenter = false;
5133 // to big, make it scroll. = But as usual stupid IE does not support
5136 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5137 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5138 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5140 bodyEl.dom.style.height = '';
5141 bodyEl.dom.style.overflowY = '';
5144 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5146 bodyEl.dom.style.overflowX = '';
5149 dlg.setContentSize(w, bodyEl.getHeight());
5150 if(dlg.isVisible()){
5151 dlg.fixedcenter = true;
5157 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5158 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5159 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5160 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5161 * @return {Roo.MessageBox} This message box
5163 updateProgress : function(value, text){
5165 this.updateText(text);
5168 if (pp) { // weird bug on my firefox - for some reason this is not defined
5169 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5170 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5176 * Returns true if the message box is currently displayed
5177 * @return {Boolean} True if the message box is visible, else false
5179 isVisible : function(){
5180 return dlg && dlg.isVisible();
5184 * Hides the message box if it is displayed
5187 if(this.isVisible()){
5193 * Displays a new message box, or reinitializes an existing message box, based on the config options
5194 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5195 * The following config object properties are supported:
5197 Property Type Description
5198 ---------- --------------- ------------------------------------------------------------------------------------
5199 animEl String/Element An id or Element from which the message box should animate as it opens and
5200 closes (defaults to undefined)
5201 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5202 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5203 closable Boolean False to hide the top-right close button (defaults to true). Note that
5204 progress and wait dialogs will ignore this property and always hide the
5205 close button as they can only be closed programmatically.
5206 cls String A custom CSS class to apply to the message box element
5207 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5208 displayed (defaults to 75)
5209 fn Function A callback function to execute after closing the dialog. The arguments to the
5210 function will be btn (the name of the button that was clicked, if applicable,
5211 e.g. "ok"), and text (the value of the active text field, if applicable).
5212 Progress and wait dialogs will ignore this option since they do not respond to
5213 user actions and can only be closed programmatically, so any required function
5214 should be called by the same code after it closes the dialog.
5215 icon String A CSS class that provides a background image to be used as an icon for
5216 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5217 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5218 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5219 modal Boolean False to allow user interaction with the page while the message box is
5220 displayed (defaults to true)
5221 msg String A string that will replace the existing message box body text (defaults
5222 to the XHTML-compliant non-breaking space character ' ')
5223 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5224 progress Boolean True to display a progress bar (defaults to false)
5225 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5226 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5227 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5228 title String The title text
5229 value String The string value to set into the active textbox element if displayed
5230 wait Boolean True to display a progress bar (defaults to false)
5231 width Number The width of the dialog in pixels
5238 msg: 'Please enter your address:',
5240 buttons: Roo.MessageBox.OKCANCEL,
5243 animEl: 'addAddressBtn'
5246 * @param {Object} config Configuration options
5247 * @return {Roo.MessageBox} This message box
5249 show : function(options)
5252 // this causes nightmares if you show one dialog after another
5253 // especially on callbacks..
5255 if(this.isVisible()){
5258 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5259 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5260 Roo.log("New Dialog Message:" + options.msg )
5261 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5262 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5265 var d = this.getDialog();
5267 d.setTitle(opt.title || " ");
5268 d.closeEl.setDisplayed(opt.closable !== false);
5269 activeTextEl = textboxEl;
5270 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5275 textareaEl.setHeight(typeof opt.multiline == "number" ?
5276 opt.multiline : this.defaultTextHeight);
5277 activeTextEl = textareaEl;
5286 progressEl.setDisplayed(opt.progress === true);
5288 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5290 this.updateProgress(0);
5291 activeTextEl.dom.value = opt.value || "";
5293 dlg.setDefaultButton(activeTextEl);
5295 var bs = opt.buttons;
5299 }else if(bs && bs.yes){
5300 db = buttons["yes"];
5302 dlg.setDefaultButton(db);
5304 bwidth = updateButtons(opt.buttons);
5305 this.updateText(opt.msg);
5307 d.el.addClass(opt.cls);
5309 d.proxyDrag = opt.proxyDrag === true;
5310 d.modal = opt.modal !== false;
5311 d.mask = opt.modal !== false ? mask : false;
5313 // force it to the end of the z-index stack so it gets a cursor in FF
5314 document.body.appendChild(dlg.el.dom);
5315 d.animateTarget = null;
5316 d.show(options.animEl);
5322 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5323 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5324 * and closing the message box when the process is complete.
5325 * @param {String} title The title bar text
5326 * @param {String} msg The message box body text
5327 * @return {Roo.MessageBox} This message box
5329 progress : function(title, msg){
5336 minWidth: this.minProgressWidth,
5343 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5344 * If a callback function is passed it will be called after the user clicks the button, and the
5345 * id of the button that was clicked will be passed as the only parameter to the callback
5346 * (could also be the top-right close button).
5347 * @param {String} title The title bar text
5348 * @param {String} msg The message box body text
5349 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5350 * @param {Object} scope (optional) The scope of the callback function
5351 * @return {Roo.MessageBox} This message box
5353 alert : function(title, msg, fn, scope)
5368 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5369 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5370 * You are responsible for closing the message box when the process is complete.
5371 * @param {String} msg The message box body text
5372 * @param {String} title (optional) The title bar text
5373 * @return {Roo.MessageBox} This message box
5375 wait : function(msg, title){
5386 waitTimer = Roo.TaskMgr.start({
5388 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5396 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5397 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5398 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5399 * @param {String} title The title bar text
5400 * @param {String} msg The message box body text
5401 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5402 * @param {Object} scope (optional) The scope of the callback function
5403 * @return {Roo.MessageBox} This message box
5405 confirm : function(title, msg, fn, scope){
5409 buttons: this.YESNO,
5418 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5419 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5420 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5421 * (could also be the top-right close button) and the text that was entered will be passed as the two
5422 * parameters to the callback.
5423 * @param {String} title The title bar text
5424 * @param {String} msg The message box body text
5425 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5426 * @param {Object} scope (optional) The scope of the callback function
5427 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5428 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5429 * @return {Roo.MessageBox} This message box
5431 prompt : function(title, msg, fn, scope, multiline){
5435 buttons: this.OKCANCEL,
5440 multiline: multiline,
5447 * Button config that displays a single OK button
5452 * Button config that displays Yes and No buttons
5455 YESNO : {yes:true, no:true},
5457 * Button config that displays OK and Cancel buttons
5460 OKCANCEL : {ok:true, cancel:true},
5462 * Button config that displays Yes, No and Cancel buttons
5465 YESNOCANCEL : {yes:true, no:true, cancel:true},
5468 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5471 defaultTextHeight : 75,
5473 * The maximum width in pixels of the message box (defaults to 600)
5478 * The minimum width in pixels of the message box (defaults to 100)
5483 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5484 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5487 minProgressWidth : 250,
5489 * An object containing the default button text strings that can be overriden for localized language support.
5490 * Supported properties are: ok, cancel, yes and no.
5491 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5504 * Shorthand for {@link Roo.MessageBox}
5506 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5507 Roo.Msg = Roo.Msg || Roo.MessageBox;
5516 * @class Roo.bootstrap.Navbar
5517 * @extends Roo.bootstrap.Component
5518 * Bootstrap Navbar class
5521 * Create a new Navbar
5522 * @param {Object} config The config object
5526 Roo.bootstrap.Navbar = function(config){
5527 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5531 * @event beforetoggle
5532 * Fire before toggle the menu
5533 * @param {Roo.EventObject} e
5535 "beforetoggle" : true
5539 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5548 getAutoCreate : function(){
5551 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5555 initEvents :function ()
5557 //Roo.log(this.el.select('.navbar-toggle',true));
5558 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5565 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5567 var size = this.el.getSize();
5568 this.maskEl.setSize(size.width, size.height);
5569 this.maskEl.enableDisplayMode("block");
5578 getChildContainer : function()
5580 if (this.el && this.el.select('.collapse').getCount()) {
5581 return this.el.select('.collapse',true).first();
5596 onToggle : function()
5599 if(this.fireEvent('beforetoggle', this) === false){
5602 var ce = this.el.select('.navbar-collapse',true).first();
5604 if (!ce.hasClass('show')) {
5614 * Expand the navbar pulldown
5616 expand : function ()
5619 var ce = this.el.select('.navbar-collapse',true).first();
5620 if (ce.hasClass('collapsing')) {
5623 ce.dom.style.height = '';
5625 ce.addClass('in'); // old...
5626 ce.removeClass('collapse');
5627 ce.addClass('show');
5628 var h = ce.getHeight();
5630 ce.removeClass('show');
5631 // at this point we should be able to see it..
5632 ce.addClass('collapsing');
5634 ce.setHeight(0); // resize it ...
5635 ce.on('transitionend', function() {
5636 //Roo.log('done transition');
5637 ce.removeClass('collapsing');
5638 ce.addClass('show');
5639 ce.removeClass('collapse');
5641 ce.dom.style.height = '';
5642 }, this, { single: true} );
5644 ce.dom.scrollTop = 0;
5647 * Collapse the navbar pulldown
5649 collapse : function()
5651 var ce = this.el.select('.navbar-collapse',true).first();
5653 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5654 // it's collapsed or collapsing..
5657 ce.removeClass('in'); // old...
5658 ce.setHeight(ce.getHeight());
5659 ce.removeClass('show');
5660 ce.addClass('collapsing');
5662 ce.on('transitionend', function() {
5663 ce.dom.style.height = '';
5664 ce.removeClass('collapsing');
5665 ce.addClass('collapse');
5666 }, this, { single: true} );
5686 * @class Roo.bootstrap.NavSimplebar
5687 * @extends Roo.bootstrap.Navbar
5688 * Bootstrap Sidebar class
5690 * @cfg {Boolean} inverse is inverted color
5692 * @cfg {String} type (nav | pills | tabs)
5693 * @cfg {Boolean} arrangement stacked | justified
5694 * @cfg {String} align (left | right) alignment
5696 * @cfg {Boolean} main (true|false) main nav bar? default false
5697 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5699 * @cfg {String} tag (header|footer|nav|div) default is nav
5701 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5705 * Create a new Sidebar
5706 * @param {Object} config The config object
5710 Roo.bootstrap.NavSimplebar = function(config){
5711 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5714 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5730 getAutoCreate : function(){
5734 tag : this.tag || 'div',
5735 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5737 if (['light','white'].indexOf(this.weight) > -1) {
5738 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5740 cfg.cls += ' bg-' + this.weight;
5743 cfg.cls += ' navbar-inverse';
5747 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5749 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5758 cls: 'nav nav-' + this.xtype,
5764 this.type = this.type || 'nav';
5765 if (['tabs','pills'].indexOf(this.type) != -1) {
5766 cfg.cn[0].cls += ' nav-' + this.type
5770 if (this.type!=='nav') {
5771 Roo.log('nav type must be nav/tabs/pills')
5773 cfg.cn[0].cls += ' navbar-nav'
5779 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5780 cfg.cn[0].cls += ' nav-' + this.arrangement;
5784 if (this.align === 'right') {
5785 cfg.cn[0].cls += ' navbar-right';
5810 * navbar-expand-md fixed-top
5814 * @class Roo.bootstrap.NavHeaderbar
5815 * @extends Roo.bootstrap.NavSimplebar
5816 * Bootstrap Sidebar class
5818 * @cfg {String} brand what is brand
5819 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5820 * @cfg {String} brand_href href of the brand
5821 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5822 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5823 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5824 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5827 * Create a new Sidebar
5828 * @param {Object} config The config object
5832 Roo.bootstrap.NavHeaderbar = function(config){
5833 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5837 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5844 desktopCenter : false,
5847 getAutoCreate : function(){
5850 tag: this.nav || 'nav',
5851 cls: 'navbar navbar-expand-md',
5857 if (this.desktopCenter) {
5858 cn.push({cls : 'container', cn : []});
5866 cls: 'navbar-toggle navbar-toggler',
5867 'data-toggle': 'collapse',
5872 html: 'Toggle navigation'
5876 cls: 'icon-bar navbar-toggler-icon'
5889 cn.push( Roo.bootstrap.version == 4 ? btn : {
5891 cls: 'navbar-header',
5900 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5904 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5906 if (['light','white'].indexOf(this.weight) > -1) {
5907 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5909 cfg.cls += ' bg-' + this.weight;
5912 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5913 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5915 // tag can override this..
5917 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5920 if (this.brand !== '') {
5921 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5922 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5924 href: this.brand_href ? this.brand_href : '#',
5925 cls: 'navbar-brand',
5933 cfg.cls += ' main-nav';
5941 getHeaderChildContainer : function()
5943 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5944 return this.el.select('.navbar-header',true).first();
5947 return this.getChildContainer();
5950 getChildContainer : function()
5953 return this.el.select('.roo-navbar-collapse',true).first();
5958 initEvents : function()
5960 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5962 if (this.autohide) {
5967 Roo.get(document).on('scroll',function(e) {
5968 var ns = Roo.get(document).getScroll().top;
5969 var os = prevScroll;
5973 ft.removeClass('slideDown');
5974 ft.addClass('slideUp');
5977 ft.removeClass('slideUp');
5978 ft.addClass('slideDown');
5999 * @class Roo.bootstrap.NavSidebar
6000 * @extends Roo.bootstrap.Navbar
6001 * Bootstrap Sidebar class
6004 * Create a new Sidebar
6005 * @param {Object} config The config object
6009 Roo.bootstrap.NavSidebar = function(config){
6010 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6013 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
6015 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6017 getAutoCreate : function(){
6022 cls: 'sidebar sidebar-nav'
6044 * @class Roo.bootstrap.NavGroup
6045 * @extends Roo.bootstrap.Component
6046 * Bootstrap NavGroup class
6047 * @cfg {String} align (left|right)
6048 * @cfg {Boolean} inverse
6049 * @cfg {String} type (nav|pills|tab) default nav
6050 * @cfg {String} navId - reference Id for navbar.
6051 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6054 * Create a new nav group
6055 * @param {Object} config The config object
6058 Roo.bootstrap.NavGroup = function(config){
6059 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6062 Roo.bootstrap.NavGroup.register(this);
6066 * Fires when the active item changes
6067 * @param {Roo.bootstrap.NavGroup} this
6068 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6069 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6076 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6088 getAutoCreate : function()
6090 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6096 if (Roo.bootstrap.version == 4) {
6097 if (['tabs','pills'].indexOf(this.type) != -1) {
6098 cfg.cls += ' nav-' + this.type;
6100 // trying to remove so header bar can right align top?
6101 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6102 // do not use on header bar...
6103 cfg.cls += ' navbar-nav';
6108 if (['tabs','pills'].indexOf(this.type) != -1) {
6109 cfg.cls += ' nav-' + this.type
6111 if (this.type !== 'nav') {
6112 Roo.log('nav type must be nav/tabs/pills')
6114 cfg.cls += ' navbar-nav'
6118 if (this.parent() && this.parent().sidebar) {
6121 cls: 'dashboard-menu sidebar-menu'
6127 if (this.form === true) {
6130 cls: 'navbar-form form-inline'
6132 //nav navbar-right ml-md-auto
6133 if (this.align === 'right') {
6134 cfg.cls += ' navbar-right ml-md-auto';
6136 cfg.cls += ' navbar-left';
6140 if (this.align === 'right') {
6141 cfg.cls += ' navbar-right ml-md-auto';
6143 cfg.cls += ' mr-auto';
6147 cfg.cls += ' navbar-inverse';
6155 * sets the active Navigation item
6156 * @param {Roo.bootstrap.NavItem} the new current navitem
6158 setActiveItem : function(item)
6161 Roo.each(this.navItems, function(v){
6166 v.setActive(false, true);
6173 item.setActive(true, true);
6174 this.fireEvent('changed', this, item, prev);
6179 * gets the active Navigation item
6180 * @return {Roo.bootstrap.NavItem} the current navitem
6182 getActive : function()
6186 Roo.each(this.navItems, function(v){
6197 indexOfNav : function()
6201 Roo.each(this.navItems, function(v,i){
6212 * adds a Navigation item
6213 * @param {Roo.bootstrap.NavItem} the navitem to add
6215 addItem : function(cfg)
6217 if (this.form && Roo.bootstrap.version == 4) {
6220 var cn = new Roo.bootstrap.NavItem(cfg);
6222 cn.parentId = this.id;
6223 cn.onRender(this.el, null);
6227 * register a Navigation item
6228 * @param {Roo.bootstrap.NavItem} the navitem to add
6230 register : function(item)
6232 this.navItems.push( item);
6233 item.navId = this.navId;
6238 * clear all the Navigation item
6241 clearAll : function()
6244 this.el.dom.innerHTML = '';
6247 getNavItem: function(tabId)
6250 Roo.each(this.navItems, function(e) {
6251 if (e.tabId == tabId) {
6261 setActiveNext : function()
6263 var i = this.indexOfNav(this.getActive());
6264 if (i > this.navItems.length) {
6267 this.setActiveItem(this.navItems[i+1]);
6269 setActivePrev : function()
6271 var i = this.indexOfNav(this.getActive());
6275 this.setActiveItem(this.navItems[i-1]);
6277 clearWasActive : function(except) {
6278 Roo.each(this.navItems, function(e) {
6279 if (e.tabId != except.tabId && e.was_active) {
6280 e.was_active = false;
6287 getWasActive : function ()
6290 Roo.each(this.navItems, function(e) {
6305 Roo.apply(Roo.bootstrap.NavGroup, {
6309 * register a Navigation Group
6310 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6312 register : function(navgrp)
6314 this.groups[navgrp.navId] = navgrp;
6318 * fetch a Navigation Group based on the navigation ID
6319 * @param {string} the navgroup to add
6320 * @returns {Roo.bootstrap.NavGroup} the navgroup
6322 get: function(navId) {
6323 if (typeof(this.groups[navId]) == 'undefined') {
6325 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6327 return this.groups[navId] ;
6342 * @class Roo.bootstrap.NavItem
6343 * @extends Roo.bootstrap.Component
6344 * Bootstrap Navbar.NavItem class
6345 * @cfg {String} href link to
6346 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6347 * @cfg {Boolean} button_outline show and outlined button
6348 * @cfg {String} html content of button
6349 * @cfg {String} badge text inside badge
6350 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6351 * @cfg {String} glyphicon DEPRICATED - use fa
6352 * @cfg {String} icon DEPRICATED - use fa
6353 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6354 * @cfg {Boolean} active Is item active
6355 * @cfg {Boolean} disabled Is item disabled
6356 * @cfg {String} linkcls Link Class
6357 * @cfg {Boolean} preventDefault (true | false) default false
6358 * @cfg {String} tabId the tab that this item activates.
6359 * @cfg {String} tagtype (a|span) render as a href or span?
6360 * @cfg {Boolean} animateRef (true|false) link to element default false
6363 * Create a new Navbar Item
6364 * @param {Object} config The config object
6366 Roo.bootstrap.NavItem = function(config){
6367 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6372 * The raw click event for the entire grid.
6373 * @param {Roo.EventObject} e
6378 * Fires when the active item active state changes
6379 * @param {Roo.bootstrap.NavItem} this
6380 * @param {boolean} state the new state
6386 * Fires when scroll to element
6387 * @param {Roo.bootstrap.NavItem} this
6388 * @param {Object} options
6389 * @param {Roo.EventObject} e
6397 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6406 preventDefault : false,
6414 button_outline : false,
6418 getAutoCreate : function(){
6425 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6428 cfg.cls += ' active' ;
6430 if (this.disabled) {
6431 cfg.cls += ' disabled';
6435 if (this.button_weight.length) {
6436 cfg.tag = this.href ? 'a' : 'button';
6437 cfg.html = this.html || '';
6438 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6440 cfg.href = this.href;
6443 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6445 cfg.cls += " nav-html";
6448 // menu .. should add dropdown-menu class - so no need for carat..
6450 if (this.badge !== '') {
6452 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6457 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6461 href : this.href || "#",
6462 html: this.html || '',
6466 if (this.tagtype == 'a') {
6467 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6471 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6472 } else if (this.fa) {
6473 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6474 } else if(this.glyphicon) {
6475 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6477 cfg.cn[0].cls += " nav-html";
6481 cfg.cn[0].html += " <span class='caret'></span>";
6485 if (this.badge !== '') {
6486 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6494 onRender : function(ct, position)
6496 // Roo.log("Call onRender: " + this.xtype);
6497 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6501 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6502 this.navLink = this.el.select('.nav-link',true).first();
6503 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6508 initEvents: function()
6510 if (typeof (this.menu) != 'undefined') {
6511 this.menu.parentType = this.xtype;
6512 this.menu.triggerEl = this.el;
6513 this.menu = this.addxtype(Roo.apply({}, this.menu));
6516 this.el.on('click', this.onClick, this);
6518 //if(this.tagtype == 'span'){
6519 // this.el.select('span',true).on('click', this.onClick, this);
6522 // at this point parent should be available..
6523 this.parent().register(this);
6526 onClick : function(e)
6528 if (e.getTarget('.dropdown-menu-item')) {
6529 // did you click on a menu itemm.... - then don't trigger onclick..
6534 this.preventDefault ||
6537 Roo.log("NavItem - prevent Default?");
6541 if (this.disabled) {
6545 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6546 if (tg && tg.transition) {
6547 Roo.log("waiting for the transitionend");
6553 //Roo.log("fire event clicked");
6554 if(this.fireEvent('click', this, e) === false){
6558 if(this.tagtype == 'span'){
6562 //Roo.log(this.href);
6563 var ael = this.el.select('a',true).first();
6566 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6567 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6568 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6569 return; // ignore... - it's a 'hash' to another page.
6571 Roo.log("NavItem - prevent Default?");
6573 this.scrollToElement(e);
6577 var p = this.parent();
6579 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6580 if (typeof(p.setActiveItem) !== 'undefined') {
6581 p.setActiveItem(this);
6585 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6586 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6587 // remove the collapsed menu expand...
6588 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6592 isActive: function () {
6595 setActive : function(state, fire, is_was_active)
6597 if (this.active && !state && this.navId) {
6598 this.was_active = true;
6599 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6601 nv.clearWasActive(this);
6605 this.active = state;
6608 this.el.removeClass('active');
6609 this.navLink ? this.navLink.removeClass('active') : false;
6610 } else if (!this.el.hasClass('active')) {
6612 this.el.addClass('active');
6613 if (Roo.bootstrap.version == 4 && this.navLink ) {
6614 this.navLink.addClass('active');
6619 this.fireEvent('changed', this, state);
6622 // show a panel if it's registered and related..
6624 if (!this.navId || !this.tabId || !state || is_was_active) {
6628 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6632 var pan = tg.getPanelByName(this.tabId);
6636 // if we can not flip to new panel - go back to old nav highlight..
6637 if (false == tg.showPanel(pan)) {
6638 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6640 var onav = nv.getWasActive();
6642 onav.setActive(true, false, true);
6651 // this should not be here...
6652 setDisabled : function(state)
6654 this.disabled = state;
6656 this.el.removeClass('disabled');
6657 } else if (!this.el.hasClass('disabled')) {
6658 this.el.addClass('disabled');
6664 * Fetch the element to display the tooltip on.
6665 * @return {Roo.Element} defaults to this.el
6667 tooltipEl : function()
6669 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6672 scrollToElement : function(e)
6674 var c = document.body;
6677 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6679 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6680 c = document.documentElement;
6683 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6689 var o = target.calcOffsetsTo(c);
6696 this.fireEvent('scrollto', this, options, e);
6698 Roo.get(c).scrollTo('top', options.value, true);
6703 * Set the HTML (text content) of the item
6704 * @param {string} html content for the nav item
6706 setHtml : function(html)
6709 this.htmlEl.dom.innerHTML = html;
6721 * <span> icon </span>
6722 * <span> text </span>
6723 * <span>badge </span>
6727 * @class Roo.bootstrap.NavSidebarItem
6728 * @extends Roo.bootstrap.NavItem
6729 * Bootstrap Navbar.NavSidebarItem class
6730 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6731 * {Boolean} open is the menu open
6732 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6733 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6734 * {String} buttonSize (sm|md|lg)the extra classes for the button
6735 * {Boolean} showArrow show arrow next to the text (default true)
6737 * Create a new Navbar Button
6738 * @param {Object} config The config object
6740 Roo.bootstrap.NavSidebarItem = function(config){
6741 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6746 * The raw click event for the entire grid.
6747 * @param {Roo.EventObject} e
6752 * Fires when the active item active state changes
6753 * @param {Roo.bootstrap.NavSidebarItem} this
6754 * @param {boolean} state the new state
6762 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6764 badgeWeight : 'default',
6770 buttonWeight : 'default',
6776 getAutoCreate : function(){
6781 href : this.href || '#',
6787 if(this.buttonView){
6790 href : this.href || '#',
6791 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6804 cfg.cls += ' active';
6807 if (this.disabled) {
6808 cfg.cls += ' disabled';
6811 cfg.cls += ' open x-open';
6814 if (this.glyphicon || this.icon) {
6815 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6816 a.cn.push({ tag : 'i', cls : c }) ;
6819 if(!this.buttonView){
6822 html : this.html || ''
6829 if (this.badge !== '') {
6830 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6836 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6839 a.cls += ' dropdown-toggle treeview' ;
6845 initEvents : function()
6847 if (typeof (this.menu) != 'undefined') {
6848 this.menu.parentType = this.xtype;
6849 this.menu.triggerEl = this.el;
6850 this.menu = this.addxtype(Roo.apply({}, this.menu));
6853 this.el.on('click', this.onClick, this);
6855 if(this.badge !== ''){
6856 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6861 onClick : function(e)
6868 if(this.preventDefault){
6872 this.fireEvent('click', this, e);
6875 disable : function()
6877 this.setDisabled(true);
6882 this.setDisabled(false);
6885 setDisabled : function(state)
6887 if(this.disabled == state){
6891 this.disabled = state;
6894 this.el.addClass('disabled');
6898 this.el.removeClass('disabled');
6903 setActive : function(state)
6905 if(this.active == state){
6909 this.active = state;
6912 this.el.addClass('active');
6916 this.el.removeClass('active');
6921 isActive: function ()
6926 setBadge : function(str)
6932 this.badgeEl.dom.innerHTML = str;
6947 Roo.namespace('Roo.bootstrap.breadcrumb');
6951 * @class Roo.bootstrap.breadcrumb.Nav
6952 * @extends Roo.bootstrap.Component
6953 * Bootstrap Breadcrumb Nav Class
6955 * @children Roo.bootstrap.breadcrumb.Item
6958 * Create a new breadcrumb.Nav
6959 * @param {Object} config The config object
6963 Roo.bootstrap.breadcrumb.Nav = function(config){
6964 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6969 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6971 getAutoCreate : function()
6988 initEvents: function()
6990 this.olEl = this.el.select('ol',true).first();
6992 getChildContainer : function()
7008 * @class Roo.bootstrap.breadcrumb.Nav
7009 * @extends Roo.bootstrap.Component
7010 * Bootstrap Breadcrumb Nav Class
7012 * @children Roo.bootstrap.breadcrumb.Component
7013 * @cfg {String} html the content of the link.
7014 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7015 * @cfg {Boolean} active is it active
7019 * Create a new breadcrumb.Nav
7020 * @param {Object} config The config object
7023 Roo.bootstrap.breadcrumb.Item = function(config){
7024 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7029 * The img click event for the img.
7030 * @param {Roo.EventObject} e
7037 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7042 getAutoCreate : function()
7047 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7049 if (this.href !== false) {
7056 cfg.html = this.html;
7062 initEvents: function()
7065 this.el.select('a', true).first().on('click',this.onClick, this)
7069 onClick : function(e)
7072 this.fireEvent('click',this, e);
7085 * @class Roo.bootstrap.Row
7086 * @extends Roo.bootstrap.Component
7087 * Bootstrap Row class (contains columns...)
7091 * @param {Object} config The config object
7094 Roo.bootstrap.Row = function(config){
7095 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7098 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7100 getAutoCreate : function(){
7119 * @class Roo.bootstrap.Pagination
7120 * @extends Roo.bootstrap.Component
7121 * Bootstrap Pagination class
7122 * @cfg {String} size xs | sm | md | lg
7123 * @cfg {Boolean} inverse false | true
7126 * Create a new Pagination
7127 * @param {Object} config The config object
7130 Roo.bootstrap.Pagination = function(config){
7131 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7134 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7140 getAutoCreate : function(){
7146 cfg.cls += ' inverse';
7152 cfg.cls += " " + this.cls;
7170 * @class Roo.bootstrap.PaginationItem
7171 * @extends Roo.bootstrap.Component
7172 * Bootstrap PaginationItem class
7173 * @cfg {String} html text
7174 * @cfg {String} href the link
7175 * @cfg {Boolean} preventDefault (true | false) default true
7176 * @cfg {Boolean} active (true | false) default false
7177 * @cfg {Boolean} disabled default false
7181 * Create a new PaginationItem
7182 * @param {Object} config The config object
7186 Roo.bootstrap.PaginationItem = function(config){
7187 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7192 * The raw click event for the entire grid.
7193 * @param {Roo.EventObject} e
7199 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7203 preventDefault: true,
7208 getAutoCreate : function(){
7214 href : this.href ? this.href : '#',
7215 html : this.html ? this.html : ''
7225 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7229 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7235 initEvents: function() {
7237 this.el.on('click', this.onClick, this);
7240 onClick : function(e)
7242 Roo.log('PaginationItem on click ');
7243 if(this.preventDefault){
7251 this.fireEvent('click', this, e);
7267 * @class Roo.bootstrap.Slider
7268 * @extends Roo.bootstrap.Component
7269 * Bootstrap Slider class
7272 * Create a new Slider
7273 * @param {Object} config The config object
7276 Roo.bootstrap.Slider = function(config){
7277 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7280 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7282 getAutoCreate : function(){
7286 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7290 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7302 * Ext JS Library 1.1.1
7303 * Copyright(c) 2006-2007, Ext JS, LLC.
7305 * Originally Released Under LGPL - original licence link has changed is not relivant.
7308 * <script type="text/javascript">
7311 * @extends Roo.dd.DDProxy
7312 * @class Roo.grid.SplitDragZone
7313 * Support for Column Header resizing
7315 * @param {Object} config
7318 // This is a support class used internally by the Grid components
7319 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7321 this.view = grid.getView();
7322 this.proxy = this.view.resizeProxy;
7323 Roo.grid.SplitDragZone.superclass.constructor.call(
7326 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7328 dragElId : Roo.id(this.proxy.dom),
7333 this.setHandleElId(Roo.id(hd));
7334 if (hd2 !== false) {
7335 this.setOuterHandleElId(Roo.id(hd2));
7338 this.scroll = false;
7340 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7341 fly: Roo.Element.fly,
7343 b4StartDrag : function(x, y){
7344 this.view.headersDisabled = true;
7345 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7346 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7348 this.proxy.setHeight(h);
7350 // for old system colWidth really stored the actual width?
7351 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7352 // which in reality did not work.. - it worked only for fixed sizes
7353 // for resizable we need to use actual sizes.
7354 var w = this.cm.getColumnWidth(this.cellIndex);
7355 if (!this.view.mainWrap) {
7357 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7362 // this was w-this.grid.minColumnWidth;
7363 // doesnt really make sense? - w = thie curren width or the rendered one?
7364 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7365 this.resetConstraints();
7366 this.setXConstraint(minw, 1000);
7367 this.setYConstraint(0, 0);
7368 this.minX = x - minw;
7369 this.maxX = x + 1000;
7371 if (!this.view.mainWrap) { // this is Bootstrap code..
7372 this.getDragEl().style.display='block';
7375 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7379 handleMouseDown : function(e){
7380 ev = Roo.EventObject.setEvent(e);
7381 var t = this.fly(ev.getTarget());
7382 if(t.hasClass("x-grid-split")){
7383 this.cellIndex = this.view.getCellIndex(t.dom);
7385 this.cm = this.grid.colModel;
7386 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7387 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7392 endDrag : function(e){
7393 this.view.headersDisabled = false;
7394 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7395 var diff = endX - this.startPos;
7397 var w = this.cm.getColumnWidth(this.cellIndex);
7398 if (!this.view.mainWrap) {
7401 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7404 autoOffset : function(){
7409 * Ext JS Library 1.1.1
7410 * Copyright(c) 2006-2007, Ext JS, LLC.
7412 * Originally Released Under LGPL - original licence link has changed is not relivant.
7415 * <script type="text/javascript">
7419 * @class Roo.grid.AbstractSelectionModel
7420 * @extends Roo.util.Observable
7421 * Abstract base class for grid SelectionModels. It provides the interface that should be
7422 * implemented by descendant classes. This class should not be directly instantiated.
7425 Roo.grid.AbstractSelectionModel = function(){
7426 this.locked = false;
7427 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7430 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7431 /** @ignore Called by the grid automatically. Do not call directly. */
7432 init : function(grid){
7438 * Locks the selections.
7445 * Unlocks the selections.
7447 unlock : function(){
7448 this.locked = false;
7452 * Returns true if the selections are locked.
7455 isLocked : function(){
7460 * Ext JS Library 1.1.1
7461 * Copyright(c) 2006-2007, Ext JS, LLC.
7463 * Originally Released Under LGPL - original licence link has changed is not relivant.
7466 * <script type="text/javascript">
7469 * @extends Roo.grid.AbstractSelectionModel
7470 * @class Roo.grid.RowSelectionModel
7471 * The default SelectionModel used by {@link Roo.grid.Grid}.
7472 * It supports multiple selections and keyboard selection/navigation.
7474 * @param {Object} config
7476 Roo.grid.RowSelectionModel = function(config){
7477 Roo.apply(this, config);
7478 this.selections = new Roo.util.MixedCollection(false, function(o){
7483 this.lastActive = false;
7487 * @event selectionchange
7488 * Fires when the selection changes
7489 * @param {SelectionModel} this
7491 "selectionchange" : true,
7493 * @event afterselectionchange
7494 * Fires after the selection changes (eg. by key press or clicking)
7495 * @param {SelectionModel} this
7497 "afterselectionchange" : true,
7499 * @event beforerowselect
7500 * Fires when a row is selected being selected, return false to cancel.
7501 * @param {SelectionModel} this
7502 * @param {Number} rowIndex The selected index
7503 * @param {Boolean} keepExisting False if other selections will be cleared
7505 "beforerowselect" : true,
7508 * Fires when a row is selected.
7509 * @param {SelectionModel} this
7510 * @param {Number} rowIndex The selected index
7511 * @param {Roo.data.Record} r The record
7515 * @event rowdeselect
7516 * Fires when a row is deselected.
7517 * @param {SelectionModel} this
7518 * @param {Number} rowIndex The selected index
7520 "rowdeselect" : true
7522 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7523 this.locked = false;
7526 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7528 * @cfg {Boolean} singleSelect
7529 * True to allow selection of only one row at a time (defaults to false)
7531 singleSelect : false,
7534 initEvents : function(){
7536 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7537 this.grid.on("mousedown", this.handleMouseDown, this);
7538 }else{ // allow click to work like normal
7539 this.grid.on("rowclick", this.handleDragableRowClick, this);
7541 // bootstrap does not have a view..
7542 var view = this.grid.view ? this.grid.view : this.grid;
7543 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7546 this.selectPrevious(e.shiftKey);
7547 }else if(this.last !== false && this.lastActive !== false){
7548 var last = this.last;
7549 this.selectRange(this.last, this.lastActive-1);
7550 view.focusRow(this.lastActive);
7555 this.selectFirstRow();
7557 this.fireEvent("afterselectionchange", this);
7559 "down" : function(e){
7561 this.selectNext(e.shiftKey);
7562 }else if(this.last !== false && this.lastActive !== false){
7563 var last = this.last;
7564 this.selectRange(this.last, this.lastActive+1);
7565 view.focusRow(this.lastActive);
7570 this.selectFirstRow();
7572 this.fireEvent("afterselectionchange", this);
7578 view.on("refresh", this.onRefresh, this);
7579 view.on("rowupdated", this.onRowUpdated, this);
7580 view.on("rowremoved", this.onRemove, this);
7584 onRefresh : function(){
7585 var ds = this.grid.ds, i, v = this.grid.view;
7586 var s = this.selections;
7588 if((i = ds.indexOfId(r.id)) != -1){
7590 s.add(ds.getAt(i)); // updating the selection relate data
7598 onRemove : function(v, index, r){
7599 this.selections.remove(r);
7603 onRowUpdated : function(v, index, r){
7604 if(this.isSelected(r)){
7605 v.onRowSelect(index);
7611 * @param {Array} records The records to select
7612 * @param {Boolean} keepExisting (optional) True to keep existing selections
7614 selectRecords : function(records, keepExisting){
7616 this.clearSelections();
7618 var ds = this.grid.ds;
7619 for(var i = 0, len = records.length; i < len; i++){
7620 this.selectRow(ds.indexOf(records[i]), true);
7625 * Gets the number of selected rows.
7628 getCount : function(){
7629 return this.selections.length;
7633 * Selects the first row in the grid.
7635 selectFirstRow : function(){
7640 * Select the last row.
7641 * @param {Boolean} keepExisting (optional) True to keep existing selections
7643 selectLastRow : function(keepExisting){
7644 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7648 * Selects the row immediately following the last selected row.
7649 * @param {Boolean} keepExisting (optional) True to keep existing selections
7651 selectNext : function(keepExisting){
7652 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7653 this.selectRow(this.last+1, keepExisting);
7654 var view = this.grid.view ? this.grid.view : this.grid;
7655 view.focusRow(this.last);
7660 * Selects the row that precedes the last selected row.
7661 * @param {Boolean} keepExisting (optional) True to keep existing selections
7663 selectPrevious : function(keepExisting){
7665 this.selectRow(this.last-1, keepExisting);
7666 var view = this.grid.view ? this.grid.view : this.grid;
7667 view.focusRow(this.last);
7672 * Returns the selected records
7673 * @return {Array} Array of selected records
7675 getSelections : function(){
7676 return [].concat(this.selections.items);
7680 * Returns the first selected record.
7683 getSelected : function(){
7684 return this.selections.itemAt(0);
7689 * Clears all selections.
7691 clearSelections : function(fast){
7696 var ds = this.grid.ds;
7697 var s = this.selections;
7699 this.deselectRow(ds.indexOfId(r.id));
7703 this.selections.clear();
7712 selectAll : function(){
7716 this.selections.clear();
7717 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7718 this.selectRow(i, true);
7723 * Returns True if there is a selection.
7726 hasSelection : function(){
7727 return this.selections.length > 0;
7731 * Returns True if the specified row is selected.
7732 * @param {Number/Record} record The record or index of the record to check
7735 isSelected : function(index){
7736 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7737 return (r && this.selections.key(r.id) ? true : false);
7741 * Returns True if the specified record id is selected.
7742 * @param {String} id The id of record to check
7745 isIdSelected : function(id){
7746 return (this.selections.key(id) ? true : false);
7750 handleMouseDown : function(e, t)
7752 var view = this.grid.view ? this.grid.view : this.grid;
7754 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7757 if(e.shiftKey && this.last !== false){
7758 var last = this.last;
7759 this.selectRange(last, rowIndex, e.ctrlKey);
7760 this.last = last; // reset the last
7761 view.focusRow(rowIndex);
7763 var isSelected = this.isSelected(rowIndex);
7764 if(e.button !== 0 && isSelected){
7765 view.focusRow(rowIndex);
7766 }else if(e.ctrlKey && isSelected){
7767 this.deselectRow(rowIndex);
7768 }else if(!isSelected){
7769 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7770 view.focusRow(rowIndex);
7773 this.fireEvent("afterselectionchange", this);
7776 handleDragableRowClick : function(grid, rowIndex, e)
7778 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7779 this.selectRow(rowIndex, false);
7780 var view = this.grid.view ? this.grid.view : this.grid;
7781 view.focusRow(rowIndex);
7782 this.fireEvent("afterselectionchange", this);
7787 * Selects multiple rows.
7788 * @param {Array} rows Array of the indexes of the row to select
7789 * @param {Boolean} keepExisting (optional) True to keep existing selections
7791 selectRows : function(rows, keepExisting){
7793 this.clearSelections();
7795 for(var i = 0, len = rows.length; i < len; i++){
7796 this.selectRow(rows[i], true);
7801 * Selects a range of rows. All rows in between startRow and endRow are also selected.
7802 * @param {Number} startRow The index of the first row in the range
7803 * @param {Number} endRow The index of the last row in the range
7804 * @param {Boolean} keepExisting (optional) True to retain existing selections
7806 selectRange : function(startRow, endRow, keepExisting){
7811 this.clearSelections();
7813 if(startRow <= endRow){
7814 for(var i = startRow; i <= endRow; i++){
7815 this.selectRow(i, true);
7818 for(var i = startRow; i >= endRow; i--){
7819 this.selectRow(i, true);
7825 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7826 * @param {Number} startRow The index of the first row in the range
7827 * @param {Number} endRow The index of the last row in the range
7829 deselectRange : function(startRow, endRow, preventViewNotify){
7833 for(var i = startRow; i <= endRow; i++){
7834 this.deselectRow(i, preventViewNotify);
7840 * @param {Number} row The index of the row to select
7841 * @param {Boolean} keepExisting (optional) True to keep existing selections
7843 selectRow : function(index, keepExisting, preventViewNotify){
7844 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7847 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7848 if(!keepExisting || this.singleSelect){
7849 this.clearSelections();
7851 var r = this.grid.ds.getAt(index);
7852 this.selections.add(r);
7853 this.last = this.lastActive = index;
7854 if(!preventViewNotify){
7855 var view = this.grid.view ? this.grid.view : this.grid;
7856 view.onRowSelect(index);
7858 this.fireEvent("rowselect", this, index, r);
7859 this.fireEvent("selectionchange", this);
7865 * @param {Number} row The index of the row to deselect
7867 deselectRow : function(index, preventViewNotify){
7871 if(this.last == index){
7874 if(this.lastActive == index){
7875 this.lastActive = false;
7877 var r = this.grid.ds.getAt(index);
7878 this.selections.remove(r);
7879 if(!preventViewNotify){
7880 var view = this.grid.view ? this.grid.view : this.grid;
7881 view.onRowDeselect(index);
7883 this.fireEvent("rowdeselect", this, index);
7884 this.fireEvent("selectionchange", this);
7888 restoreLast : function(){
7890 this.last = this._last;
7895 acceptsNav : function(row, col, cm){
7896 return !cm.isHidden(col) && cm.isCellEditable(col, row);
7900 onEditorKey : function(field, e){
7901 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7906 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7908 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7910 }else if(k == e.ENTER && !e.ctrlKey){
7914 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7916 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7918 }else if(k == e.ESC){
7922 g.startEditing(newCell[0], newCell[1]);
7927 * Ext JS Library 1.1.1
7928 * Copyright(c) 2006-2007, Ext JS, LLC.
7930 * Originally Released Under LGPL - original licence link has changed is not relivant.
7933 * <script type="text/javascript">
7938 * @class Roo.grid.ColumnModel
7939 * @extends Roo.util.Observable
7940 * This is the default implementation of a ColumnModel used by the Grid. It defines
7941 * the columns in the grid.
7944 var colModel = new Roo.grid.ColumnModel([
7945 {header: "Ticker", width: 60, sortable: true, locked: true},
7946 {header: "Company Name", width: 150, sortable: true},
7947 {header: "Market Cap.", width: 100, sortable: true},
7948 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7949 {header: "Employees", width: 100, sortable: true, resizable: false}
7954 * The config options listed for this class are options which may appear in each
7955 * individual column definition.
7956 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7958 * @param {Object} config An Array of column config objects. See this class's
7959 * config objects for details.
7961 Roo.grid.ColumnModel = function(config){
7963 * The config passed into the constructor
7965 this.config = []; //config;
7968 // if no id, create one
7969 // if the column does not have a dataIndex mapping,
7970 // map it to the order it is in the config
7971 for(var i = 0, len = config.length; i < len; i++){
7972 this.addColumn(config[i]);
7977 * The width of columns which have no width specified (defaults to 100)
7980 this.defaultWidth = 100;
7983 * Default sortable of columns which have no sortable specified (defaults to false)
7986 this.defaultSortable = false;
7990 * @event widthchange
7991 * Fires when the width of a column changes.
7992 * @param {ColumnModel} this
7993 * @param {Number} columnIndex The column index
7994 * @param {Number} newWidth The new width
7996 "widthchange": true,
7998 * @event headerchange
7999 * Fires when the text of a header changes.
8000 * @param {ColumnModel} this
8001 * @param {Number} columnIndex The column index
8002 * @param {Number} newText The new header text
8004 "headerchange": true,
8006 * @event hiddenchange
8007 * Fires when a column is hidden or "unhidden".
8008 * @param {ColumnModel} this
8009 * @param {Number} columnIndex The column index
8010 * @param {Boolean} hidden true if hidden, false otherwise
8012 "hiddenchange": true,
8014 * @event columnmoved
8015 * Fires when a column is moved.
8016 * @param {ColumnModel} this
8017 * @param {Number} oldIndex
8018 * @param {Number} newIndex
8020 "columnmoved" : true,
8022 * @event columlockchange
8023 * Fires when a column's locked state is changed
8024 * @param {ColumnModel} this
8025 * @param {Number} colIndex
8026 * @param {Boolean} locked true if locked
8028 "columnlockchange" : true
8030 Roo.grid.ColumnModel.superclass.constructor.call(this);
8032 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8034 * @cfg {String} header The header text to display in the Grid view.
8037 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8040 * @cfg {String} smHeader Header at Bootsrap Small width
8043 * @cfg {String} mdHeader Header at Bootsrap Medium width
8046 * @cfg {String} lgHeader Header at Bootsrap Large width
8049 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8052 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8053 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8054 * specified, the column's index is used as an index into the Record's data Array.
8057 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8058 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8061 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8062 * Defaults to the value of the {@link #defaultSortable} property.
8063 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8066 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8069 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8072 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8075 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8078 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8079 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8080 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8081 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8084 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8087 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8090 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8093 * @cfg {String} cursor (Optional)
8096 * @cfg {String} tooltip (Optional)
8099 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8102 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8105 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8108 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8111 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8114 * Returns the id of the column at the specified index.
8115 * @param {Number} index The column index
8116 * @return {String} the id
8118 getColumnId : function(index){
8119 return this.config[index].id;
8123 * Returns the column for a specified id.
8124 * @param {String} id The column id
8125 * @return {Object} the column
8127 getColumnById : function(id){
8128 return this.lookup[id];
8133 * Returns the column Object for a specified dataIndex.
8134 * @param {String} dataIndex The column dataIndex
8135 * @return {Object|Boolean} the column or false if not found
8137 getColumnByDataIndex: function(dataIndex){
8138 var index = this.findColumnIndex(dataIndex);
8139 return index > -1 ? this.config[index] : false;
8143 * Returns the index for a specified column id.
8144 * @param {String} id The column id
8145 * @return {Number} the index, or -1 if not found
8147 getIndexById : function(id){
8148 for(var i = 0, len = this.config.length; i < len; i++){
8149 if(this.config[i].id == id){
8157 * Returns the index for a specified column dataIndex.
8158 * @param {String} dataIndex The column dataIndex
8159 * @return {Number} the index, or -1 if not found
8162 findColumnIndex : function(dataIndex){
8163 for(var i = 0, len = this.config.length; i < len; i++){
8164 if(this.config[i].dataIndex == dataIndex){
8172 moveColumn : function(oldIndex, newIndex){
8173 var c = this.config[oldIndex];
8174 this.config.splice(oldIndex, 1);
8175 this.config.splice(newIndex, 0, c);
8176 this.dataMap = null;
8177 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8180 isLocked : function(colIndex){
8181 return this.config[colIndex].locked === true;
8184 setLocked : function(colIndex, value, suppressEvent){
8185 if(this.isLocked(colIndex) == value){
8188 this.config[colIndex].locked = value;
8190 this.fireEvent("columnlockchange", this, colIndex, value);
8194 getTotalLockedWidth : function(){
8196 for(var i = 0; i < this.config.length; i++){
8197 if(this.isLocked(i) && !this.isHidden(i)){
8198 this.totalWidth += this.getColumnWidth(i);
8204 getLockedCount : function(){
8205 for(var i = 0, len = this.config.length; i < len; i++){
8206 if(!this.isLocked(i)){
8211 return this.config.length;
8215 * Returns the number of columns.
8218 getColumnCount : function(visibleOnly){
8219 if(visibleOnly === true){
8221 for(var i = 0, len = this.config.length; i < len; i++){
8222 if(!this.isHidden(i)){
8228 return this.config.length;
8232 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8233 * @param {Function} fn
8234 * @param {Object} scope (optional)
8235 * @return {Array} result
8237 getColumnsBy : function(fn, scope){
8239 for(var i = 0, len = this.config.length; i < len; i++){
8240 var c = this.config[i];
8241 if(fn.call(scope||this, c, i) === true){
8249 * Returns true if the specified column is sortable.
8250 * @param {Number} col The column index
8253 isSortable : function(col){
8254 if(typeof this.config[col].sortable == "undefined"){
8255 return this.defaultSortable;
8257 return this.config[col].sortable;
8261 * Returns the rendering (formatting) function defined for the column.
8262 * @param {Number} col The column index.
8263 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8265 getRenderer : function(col){
8266 if(!this.config[col].renderer){
8267 return Roo.grid.ColumnModel.defaultRenderer;
8269 return this.config[col].renderer;
8273 * Sets the rendering (formatting) function for a column.
8274 * @param {Number} col The column index
8275 * @param {Function} fn The function to use to process the cell's raw data
8276 * to return HTML markup for the grid view. The render function is called with
8277 * the following parameters:<ul>
8278 * <li>Data value.</li>
8279 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8280 * <li>css A CSS style string to apply to the table cell.</li>
8281 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8282 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8283 * <li>Row index</li>
8284 * <li>Column index</li>
8285 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8287 setRenderer : function(col, fn){
8288 this.config[col].renderer = fn;
8292 * Returns the width for the specified column.
8293 * @param {Number} col The column index
8294 * @param (optional) {String} gridSize bootstrap width size.
8297 getColumnWidth : function(col, gridSize)
8299 var cfg = this.config[col];
8301 if (typeof(gridSize) == 'undefined') {
8302 return cfg.width * 1 || this.defaultWidth;
8304 if (gridSize === false) { // if we set it..
8305 return cfg.width || false;
8307 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8309 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8310 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8313 return cfg[ sizes[i] ];
8320 * Sets the width for a column.
8321 * @param {Number} col The column index
8322 * @param {Number} width The new width
8324 setColumnWidth : function(col, width, suppressEvent){
8325 this.config[col].width = width;
8326 this.totalWidth = null;
8328 this.fireEvent("widthchange", this, col, width);
8333 * Returns the total width of all columns.
8334 * @param {Boolean} includeHidden True to include hidden column widths
8337 getTotalWidth : function(includeHidden){
8338 if(!this.totalWidth){
8339 this.totalWidth = 0;
8340 for(var i = 0, len = this.config.length; i < len; i++){
8341 if(includeHidden || !this.isHidden(i)){
8342 this.totalWidth += this.getColumnWidth(i);
8346 return this.totalWidth;
8350 * Returns the header for the specified column.
8351 * @param {Number} col The column index
8354 getColumnHeader : function(col){
8355 return this.config[col].header;
8359 * Sets the header for a column.
8360 * @param {Number} col The column index
8361 * @param {String} header The new header
8363 setColumnHeader : function(col, header){
8364 this.config[col].header = header;
8365 this.fireEvent("headerchange", this, col, header);
8369 * Returns the tooltip for the specified column.
8370 * @param {Number} col The column index
8373 getColumnTooltip : function(col){
8374 return this.config[col].tooltip;
8377 * Sets the tooltip for a column.
8378 * @param {Number} col The column index
8379 * @param {String} tooltip The new tooltip
8381 setColumnTooltip : function(col, tooltip){
8382 this.config[col].tooltip = tooltip;
8386 * Returns the dataIndex for the specified column.
8387 * @param {Number} col The column index
8390 getDataIndex : function(col){
8391 return this.config[col].dataIndex;
8395 * Sets the dataIndex for a column.
8396 * @param {Number} col The column index
8397 * @param {Number} dataIndex The new dataIndex
8399 setDataIndex : function(col, dataIndex){
8400 this.config[col].dataIndex = dataIndex;
8406 * Returns true if the cell is editable.
8407 * @param {Number} colIndex The column index
8408 * @param {Number} rowIndex The row index - this is nto actually used..?
8411 isCellEditable : function(colIndex, rowIndex){
8412 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8416 * Returns the editor defined for the cell/column.
8417 * return false or null to disable editing.
8418 * @param {Number} colIndex The column index
8419 * @param {Number} rowIndex The row index
8422 getCellEditor : function(colIndex, rowIndex){
8423 return this.config[colIndex].editor;
8427 * Sets if a column is editable.
8428 * @param {Number} col The column index
8429 * @param {Boolean} editable True if the column is editable
8431 setEditable : function(col, editable){
8432 this.config[col].editable = editable;
8437 * Returns true if the column is hidden.
8438 * @param {Number} colIndex The column index
8441 isHidden : function(colIndex){
8442 return this.config[colIndex].hidden;
8447 * Returns true if the column width cannot be changed
8449 isFixed : function(colIndex){
8450 return this.config[colIndex].fixed;
8454 * Returns true if the column can be resized
8457 isResizable : function(colIndex){
8458 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8461 * Sets if a column is hidden.
8462 * @param {Number} colIndex The column index
8463 * @param {Boolean} hidden True if the column is hidden
8465 setHidden : function(colIndex, hidden){
8466 this.config[colIndex].hidden = hidden;
8467 this.totalWidth = null;
8468 this.fireEvent("hiddenchange", this, colIndex, hidden);
8472 * Sets the editor for a column.
8473 * @param {Number} col The column index
8474 * @param {Object} editor The editor object
8476 setEditor : function(col, editor){
8477 this.config[col].editor = editor;
8480 * Add a column (experimental...) - defaults to adding to the end..
8481 * @param {Object} config
8483 addColumn : function(c)
8486 var i = this.config.length;
8489 if(typeof c.dataIndex == "undefined"){
8492 if(typeof c.renderer == "string"){
8493 c.renderer = Roo.util.Format[c.renderer];
8495 if(typeof c.id == "undefined"){
8498 if(c.editor && c.editor.xtype){
8499 c.editor = Roo.factory(c.editor, Roo.grid);
8501 if(c.editor && c.editor.isFormField){
8502 c.editor = new Roo.grid.GridEditor(c.editor);
8504 this.lookup[c.id] = c;
8509 Roo.grid.ColumnModel.defaultRenderer = function(value)
8511 if(typeof value == "object") {
8514 if(typeof value == "string" && value.length < 1){
8518 return String.format("{0}", value);
8521 // Alias for backwards compatibility
8522 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8525 * Ext JS Library 1.1.1
8526 * Copyright(c) 2006-2007, Ext JS, LLC.
8528 * Originally Released Under LGPL - original licence link has changed is not relivant.
8531 * <script type="text/javascript">
8535 * @class Roo.LoadMask
8536 * A simple utility class for generically masking elements while loading data. If the element being masked has
8537 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8538 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8539 * element's UpdateManager load indicator and will be destroyed after the initial load.
8541 * Create a new LoadMask
8542 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8543 * @param {Object} config The config object
8545 Roo.LoadMask = function(el, config){
8546 this.el = Roo.get(el);
8547 Roo.apply(this, config);
8549 this.store.on('beforeload', this.onBeforeLoad, this);
8550 this.store.on('load', this.onLoad, this);
8551 this.store.on('loadexception', this.onLoadException, this);
8552 this.removeMask = false;
8554 var um = this.el.getUpdateManager();
8555 um.showLoadIndicator = false; // disable the default indicator
8556 um.on('beforeupdate', this.onBeforeLoad, this);
8557 um.on('update', this.onLoad, this);
8558 um.on('failure', this.onLoad, this);
8559 this.removeMask = true;
8563 Roo.LoadMask.prototype = {
8565 * @cfg {Boolean} removeMask
8566 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8567 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8571 * The text to display in a centered loading message box (defaults to 'Loading...')
8575 * @cfg {String} msgCls
8576 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8578 msgCls : 'x-mask-loading',
8581 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8587 * Disables the mask to prevent it from being displayed
8589 disable : function(){
8590 this.disabled = true;
8594 * Enables the mask so that it can be displayed
8596 enable : function(){
8597 this.disabled = false;
8600 onLoadException : function()
8604 if (typeof(arguments[3]) != 'undefined') {
8605 Roo.MessageBox.alert("Error loading",arguments[3]);
8609 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8610 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8617 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8622 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8626 onBeforeLoad : function(){
8628 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8633 destroy : function(){
8635 this.store.un('beforeload', this.onBeforeLoad, this);
8636 this.store.un('load', this.onLoad, this);
8637 this.store.un('loadexception', this.onLoadException, this);
8639 var um = this.el.getUpdateManager();
8640 um.un('beforeupdate', this.onBeforeLoad, this);
8641 um.un('update', this.onLoad, this);
8642 um.un('failure', this.onLoad, this);
8646 * @class Roo.bootstrap.Table
8648 * @extends Roo.bootstrap.Component
8649 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
8650 * Similar to Roo.grid.Grid
8652 var table = Roo.factory({
8654 xns : Roo.bootstrap,
8655 autoSizeColumns: true,
8662 sortInfo : { direction : 'ASC', field: 'name' },
8664 xtype : 'HttpProxy',
8667 url : 'https://example.com/some.data.url.json'
8670 xtype : 'JsonReader',
8672 fields : [ 'id', 'name', whatever' ],
8679 xtype : 'ColumnModel',
8683 dataIndex : 'is_in_group',
8686 renderer : function(v, x , r) {
8688 return String.format("{0}", v)
8694 xtype : 'RowSelectionModel',
8695 xns : Roo.bootstrap.Table
8696 // you can add listeners to catch selection change here....
8702 grid.render(Roo.get("some-div"));
8705 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8710 * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8711 * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8712 * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8714 * @cfg {String} cls table class
8717 * @cfg {boolean} striped Should the rows be alternative striped
8718 * @cfg {boolean} bordered Add borders to the table
8719 * @cfg {boolean} hover Add hover highlighting
8720 * @cfg {boolean} condensed Format condensed
8721 * @cfg {boolean} responsive Format condensed
8722 * @cfg {Boolean} loadMask (true|false) default false
8723 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8724 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8725 * @cfg {Boolean} rowSelection (true|false) default false
8726 * @cfg {Boolean} cellSelection (true|false) default false
8727 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8728 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8729 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8730 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8731 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8732 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
8735 * Create a new Table
8736 * @param {Object} config The config object
8739 Roo.bootstrap.Table = function(config)
8741 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8744 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8745 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8746 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8747 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8749 this.view = this; // compat with grid.
8751 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8753 this.sm.grid = this;
8754 this.selModel = Roo.factory(this.sm, Roo.grid);
8755 this.sm = this.selModel;
8756 this.sm.xmodule = this.xmodule || false;
8759 if (this.cm && typeof(this.cm.config) == 'undefined') {
8760 this.colModel = new Roo.grid.ColumnModel(this.cm);
8761 this.cm = this.colModel;
8762 this.cm.xmodule = this.xmodule || false;
8765 this.store= Roo.factory(this.store, Roo.data);
8766 this.ds = this.store;
8767 this.ds.xmodule = this.xmodule || false;
8770 if (this.footer && this.store) {
8771 this.footer.dataSource = this.ds;
8772 this.footer = Roo.factory(this.footer);
8779 * Fires when a cell is clicked
8780 * @param {Roo.bootstrap.Table} this
8781 * @param {Roo.Element} el
8782 * @param {Number} rowIndex
8783 * @param {Number} columnIndex
8784 * @param {Roo.EventObject} e
8788 * @event celldblclick
8789 * Fires when a cell is double clicked
8790 * @param {Roo.bootstrap.Table} this
8791 * @param {Roo.Element} el
8792 * @param {Number} rowIndex
8793 * @param {Number} columnIndex
8794 * @param {Roo.EventObject} e
8796 "celldblclick" : true,
8799 * Fires when a row is clicked
8800 * @param {Roo.bootstrap.Table} this
8801 * @param {Roo.Element} el
8802 * @param {Number} rowIndex
8803 * @param {Roo.EventObject} e
8807 * @event rowdblclick
8808 * Fires when a row is double clicked
8809 * @param {Roo.bootstrap.Table} this
8810 * @param {Roo.Element} el
8811 * @param {Number} rowIndex
8812 * @param {Roo.EventObject} e
8814 "rowdblclick" : true,
8817 * Fires when a mouseover occur
8818 * @param {Roo.bootstrap.Table} this
8819 * @param {Roo.Element} el
8820 * @param {Number} rowIndex
8821 * @param {Number} columnIndex
8822 * @param {Roo.EventObject} e
8827 * Fires when a mouseout occur
8828 * @param {Roo.bootstrap.Table} this
8829 * @param {Roo.Element} el
8830 * @param {Number} rowIndex
8831 * @param {Number} columnIndex
8832 * @param {Roo.EventObject} e
8837 * Fires when a row is rendered, so you can change add a style to it.
8838 * @param {Roo.bootstrap.Table} this
8839 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8843 * @event rowsrendered
8844 * Fires when all the rows have been rendered
8845 * @param {Roo.bootstrap.Table} this
8847 'rowsrendered' : true,
8849 * @event contextmenu
8850 * The raw contextmenu event for the entire grid.
8851 * @param {Roo.EventObject} e
8853 "contextmenu" : true,
8855 * @event rowcontextmenu
8856 * Fires when a row is right clicked
8857 * @param {Roo.bootstrap.Table} this
8858 * @param {Number} rowIndex
8859 * @param {Roo.EventObject} e
8861 "rowcontextmenu" : true,
8863 * @event cellcontextmenu
8864 * Fires when a cell is right clicked
8865 * @param {Roo.bootstrap.Table} this
8866 * @param {Number} rowIndex
8867 * @param {Number} cellIndex
8868 * @param {Roo.EventObject} e
8870 "cellcontextmenu" : true,
8872 * @event headercontextmenu
8873 * Fires when a header is right clicked
8874 * @param {Roo.bootstrap.Table} this
8875 * @param {Number} columnIndex
8876 * @param {Roo.EventObject} e
8878 "headercontextmenu" : true,
8881 * The raw mousedown event for the entire grid.
8882 * @param {Roo.EventObject} e
8889 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8905 enableColumnResize: true,
8907 rowSelection : false,
8908 cellSelection : false,
8911 minColumnWidth : 50,
8913 // Roo.Element - the tbody
8914 bodyEl: false, // <tbody> Roo.Element - thead element
8915 headEl: false, // <thead> Roo.Element - thead element
8916 resizeProxy : false, // proxy element for dragging?
8920 container: false, // used by gridpanel...
8926 auto_hide_footer : false,
8928 view: false, // actually points to this..
8930 getAutoCreate : function()
8932 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8939 // this get's auto added by panel.Grid
8940 if (this.scrollBody) {
8941 cfg.cls += ' table-body-fixed';
8944 cfg.cls += ' table-striped';
8948 cfg.cls += ' table-hover';
8950 if (this.bordered) {
8951 cfg.cls += ' table-bordered';
8953 if (this.condensed) {
8954 cfg.cls += ' table-condensed';
8957 if (this.responsive) {
8958 cfg.cls += ' table-responsive';
8962 cfg.cls+= ' ' +this.cls;
8968 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8971 if(this.store || this.cm){
8972 if(this.headerShow){
8973 cfg.cn.push(this.renderHeader());
8976 cfg.cn.push(this.renderBody());
8978 if(this.footerShow){
8979 cfg.cn.push(this.renderFooter());
8981 // where does this come from?
8982 //cfg.cls+= ' TableGrid';
8985 return { cn : [ cfg ] };
8988 initEvents : function()
8990 if(!this.store || !this.cm){
8993 if (this.selModel) {
8994 this.selModel.initEvents();
8998 //Roo.log('initEvents with ds!!!!');
9000 this.bodyEl = this.el.select('tbody', true).first();
9001 this.headEl = this.el.select('thead', true).first();
9002 this.mainFoot = this.el.select('tfoot', true).first();
9007 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9008 e.on('click', this.sort, this);
9012 // why is this done????? = it breaks dialogs??
9013 //this.parent().el.setStyle('position', 'relative');
9017 this.footer.parentId = this.id;
9018 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9021 this.el.select('tfoot tr td').first().addClass('hide');
9026 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9029 this.store.on('load', this.onLoad, this);
9030 this.store.on('beforeload', this.onBeforeLoad, this);
9031 this.store.on('update', this.onUpdate, this);
9032 this.store.on('add', this.onAdd, this);
9033 this.store.on("clear", this.clear, this);
9035 this.el.on("contextmenu", this.onContextMenu, this);
9038 this.cm.on("headerchange", this.onHeaderChange, this);
9039 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9041 //?? does bodyEl get replaced on render?
9042 this.bodyEl.on("click", this.onClick, this);
9043 this.bodyEl.on("dblclick", this.onDblClick, this);
9044 this.bodyEl.on('scroll', this.onBodyScroll, this);
9046 // guessing mainbody will work - this relays usually caught by selmodel at present.
9047 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9050 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9053 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9054 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9059 // Compatibility with grid - we implement all the view features at present.
9060 getView : function()
9065 initCSS : function()
9069 var cm = this.cm, styles = [];
9070 this.CSS.removeStyleSheet(this.id + '-cssrules');
9071 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9072 // we can honour xs/sm/md/xl as widths...
9073 // we first have to decide what widht we are currently at...
9074 var sz = Roo.getGridSize();
9078 var cols = []; // visable cols.
9080 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9081 var w = cm.getColumnWidth(i, false);
9083 cols.push( { rel : false, abs : 0 });
9087 cols.push( { rel : false, abs : w });
9089 last = i; // not really..
9092 var w = cm.getColumnWidth(i, sz);
9097 cols.push( { rel : w, abs : false });
9100 var avail = this.bodyEl.dom.clientWidth - total_abs;
9102 var unitWidth = Math.floor(avail / total);
9103 var rem = avail - (unitWidth * total);
9105 var hidden, width, pos = 0 , splithide , left;
9106 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9108 hidden = 'display:none;';
9110 width = 'width:0px;';
9112 if(!cm.isHidden(i)){
9116 // we can honour xs/sm/md/xl ?
9117 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9119 hidden = 'display:none;';
9121 // width should return a small number...
9123 w+=rem; // add the remaining with..
9126 left = "left:" + (pos -4) + "px;";
9127 width = "width:" + w+ "px;";
9130 if (this.responsive) {
9133 hidden = cm.isHidden(i) ? 'display:none' : '';
9134 splithide = 'display: none';
9137 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9140 splithide = 'display:none;';
9143 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9144 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9149 //Roo.log(styles.join(''));
9150 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9156 onContextMenu : function(e, t)
9158 this.processEvent("contextmenu", e);
9161 processEvent : function(name, e)
9163 if (name != 'touchstart' ) {
9164 this.fireEvent(name, e);
9167 var t = e.getTarget();
9169 var cell = Roo.get(t);
9175 if(cell.findParent('tfoot', false, true)){
9179 if(cell.findParent('thead', false, true)){
9181 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9182 cell = Roo.get(t).findParent('th', false, true);
9184 Roo.log("failed to find th in thead?");
9185 Roo.log(e.getTarget());
9190 var cellIndex = cell.dom.cellIndex;
9192 var ename = name == 'touchstart' ? 'click' : name;
9193 this.fireEvent("header" + ename, this, cellIndex, e);
9198 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9199 cell = Roo.get(t).findParent('td', false, true);
9201 Roo.log("failed to find th in tbody?");
9202 Roo.log(e.getTarget());
9207 var row = cell.findParent('tr', false, true);
9208 var cellIndex = cell.dom.cellIndex;
9209 var rowIndex = row.dom.rowIndex - 1;
9213 this.fireEvent("row" + name, this, rowIndex, e);
9217 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9223 onMouseover : function(e, el)
9225 var cell = Roo.get(el);
9231 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9232 cell = cell.findParent('td', false, true);
9235 var row = cell.findParent('tr', false, true);
9236 var cellIndex = cell.dom.cellIndex;
9237 var rowIndex = row.dom.rowIndex - 1; // start from 0
9239 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9243 onMouseout : function(e, el)
9245 var cell = Roo.get(el);
9251 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9252 cell = cell.findParent('td', false, true);
9255 var row = cell.findParent('tr', false, true);
9256 var cellIndex = cell.dom.cellIndex;
9257 var rowIndex = row.dom.rowIndex - 1; // start from 0
9259 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9263 onClick : function(e, el)
9265 var cell = Roo.get(el);
9267 if(!cell || (!this.cellSelection && !this.rowSelection)){
9271 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9272 cell = cell.findParent('td', false, true);
9275 if(!cell || typeof(cell) == 'undefined'){
9279 var row = cell.findParent('tr', false, true);
9281 if(!row || typeof(row) == 'undefined'){
9285 var cellIndex = cell.dom.cellIndex;
9286 var rowIndex = this.getRowIndex(row);
9288 // why??? - should these not be based on SelectionModel?
9289 //if(this.cellSelection){
9290 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9293 //if(this.rowSelection){
9294 this.fireEvent('rowclick', this, row, rowIndex, e);
9299 onDblClick : function(e,el)
9301 var cell = Roo.get(el);
9303 if(!cell || (!this.cellSelection && !this.rowSelection)){
9307 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9308 cell = cell.findParent('td', false, true);
9311 if(!cell || typeof(cell) == 'undefined'){
9315 var row = cell.findParent('tr', false, true);
9317 if(!row || typeof(row) == 'undefined'){
9321 var cellIndex = cell.dom.cellIndex;
9322 var rowIndex = this.getRowIndex(row);
9324 if(this.cellSelection){
9325 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9328 if(this.rowSelection){
9329 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9332 findRowIndex : function(el)
9334 var cell = Roo.get(el);
9338 var row = cell.findParent('tr', false, true);
9340 if(!row || typeof(row) == 'undefined'){
9343 return this.getRowIndex(row);
9345 sort : function(e,el)
9347 var col = Roo.get(el);
9349 if(!col.hasClass('sortable')){
9353 var sort = col.attr('sort');
9356 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9360 this.store.sortInfo = {field : sort, direction : dir};
9363 Roo.log("calling footer first");
9364 this.footer.onClick('first');
9367 this.store.load({ params : { start : 0 } });
9371 renderHeader : function()
9379 this.totalWidth = 0;
9381 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9383 var config = cm.config[i];
9387 cls : 'x-hcol-' + i,
9390 html: cm.getColumnHeader(i)
9393 var tooltip = cm.getColumnTooltip(i);
9395 c.tooltip = tooltip;
9401 if(typeof(config.sortable) != 'undefined' && config.sortable){
9402 c.cls += ' sortable';
9403 c.html = '<i class="fa"></i>' + c.html;
9406 // could use BS4 hidden-..-down
9408 if(typeof(config.lgHeader) != 'undefined'){
9409 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9412 if(typeof(config.mdHeader) != 'undefined'){
9413 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9416 if(typeof(config.smHeader) != 'undefined'){
9417 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9420 if(typeof(config.xsHeader) != 'undefined'){
9421 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9428 if(typeof(config.tooltip) != 'undefined'){
9429 c.tooltip = config.tooltip;
9432 if(typeof(config.colspan) != 'undefined'){
9433 c.colspan = config.colspan;
9436 // hidden is handled by CSS now
9438 if(typeof(config.dataIndex) != 'undefined'){
9439 c.sort = config.dataIndex;
9444 if(typeof(config.align) != 'undefined' && config.align.length){
9445 c.style += ' text-align:' + config.align + ';';
9448 /* width is done in CSS
9449 *if(typeof(config.width) != 'undefined'){
9450 c.style += ' width:' + config.width + 'px;';
9451 this.totalWidth += config.width;
9453 this.totalWidth += 100; // assume minimum of 100 per column?
9457 if(typeof(config.cls) != 'undefined'){
9458 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9460 // this is the bit that doesnt reall work at all...
9462 if (this.responsive) {
9465 ['xs','sm','md','lg'].map(function(size){
9467 if(typeof(config[size]) == 'undefined'){
9471 if (!config[size]) { // 0 = hidden
9472 // BS 4 '0' is treated as hide that column and below.
9473 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9477 c.cls += ' col-' + size + '-' + config[size] + (
9478 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9486 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9497 renderBody : function()
9507 colspan : this.cm.getColumnCount()
9517 renderFooter : function()
9527 colspan : this.cm.getColumnCount()
9541 // Roo.log('ds onload');
9546 var ds = this.store;
9548 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9549 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9550 if (_this.store.sortInfo) {
9552 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9553 e.select('i', true).addClass(['fa-arrow-up']);
9556 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9557 e.select('i', true).addClass(['fa-arrow-down']);
9562 var tbody = this.bodyEl;
9564 if(ds.getCount() > 0){
9565 ds.data.each(function(d,rowIndex){
9566 var row = this.renderRow(cm, ds, rowIndex);
9568 tbody.createChild(row);
9572 if(row.cellObjects.length){
9573 Roo.each(row.cellObjects, function(r){
9574 _this.renderCellObject(r);
9581 var tfoot = this.el.select('tfoot', true).first();
9583 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9585 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9587 var total = this.ds.getTotalCount();
9589 if(this.footer.pageSize < total){
9590 this.mainFoot.show();
9594 Roo.each(this.el.select('tbody td', true).elements, function(e){
9595 e.on('mouseover', _this.onMouseover, _this);
9598 Roo.each(this.el.select('tbody td', true).elements, function(e){
9599 e.on('mouseout', _this.onMouseout, _this);
9601 this.fireEvent('rowsrendered', this);
9605 this.initCSS(); /// resize cols
9611 onUpdate : function(ds,record)
9613 this.refreshRow(record);
9617 onRemove : function(ds, record, index, isUpdate){
9618 if(isUpdate !== true){
9619 this.fireEvent("beforerowremoved", this, index, record);
9621 var bt = this.bodyEl.dom;
9623 var rows = this.el.select('tbody > tr', true).elements;
9625 if(typeof(rows[index]) != 'undefined'){
9626 bt.removeChild(rows[index].dom);
9629 // if(bt.rows[index]){
9630 // bt.removeChild(bt.rows[index]);
9633 if(isUpdate !== true){
9634 //this.stripeRows(index);
9635 //this.syncRowHeights(index, index);
9637 this.fireEvent("rowremoved", this, index, record);
9641 onAdd : function(ds, records, rowIndex)
9643 //Roo.log('on Add called');
9644 // - note this does not handle multiple adding very well..
9645 var bt = this.bodyEl.dom;
9646 for (var i =0 ; i < records.length;i++) {
9647 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9648 //Roo.log(records[i]);
9649 //Roo.log(this.store.getAt(rowIndex+i));
9650 this.insertRow(this.store, rowIndex + i, false);
9657 refreshRow : function(record){
9658 var ds = this.store, index;
9659 if(typeof record == 'number'){
9661 record = ds.getAt(index);
9663 index = ds.indexOf(record);
9665 return; // should not happen - but seems to
9668 this.insertRow(ds, index, true);
9670 this.onRemove(ds, record, index+1, true);
9672 //this.syncRowHeights(index, index);
9674 this.fireEvent("rowupdated", this, index, record);
9676 // private - called by RowSelection
9677 onRowSelect : function(rowIndex){
9678 var row = this.getRowDom(rowIndex);
9679 row.addClass(['bg-info','info']);
9681 // private - called by RowSelection
9682 onRowDeselect : function(rowIndex)
9687 var row = this.getRowDom(rowIndex);
9688 row.removeClass(['bg-info','info']);
9691 * Focuses the specified row.
9692 * @param {Number} row The row index
9694 focusRow : function(row)
9696 //Roo.log('GridView.focusRow');
9697 var x = this.bodyEl.dom.scrollLeft;
9698 this.focusCell(row, 0, false);
9699 this.bodyEl.dom.scrollLeft = x;
9703 * Focuses the specified cell.
9704 * @param {Number} row The row index
9705 * @param {Number} col The column index
9706 * @param {Boolean} hscroll false to disable horizontal scrolling
9708 focusCell : function(row, col, hscroll)
9710 //Roo.log('GridView.focusCell');
9711 var el = this.ensureVisible(row, col, hscroll);
9712 // not sure what focusEL achives = it's a <a> pos relative
9713 //this.focusEl.alignTo(el, "tl-tl");
9715 // this.focusEl.focus();
9717 // this.focusEl.focus.defer(1, this.focusEl);
9722 * Scrolls the specified cell into view
9723 * @param {Number} row The row index
9724 * @param {Number} col The column index
9725 * @param {Boolean} hscroll false to disable horizontal scrolling
9727 ensureVisible : function(row, col, hscroll)
9729 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9730 //return null; //disable for testing.
9731 if(typeof row != "number"){
9734 if(row < 0 && row >= this.ds.getCount()){
9737 col = (col !== undefined ? col : 0);
9739 while(cm.isHidden(col)){
9743 var el = this.getCellDom(row, col);
9747 var c = this.bodyEl.dom;
9749 var ctop = parseInt(el.offsetTop, 10);
9750 var cleft = parseInt(el.offsetLeft, 10);
9751 var cbot = ctop + el.offsetHeight;
9752 var cright = cleft + el.offsetWidth;
9754 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9755 var ch = 0; //?? header is not withing the area?
9756 var stop = parseInt(c.scrollTop, 10);
9757 var sleft = parseInt(c.scrollLeft, 10);
9758 var sbot = stop + ch;
9759 var sright = sleft + c.clientWidth;
9761 Roo.log('GridView.ensureVisible:' +
9763 ' c.clientHeight:' + c.clientHeight +
9764 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9773 //Roo.log("set scrolltop to ctop DISABLE?");
9774 }else if(cbot > sbot){
9775 //Roo.log("set scrolltop to cbot-ch");
9776 c.scrollTop = cbot-ch;
9779 if(hscroll !== false){
9781 c.scrollLeft = cleft;
9782 }else if(cright > sright){
9783 c.scrollLeft = cright-c.clientWidth;
9791 insertRow : function(dm, rowIndex, isUpdate){
9794 this.fireEvent("beforerowsinserted", this, rowIndex);
9796 //var s = this.getScrollState();
9797 var row = this.renderRow(this.cm, this.store, rowIndex);
9798 // insert before rowIndex..
9799 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9803 if(row.cellObjects.length){
9804 Roo.each(row.cellObjects, function(r){
9805 _this.renderCellObject(r);
9810 this.fireEvent("rowsinserted", this, rowIndex);
9811 //this.syncRowHeights(firstRow, lastRow);
9812 //this.stripeRows(firstRow);
9819 getRowDom : function(rowIndex)
9821 var rows = this.el.select('tbody > tr', true).elements;
9823 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9826 getCellDom : function(rowIndex, colIndex)
9828 var row = this.getRowDom(rowIndex);
9829 if (row === false) {
9832 var cols = row.select('td', true).elements;
9833 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9837 // returns the object tree for a tr..
9840 renderRow : function(cm, ds, rowIndex)
9842 var d = ds.getAt(rowIndex);
9846 cls : 'x-row-' + rowIndex,
9850 var cellObjects = [];
9852 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9853 var config = cm.config[i];
9855 var renderer = cm.getRenderer(i);
9859 if(typeof(renderer) !== 'undefined'){
9860 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9862 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9863 // and are rendered into the cells after the row is rendered - using the id for the element.
9865 if(typeof(value) === 'object'){
9875 rowIndex : rowIndex,
9880 this.fireEvent('rowclass', this, rowcfg);
9884 // this might end up displaying HTML?
9885 // this is too messy... - better to only do it on columsn you know are going to be too long
9886 //tooltip : (typeof(value) === 'object') ? '' : value,
9887 cls : rowcfg.rowClass + ' x-col-' + i,
9889 html: (typeof(value) === 'object') ? '' : value
9896 if(typeof(config.colspan) != 'undefined'){
9897 td.colspan = config.colspan;
9902 if(typeof(config.align) != 'undefined' && config.align.length){
9903 td.style += ' text-align:' + config.align + ';';
9905 if(typeof(config.valign) != 'undefined' && config.valign.length){
9906 td.style += ' vertical-align:' + config.valign + ';';
9909 if(typeof(config.width) != 'undefined'){
9910 td.style += ' width:' + config.width + 'px;';
9914 if(typeof(config.cursor) != 'undefined'){
9915 td.style += ' cursor:' + config.cursor + ';';
9918 if(typeof(config.cls) != 'undefined'){
9919 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9921 if (this.responsive) {
9922 ['xs','sm','md','lg'].map(function(size){
9924 if(typeof(config[size]) == 'undefined'){
9930 if (!config[size]) { // 0 = hidden
9931 // BS 4 '0' is treated as hide that column and below.
9932 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9936 td.cls += ' col-' + size + '-' + config[size] + (
9937 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9947 row.cellObjects = cellObjects;
9955 onBeforeLoad : function()
9964 this.el.select('tbody', true).first().dom.innerHTML = '';
9967 * Show or hide a row.
9968 * @param {Number} rowIndex to show or hide
9969 * @param {Boolean} state hide
9971 setRowVisibility : function(rowIndex, state)
9973 var bt = this.bodyEl.dom;
9975 var rows = this.el.select('tbody > tr', true).elements;
9977 if(typeof(rows[rowIndex]) == 'undefined'){
9980 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9985 getSelectionModel : function(){
9987 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9989 return this.selModel;
9992 * Render the Roo.bootstrap object from renderder
9994 renderCellObject : function(r)
9998 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10000 var t = r.cfg.render(r.container);
10003 Roo.each(r.cfg.cn, function(c){
10005 container: t.getChildContainer(),
10008 _this.renderCellObject(child);
10013 * get the Row Index from a dom element.
10014 * @param {Roo.Element} row The row to look for
10015 * @returns {Number} the row
10017 getRowIndex : function(row)
10021 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10032 * get the header TH element for columnIndex
10033 * @param {Number} columnIndex
10034 * @returns {Roo.Element}
10036 getHeaderIndex: function(colIndex)
10038 var cols = this.headEl.select('th', true).elements;
10039 return cols[colIndex];
10042 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10043 * @param {domElement} cell to look for
10044 * @returns {Number} the column
10046 getCellIndex : function(cell)
10048 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10050 return parseInt(id[1], 10);
10055 * Returns the grid's underlying element = used by panel.Grid
10056 * @return {Element} The element
10058 getGridEl : function(){
10062 * Forces a resize - used by panel.Grid
10063 * @return {Element} The element
10065 autoSize : function()
10067 //var ctr = Roo.get(this.container.dom.parentElement);
10068 var ctr = Roo.get(this.el.dom);
10070 var thd = this.getGridEl().select('thead',true).first();
10071 var tbd = this.getGridEl().select('tbody', true).first();
10072 var tfd = this.getGridEl().select('tfoot', true).first();
10074 var cw = ctr.getWidth();
10075 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10079 tbd.setWidth(ctr.getWidth());
10080 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10081 // this needs fixing for various usage - currently only hydra job advers I think..
10083 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10085 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10088 cw = Math.max(cw, this.totalWidth);
10089 this.getGridEl().select('tbody tr',true).setWidth(cw);
10092 // resize 'expandable coloumn?
10094 return; // we doe not have a view in this design..
10097 onBodyScroll: function()
10099 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10101 this.headEl.setStyle({
10102 'position' : 'relative',
10103 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10109 var scrollHeight = this.bodyEl.dom.scrollHeight;
10111 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10113 var height = this.bodyEl.getHeight();
10115 if(scrollHeight - height == scrollTop) {
10117 var total = this.ds.getTotalCount();
10119 if(this.footer.cursor + this.footer.pageSize < total){
10121 this.footer.ds.load({
10123 start : this.footer.cursor + this.footer.pageSize,
10124 limit : this.footer.pageSize
10133 onColumnSplitterMoved : function(i, diff)
10135 this.userResized = true;
10137 var cm = this.colModel;
10139 var w = this.getHeaderIndex(i).getWidth() + diff;
10142 cm.setColumnWidth(i, w, true);
10144 //var cid = cm.getColumnId(i); << not used in this version?
10145 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10147 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10148 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10149 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10151 //this.updateSplitters();
10152 //this.layout(); << ??
10153 this.fireEvent("columnresize", i, w);
10155 onHeaderChange : function()
10157 var header = this.renderHeader();
10158 var table = this.el.select('table', true).first();
10160 this.headEl.remove();
10161 this.headEl = table.createChild(header, this.bodyEl, false);
10163 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10164 e.on('click', this.sort, this);
10167 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10168 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10173 onHiddenChange : function(colModel, colIndex, hidden)
10175 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10176 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10178 this.CSS.updateRule(thSelector, "display", "");
10179 this.CSS.updateRule(tdSelector, "display", "");
10182 this.CSS.updateRule(thSelector, "display", "none");
10183 this.CSS.updateRule(tdSelector, "display", "none");
10186 this.onHeaderChange();
10190 setColumnWidth: function(col_index, width)
10192 // width = "md-2 xs-2..."
10193 if(!this.colModel.config[col_index]) {
10197 var w = width.split(" ");
10199 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10201 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10204 for(var j = 0; j < w.length; j++) {
10210 var size_cls = w[j].split("-");
10212 if(!Number.isInteger(size_cls[1] * 1)) {
10216 if(!this.colModel.config[col_index][size_cls[0]]) {
10220 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10224 h_row[0].classList.replace(
10225 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10226 "col-"+size_cls[0]+"-"+size_cls[1]
10229 for(var i = 0; i < rows.length; i++) {
10231 var size_cls = w[j].split("-");
10233 if(!Number.isInteger(size_cls[1] * 1)) {
10237 if(!this.colModel.config[col_index][size_cls[0]]) {
10241 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10245 rows[i].classList.replace(
10246 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10247 "col-"+size_cls[0]+"-"+size_cls[1]
10251 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10256 // currently only used to find the split on drag..
10257 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10262 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10263 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10272 * @class Roo.bootstrap.TableCell
10273 * @extends Roo.bootstrap.Component
10274 * Bootstrap TableCell class
10275 * @cfg {String} html cell contain text
10276 * @cfg {String} cls cell class
10277 * @cfg {String} tag cell tag (td|th) default td
10278 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10279 * @cfg {String} align Aligns the content in a cell
10280 * @cfg {String} axis Categorizes cells
10281 * @cfg {String} bgcolor Specifies the background color of a cell
10282 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10283 * @cfg {Number} colspan Specifies the number of columns a cell should span
10284 * @cfg {String} headers Specifies one or more header cells a cell is related to
10285 * @cfg {Number} height Sets the height of a cell
10286 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10287 * @cfg {Number} rowspan Sets the number of rows a cell should span
10288 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10289 * @cfg {String} valign Vertical aligns the content in a cell
10290 * @cfg {Number} width Specifies the width of a cell
10293 * Create a new TableCell
10294 * @param {Object} config The config object
10297 Roo.bootstrap.TableCell = function(config){
10298 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10301 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10321 getAutoCreate : function(){
10322 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10329 cfg.tag = this.tag;
10342 cfg.align=this.align
10347 if (this.bgcolor) {
10348 cfg.bgcolor=this.bgcolor
10350 if (this.charoff) {
10351 cfg.charoff=this.charoff
10353 if (this.colspan) {
10354 cfg.colspan=this.colspan
10356 if (this.headers) {
10357 cfg.headers=this.headers
10360 cfg.height=this.height
10363 cfg.nowrap=this.nowrap
10365 if (this.rowspan) {
10366 cfg.rowspan=this.rowspan
10369 cfg.scope=this.scope
10372 cfg.valign=this.valign
10375 cfg.width=this.width
10394 * @class Roo.bootstrap.TableRow
10395 * @extends Roo.bootstrap.Component
10396 * Bootstrap TableRow class
10397 * @cfg {String} cls row class
10398 * @cfg {String} align Aligns the content in a table row
10399 * @cfg {String} bgcolor Specifies a background color for a table row
10400 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10401 * @cfg {String} valign Vertical aligns the content in a table row
10404 * Create a new TableRow
10405 * @param {Object} config The config object
10408 Roo.bootstrap.TableRow = function(config){
10409 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10412 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10420 getAutoCreate : function(){
10421 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10428 cfg.cls = this.cls;
10431 cfg.align = this.align;
10434 cfg.bgcolor = this.bgcolor;
10437 cfg.charoff = this.charoff;
10440 cfg.valign = this.valign;
10458 * @class Roo.bootstrap.TableBody
10459 * @extends Roo.bootstrap.Component
10460 * Bootstrap TableBody class
10461 * @cfg {String} cls element class
10462 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10463 * @cfg {String} align Aligns the content inside the element
10464 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10465 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10468 * Create a new TableBody
10469 * @param {Object} config The config object
10472 Roo.bootstrap.TableBody = function(config){
10473 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10476 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10484 getAutoCreate : function(){
10485 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10495 cfg.tag = this.tag;
10499 cfg.align = this.align;
10502 cfg.charoff = this.charoff;
10505 cfg.valign = this.valign;
10512 // initEvents : function()
10515 // if(!this.store){
10519 // this.store = Roo.factory(this.store, Roo.data);
10520 // this.store.on('load', this.onLoad, this);
10522 // this.store.load();
10526 // onLoad: function ()
10528 // this.fireEvent('load', this);
10538 * Ext JS Library 1.1.1
10539 * Copyright(c) 2006-2007, Ext JS, LLC.
10541 * Originally Released Under LGPL - original licence link has changed is not relivant.
10544 * <script type="text/javascript">
10547 // as we use this in bootstrap.
10548 Roo.namespace('Roo.form');
10550 * @class Roo.form.Action
10551 * Internal Class used to handle form actions
10553 * @param {Roo.form.BasicForm} el The form element or its id
10554 * @param {Object} config Configuration options
10559 // define the action interface
10560 Roo.form.Action = function(form, options){
10562 this.options = options || {};
10565 * Client Validation Failed
10568 Roo.form.Action.CLIENT_INVALID = 'client';
10570 * Server Validation Failed
10573 Roo.form.Action.SERVER_INVALID = 'server';
10575 * Connect to Server Failed
10578 Roo.form.Action.CONNECT_FAILURE = 'connect';
10580 * Reading Data from Server Failed
10583 Roo.form.Action.LOAD_FAILURE = 'load';
10585 Roo.form.Action.prototype = {
10587 failureType : undefined,
10588 response : undefined,
10589 result : undefined,
10591 // interface method
10592 run : function(options){
10596 // interface method
10597 success : function(response){
10601 // interface method
10602 handleResponse : function(response){
10606 // default connection failure
10607 failure : function(response){
10609 this.response = response;
10610 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10611 this.form.afterAction(this, false);
10614 processResponse : function(response){
10615 this.response = response;
10616 if(!response.responseText){
10619 this.result = this.handleResponse(response);
10620 return this.result;
10623 // utility functions used internally
10624 getUrl : function(appendParams){
10625 var url = this.options.url || this.form.url || this.form.el.dom.action;
10627 var p = this.getParams();
10629 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10635 getMethod : function(){
10636 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10639 getParams : function(){
10640 var bp = this.form.baseParams;
10641 var p = this.options.params;
10643 if(typeof p == "object"){
10644 p = Roo.urlEncode(Roo.applyIf(p, bp));
10645 }else if(typeof p == 'string' && bp){
10646 p += '&' + Roo.urlEncode(bp);
10649 p = Roo.urlEncode(bp);
10654 createCallback : function(){
10656 success: this.success,
10657 failure: this.failure,
10659 timeout: (this.form.timeout*1000),
10660 upload: this.form.fileUpload ? this.success : undefined
10665 Roo.form.Action.Submit = function(form, options){
10666 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10669 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10672 haveProgress : false,
10673 uploadComplete : false,
10675 // uploadProgress indicator.
10676 uploadProgress : function()
10678 if (!this.form.progressUrl) {
10682 if (!this.haveProgress) {
10683 Roo.MessageBox.progress("Uploading", "Uploading");
10685 if (this.uploadComplete) {
10686 Roo.MessageBox.hide();
10690 this.haveProgress = true;
10692 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10694 var c = new Roo.data.Connection();
10696 url : this.form.progressUrl,
10701 success : function(req){
10702 //console.log(data);
10706 rdata = Roo.decode(req.responseText)
10708 Roo.log("Invalid data from server..");
10712 if (!rdata || !rdata.success) {
10714 Roo.MessageBox.alert(Roo.encode(rdata));
10717 var data = rdata.data;
10719 if (this.uploadComplete) {
10720 Roo.MessageBox.hide();
10725 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10726 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10729 this.uploadProgress.defer(2000,this);
10732 failure: function(data) {
10733 Roo.log('progress url failed ');
10744 // run get Values on the form, so it syncs any secondary forms.
10745 this.form.getValues();
10747 var o = this.options;
10748 var method = this.getMethod();
10749 var isPost = method == 'POST';
10750 if(o.clientValidation === false || this.form.isValid()){
10752 if (this.form.progressUrl) {
10753 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10754 (new Date() * 1) + '' + Math.random());
10759 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10760 form:this.form.el.dom,
10761 url:this.getUrl(!isPost),
10763 params:isPost ? this.getParams() : null,
10764 isUpload: this.form.fileUpload,
10765 formData : this.form.formData
10768 this.uploadProgress();
10770 }else if (o.clientValidation !== false){ // client validation failed
10771 this.failureType = Roo.form.Action.CLIENT_INVALID;
10772 this.form.afterAction(this, false);
10776 success : function(response)
10778 this.uploadComplete= true;
10779 if (this.haveProgress) {
10780 Roo.MessageBox.hide();
10784 var result = this.processResponse(response);
10785 if(result === true || result.success){
10786 this.form.afterAction(this, true);
10790 this.form.markInvalid(result.errors);
10791 this.failureType = Roo.form.Action.SERVER_INVALID;
10793 this.form.afterAction(this, false);
10795 failure : function(response)
10797 this.uploadComplete= true;
10798 if (this.haveProgress) {
10799 Roo.MessageBox.hide();
10802 this.response = response;
10803 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10804 this.form.afterAction(this, false);
10807 handleResponse : function(response){
10808 if(this.form.errorReader){
10809 var rs = this.form.errorReader.read(response);
10812 for(var i = 0, len = rs.records.length; i < len; i++) {
10813 var r = rs.records[i];
10814 errors[i] = r.data;
10817 if(errors.length < 1){
10821 success : rs.success,
10827 ret = Roo.decode(response.responseText);
10831 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10841 Roo.form.Action.Load = function(form, options){
10842 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10843 this.reader = this.form.reader;
10846 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10851 Roo.Ajax.request(Roo.apply(
10852 this.createCallback(), {
10853 method:this.getMethod(),
10854 url:this.getUrl(false),
10855 params:this.getParams()
10859 success : function(response){
10861 var result = this.processResponse(response);
10862 if(result === true || !result.success || !result.data){
10863 this.failureType = Roo.form.Action.LOAD_FAILURE;
10864 this.form.afterAction(this, false);
10867 this.form.clearInvalid();
10868 this.form.setValues(result.data);
10869 this.form.afterAction(this, true);
10872 handleResponse : function(response){
10873 if(this.form.reader){
10874 var rs = this.form.reader.read(response);
10875 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10877 success : rs.success,
10881 return Roo.decode(response.responseText);
10885 Roo.form.Action.ACTION_TYPES = {
10886 'load' : Roo.form.Action.Load,
10887 'submit' : Roo.form.Action.Submit
10896 * @class Roo.bootstrap.Form
10897 * @extends Roo.bootstrap.Component
10898 * Bootstrap Form class
10899 * @cfg {String} method GET | POST (default POST)
10900 * @cfg {String} labelAlign top | left (default top)
10901 * @cfg {String} align left | right - for navbars
10902 * @cfg {Boolean} loadMask load mask when submit (default true)
10906 * Create a new Form
10907 * @param {Object} config The config object
10911 Roo.bootstrap.Form = function(config){
10913 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10915 Roo.bootstrap.Form.popover.apply();
10919 * @event clientvalidation
10920 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10921 * @param {Form} this
10922 * @param {Boolean} valid true if the form has passed client-side validation
10924 clientvalidation: true,
10926 * @event beforeaction
10927 * Fires before any action is performed. Return false to cancel the action.
10928 * @param {Form} this
10929 * @param {Action} action The action to be performed
10931 beforeaction: true,
10933 * @event actionfailed
10934 * Fires when an action fails.
10935 * @param {Form} this
10936 * @param {Action} action The action that failed
10938 actionfailed : true,
10940 * @event actioncomplete
10941 * Fires when an action is completed.
10942 * @param {Form} this
10943 * @param {Action} action The action that completed
10945 actioncomplete : true
10949 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10952 * @cfg {String} method
10953 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10957 * @cfg {String} url
10958 * The URL to use for form actions if one isn't supplied in the action options.
10961 * @cfg {Boolean} fileUpload
10962 * Set to true if this form is a file upload.
10966 * @cfg {Object} baseParams
10967 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10971 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10975 * @cfg {Sting} align (left|right) for navbar forms
10980 activeAction : null,
10983 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10984 * element by passing it or its id or mask the form itself by passing in true.
10987 waitMsgTarget : false,
10992 * @cfg {Boolean} errorMask (true|false) default false
10997 * @cfg {Number} maskOffset Default 100
11002 * @cfg {Boolean} maskBody
11006 getAutoCreate : function(){
11010 method : this.method || 'POST',
11011 id : this.id || Roo.id(),
11014 if (this.parent().xtype.match(/^Nav/)) {
11015 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11019 if (this.labelAlign == 'left' ) {
11020 cfg.cls += ' form-horizontal';
11026 initEvents : function()
11028 this.el.on('submit', this.onSubmit, this);
11029 // this was added as random key presses on the form where triggering form submit.
11030 this.el.on('keypress', function(e) {
11031 if (e.getCharCode() != 13) {
11034 // we might need to allow it for textareas.. and some other items.
11035 // check e.getTarget().
11037 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11041 Roo.log("keypress blocked");
11043 e.preventDefault();
11049 onSubmit : function(e){
11054 * Returns true if client-side validation on the form is successful.
11057 isValid : function(){
11058 var items = this.getItems();
11060 var target = false;
11062 items.each(function(f){
11068 Roo.log('invalid field: ' + f.name);
11072 if(!target && f.el.isVisible(true)){
11078 if(this.errorMask && !valid){
11079 Roo.bootstrap.Form.popover.mask(this, target);
11086 * Returns true if any fields in this form have changed since their original load.
11089 isDirty : function(){
11091 var items = this.getItems();
11092 items.each(function(f){
11102 * Performs a predefined action (submit or load) or custom actions you define on this form.
11103 * @param {String} actionName The name of the action type
11104 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11105 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11106 * accept other config options):
11108 Property Type Description
11109 ---------------- --------------- ----------------------------------------------------------------------------------
11110 url String The url for the action (defaults to the form's url)
11111 method String The form method to use (defaults to the form's method, or POST if not defined)
11112 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11113 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11114 validate the form on the client (defaults to false)
11116 * @return {BasicForm} this
11118 doAction : function(action, options){
11119 if(typeof action == 'string'){
11120 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11122 if(this.fireEvent('beforeaction', this, action) !== false){
11123 this.beforeAction(action);
11124 action.run.defer(100, action);
11130 beforeAction : function(action){
11131 var o = action.options;
11136 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11138 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11141 // not really supported yet.. ??
11143 //if(this.waitMsgTarget === true){
11144 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11145 //}else if(this.waitMsgTarget){
11146 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11147 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11149 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11155 afterAction : function(action, success){
11156 this.activeAction = null;
11157 var o = action.options;
11162 Roo.get(document.body).unmask();
11168 //if(this.waitMsgTarget === true){
11169 // this.el.unmask();
11170 //}else if(this.waitMsgTarget){
11171 // this.waitMsgTarget.unmask();
11173 // Roo.MessageBox.updateProgress(1);
11174 // Roo.MessageBox.hide();
11181 Roo.callback(o.success, o.scope, [this, action]);
11182 this.fireEvent('actioncomplete', this, action);
11186 // failure condition..
11187 // we have a scenario where updates need confirming.
11188 // eg. if a locking scenario exists..
11189 // we look for { errors : { needs_confirm : true }} in the response.
11191 (typeof(action.result) != 'undefined') &&
11192 (typeof(action.result.errors) != 'undefined') &&
11193 (typeof(action.result.errors.needs_confirm) != 'undefined')
11196 Roo.log("not supported yet");
11199 Roo.MessageBox.confirm(
11200 "Change requires confirmation",
11201 action.result.errorMsg,
11206 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11216 Roo.callback(o.failure, o.scope, [this, action]);
11217 // show an error message if no failed handler is set..
11218 if (!this.hasListener('actionfailed')) {
11219 Roo.log("need to add dialog support");
11221 Roo.MessageBox.alert("Error",
11222 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11223 action.result.errorMsg :
11224 "Saving Failed, please check your entries or try again"
11229 this.fireEvent('actionfailed', this, action);
11234 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11235 * @param {String} id The value to search for
11238 findField : function(id){
11239 var items = this.getItems();
11240 var field = items.get(id);
11242 items.each(function(f){
11243 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11250 return field || null;
11253 * Mark fields in this form invalid in bulk.
11254 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11255 * @return {BasicForm} this
11257 markInvalid : function(errors){
11258 if(errors instanceof Array){
11259 for(var i = 0, len = errors.length; i < len; i++){
11260 var fieldError = errors[i];
11261 var f = this.findField(fieldError.id);
11263 f.markInvalid(fieldError.msg);
11269 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11270 field.markInvalid(errors[id]);
11274 //Roo.each(this.childForms || [], function (f) {
11275 // f.markInvalid(errors);
11282 * Set values for fields in this form in bulk.
11283 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11284 * @return {BasicForm} this
11286 setValues : function(values){
11287 if(values instanceof Array){ // array of objects
11288 for(var i = 0, len = values.length; i < len; i++){
11290 var f = this.findField(v.id);
11292 f.setValue(v.value);
11293 if(this.trackResetOnLoad){
11294 f.originalValue = f.getValue();
11298 }else{ // object hash
11301 if(typeof values[id] != 'function' && (field = this.findField(id))){
11303 if (field.setFromData &&
11304 field.valueField &&
11305 field.displayField &&
11306 // combos' with local stores can
11307 // be queried via setValue()
11308 // to set their value..
11309 (field.store && !field.store.isLocal)
11313 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11314 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11315 field.setFromData(sd);
11317 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11319 field.setFromData(values);
11322 field.setValue(values[id]);
11326 if(this.trackResetOnLoad){
11327 field.originalValue = field.getValue();
11333 //Roo.each(this.childForms || [], function (f) {
11334 // f.setValues(values);
11341 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11342 * they are returned as an array.
11343 * @param {Boolean} asString
11346 getValues : function(asString){
11347 //if (this.childForms) {
11348 // copy values from the child forms
11349 // Roo.each(this.childForms, function (f) {
11350 // this.setValues(f.getValues());
11356 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11357 if(asString === true){
11360 return Roo.urlDecode(fs);
11364 * Returns the fields in this form as an object with key/value pairs.
11365 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11368 getFieldValues : function(with_hidden)
11370 var items = this.getItems();
11372 items.each(function(f){
11374 if (!f.getName()) {
11378 var v = f.getValue();
11380 if (f.inputType =='radio') {
11381 if (typeof(ret[f.getName()]) == 'undefined') {
11382 ret[f.getName()] = ''; // empty..
11385 if (!f.el.dom.checked) {
11389 v = f.el.dom.value;
11393 if(f.xtype == 'MoneyField'){
11394 ret[f.currencyName] = f.getCurrency();
11397 // not sure if this supported any more..
11398 if ((typeof(v) == 'object') && f.getRawValue) {
11399 v = f.getRawValue() ; // dates..
11401 // combo boxes where name != hiddenName...
11402 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11403 ret[f.name] = f.getRawValue();
11405 ret[f.getName()] = v;
11412 * Clears all invalid messages in this form.
11413 * @return {BasicForm} this
11415 clearInvalid : function(){
11416 var items = this.getItems();
11418 items.each(function(f){
11426 * Resets this form.
11427 * @return {BasicForm} this
11429 reset : function(){
11430 var items = this.getItems();
11431 items.each(function(f){
11435 Roo.each(this.childForms || [], function (f) {
11443 getItems : function()
11445 var r=new Roo.util.MixedCollection(false, function(o){
11446 return o.id || (o.id = Roo.id());
11448 var iter = function(el) {
11455 Roo.each(el.items,function(e) {
11464 hideFields : function(items)
11466 Roo.each(items, function(i){
11468 var f = this.findField(i);
11479 showFields : function(items)
11481 Roo.each(items, function(i){
11483 var f = this.findField(i);
11496 Roo.apply(Roo.bootstrap.Form, {
11512 intervalID : false,
11518 if(this.isApplied){
11523 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11524 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11525 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11526 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11529 this.maskEl.top.enableDisplayMode("block");
11530 this.maskEl.left.enableDisplayMode("block");
11531 this.maskEl.bottom.enableDisplayMode("block");
11532 this.maskEl.right.enableDisplayMode("block");
11534 this.toolTip = new Roo.bootstrap.Tooltip({
11535 cls : 'roo-form-error-popover',
11537 'left' : ['r-l', [-2,0], 'right'],
11538 'right' : ['l-r', [2,0], 'left'],
11539 'bottom' : ['tl-bl', [0,2], 'top'],
11540 'top' : [ 'bl-tl', [0,-2], 'bottom']
11544 this.toolTip.render(Roo.get(document.body));
11546 this.toolTip.el.enableDisplayMode("block");
11548 Roo.get(document.body).on('click', function(){
11552 Roo.get(document.body).on('touchstart', function(){
11556 this.isApplied = true
11559 mask : function(form, target)
11563 this.target = target;
11565 if(!this.form.errorMask || !target.el){
11569 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11571 Roo.log(scrollable);
11573 var ot = this.target.el.calcOffsetsTo(scrollable);
11575 var scrollTo = ot[1] - this.form.maskOffset;
11577 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11579 scrollable.scrollTo('top', scrollTo);
11581 var box = this.target.el.getBox();
11583 var zIndex = Roo.bootstrap.Modal.zIndex++;
11586 this.maskEl.top.setStyle('position', 'absolute');
11587 this.maskEl.top.setStyle('z-index', zIndex);
11588 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11589 this.maskEl.top.setLeft(0);
11590 this.maskEl.top.setTop(0);
11591 this.maskEl.top.show();
11593 this.maskEl.left.setStyle('position', 'absolute');
11594 this.maskEl.left.setStyle('z-index', zIndex);
11595 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11596 this.maskEl.left.setLeft(0);
11597 this.maskEl.left.setTop(box.y - this.padding);
11598 this.maskEl.left.show();
11600 this.maskEl.bottom.setStyle('position', 'absolute');
11601 this.maskEl.bottom.setStyle('z-index', zIndex);
11602 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11603 this.maskEl.bottom.setLeft(0);
11604 this.maskEl.bottom.setTop(box.bottom + this.padding);
11605 this.maskEl.bottom.show();
11607 this.maskEl.right.setStyle('position', 'absolute');
11608 this.maskEl.right.setStyle('z-index', zIndex);
11609 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11610 this.maskEl.right.setLeft(box.right + this.padding);
11611 this.maskEl.right.setTop(box.y - this.padding);
11612 this.maskEl.right.show();
11614 this.toolTip.bindEl = this.target.el;
11616 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11618 var tip = this.target.blankText;
11620 if(this.target.getValue() !== '' ) {
11622 if (this.target.invalidText.length) {
11623 tip = this.target.invalidText;
11624 } else if (this.target.regexText.length){
11625 tip = this.target.regexText;
11629 this.toolTip.show(tip);
11631 this.intervalID = window.setInterval(function() {
11632 Roo.bootstrap.Form.popover.unmask();
11635 window.onwheel = function(){ return false;};
11637 (function(){ this.isMasked = true; }).defer(500, this);
11641 unmask : function()
11643 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11647 this.maskEl.top.setStyle('position', 'absolute');
11648 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11649 this.maskEl.top.hide();
11651 this.maskEl.left.setStyle('position', 'absolute');
11652 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11653 this.maskEl.left.hide();
11655 this.maskEl.bottom.setStyle('position', 'absolute');
11656 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11657 this.maskEl.bottom.hide();
11659 this.maskEl.right.setStyle('position', 'absolute');
11660 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11661 this.maskEl.right.hide();
11663 this.toolTip.hide();
11665 this.toolTip.el.hide();
11667 window.onwheel = function(){ return true;};
11669 if(this.intervalID){
11670 window.clearInterval(this.intervalID);
11671 this.intervalID = false;
11674 this.isMasked = false;
11684 * Ext JS Library 1.1.1
11685 * Copyright(c) 2006-2007, Ext JS, LLC.
11687 * Originally Released Under LGPL - original licence link has changed is not relivant.
11690 * <script type="text/javascript">
11693 * @class Roo.form.VTypes
11694 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11697 Roo.form.VTypes = function(){
11698 // closure these in so they are only created once.
11699 var alpha = /^[a-zA-Z_]+$/;
11700 var alphanum = /^[a-zA-Z0-9_]+$/;
11701 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11702 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11704 // All these messages and functions are configurable
11707 * The function used to validate email addresses
11708 * @param {String} value The email address
11710 'email' : function(v){
11711 return email.test(v);
11714 * The error text to display when the email validation function returns false
11717 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11719 * The keystroke filter mask to be applied on email input
11722 'emailMask' : /[a-z0-9_\.\-@]/i,
11725 * The function used to validate URLs
11726 * @param {String} value The URL
11728 'url' : function(v){
11729 return url.test(v);
11732 * The error text to display when the url validation function returns false
11735 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11738 * The function used to validate alpha values
11739 * @param {String} value The value
11741 'alpha' : function(v){
11742 return alpha.test(v);
11745 * The error text to display when the alpha validation function returns false
11748 'alphaText' : 'This field should only contain letters and _',
11750 * The keystroke filter mask to be applied on alpha input
11753 'alphaMask' : /[a-z_]/i,
11756 * The function used to validate alphanumeric values
11757 * @param {String} value The value
11759 'alphanum' : function(v){
11760 return alphanum.test(v);
11763 * The error text to display when the alphanumeric validation function returns false
11766 'alphanumText' : 'This field should only contain letters, numbers and _',
11768 * The keystroke filter mask to be applied on alphanumeric input
11771 'alphanumMask' : /[a-z0-9_]/i
11781 * @class Roo.bootstrap.Input
11782 * @extends Roo.bootstrap.Component
11783 * Bootstrap Input class
11784 * @cfg {Boolean} disabled is it disabled
11785 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11786 * @cfg {String} name name of the input
11787 * @cfg {string} fieldLabel - the label associated
11788 * @cfg {string} placeholder - placeholder to put in text.
11789 * @cfg {string} before - input group add on before
11790 * @cfg {string} after - input group add on after
11791 * @cfg {string} size - (lg|sm) or leave empty..
11792 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11793 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11794 * @cfg {Number} md colspan out of 12 for computer-sized screens
11795 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11796 * @cfg {string} value default value of the input
11797 * @cfg {Number} labelWidth set the width of label
11798 * @cfg {Number} labellg set the width of label (1-12)
11799 * @cfg {Number} labelmd set the width of label (1-12)
11800 * @cfg {Number} labelsm set the width of label (1-12)
11801 * @cfg {Number} labelxs set the width of label (1-12)
11802 * @cfg {String} labelAlign (top|left)
11803 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11804 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11805 * @cfg {String} indicatorpos (left|right) default left
11806 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11807 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11808 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11810 * @cfg {String} align (left|center|right) Default left
11811 * @cfg {Boolean} forceFeedback (true|false) Default false
11814 * Create a new Input
11815 * @param {Object} config The config object
11818 Roo.bootstrap.Input = function(config){
11820 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11825 * Fires when this field receives input focus.
11826 * @param {Roo.form.Field} this
11831 * Fires when this field loses input focus.
11832 * @param {Roo.form.Field} this
11836 * @event specialkey
11837 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11838 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11839 * @param {Roo.form.Field} this
11840 * @param {Roo.EventObject} e The event object
11845 * Fires just before the field blurs if the field value has changed.
11846 * @param {Roo.form.Field} this
11847 * @param {Mixed} newValue The new value
11848 * @param {Mixed} oldValue The original value
11853 * Fires after the field has been marked as invalid.
11854 * @param {Roo.form.Field} this
11855 * @param {String} msg The validation message
11860 * Fires after the field has been validated with no errors.
11861 * @param {Roo.form.Field} this
11866 * Fires after the key up
11867 * @param {Roo.form.Field} this
11868 * @param {Roo.EventObject} e The event Object
11873 * Fires after the user pastes into input
11874 * @param {Roo.form.Field} this
11875 * @param {Roo.EventObject} e The event Object
11881 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11883 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11884 automatic validation (defaults to "keyup").
11886 validationEvent : "keyup",
11888 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11890 validateOnBlur : true,
11892 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11894 validationDelay : 250,
11896 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11898 focusClass : "x-form-focus", // not needed???
11902 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11904 invalidClass : "has-warning",
11907 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11909 validClass : "has-success",
11912 * @cfg {Boolean} hasFeedback (true|false) default true
11914 hasFeedback : true,
11917 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11919 invalidFeedbackClass : "glyphicon-warning-sign",
11922 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11924 validFeedbackClass : "glyphicon-ok",
11927 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11929 selectOnFocus : false,
11932 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11936 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11941 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11943 disableKeyFilter : false,
11946 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11950 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11954 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11956 blankText : "Please complete this mandatory field",
11959 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11963 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11965 maxLength : Number.MAX_VALUE,
11967 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11969 minLengthText : "The minimum length for this field is {0}",
11971 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11973 maxLengthText : "The maximum length for this field is {0}",
11977 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11978 * If available, this function will be called only after the basic validators all return true, and will be passed the
11979 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11983 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11984 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11985 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
11989 * @cfg {String} regexText -- Depricated - use Invalid Text
11994 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12000 autocomplete: false,
12004 inputType : 'text',
12007 placeholder: false,
12012 preventMark: false,
12013 isFormField : true,
12016 labelAlign : false,
12019 formatedValue : false,
12020 forceFeedback : false,
12022 indicatorpos : 'left',
12032 parentLabelAlign : function()
12035 while (parent.parent()) {
12036 parent = parent.parent();
12037 if (typeof(parent.labelAlign) !='undefined') {
12038 return parent.labelAlign;
12045 getAutoCreate : function()
12047 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12053 if(this.inputType != 'hidden'){
12054 cfg.cls = 'form-group' //input-group
12060 type : this.inputType,
12061 value : this.value,
12062 cls : 'form-control',
12063 placeholder : this.placeholder || '',
12064 autocomplete : this.autocomplete || 'new-password'
12066 if (this.inputType == 'file') {
12067 input.style = 'overflow:hidden'; // why not in CSS?
12070 if(this.capture.length){
12071 input.capture = this.capture;
12074 if(this.accept.length){
12075 input.accept = this.accept + "/*";
12079 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12082 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12083 input.maxLength = this.maxLength;
12086 if (this.disabled) {
12087 input.disabled=true;
12090 if (this.readOnly) {
12091 input.readonly=true;
12095 input.name = this.name;
12099 input.cls += ' input-' + this.size;
12103 ['xs','sm','md','lg'].map(function(size){
12104 if (settings[size]) {
12105 cfg.cls += ' col-' + size + '-' + settings[size];
12109 var inputblock = input;
12113 cls: 'glyphicon form-control-feedback'
12116 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12119 cls : 'has-feedback',
12127 if (this.before || this.after) {
12130 cls : 'input-group',
12134 if (this.before && typeof(this.before) == 'string') {
12136 inputblock.cn.push({
12138 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12142 if (this.before && typeof(this.before) == 'object') {
12143 this.before = Roo.factory(this.before);
12145 inputblock.cn.push({
12147 cls : 'roo-input-before input-group-prepend input-group-' +
12148 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12152 inputblock.cn.push(input);
12154 if (this.after && typeof(this.after) == 'string') {
12155 inputblock.cn.push({
12157 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12161 if (this.after && typeof(this.after) == 'object') {
12162 this.after = Roo.factory(this.after);
12164 inputblock.cn.push({
12166 cls : 'roo-input-after input-group-append input-group-' +
12167 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12171 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12172 inputblock.cls += ' has-feedback';
12173 inputblock.cn.push(feedback);
12178 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12179 tooltip : 'This field is required'
12181 if (this.allowBlank ) {
12182 indicator.style = this.allowBlank ? ' display:none' : '';
12184 if (align ==='left' && this.fieldLabel.length) {
12186 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12193 cls : 'control-label col-form-label',
12194 html : this.fieldLabel
12205 var labelCfg = cfg.cn[1];
12206 var contentCfg = cfg.cn[2];
12208 if(this.indicatorpos == 'right'){
12213 cls : 'control-label col-form-label',
12217 html : this.fieldLabel
12231 labelCfg = cfg.cn[0];
12232 contentCfg = cfg.cn[1];
12236 if(this.labelWidth > 12){
12237 labelCfg.style = "width: " + this.labelWidth + 'px';
12240 if(this.labelWidth < 13 && this.labelmd == 0){
12241 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12244 if(this.labellg > 0){
12245 labelCfg.cls += ' col-lg-' + this.labellg;
12246 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12249 if(this.labelmd > 0){
12250 labelCfg.cls += ' col-md-' + this.labelmd;
12251 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12254 if(this.labelsm > 0){
12255 labelCfg.cls += ' col-sm-' + this.labelsm;
12256 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12259 if(this.labelxs > 0){
12260 labelCfg.cls += ' col-xs-' + this.labelxs;
12261 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12265 } else if ( this.fieldLabel.length) {
12272 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12273 tooltip : 'This field is required',
12274 style : this.allowBlank ? ' display:none' : ''
12278 //cls : 'input-group-addon',
12279 html : this.fieldLabel
12287 if(this.indicatorpos == 'right'){
12292 //cls : 'input-group-addon',
12293 html : this.fieldLabel
12298 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12299 tooltip : 'This field is required',
12300 style : this.allowBlank ? ' display:none' : ''
12320 if (this.parentType === 'Navbar' && this.parent().bar) {
12321 cfg.cls += ' navbar-form';
12324 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12325 // on BS4 we do this only if not form
12326 cfg.cls += ' navbar-form';
12334 * return the real input element.
12336 inputEl: function ()
12338 return this.el.select('input.form-control',true).first();
12341 tooltipEl : function()
12343 return this.inputEl();
12346 indicatorEl : function()
12348 if (Roo.bootstrap.version == 4) {
12349 return false; // not enabled in v4 yet.
12352 var indicator = this.el.select('i.roo-required-indicator',true).first();
12362 setDisabled : function(v)
12364 var i = this.inputEl().dom;
12366 i.removeAttribute('disabled');
12370 i.setAttribute('disabled','true');
12372 initEvents : function()
12375 this.inputEl().on("keydown" , this.fireKey, this);
12376 this.inputEl().on("focus", this.onFocus, this);
12377 this.inputEl().on("blur", this.onBlur, this);
12379 this.inputEl().relayEvent('keyup', this);
12380 this.inputEl().relayEvent('paste', this);
12382 this.indicator = this.indicatorEl();
12384 if(this.indicator){
12385 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12388 // reference to original value for reset
12389 this.originalValue = this.getValue();
12390 //Roo.form.TextField.superclass.initEvents.call(this);
12391 if(this.validationEvent == 'keyup'){
12392 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12393 this.inputEl().on('keyup', this.filterValidation, this);
12395 else if(this.validationEvent !== false){
12396 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12399 if(this.selectOnFocus){
12400 this.on("focus", this.preFocus, this);
12403 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12404 this.inputEl().on("keypress", this.filterKeys, this);
12406 this.inputEl().relayEvent('keypress', this);
12409 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12410 this.el.on("click", this.autoSize, this);
12413 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12414 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12417 if (typeof(this.before) == 'object') {
12418 this.before.render(this.el.select('.roo-input-before',true).first());
12420 if (typeof(this.after) == 'object') {
12421 this.after.render(this.el.select('.roo-input-after',true).first());
12424 this.inputEl().on('change', this.onChange, this);
12427 filterValidation : function(e){
12428 if(!e.isNavKeyPress()){
12429 this.validationTask.delay(this.validationDelay);
12433 * Validates the field value
12434 * @return {Boolean} True if the value is valid, else false
12436 validate : function(){
12437 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12438 if(this.disabled || this.validateValue(this.getRawValue())){
12443 this.markInvalid();
12449 * Validates a value according to the field's validation rules and marks the field as invalid
12450 * if the validation fails
12451 * @param {Mixed} value The value to validate
12452 * @return {Boolean} True if the value is valid, else false
12454 validateValue : function(value)
12456 if(this.getVisibilityEl().hasClass('hidden')){
12460 if(value.length < 1) { // if it's blank
12461 if(this.allowBlank){
12467 if(value.length < this.minLength){
12470 if(value.length > this.maxLength){
12474 var vt = Roo.form.VTypes;
12475 if(!vt[this.vtype](value, this)){
12479 if(typeof this.validator == "function"){
12480 var msg = this.validator(value);
12484 if (typeof(msg) == 'string') {
12485 this.invalidText = msg;
12489 if(this.regex && !this.regex.test(value)){
12497 fireKey : function(e){
12498 //Roo.log('field ' + e.getKey());
12499 if(e.isNavKeyPress()){
12500 this.fireEvent("specialkey", this, e);
12503 focus : function (selectText){
12505 this.inputEl().focus();
12506 if(selectText === true){
12507 this.inputEl().dom.select();
12513 onFocus : function(){
12514 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12515 // this.el.addClass(this.focusClass);
12517 if(!this.hasFocus){
12518 this.hasFocus = true;
12519 this.startValue = this.getValue();
12520 this.fireEvent("focus", this);
12524 beforeBlur : Roo.emptyFn,
12528 onBlur : function(){
12530 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12531 //this.el.removeClass(this.focusClass);
12533 this.hasFocus = false;
12534 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12537 var v = this.getValue();
12538 if(String(v) !== String(this.startValue)){
12539 this.fireEvent('change', this, v, this.startValue);
12541 this.fireEvent("blur", this);
12544 onChange : function(e)
12546 var v = this.getValue();
12547 if(String(v) !== String(this.startValue)){
12548 this.fireEvent('change', this, v, this.startValue);
12554 * Resets the current field value to the originally loaded value and clears any validation messages
12556 reset : function(){
12557 this.setValue(this.originalValue);
12561 * Returns the name of the field
12562 * @return {Mixed} name The name field
12564 getName: function(){
12568 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12569 * @return {Mixed} value The field value
12571 getValue : function(){
12573 var v = this.inputEl().getValue();
12578 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12579 * @return {Mixed} value The field value
12581 getRawValue : function(){
12582 var v = this.inputEl().getValue();
12588 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12589 * @param {Mixed} value The value to set
12591 setRawValue : function(v){
12592 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12595 selectText : function(start, end){
12596 var v = this.getRawValue();
12598 start = start === undefined ? 0 : start;
12599 end = end === undefined ? v.length : end;
12600 var d = this.inputEl().dom;
12601 if(d.setSelectionRange){
12602 d.setSelectionRange(start, end);
12603 }else if(d.createTextRange){
12604 var range = d.createTextRange();
12605 range.moveStart("character", start);
12606 range.moveEnd("character", v.length-end);
12613 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12614 * @param {Mixed} value The value to set
12616 setValue : function(v){
12619 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12625 processValue : function(value){
12626 if(this.stripCharsRe){
12627 var newValue = value.replace(this.stripCharsRe, '');
12628 if(newValue !== value){
12629 this.setRawValue(newValue);
12636 preFocus : function(){
12638 if(this.selectOnFocus){
12639 this.inputEl().dom.select();
12642 filterKeys : function(e){
12643 var k = e.getKey();
12644 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12647 var c = e.getCharCode(), cc = String.fromCharCode(c);
12648 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12651 if(!this.maskRe.test(cc)){
12656 * Clear any invalid styles/messages for this field
12658 clearInvalid : function(){
12660 if(!this.el || this.preventMark){ // not rendered
12665 this.el.removeClass([this.invalidClass, 'is-invalid']);
12667 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12669 var feedback = this.el.select('.form-control-feedback', true).first();
12672 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12677 if(this.indicator){
12678 this.indicator.removeClass('visible');
12679 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12682 this.fireEvent('valid', this);
12686 * Mark this field as valid
12688 markValid : function()
12690 if(!this.el || this.preventMark){ // not rendered...
12694 this.el.removeClass([this.invalidClass, this.validClass]);
12695 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12697 var feedback = this.el.select('.form-control-feedback', true).first();
12700 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12703 if(this.indicator){
12704 this.indicator.removeClass('visible');
12705 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12713 if(this.allowBlank && !this.getRawValue().length){
12716 if (Roo.bootstrap.version == 3) {
12717 this.el.addClass(this.validClass);
12719 this.inputEl().addClass('is-valid');
12722 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12724 var feedback = this.el.select('.form-control-feedback', true).first();
12727 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12728 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12733 this.fireEvent('valid', this);
12737 * Mark this field as invalid
12738 * @param {String} msg The validation message
12740 markInvalid : function(msg)
12742 if(!this.el || this.preventMark){ // not rendered
12746 this.el.removeClass([this.invalidClass, this.validClass]);
12747 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12749 var feedback = this.el.select('.form-control-feedback', true).first();
12752 this.el.select('.form-control-feedback', true).first().removeClass(
12753 [this.invalidFeedbackClass, this.validFeedbackClass]);
12760 if(this.allowBlank && !this.getRawValue().length){
12764 if(this.indicator){
12765 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12766 this.indicator.addClass('visible');
12768 if (Roo.bootstrap.version == 3) {
12769 this.el.addClass(this.invalidClass);
12771 this.inputEl().addClass('is-invalid');
12776 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12778 var feedback = this.el.select('.form-control-feedback', true).first();
12781 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12783 if(this.getValue().length || this.forceFeedback){
12784 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12791 this.fireEvent('invalid', this, msg);
12794 SafariOnKeyDown : function(event)
12796 // this is a workaround for a password hang bug on chrome/ webkit.
12797 if (this.inputEl().dom.type != 'password') {
12801 var isSelectAll = false;
12803 if(this.inputEl().dom.selectionEnd > 0){
12804 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12806 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12807 event.preventDefault();
12812 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12814 event.preventDefault();
12815 // this is very hacky as keydown always get's upper case.
12817 var cc = String.fromCharCode(event.getCharCode());
12818 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12822 adjustWidth : function(tag, w){
12823 tag = tag.toLowerCase();
12824 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12825 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12826 if(tag == 'input'){
12829 if(tag == 'textarea'){
12832 }else if(Roo.isOpera){
12833 if(tag == 'input'){
12836 if(tag == 'textarea'){
12844 setFieldLabel : function(v)
12846 if(!this.rendered){
12850 if(this.indicatorEl()){
12851 var ar = this.el.select('label > span',true);
12853 if (ar.elements.length) {
12854 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12855 this.fieldLabel = v;
12859 var br = this.el.select('label',true);
12861 if(br.elements.length) {
12862 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12863 this.fieldLabel = v;
12867 Roo.log('Cannot Found any of label > span || label in input');
12871 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12872 this.fieldLabel = v;
12887 * @class Roo.bootstrap.TextArea
12888 * @extends Roo.bootstrap.Input
12889 * Bootstrap TextArea class
12890 * @cfg {Number} cols Specifies the visible width of a text area
12891 * @cfg {Number} rows Specifies the visible number of lines in a text area
12892 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12893 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12894 * @cfg {string} html text
12897 * Create a new TextArea
12898 * @param {Object} config The config object
12901 Roo.bootstrap.TextArea = function(config){
12902 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12906 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12916 getAutoCreate : function(){
12918 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12924 if(this.inputType != 'hidden'){
12925 cfg.cls = 'form-group' //input-group
12933 value : this.value || '',
12934 html: this.html || '',
12935 cls : 'form-control',
12936 placeholder : this.placeholder || ''
12940 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12941 input.maxLength = this.maxLength;
12945 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12949 input.cols = this.cols;
12952 if (this.readOnly) {
12953 input.readonly = true;
12957 input.name = this.name;
12961 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12965 ['xs','sm','md','lg'].map(function(size){
12966 if (settings[size]) {
12967 cfg.cls += ' col-' + size + '-' + settings[size];
12971 var inputblock = input;
12973 if(this.hasFeedback && !this.allowBlank){
12977 cls: 'glyphicon form-control-feedback'
12981 cls : 'has-feedback',
12990 if (this.before || this.after) {
12993 cls : 'input-group',
12997 inputblock.cn.push({
12999 cls : 'input-group-addon',
13004 inputblock.cn.push(input);
13006 if(this.hasFeedback && !this.allowBlank){
13007 inputblock.cls += ' has-feedback';
13008 inputblock.cn.push(feedback);
13012 inputblock.cn.push({
13014 cls : 'input-group-addon',
13021 if (align ==='left' && this.fieldLabel.length) {
13026 cls : 'control-label',
13027 html : this.fieldLabel
13038 if(this.labelWidth > 12){
13039 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13042 if(this.labelWidth < 13 && this.labelmd == 0){
13043 this.labelmd = this.labelWidth;
13046 if(this.labellg > 0){
13047 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13048 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13051 if(this.labelmd > 0){
13052 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13053 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13056 if(this.labelsm > 0){
13057 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13058 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13061 if(this.labelxs > 0){
13062 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13063 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13066 } else if ( this.fieldLabel.length) {
13071 //cls : 'input-group-addon',
13072 html : this.fieldLabel
13090 if (this.disabled) {
13091 input.disabled=true;
13098 * return the real textarea element.
13100 inputEl: function ()
13102 return this.el.select('textarea.form-control',true).first();
13106 * Clear any invalid styles/messages for this field
13108 clearInvalid : function()
13111 if(!this.el || this.preventMark){ // not rendered
13115 var label = this.el.select('label', true).first();
13116 var icon = this.el.select('i.fa-star', true).first();
13121 this.el.removeClass( this.validClass);
13122 this.inputEl().removeClass('is-invalid');
13124 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13126 var feedback = this.el.select('.form-control-feedback', true).first();
13129 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13134 this.fireEvent('valid', this);
13138 * Mark this field as valid
13140 markValid : function()
13142 if(!this.el || this.preventMark){ // not rendered
13146 this.el.removeClass([this.invalidClass, this.validClass]);
13147 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13149 var feedback = this.el.select('.form-control-feedback', true).first();
13152 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13155 if(this.disabled || this.allowBlank){
13159 var label = this.el.select('label', true).first();
13160 var icon = this.el.select('i.fa-star', true).first();
13165 if (Roo.bootstrap.version == 3) {
13166 this.el.addClass(this.validClass);
13168 this.inputEl().addClass('is-valid');
13172 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13174 var feedback = this.el.select('.form-control-feedback', true).first();
13177 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13178 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13183 this.fireEvent('valid', this);
13187 * Mark this field as invalid
13188 * @param {String} msg The validation message
13190 markInvalid : function(msg)
13192 if(!this.el || this.preventMark){ // not rendered
13196 this.el.removeClass([this.invalidClass, this.validClass]);
13197 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13199 var feedback = this.el.select('.form-control-feedback', true).first();
13202 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13205 if(this.disabled || this.allowBlank){
13209 var label = this.el.select('label', true).first();
13210 var icon = this.el.select('i.fa-star', true).first();
13212 if(!this.getValue().length && label && !icon){
13213 this.el.createChild({
13215 cls : 'text-danger fa fa-lg fa-star',
13216 tooltip : 'This field is required',
13217 style : 'margin-right:5px;'
13221 if (Roo.bootstrap.version == 3) {
13222 this.el.addClass(this.invalidClass);
13224 this.inputEl().addClass('is-invalid');
13227 // fixme ... this may be depricated need to test..
13228 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13230 var feedback = this.el.select('.form-control-feedback', true).first();
13233 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13235 if(this.getValue().length || this.forceFeedback){
13236 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13243 this.fireEvent('invalid', this, msg);
13251 * trigger field - base class for combo..
13256 * @class Roo.bootstrap.TriggerField
13257 * @extends Roo.bootstrap.Input
13258 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13259 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13260 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13261 * for which you can provide a custom implementation. For example:
13263 var trigger = new Roo.bootstrap.TriggerField();
13264 trigger.onTriggerClick = myTriggerFn;
13265 trigger.applyTo('my-field');
13268 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13269 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13270 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13271 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13272 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13275 * Create a new TriggerField.
13276 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13277 * to the base TextField)
13279 Roo.bootstrap.TriggerField = function(config){
13280 this.mimicing = false;
13281 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13284 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13286 * @cfg {String} triggerClass A CSS class to apply to the trigger
13289 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13294 * @cfg {Boolean} removable (true|false) special filter default false
13298 /** @cfg {Boolean} grow @hide */
13299 /** @cfg {Number} growMin @hide */
13300 /** @cfg {Number} growMax @hide */
13306 autoSize: Roo.emptyFn,
13310 deferHeight : true,
13313 actionMode : 'wrap',
13318 getAutoCreate : function(){
13320 var align = this.labelAlign || this.parentLabelAlign();
13325 cls: 'form-group' //input-group
13332 type : this.inputType,
13333 cls : 'form-control',
13334 autocomplete: 'new-password',
13335 placeholder : this.placeholder || ''
13339 input.name = this.name;
13342 input.cls += ' input-' + this.size;
13345 if (this.disabled) {
13346 input.disabled=true;
13349 var inputblock = input;
13351 if(this.hasFeedback && !this.allowBlank){
13355 cls: 'glyphicon form-control-feedback'
13358 if(this.removable && !this.editable ){
13360 cls : 'has-feedback',
13366 cls : 'roo-combo-removable-btn close'
13373 cls : 'has-feedback',
13382 if(this.removable && !this.editable ){
13384 cls : 'roo-removable',
13390 cls : 'roo-combo-removable-btn close'
13397 if (this.before || this.after) {
13400 cls : 'input-group',
13404 inputblock.cn.push({
13406 cls : 'input-group-addon input-group-prepend input-group-text',
13411 inputblock.cn.push(input);
13413 if(this.hasFeedback && !this.allowBlank){
13414 inputblock.cls += ' has-feedback';
13415 inputblock.cn.push(feedback);
13419 inputblock.cn.push({
13421 cls : 'input-group-addon input-group-append input-group-text',
13430 var ibwrap = inputblock;
13435 cls: 'roo-select2-choices',
13439 cls: 'roo-select2-search-field',
13451 cls: 'roo-select2-container input-group',
13456 cls: 'form-hidden-field'
13462 if(!this.multiple && this.showToggleBtn){
13468 if (this.caret != false) {
13471 cls: 'fa fa-' + this.caret
13478 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13480 Roo.bootstrap.version == 3 ? caret : '',
13483 cls: 'combobox-clear',
13497 combobox.cls += ' roo-select2-container-multi';
13501 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13502 tooltip : 'This field is required'
13504 if (Roo.bootstrap.version == 4) {
13507 style : 'display:none'
13512 if (align ==='left' && this.fieldLabel.length) {
13514 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13521 cls : 'control-label',
13522 html : this.fieldLabel
13534 var labelCfg = cfg.cn[1];
13535 var contentCfg = cfg.cn[2];
13537 if(this.indicatorpos == 'right'){
13542 cls : 'control-label',
13546 html : this.fieldLabel
13560 labelCfg = cfg.cn[0];
13561 contentCfg = cfg.cn[1];
13564 if(this.labelWidth > 12){
13565 labelCfg.style = "width: " + this.labelWidth + 'px';
13568 if(this.labelWidth < 13 && this.labelmd == 0){
13569 this.labelmd = this.labelWidth;
13572 if(this.labellg > 0){
13573 labelCfg.cls += ' col-lg-' + this.labellg;
13574 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13577 if(this.labelmd > 0){
13578 labelCfg.cls += ' col-md-' + this.labelmd;
13579 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13582 if(this.labelsm > 0){
13583 labelCfg.cls += ' col-sm-' + this.labelsm;
13584 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13587 if(this.labelxs > 0){
13588 labelCfg.cls += ' col-xs-' + this.labelxs;
13589 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13592 } else if ( this.fieldLabel.length) {
13593 // Roo.log(" label");
13598 //cls : 'input-group-addon',
13599 html : this.fieldLabel
13607 if(this.indicatorpos == 'right'){
13615 html : this.fieldLabel
13629 // Roo.log(" no label && no align");
13636 ['xs','sm','md','lg'].map(function(size){
13637 if (settings[size]) {
13638 cfg.cls += ' col-' + size + '-' + settings[size];
13649 onResize : function(w, h){
13650 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13651 // if(typeof w == 'number'){
13652 // var x = w - this.trigger.getWidth();
13653 // this.inputEl().setWidth(this.adjustWidth('input', x));
13654 // this.trigger.setStyle('left', x+'px');
13659 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13662 getResizeEl : function(){
13663 return this.inputEl();
13667 getPositionEl : function(){
13668 return this.inputEl();
13672 alignErrorIcon : function(){
13673 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13677 initEvents : function(){
13681 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13682 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13683 if(!this.multiple && this.showToggleBtn){
13684 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13685 if(this.hideTrigger){
13686 this.trigger.setDisplayed(false);
13688 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13692 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13695 if(this.removable && !this.editable && !this.tickable){
13696 var close = this.closeTriggerEl();
13699 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13700 close.on('click', this.removeBtnClick, this, close);
13704 //this.trigger.addClassOnOver('x-form-trigger-over');
13705 //this.trigger.addClassOnClick('x-form-trigger-click');
13708 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13712 closeTriggerEl : function()
13714 var close = this.el.select('.roo-combo-removable-btn', true).first();
13715 return close ? close : false;
13718 removeBtnClick : function(e, h, el)
13720 e.preventDefault();
13722 if(this.fireEvent("remove", this) !== false){
13724 this.fireEvent("afterremove", this)
13728 createList : function()
13730 this.list = Roo.get(document.body).createChild({
13731 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13732 cls: 'typeahead typeahead-long dropdown-menu shadow',
13733 style: 'display:none'
13736 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13741 initTrigger : function(){
13746 onDestroy : function(){
13748 this.trigger.removeAllListeners();
13749 // this.trigger.remove();
13752 // this.wrap.remove();
13754 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13758 onFocus : function(){
13759 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13761 if(!this.mimicing){
13762 this.wrap.addClass('x-trigger-wrap-focus');
13763 this.mimicing = true;
13764 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13765 if(this.monitorTab){
13766 this.el.on("keydown", this.checkTab, this);
13773 checkTab : function(e){
13774 if(e.getKey() == e.TAB){
13775 this.triggerBlur();
13780 onBlur : function(){
13785 mimicBlur : function(e, t){
13787 if(!this.wrap.contains(t) && this.validateBlur()){
13788 this.triggerBlur();
13794 triggerBlur : function(){
13795 this.mimicing = false;
13796 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13797 if(this.monitorTab){
13798 this.el.un("keydown", this.checkTab, this);
13800 //this.wrap.removeClass('x-trigger-wrap-focus');
13801 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13805 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13806 validateBlur : function(e, t){
13811 onDisable : function(){
13812 this.inputEl().dom.disabled = true;
13813 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13815 // this.wrap.addClass('x-item-disabled');
13820 onEnable : function(){
13821 this.inputEl().dom.disabled = false;
13822 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13824 // this.el.removeClass('x-item-disabled');
13829 onShow : function(){
13830 var ae = this.getActionEl();
13833 ae.dom.style.display = '';
13834 ae.dom.style.visibility = 'visible';
13840 onHide : function(){
13841 var ae = this.getActionEl();
13842 ae.dom.style.display = 'none';
13846 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13847 * by an implementing function.
13849 * @param {EventObject} e
13851 onTriggerClick : Roo.emptyFn
13859 * @class Roo.bootstrap.CardUploader
13860 * @extends Roo.bootstrap.Button
13861 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13862 * @cfg {Number} errorTimeout default 3000
13863 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13864 * @cfg {Array} html The button text.
13868 * Create a new CardUploader
13869 * @param {Object} config The config object
13872 Roo.bootstrap.CardUploader = function(config){
13876 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13879 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13887 * When a image is clicked on - and needs to display a slideshow or similar..
13888 * @param {Roo.bootstrap.Card} this
13889 * @param {Object} The image information data
13895 * When a the download link is clicked
13896 * @param {Roo.bootstrap.Card} this
13897 * @param {Object} The image information data contains
13904 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13907 errorTimeout : 3000,
13911 fileCollection : false,
13914 getAutoCreate : function()
13918 cls :'form-group' ,
13923 //cls : 'input-group-addon',
13924 html : this.fieldLabel
13932 value : this.value,
13933 cls : 'd-none form-control'
13938 multiple : 'multiple',
13940 cls : 'd-none roo-card-upload-selector'
13944 cls : 'roo-card-uploader-button-container w-100 mb-2'
13947 cls : 'card-columns roo-card-uploader-container'
13957 getChildContainer : function() /// what children are added to.
13959 return this.containerEl;
13962 getButtonContainer : function() /// what children are added to.
13964 return this.el.select(".roo-card-uploader-button-container").first();
13967 initEvents : function()
13970 Roo.bootstrap.Input.prototype.initEvents.call(this);
13974 xns: Roo.bootstrap,
13977 container_method : 'getButtonContainer' ,
13978 html : this.html, // fix changable?
13981 'click' : function(btn, e) {
13990 this.urlAPI = (window.createObjectURL && window) ||
13991 (window.URL && URL.revokeObjectURL && URL) ||
13992 (window.webkitURL && webkitURL);
13997 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
13999 this.selectorEl.on('change', this.onFileSelected, this);
14002 this.images.forEach(function(img) {
14005 this.images = false;
14007 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14013 onClick : function(e)
14015 e.preventDefault();
14017 this.selectorEl.dom.click();
14021 onFileSelected : function(e)
14023 e.preventDefault();
14025 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14029 Roo.each(this.selectorEl.dom.files, function(file){
14030 this.addFile(file);
14039 addFile : function(file)
14042 if(typeof(file) === 'string'){
14043 throw "Add file by name?"; // should not happen
14047 if(!file || !this.urlAPI){
14057 var url = _this.urlAPI.createObjectURL( file);
14060 id : Roo.bootstrap.CardUploader.ID--,
14061 is_uploaded : false,
14065 mimetype : file.type,
14073 * addCard - add an Attachment to the uploader
14074 * @param data - the data about the image to upload
14078 title : "Title of file",
14079 is_uploaded : false,
14080 src : "http://.....",
14081 srcfile : { the File upload object },
14082 mimetype : file.type,
14085 .. any other data...
14091 addCard : function (data)
14093 // hidden input element?
14094 // if the file is not an image...
14095 //then we need to use something other that and header_image
14100 xns : Roo.bootstrap,
14101 xtype : 'CardFooter',
14104 xns : Roo.bootstrap,
14110 xns : Roo.bootstrap,
14112 html : String.format("<small>{0}</small>", data.title),
14113 cls : 'col-10 text-left',
14118 click : function() {
14120 t.fireEvent( "download", t, data );
14126 xns : Roo.bootstrap,
14128 style: 'max-height: 28px; ',
14134 click : function() {
14135 t.removeCard(data.id)
14147 var cn = this.addxtype(
14150 xns : Roo.bootstrap,
14153 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14154 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14155 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14160 initEvents : function() {
14161 Roo.bootstrap.Card.prototype.initEvents.call(this);
14163 this.imgEl = this.el.select('.card-img-top').first();
14165 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14166 this.imgEl.set({ 'pointer' : 'cursor' });
14169 this.getCardFooter().addClass('p-1');
14176 // dont' really need ot update items.
14177 // this.items.push(cn);
14178 this.fileCollection.add(cn);
14180 if (!data.srcfile) {
14181 this.updateInput();
14186 var reader = new FileReader();
14187 reader.addEventListener("load", function() {
14188 data.srcdata = reader.result;
14191 reader.readAsDataURL(data.srcfile);
14196 removeCard : function(id)
14199 var card = this.fileCollection.get(id);
14200 card.data.is_deleted = 1;
14201 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14202 //this.fileCollection.remove(card);
14203 //this.items = this.items.filter(function(e) { return e != card });
14204 // dont' really need ot update items.
14205 card.el.dom.parentNode.removeChild(card.el.dom);
14206 this.updateInput();
14212 this.fileCollection.each(function(card) {
14213 if (card.el.dom && card.el.dom.parentNode) {
14214 card.el.dom.parentNode.removeChild(card.el.dom);
14217 this.fileCollection.clear();
14218 this.updateInput();
14221 updateInput : function()
14224 this.fileCollection.each(function(e) {
14228 this.inputEl().dom.value = JSON.stringify(data);
14238 Roo.bootstrap.CardUploader.ID = -1;/*
14240 * Ext JS Library 1.1.1
14241 * Copyright(c) 2006-2007, Ext JS, LLC.
14243 * Originally Released Under LGPL - original licence link has changed is not relivant.
14246 * <script type="text/javascript">
14251 * @class Roo.data.SortTypes
14253 * Defines the default sorting (casting?) comparison functions used when sorting data.
14255 Roo.data.SortTypes = {
14257 * Default sort that does nothing
14258 * @param {Mixed} s The value being converted
14259 * @return {Mixed} The comparison value
14261 none : function(s){
14266 * The regular expression used to strip tags
14270 stripTagsRE : /<\/?[^>]+>/gi,
14273 * Strips all HTML tags to sort on text only
14274 * @param {Mixed} s The value being converted
14275 * @return {String} The comparison value
14277 asText : function(s){
14278 return String(s).replace(this.stripTagsRE, "");
14282 * Strips all HTML tags to sort on text only - Case insensitive
14283 * @param {Mixed} s The value being converted
14284 * @return {String} The comparison value
14286 asUCText : function(s){
14287 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14291 * Case insensitive string
14292 * @param {Mixed} s The value being converted
14293 * @return {String} The comparison value
14295 asUCString : function(s) {
14296 return String(s).toUpperCase();
14301 * @param {Mixed} s The value being converted
14302 * @return {Number} The comparison value
14304 asDate : function(s) {
14308 if(s instanceof Date){
14309 return s.getTime();
14311 return Date.parse(String(s));
14316 * @param {Mixed} s The value being converted
14317 * @return {Float} The comparison value
14319 asFloat : function(s) {
14320 var val = parseFloat(String(s).replace(/,/g, ""));
14329 * @param {Mixed} s The value being converted
14330 * @return {Number} The comparison value
14332 asInt : function(s) {
14333 var val = parseInt(String(s).replace(/,/g, ""));
14341 * Ext JS Library 1.1.1
14342 * Copyright(c) 2006-2007, Ext JS, LLC.
14344 * Originally Released Under LGPL - original licence link has changed is not relivant.
14347 * <script type="text/javascript">
14351 * @class Roo.data.Record
14352 * Instances of this class encapsulate both record <em>definition</em> information, and record
14353 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14354 * to access Records cached in an {@link Roo.data.Store} object.<br>
14356 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14357 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14360 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14362 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14363 * {@link #create}. The parameters are the same.
14364 * @param {Array} data An associative Array of data values keyed by the field name.
14365 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14366 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14367 * not specified an integer id is generated.
14369 Roo.data.Record = function(data, id){
14370 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14375 * Generate a constructor for a specific record layout.
14376 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14377 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14378 * Each field definition object may contain the following properties: <ul>
14379 * <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,
14380 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14381 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14382 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14383 * is being used, then this is a string containing the javascript expression to reference the data relative to
14384 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14385 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14386 * this may be omitted.</p></li>
14387 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14388 * <ul><li>auto (Default, implies no conversion)</li>
14393 * <li>date</li></ul></p></li>
14394 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14395 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14396 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14397 * by the Reader into an object that will be stored in the Record. It is passed the
14398 * following parameters:<ul>
14399 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14401 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14403 * <br>usage:<br><pre><code>
14404 var TopicRecord = Roo.data.Record.create(
14405 {name: 'title', mapping: 'topic_title'},
14406 {name: 'author', mapping: 'username'},
14407 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14408 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14409 {name: 'lastPoster', mapping: 'user2'},
14410 {name: 'excerpt', mapping: 'post_text'}
14413 var myNewRecord = new TopicRecord({
14414 title: 'Do my job please',
14417 lastPost: new Date(),
14418 lastPoster: 'Animal',
14419 excerpt: 'No way dude!'
14421 myStore.add(myNewRecord);
14426 Roo.data.Record.create = function(o){
14427 var f = function(){
14428 f.superclass.constructor.apply(this, arguments);
14430 Roo.extend(f, Roo.data.Record);
14431 var p = f.prototype;
14432 p.fields = new Roo.util.MixedCollection(false, function(field){
14435 for(var i = 0, len = o.length; i < len; i++){
14436 p.fields.add(new Roo.data.Field(o[i]));
14438 f.getField = function(name){
14439 return p.fields.get(name);
14444 Roo.data.Record.AUTO_ID = 1000;
14445 Roo.data.Record.EDIT = 'edit';
14446 Roo.data.Record.REJECT = 'reject';
14447 Roo.data.Record.COMMIT = 'commit';
14449 Roo.data.Record.prototype = {
14451 * Readonly flag - true if this record has been modified.
14460 join : function(store){
14461 this.store = store;
14465 * Set the named field to the specified value.
14466 * @param {String} name The name of the field to set.
14467 * @param {Object} value The value to set the field to.
14469 set : function(name, value){
14470 if(this.data[name] == value){
14474 if(!this.modified){
14475 this.modified = {};
14477 if(typeof this.modified[name] == 'undefined'){
14478 this.modified[name] = this.data[name];
14480 this.data[name] = value;
14481 if(!this.editing && this.store){
14482 this.store.afterEdit(this);
14487 * Get the value of the named field.
14488 * @param {String} name The name of the field to get the value of.
14489 * @return {Object} The value of the field.
14491 get : function(name){
14492 return this.data[name];
14496 beginEdit : function(){
14497 this.editing = true;
14498 this.modified = {};
14502 cancelEdit : function(){
14503 this.editing = false;
14504 delete this.modified;
14508 endEdit : function(){
14509 this.editing = false;
14510 if(this.dirty && this.store){
14511 this.store.afterEdit(this);
14516 * Usually called by the {@link Roo.data.Store} which owns the Record.
14517 * Rejects all changes made to the Record since either creation, or the last commit operation.
14518 * Modified fields are reverted to their original values.
14520 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14521 * of reject operations.
14523 reject : function(){
14524 var m = this.modified;
14526 if(typeof m[n] != "function"){
14527 this.data[n] = m[n];
14530 this.dirty = false;
14531 delete this.modified;
14532 this.editing = false;
14534 this.store.afterReject(this);
14539 * Usually called by the {@link Roo.data.Store} which owns the Record.
14540 * Commits all changes made to the Record since either creation, or the last commit operation.
14542 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14543 * of commit operations.
14545 commit : function(){
14546 this.dirty = false;
14547 delete this.modified;
14548 this.editing = false;
14550 this.store.afterCommit(this);
14555 hasError : function(){
14556 return this.error != null;
14560 clearError : function(){
14565 * Creates a copy of this record.
14566 * @param {String} id (optional) A new record id if you don't want to use this record's id
14569 copy : function(newId) {
14570 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14574 * Ext JS Library 1.1.1
14575 * Copyright(c) 2006-2007, Ext JS, LLC.
14577 * Originally Released Under LGPL - original licence link has changed is not relivant.
14580 * <script type="text/javascript">
14586 * @class Roo.data.Store
14587 * @extends Roo.util.Observable
14588 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14589 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14591 * 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
14592 * has no knowledge of the format of the data returned by the Proxy.<br>
14594 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14595 * instances from the data object. These records are cached and made available through accessor functions.
14597 * Creates a new Store.
14598 * @param {Object} config A config object containing the objects needed for the Store to access data,
14599 * and read the data into Records.
14601 Roo.data.Store = function(config){
14602 this.data = new Roo.util.MixedCollection(false);
14603 this.data.getKey = function(o){
14606 this.baseParams = {};
14608 this.paramNames = {
14613 "multisort" : "_multisort"
14616 if(config && config.data){
14617 this.inlineData = config.data;
14618 delete config.data;
14621 Roo.apply(this, config);
14623 if(this.reader){ // reader passed
14624 this.reader = Roo.factory(this.reader, Roo.data);
14625 this.reader.xmodule = this.xmodule || false;
14626 if(!this.recordType){
14627 this.recordType = this.reader.recordType;
14629 if(this.reader.onMetaChange){
14630 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14634 if(this.recordType){
14635 this.fields = this.recordType.prototype.fields;
14637 this.modified = [];
14641 * @event datachanged
14642 * Fires when the data cache has changed, and a widget which is using this Store
14643 * as a Record cache should refresh its view.
14644 * @param {Store} this
14646 datachanged : true,
14648 * @event metachange
14649 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14650 * @param {Store} this
14651 * @param {Object} meta The JSON metadata
14656 * Fires when Records have been added to the Store
14657 * @param {Store} this
14658 * @param {Roo.data.Record[]} records The array of Records added
14659 * @param {Number} index The index at which the record(s) were added
14664 * Fires when a Record has been removed from the Store
14665 * @param {Store} this
14666 * @param {Roo.data.Record} record The Record that was removed
14667 * @param {Number} index The index at which the record was removed
14672 * Fires when a Record has been updated
14673 * @param {Store} this
14674 * @param {Roo.data.Record} record The Record that was updated
14675 * @param {String} operation The update operation being performed. Value may be one of:
14677 Roo.data.Record.EDIT
14678 Roo.data.Record.REJECT
14679 Roo.data.Record.COMMIT
14685 * Fires when the data cache has been cleared.
14686 * @param {Store} this
14690 * @event beforeload
14691 * Fires before a request is made for a new data object. If the beforeload handler returns false
14692 * the load action will be canceled.
14693 * @param {Store} this
14694 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14698 * @event beforeloadadd
14699 * Fires after a new set of Records has been loaded.
14700 * @param {Store} this
14701 * @param {Roo.data.Record[]} records The Records that were loaded
14702 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14704 beforeloadadd : true,
14707 * Fires after a new set of Records has been loaded, before they are added to the store.
14708 * @param {Store} this
14709 * @param {Roo.data.Record[]} records The Records that were loaded
14710 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14711 * @params {Object} return from reader
14715 * @event loadexception
14716 * Fires if an exception occurs in the Proxy during loading.
14717 * Called with the signature of the Proxy's "loadexception" event.
14718 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14721 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14722 * @param {Object} load options
14723 * @param {Object} jsonData from your request (normally this contains the Exception)
14725 loadexception : true
14729 this.proxy = Roo.factory(this.proxy, Roo.data);
14730 this.proxy.xmodule = this.xmodule || false;
14731 this.relayEvents(this.proxy, ["loadexception"]);
14733 this.sortToggle = {};
14734 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14736 Roo.data.Store.superclass.constructor.call(this);
14738 if(this.inlineData){
14739 this.loadData(this.inlineData);
14740 delete this.inlineData;
14744 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14746 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14747 * without a remote query - used by combo/forms at present.
14751 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14754 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14757 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14758 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14761 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14762 * on any HTTP request
14765 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14768 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14772 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14773 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14775 remoteSort : false,
14778 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14779 * loaded or when a record is removed. (defaults to false).
14781 pruneModifiedRecords : false,
14784 lastOptions : null,
14787 * Add Records to the Store and fires the add event.
14788 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14790 add : function(records){
14791 records = [].concat(records);
14792 for(var i = 0, len = records.length; i < len; i++){
14793 records[i].join(this);
14795 var index = this.data.length;
14796 this.data.addAll(records);
14797 this.fireEvent("add", this, records, index);
14801 * Remove a Record from the Store and fires the remove event.
14802 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14804 remove : function(record){
14805 var index = this.data.indexOf(record);
14806 this.data.removeAt(index);
14808 if(this.pruneModifiedRecords){
14809 this.modified.remove(record);
14811 this.fireEvent("remove", this, record, index);
14815 * Remove all Records from the Store and fires the clear event.
14817 removeAll : function(){
14819 if(this.pruneModifiedRecords){
14820 this.modified = [];
14822 this.fireEvent("clear", this);
14826 * Inserts Records to the Store at the given index and fires the add event.
14827 * @param {Number} index The start index at which to insert the passed Records.
14828 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14830 insert : function(index, records){
14831 records = [].concat(records);
14832 for(var i = 0, len = records.length; i < len; i++){
14833 this.data.insert(index, records[i]);
14834 records[i].join(this);
14836 this.fireEvent("add", this, records, index);
14840 * Get the index within the cache of the passed Record.
14841 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14842 * @return {Number} The index of the passed Record. Returns -1 if not found.
14844 indexOf : function(record){
14845 return this.data.indexOf(record);
14849 * Get the index within the cache of the Record with the passed id.
14850 * @param {String} id The id of the Record to find.
14851 * @return {Number} The index of the Record. Returns -1 if not found.
14853 indexOfId : function(id){
14854 return this.data.indexOfKey(id);
14858 * Get the Record with the specified id.
14859 * @param {String} id The id of the Record to find.
14860 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14862 getById : function(id){
14863 return this.data.key(id);
14867 * Get the Record at the specified index.
14868 * @param {Number} index The index of the Record to find.
14869 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14871 getAt : function(index){
14872 return this.data.itemAt(index);
14876 * Returns a range of Records between specified indices.
14877 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14878 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14879 * @return {Roo.data.Record[]} An array of Records
14881 getRange : function(start, end){
14882 return this.data.getRange(start, end);
14886 storeOptions : function(o){
14887 o = Roo.apply({}, o);
14890 this.lastOptions = o;
14894 * Loads the Record cache from the configured Proxy using the configured Reader.
14896 * If using remote paging, then the first load call must specify the <em>start</em>
14897 * and <em>limit</em> properties in the options.params property to establish the initial
14898 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14900 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14901 * and this call will return before the new data has been loaded. Perform any post-processing
14902 * in a callback function, or in a "load" event handler.</strong>
14904 * @param {Object} options An object containing properties which control loading options:<ul>
14905 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14906 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14907 * passed the following arguments:<ul>
14908 * <li>r : Roo.data.Record[]</li>
14909 * <li>options: Options object from the load call</li>
14910 * <li>success: Boolean success indicator</li></ul></li>
14911 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14912 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14915 load : function(options){
14916 options = options || {};
14917 if(this.fireEvent("beforeload", this, options) !== false){
14918 this.storeOptions(options);
14919 var p = Roo.apply(options.params || {}, this.baseParams);
14920 // if meta was not loaded from remote source.. try requesting it.
14921 if (!this.reader.metaFromRemote) {
14922 p._requestMeta = 1;
14924 if(this.sortInfo && this.remoteSort){
14925 var pn = this.paramNames;
14926 p[pn["sort"]] = this.sortInfo.field;
14927 p[pn["dir"]] = this.sortInfo.direction;
14929 if (this.multiSort) {
14930 var pn = this.paramNames;
14931 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14934 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14939 * Reloads the Record cache from the configured Proxy using the configured Reader and
14940 * the options from the last load operation performed.
14941 * @param {Object} options (optional) An object containing properties which may override the options
14942 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14943 * the most recently used options are reused).
14945 reload : function(options){
14946 this.load(Roo.applyIf(options||{}, this.lastOptions));
14950 // Called as a callback by the Reader during a load operation.
14951 loadRecords : function(o, options, success){
14952 if(!o || success === false){
14953 if(success !== false){
14954 this.fireEvent("load", this, [], options, o);
14956 if(options.callback){
14957 options.callback.call(options.scope || this, [], options, false);
14961 // if data returned failure - throw an exception.
14962 if (o.success === false) {
14963 // show a message if no listener is registered.
14964 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14965 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14967 // loadmask wil be hooked into this..
14968 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14971 var r = o.records, t = o.totalRecords || r.length;
14973 this.fireEvent("beforeloadadd", this, r, options, o);
14975 if(!options || options.add !== true){
14976 if(this.pruneModifiedRecords){
14977 this.modified = [];
14979 for(var i = 0, len = r.length; i < len; i++){
14983 this.data = this.snapshot;
14984 delete this.snapshot;
14987 this.data.addAll(r);
14988 this.totalLength = t;
14990 this.fireEvent("datachanged", this);
14992 this.totalLength = Math.max(t, this.data.length+r.length);
14996 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
14998 var e = new Roo.data.Record({});
15000 e.set(this.parent.displayField, this.parent.emptyTitle);
15001 e.set(this.parent.valueField, '');
15006 this.fireEvent("load", this, r, options, o);
15007 if(options.callback){
15008 options.callback.call(options.scope || this, r, options, true);
15014 * Loads data from a passed data block. A Reader which understands the format of the data
15015 * must have been configured in the constructor.
15016 * @param {Object} data The data block from which to read the Records. The format of the data expected
15017 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15018 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15020 loadData : function(o, append){
15021 var r = this.reader.readRecords(o);
15022 this.loadRecords(r, {add: append}, true);
15026 * using 'cn' the nested child reader read the child array into it's child stores.
15027 * @param {Object} rec The record with a 'children array
15029 loadDataFromChildren : function(rec)
15031 this.loadData(this.reader.toLoadData(rec));
15036 * Gets the number of cached records.
15038 * <em>If using paging, this may not be the total size of the dataset. If the data object
15039 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15040 * the data set size</em>
15042 getCount : function(){
15043 return this.data.length || 0;
15047 * Gets the total number of records in the dataset as returned by the server.
15049 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15050 * the dataset size</em>
15052 getTotalCount : function(){
15053 return this.totalLength || 0;
15057 * Returns the sort state of the Store as an object with two properties:
15059 field {String} The name of the field by which the Records are sorted
15060 direction {String} The sort order, "ASC" or "DESC"
15063 getSortState : function(){
15064 return this.sortInfo;
15068 applySort : function(){
15069 if(this.sortInfo && !this.remoteSort){
15070 var s = this.sortInfo, f = s.field;
15071 var st = this.fields.get(f).sortType;
15072 var fn = function(r1, r2){
15073 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15074 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15076 this.data.sort(s.direction, fn);
15077 if(this.snapshot && this.snapshot != this.data){
15078 this.snapshot.sort(s.direction, fn);
15084 * Sets the default sort column and order to be used by the next load operation.
15085 * @param {String} fieldName The name of the field to sort by.
15086 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15088 setDefaultSort : function(field, dir){
15089 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15093 * Sort the Records.
15094 * If remote sorting is used, the sort is performed on the server, and the cache is
15095 * reloaded. If local sorting is used, the cache is sorted internally.
15096 * @param {String} fieldName The name of the field to sort by.
15097 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15099 sort : function(fieldName, dir){
15100 var f = this.fields.get(fieldName);
15102 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15104 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15105 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15110 this.sortToggle[f.name] = dir;
15111 this.sortInfo = {field: f.name, direction: dir};
15112 if(!this.remoteSort){
15114 this.fireEvent("datachanged", this);
15116 this.load(this.lastOptions);
15121 * Calls the specified function for each of the Records in the cache.
15122 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15123 * Returning <em>false</em> aborts and exits the iteration.
15124 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15126 each : function(fn, scope){
15127 this.data.each(fn, scope);
15131 * Gets all records modified since the last commit. Modified records are persisted across load operations
15132 * (e.g., during paging).
15133 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15135 getModifiedRecords : function(){
15136 return this.modified;
15140 createFilterFn : function(property, value, anyMatch){
15141 if(!value.exec){ // not a regex
15142 value = String(value);
15143 if(value.length == 0){
15146 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15148 return function(r){
15149 return value.test(r.data[property]);
15154 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15155 * @param {String} property A field on your records
15156 * @param {Number} start The record index to start at (defaults to 0)
15157 * @param {Number} end The last record index to include (defaults to length - 1)
15158 * @return {Number} The sum
15160 sum : function(property, start, end){
15161 var rs = this.data.items, v = 0;
15162 start = start || 0;
15163 end = (end || end === 0) ? end : rs.length-1;
15165 for(var i = start; i <= end; i++){
15166 v += (rs[i].data[property] || 0);
15172 * Filter the records by a specified property.
15173 * @param {String} field A field on your records
15174 * @param {String/RegExp} value Either a string that the field
15175 * should start with or a RegExp to test against the field
15176 * @param {Boolean} anyMatch True to match any part not just the beginning
15178 filter : function(property, value, anyMatch){
15179 var fn = this.createFilterFn(property, value, anyMatch);
15180 return fn ? this.filterBy(fn) : this.clearFilter();
15184 * Filter by a function. The specified function will be called with each
15185 * record in this data source. If the function returns true the record is included,
15186 * otherwise it is filtered.
15187 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15188 * @param {Object} scope (optional) The scope of the function (defaults to this)
15190 filterBy : function(fn, scope){
15191 this.snapshot = this.snapshot || this.data;
15192 this.data = this.queryBy(fn, scope||this);
15193 this.fireEvent("datachanged", this);
15197 * Query the records by a specified property.
15198 * @param {String} field A field on your records
15199 * @param {String/RegExp} value Either a string that the field
15200 * should start with or a RegExp to test against the field
15201 * @param {Boolean} anyMatch True to match any part not just the beginning
15202 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15204 query : function(property, value, anyMatch){
15205 var fn = this.createFilterFn(property, value, anyMatch);
15206 return fn ? this.queryBy(fn) : this.data.clone();
15210 * Query by a function. The specified function will be called with each
15211 * record in this data source. If the function returns true the record is included
15213 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15214 * @param {Object} scope (optional) The scope of the function (defaults to this)
15215 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15217 queryBy : function(fn, scope){
15218 var data = this.snapshot || this.data;
15219 return data.filterBy(fn, scope||this);
15223 * Collects unique values for a particular dataIndex from this store.
15224 * @param {String} dataIndex The property to collect
15225 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15226 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15227 * @return {Array} An array of the unique values
15229 collect : function(dataIndex, allowNull, bypassFilter){
15230 var d = (bypassFilter === true && this.snapshot) ?
15231 this.snapshot.items : this.data.items;
15232 var v, sv, r = [], l = {};
15233 for(var i = 0, len = d.length; i < len; i++){
15234 v = d[i].data[dataIndex];
15236 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15245 * Revert to a view of the Record cache with no filtering applied.
15246 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15248 clearFilter : function(suppressEvent){
15249 if(this.snapshot && this.snapshot != this.data){
15250 this.data = this.snapshot;
15251 delete this.snapshot;
15252 if(suppressEvent !== true){
15253 this.fireEvent("datachanged", this);
15259 afterEdit : function(record){
15260 if(this.modified.indexOf(record) == -1){
15261 this.modified.push(record);
15263 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15267 afterReject : function(record){
15268 this.modified.remove(record);
15269 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15273 afterCommit : function(record){
15274 this.modified.remove(record);
15275 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15279 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15280 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15282 commitChanges : function(){
15283 var m = this.modified.slice(0);
15284 this.modified = [];
15285 for(var i = 0, len = m.length; i < len; i++){
15291 * Cancel outstanding changes on all changed records.
15293 rejectChanges : function(){
15294 var m = this.modified.slice(0);
15295 this.modified = [];
15296 for(var i = 0, len = m.length; i < len; i++){
15301 onMetaChange : function(meta, rtype, o){
15302 this.recordType = rtype;
15303 this.fields = rtype.prototype.fields;
15304 delete this.snapshot;
15305 this.sortInfo = meta.sortInfo || this.sortInfo;
15306 this.modified = [];
15307 this.fireEvent('metachange', this, this.reader.meta);
15310 moveIndex : function(data, type)
15312 var index = this.indexOf(data);
15314 var newIndex = index + type;
15318 this.insert(newIndex, data);
15323 * Ext JS Library 1.1.1
15324 * Copyright(c) 2006-2007, Ext JS, LLC.
15326 * Originally Released Under LGPL - original licence link has changed is not relivant.
15329 * <script type="text/javascript">
15333 * @class Roo.data.SimpleStore
15334 * @extends Roo.data.Store
15335 * Small helper class to make creating Stores from Array data easier.
15336 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15337 * @cfg {Array} fields An array of field definition objects, or field name strings.
15338 * @cfg {Object} an existing reader (eg. copied from another store)
15339 * @cfg {Array} data The multi-dimensional array of data
15341 * @param {Object} config
15343 Roo.data.SimpleStore = function(config)
15345 Roo.data.SimpleStore.superclass.constructor.call(this, {
15347 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15350 Roo.data.Record.create(config.fields)
15352 proxy : new Roo.data.MemoryProxy(config.data)
15356 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15358 * Ext JS Library 1.1.1
15359 * Copyright(c) 2006-2007, Ext JS, LLC.
15361 * Originally Released Under LGPL - original licence link has changed is not relivant.
15364 * <script type="text/javascript">
15369 * @extends Roo.data.Store
15370 * @class Roo.data.JsonStore
15371 * Small helper class to make creating Stores for JSON data easier. <br/>
15373 var store = new Roo.data.JsonStore({
15374 url: 'get-images.php',
15376 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15379 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15380 * JsonReader and HttpProxy (unless inline data is provided).</b>
15381 * @cfg {Array} fields An array of field definition objects, or field name strings.
15383 * @param {Object} config
15385 Roo.data.JsonStore = function(c){
15386 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15387 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15388 reader: new Roo.data.JsonReader(c, c.fields)
15391 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15393 * Ext JS Library 1.1.1
15394 * Copyright(c) 2006-2007, Ext JS, LLC.
15396 * Originally Released Under LGPL - original licence link has changed is not relivant.
15399 * <script type="text/javascript">
15403 Roo.data.Field = function(config){
15404 if(typeof config == "string"){
15405 config = {name: config};
15407 Roo.apply(this, config);
15410 this.type = "auto";
15413 var st = Roo.data.SortTypes;
15414 // named sortTypes are supported, here we look them up
15415 if(typeof this.sortType == "string"){
15416 this.sortType = st[this.sortType];
15419 // set default sortType for strings and dates
15420 if(!this.sortType){
15423 this.sortType = st.asUCString;
15426 this.sortType = st.asDate;
15429 this.sortType = st.none;
15434 var stripRe = /[\$,%]/g;
15436 // prebuilt conversion function for this field, instead of
15437 // switching every time we're reading a value
15439 var cv, dateFormat = this.dateFormat;
15444 cv = function(v){ return v; };
15447 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15451 return v !== undefined && v !== null && v !== '' ?
15452 parseInt(String(v).replace(stripRe, ""), 10) : '';
15457 return v !== undefined && v !== null && v !== '' ?
15458 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15463 cv = function(v){ return v === true || v === "true" || v == 1; };
15470 if(v instanceof Date){
15474 if(dateFormat == "timestamp"){
15475 return new Date(v*1000);
15477 return Date.parseDate(v, dateFormat);
15479 var parsed = Date.parse(v);
15480 return parsed ? new Date(parsed) : null;
15489 Roo.data.Field.prototype = {
15497 * Ext JS Library 1.1.1
15498 * Copyright(c) 2006-2007, Ext JS, LLC.
15500 * Originally Released Under LGPL - original licence link has changed is not relivant.
15503 * <script type="text/javascript">
15506 // Base class for reading structured data from a data source. This class is intended to be
15507 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15510 * @class Roo.data.DataReader
15511 * Base class for reading structured data from a data source. This class is intended to be
15512 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15515 Roo.data.DataReader = function(meta, recordType){
15519 this.recordType = recordType instanceof Array ?
15520 Roo.data.Record.create(recordType) : recordType;
15523 Roo.data.DataReader.prototype = {
15526 readerType : 'Data',
15528 * Create an empty record
15529 * @param {Object} data (optional) - overlay some values
15530 * @return {Roo.data.Record} record created.
15532 newRow : function(d) {
15534 this.recordType.prototype.fields.each(function(c) {
15536 case 'int' : da[c.name] = 0; break;
15537 case 'date' : da[c.name] = new Date(); break;
15538 case 'float' : da[c.name] = 0.0; break;
15539 case 'boolean' : da[c.name] = false; break;
15540 default : da[c.name] = ""; break;
15544 return new this.recordType(Roo.apply(da, d));
15550 * Ext JS Library 1.1.1
15551 * Copyright(c) 2006-2007, Ext JS, LLC.
15553 * Originally Released Under LGPL - original licence link has changed is not relivant.
15556 * <script type="text/javascript">
15560 * @class Roo.data.DataProxy
15561 * @extends Roo.data.Observable
15562 * This class is an abstract base class for implementations which provide retrieval of
15563 * unformatted data objects.<br>
15565 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15566 * (of the appropriate type which knows how to parse the data object) to provide a block of
15567 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15569 * Custom implementations must implement the load method as described in
15570 * {@link Roo.data.HttpProxy#load}.
15572 Roo.data.DataProxy = function(){
15575 * @event beforeload
15576 * Fires before a network request is made to retrieve a data object.
15577 * @param {Object} This DataProxy object.
15578 * @param {Object} params The params parameter to the load function.
15583 * Fires before the load method's callback is called.
15584 * @param {Object} This DataProxy object.
15585 * @param {Object} o The data object.
15586 * @param {Object} arg The callback argument object passed to the load function.
15590 * @event loadexception
15591 * Fires if an Exception occurs during data retrieval.
15592 * @param {Object} This DataProxy object.
15593 * @param {Object} o The data object.
15594 * @param {Object} arg The callback argument object passed to the load function.
15595 * @param {Object} e The Exception.
15597 loadexception : true
15599 Roo.data.DataProxy.superclass.constructor.call(this);
15602 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15605 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15609 * Ext JS Library 1.1.1
15610 * Copyright(c) 2006-2007, Ext JS, LLC.
15612 * Originally Released Under LGPL - original licence link has changed is not relivant.
15615 * <script type="text/javascript">
15618 * @class Roo.data.MemoryProxy
15619 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15620 * to the Reader when its load method is called.
15622 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15624 Roo.data.MemoryProxy = function(data){
15628 Roo.data.MemoryProxy.superclass.constructor.call(this);
15632 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15635 * Load data from the requested source (in this case an in-memory
15636 * data object passed to the constructor), read the data object into
15637 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15638 * process that block using the passed callback.
15639 * @param {Object} params This parameter is not used by the MemoryProxy class.
15640 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15641 * object into a block of Roo.data.Records.
15642 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15643 * The function must be passed <ul>
15644 * <li>The Record block object</li>
15645 * <li>The "arg" argument from the load function</li>
15646 * <li>A boolean success indicator</li>
15648 * @param {Object} scope The scope in which to call the callback
15649 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15651 load : function(params, reader, callback, scope, arg){
15652 params = params || {};
15655 result = reader.readRecords(params.data ? params.data :this.data);
15657 this.fireEvent("loadexception", this, arg, null, e);
15658 callback.call(scope, null, arg, false);
15661 callback.call(scope, result, arg, true);
15665 update : function(params, records){
15670 * Ext JS Library 1.1.1
15671 * Copyright(c) 2006-2007, Ext JS, LLC.
15673 * Originally Released Under LGPL - original licence link has changed is not relivant.
15676 * <script type="text/javascript">
15679 * @class Roo.data.HttpProxy
15680 * @extends Roo.data.DataProxy
15681 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15682 * configured to reference a certain URL.<br><br>
15684 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15685 * from which the running page was served.<br><br>
15687 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15689 * Be aware that to enable the browser to parse an XML document, the server must set
15690 * the Content-Type header in the HTTP response to "text/xml".
15692 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15693 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15694 * will be used to make the request.
15696 Roo.data.HttpProxy = function(conn){
15697 Roo.data.HttpProxy.superclass.constructor.call(this);
15698 // is conn a conn config or a real conn?
15700 this.useAjax = !conn || !conn.events;
15704 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15705 // thse are take from connection...
15708 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15711 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15712 * extra parameters to each request made by this object. (defaults to undefined)
15715 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15716 * to each request made by this object. (defaults to undefined)
15719 * @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)
15722 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15725 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15731 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15735 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15736 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15737 * a finer-grained basis than the DataProxy events.
15739 getConnection : function(){
15740 return this.useAjax ? Roo.Ajax : this.conn;
15744 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15745 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15746 * process that block using the passed callback.
15747 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15748 * for the request to the remote server.
15749 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15750 * object into a block of Roo.data.Records.
15751 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15752 * The function must be passed <ul>
15753 * <li>The Record block object</li>
15754 * <li>The "arg" argument from the load function</li>
15755 * <li>A boolean success indicator</li>
15757 * @param {Object} scope The scope in which to call the callback
15758 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15760 load : function(params, reader, callback, scope, arg){
15761 if(this.fireEvent("beforeload", this, params) !== false){
15763 params : params || {},
15765 callback : callback,
15770 callback : this.loadResponse,
15774 Roo.applyIf(o, this.conn);
15775 if(this.activeRequest){
15776 Roo.Ajax.abort(this.activeRequest);
15778 this.activeRequest = Roo.Ajax.request(o);
15780 this.conn.request(o);
15783 callback.call(scope||this, null, arg, false);
15788 loadResponse : function(o, success, response){
15789 delete this.activeRequest;
15791 this.fireEvent("loadexception", this, o, response);
15792 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15797 result = o.reader.read(response);
15799 this.fireEvent("loadexception", this, o, response, e);
15800 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15804 this.fireEvent("load", this, o, o.request.arg);
15805 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15809 update : function(dataSet){
15814 updateResponse : function(dataSet){
15819 * Ext JS Library 1.1.1
15820 * Copyright(c) 2006-2007, Ext JS, LLC.
15822 * Originally Released Under LGPL - original licence link has changed is not relivant.
15825 * <script type="text/javascript">
15829 * @class Roo.data.ScriptTagProxy
15830 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15831 * other than the originating domain of the running page.<br><br>
15833 * <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
15834 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15836 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15837 * source code that is used as the source inside a <script> tag.<br><br>
15839 * In order for the browser to process the returned data, the server must wrap the data object
15840 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15841 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15842 * depending on whether the callback name was passed:
15845 boolean scriptTag = false;
15846 String cb = request.getParameter("callback");
15849 response.setContentType("text/javascript");
15851 response.setContentType("application/x-json");
15853 Writer out = response.getWriter();
15855 out.write(cb + "(");
15857 out.print(dataBlock.toJsonString());
15864 * @param {Object} config A configuration object.
15866 Roo.data.ScriptTagProxy = function(config){
15867 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15868 Roo.apply(this, config);
15869 this.head = document.getElementsByTagName("head")[0];
15872 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15874 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15876 * @cfg {String} url The URL from which to request the data object.
15879 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15883 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15884 * the server the name of the callback function set up by the load call to process the returned data object.
15885 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15886 * javascript output which calls this named function passing the data object as its only parameter.
15888 callbackParam : "callback",
15890 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15891 * name to the request.
15896 * Load data from the configured URL, read the data object into
15897 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15898 * process that block using the passed callback.
15899 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15900 * for the request to the remote server.
15901 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15902 * object into a block of Roo.data.Records.
15903 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15904 * The function must be passed <ul>
15905 * <li>The Record block object</li>
15906 * <li>The "arg" argument from the load function</li>
15907 * <li>A boolean success indicator</li>
15909 * @param {Object} scope The scope in which to call the callback
15910 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15912 load : function(params, reader, callback, scope, arg){
15913 if(this.fireEvent("beforeload", this, params) !== false){
15915 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15917 var url = this.url;
15918 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15920 url += "&_dc=" + (new Date().getTime());
15922 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15925 cb : "stcCallback"+transId,
15926 scriptId : "stcScript"+transId,
15930 callback : callback,
15936 window[trans.cb] = function(o){
15937 conn.handleResponse(o, trans);
15940 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15942 if(this.autoAbort !== false){
15946 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15948 var script = document.createElement("script");
15949 script.setAttribute("src", url);
15950 script.setAttribute("type", "text/javascript");
15951 script.setAttribute("id", trans.scriptId);
15952 this.head.appendChild(script);
15954 this.trans = trans;
15956 callback.call(scope||this, null, arg, false);
15961 isLoading : function(){
15962 return this.trans ? true : false;
15966 * Abort the current server request.
15968 abort : function(){
15969 if(this.isLoading()){
15970 this.destroyTrans(this.trans);
15975 destroyTrans : function(trans, isLoaded){
15976 this.head.removeChild(document.getElementById(trans.scriptId));
15977 clearTimeout(trans.timeoutId);
15979 window[trans.cb] = undefined;
15981 delete window[trans.cb];
15984 // if hasn't been loaded, wait for load to remove it to prevent script error
15985 window[trans.cb] = function(){
15986 window[trans.cb] = undefined;
15988 delete window[trans.cb];
15995 handleResponse : function(o, trans){
15996 this.trans = false;
15997 this.destroyTrans(trans, true);
16000 result = trans.reader.readRecords(o);
16002 this.fireEvent("loadexception", this, o, trans.arg, e);
16003 trans.callback.call(trans.scope||window, null, trans.arg, false);
16006 this.fireEvent("load", this, o, trans.arg);
16007 trans.callback.call(trans.scope||window, result, trans.arg, true);
16011 handleFailure : function(trans){
16012 this.trans = false;
16013 this.destroyTrans(trans, false);
16014 this.fireEvent("loadexception", this, null, trans.arg);
16015 trans.callback.call(trans.scope||window, null, trans.arg, false);
16019 * Ext JS Library 1.1.1
16020 * Copyright(c) 2006-2007, Ext JS, LLC.
16022 * Originally Released Under LGPL - original licence link has changed is not relivant.
16025 * <script type="text/javascript">
16029 * @class Roo.data.JsonReader
16030 * @extends Roo.data.DataReader
16031 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16032 * based on mappings in a provided Roo.data.Record constructor.
16034 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16035 * in the reply previously.
16040 var RecordDef = Roo.data.Record.create([
16041 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16042 {name: 'occupation'} // This field will use "occupation" as the mapping.
16044 var myReader = new Roo.data.JsonReader({
16045 totalProperty: "results", // The property which contains the total dataset size (optional)
16046 root: "rows", // The property which contains an Array of row objects
16047 id: "id" // The property within each row object that provides an ID for the record (optional)
16051 * This would consume a JSON file like this:
16053 { 'results': 2, 'rows': [
16054 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16055 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16058 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16059 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16060 * paged from the remote server.
16061 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16062 * @cfg {String} root name of the property which contains the Array of row objects.
16063 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16064 * @cfg {Array} fields Array of field definition objects
16066 * Create a new JsonReader
16067 * @param {Object} meta Metadata configuration options
16068 * @param {Object} recordType Either an Array of field definition objects,
16069 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16071 Roo.data.JsonReader = function(meta, recordType){
16074 // set some defaults:
16075 Roo.applyIf(meta, {
16076 totalProperty: 'total',
16077 successProperty : 'success',
16082 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16084 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16086 readerType : 'Json',
16089 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16090 * Used by Store query builder to append _requestMeta to params.
16093 metaFromRemote : false,
16095 * This method is only used by a DataProxy which has retrieved data from a remote server.
16096 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16097 * @return {Object} data A data block which is used by an Roo.data.Store object as
16098 * a cache of Roo.data.Records.
16100 read : function(response){
16101 var json = response.responseText;
16103 var o = /* eval:var:o */ eval("("+json+")");
16105 throw {message: "JsonReader.read: Json object not found"};
16111 this.metaFromRemote = true;
16112 this.meta = o.metaData;
16113 this.recordType = Roo.data.Record.create(o.metaData.fields);
16114 this.onMetaChange(this.meta, this.recordType, o);
16116 return this.readRecords(o);
16119 // private function a store will implement
16120 onMetaChange : function(meta, recordType, o){
16127 simpleAccess: function(obj, subsc) {
16134 getJsonAccessor: function(){
16136 return function(expr) {
16138 return(re.test(expr))
16139 ? new Function("obj", "return obj." + expr)
16144 return Roo.emptyFn;
16149 * Create a data block containing Roo.data.Records from an XML document.
16150 * @param {Object} o An object which contains an Array of row objects in the property specified
16151 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16152 * which contains the total size of the dataset.
16153 * @return {Object} data A data block which is used by an Roo.data.Store object as
16154 * a cache of Roo.data.Records.
16156 readRecords : function(o){
16158 * After any data loads, the raw JSON data is available for further custom processing.
16162 var s = this.meta, Record = this.recordType,
16163 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16165 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16167 if(s.totalProperty) {
16168 this.getTotal = this.getJsonAccessor(s.totalProperty);
16170 if(s.successProperty) {
16171 this.getSuccess = this.getJsonAccessor(s.successProperty);
16173 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16175 var g = this.getJsonAccessor(s.id);
16176 this.getId = function(rec) {
16178 return (r === undefined || r === "") ? null : r;
16181 this.getId = function(){return null;};
16184 for(var jj = 0; jj < fl; jj++){
16186 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16187 this.ef[jj] = this.getJsonAccessor(map);
16191 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16192 if(s.totalProperty){
16193 var vt = parseInt(this.getTotal(o), 10);
16198 if(s.successProperty){
16199 var vs = this.getSuccess(o);
16200 if(vs === false || vs === 'false'){
16205 for(var i = 0; i < c; i++){
16208 var id = this.getId(n);
16209 for(var j = 0; j < fl; j++){
16211 var v = this.ef[j](n);
16213 Roo.log('missing convert for ' + f.name);
16217 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16219 var record = new Record(values, id);
16221 records[i] = record;
16227 totalRecords : totalRecords
16230 // used when loading children.. @see loadDataFromChildren
16231 toLoadData: function(rec)
16233 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16234 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16235 return { data : data, total : data.length };
16240 * Ext JS Library 1.1.1
16241 * Copyright(c) 2006-2007, Ext JS, LLC.
16243 * Originally Released Under LGPL - original licence link has changed is not relivant.
16246 * <script type="text/javascript">
16250 * @class Roo.data.ArrayReader
16251 * @extends Roo.data.DataReader
16252 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16253 * Each element of that Array represents a row of data fields. The
16254 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16255 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16259 var RecordDef = Roo.data.Record.create([
16260 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16261 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16263 var myReader = new Roo.data.ArrayReader({
16264 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16268 * This would consume an Array like this:
16270 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16274 * Create a new JsonReader
16275 * @param {Object} meta Metadata configuration options.
16276 * @param {Object|Array} recordType Either an Array of field definition objects
16278 * @cfg {Array} fields Array of field definition objects
16279 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16280 * as specified to {@link Roo.data.Record#create},
16281 * or an {@link Roo.data.Record} object
16284 * created using {@link Roo.data.Record#create}.
16286 Roo.data.ArrayReader = function(meta, recordType)
16288 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16291 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16294 * Create a data block containing Roo.data.Records from an XML document.
16295 * @param {Object} o An Array of row objects which represents the dataset.
16296 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16297 * a cache of Roo.data.Records.
16299 readRecords : function(o)
16301 var sid = this.meta ? this.meta.id : null;
16302 var recordType = this.recordType, fields = recordType.prototype.fields;
16305 for(var i = 0; i < root.length; i++){
16308 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16309 for(var j = 0, jlen = fields.length; j < jlen; j++){
16310 var f = fields.items[j];
16311 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16312 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16314 values[f.name] = v;
16316 var record = new recordType(values, id);
16318 records[records.length] = record;
16322 totalRecords : records.length
16325 // used when loading children.. @see loadDataFromChildren
16326 toLoadData: function(rec)
16328 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16329 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16340 * @class Roo.bootstrap.ComboBox
16341 * @extends Roo.bootstrap.TriggerField
16342 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16343 * @cfg {Boolean} append (true|false) default false
16344 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16345 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16346 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16347 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16348 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16349 * @cfg {Boolean} animate default true
16350 * @cfg {Boolean} emptyResultText only for touch device
16351 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16352 * @cfg {String} emptyTitle default ''
16353 * @cfg {Number} width fixed with? experimental
16355 * Create a new ComboBox.
16356 * @param {Object} config Configuration options
16358 Roo.bootstrap.ComboBox = function(config){
16359 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16363 * Fires when the dropdown list is expanded
16364 * @param {Roo.bootstrap.ComboBox} combo This combo box
16369 * Fires when the dropdown list is collapsed
16370 * @param {Roo.bootstrap.ComboBox} combo This combo box
16374 * @event beforeselect
16375 * Fires before a list item is selected. Return false to cancel the selection.
16376 * @param {Roo.bootstrap.ComboBox} combo This combo box
16377 * @param {Roo.data.Record} record The data record returned from the underlying store
16378 * @param {Number} index The index of the selected item in the dropdown list
16380 'beforeselect' : true,
16383 * Fires when a list item is selected
16384 * @param {Roo.bootstrap.ComboBox} combo This combo box
16385 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16386 * @param {Number} index The index of the selected item in the dropdown list
16390 * @event beforequery
16391 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16392 * The event object passed has these properties:
16393 * @param {Roo.bootstrap.ComboBox} combo This combo box
16394 * @param {String} query The query
16395 * @param {Boolean} forceAll true to force "all" query
16396 * @param {Boolean} cancel true to cancel the query
16397 * @param {Object} e The query event object
16399 'beforequery': true,
16402 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16403 * @param {Roo.bootstrap.ComboBox} combo This combo box
16408 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16409 * @param {Roo.bootstrap.ComboBox} combo This combo box
16410 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16415 * Fires when the remove value from the combobox array
16416 * @param {Roo.bootstrap.ComboBox} combo This combo box
16420 * @event afterremove
16421 * Fires when the remove value from the combobox array
16422 * @param {Roo.bootstrap.ComboBox} combo This combo box
16424 'afterremove' : true,
16426 * @event specialfilter
16427 * Fires when specialfilter
16428 * @param {Roo.bootstrap.ComboBox} combo This combo box
16430 'specialfilter' : true,
16433 * Fires when tick the element
16434 * @param {Roo.bootstrap.ComboBox} combo This combo box
16438 * @event touchviewdisplay
16439 * Fires when touch view require special display (default is using displayField)
16440 * @param {Roo.bootstrap.ComboBox} combo This combo box
16441 * @param {Object} cfg set html .
16443 'touchviewdisplay' : true
16448 this.tickItems = [];
16450 this.selectedIndex = -1;
16451 if(this.mode == 'local'){
16452 if(config.queryDelay === undefined){
16453 this.queryDelay = 10;
16455 if(config.minChars === undefined){
16461 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16464 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16465 * rendering into an Roo.Editor, defaults to false)
16468 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16469 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16472 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16475 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16476 * the dropdown list (defaults to undefined, with no header element)
16480 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16484 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16486 listWidth: undefined,
16488 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16489 * mode = 'remote' or 'text' if mode = 'local')
16491 displayField: undefined,
16494 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16495 * mode = 'remote' or 'value' if mode = 'local').
16496 * Note: use of a valueField requires the user make a selection
16497 * in order for a value to be mapped.
16499 valueField: undefined,
16501 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16506 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16507 * field's data value (defaults to the underlying DOM element's name)
16509 hiddenName: undefined,
16511 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16515 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16517 selectedClass: 'active',
16520 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16524 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16525 * anchor positions (defaults to 'tl-bl')
16527 listAlign: 'tl-bl?',
16529 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16533 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16534 * query specified by the allQuery config option (defaults to 'query')
16536 triggerAction: 'query',
16538 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16539 * (defaults to 4, does not apply if editable = false)
16543 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16544 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16548 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16549 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16553 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16554 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16558 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16559 * when editable = true (defaults to false)
16561 selectOnFocus:false,
16563 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16565 queryParam: 'query',
16567 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16568 * when mode = 'remote' (defaults to 'Loading...')
16570 loadingText: 'Loading...',
16572 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16576 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16580 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16581 * traditional select (defaults to true)
16585 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16589 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16593 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16594 * listWidth has a higher value)
16598 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16599 * allow the user to set arbitrary text into the field (defaults to false)
16601 forceSelection:false,
16603 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16604 * if typeAhead = true (defaults to 250)
16606 typeAheadDelay : 250,
16608 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16609 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16611 valueNotFoundText : undefined,
16613 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16615 blockFocus : false,
16618 * @cfg {Boolean} disableClear Disable showing of clear button.
16620 disableClear : false,
16622 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16624 alwaysQuery : false,
16627 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16632 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16634 invalidClass : "has-warning",
16637 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16639 validClass : "has-success",
16642 * @cfg {Boolean} specialFilter (true|false) special filter default false
16644 specialFilter : false,
16647 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16649 mobileTouchView : true,
16652 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16654 useNativeIOS : false,
16657 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16659 mobile_restrict_height : false,
16661 ios_options : false,
16673 btnPosition : 'right',
16674 triggerList : true,
16675 showToggleBtn : true,
16677 emptyResultText: 'Empty',
16678 triggerText : 'Select',
16682 // element that contains real text value.. (when hidden is used..)
16684 getAutoCreate : function()
16689 * Render classic select for iso
16692 if(Roo.isIOS && this.useNativeIOS){
16693 cfg = this.getAutoCreateNativeIOS();
16701 if(Roo.isTouch && this.mobileTouchView){
16702 cfg = this.getAutoCreateTouchView();
16709 if(!this.tickable){
16710 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16715 * ComboBox with tickable selections
16718 var align = this.labelAlign || this.parentLabelAlign();
16721 cls : 'form-group roo-combobox-tickable' //input-group
16724 var btn_text_select = '';
16725 var btn_text_done = '';
16726 var btn_text_cancel = '';
16728 if (this.btn_text_show) {
16729 btn_text_select = 'Select';
16730 btn_text_done = 'Done';
16731 btn_text_cancel = 'Cancel';
16736 cls : 'tickable-buttons',
16741 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16742 //html : this.triggerText
16743 html: btn_text_select
16749 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16751 html: btn_text_done
16757 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16759 html: btn_text_cancel
16765 buttons.cn.unshift({
16767 cls: 'roo-select2-search-field-input'
16773 Roo.each(buttons.cn, function(c){
16775 c.cls += ' btn-' + _this.size;
16778 if (_this.disabled) {
16785 style : 'display: contents',
16790 cls: 'form-hidden-field'
16794 cls: 'roo-select2-choices',
16798 cls: 'roo-select2-search-field',
16809 cls: 'roo-select2-container input-group roo-select2-container-multi',
16815 // cls: 'typeahead typeahead-long dropdown-menu',
16816 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16821 if(this.hasFeedback && !this.allowBlank){
16825 cls: 'glyphicon form-control-feedback'
16828 combobox.cn.push(feedback);
16835 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16836 tooltip : 'This field is required'
16838 if (Roo.bootstrap.version == 4) {
16841 style : 'display:none'
16844 if (align ==='left' && this.fieldLabel.length) {
16846 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16853 cls : 'control-label col-form-label',
16854 html : this.fieldLabel
16866 var labelCfg = cfg.cn[1];
16867 var contentCfg = cfg.cn[2];
16870 if(this.indicatorpos == 'right'){
16876 cls : 'control-label col-form-label',
16880 html : this.fieldLabel
16896 labelCfg = cfg.cn[0];
16897 contentCfg = cfg.cn[1];
16901 if(this.labelWidth > 12){
16902 labelCfg.style = "width: " + this.labelWidth + 'px';
16904 if(this.width * 1 > 0){
16905 contentCfg.style = "width: " + this.width + 'px';
16907 if(this.labelWidth < 13 && this.labelmd == 0){
16908 this.labelmd = this.labelWidth;
16911 if(this.labellg > 0){
16912 labelCfg.cls += ' col-lg-' + this.labellg;
16913 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16916 if(this.labelmd > 0){
16917 labelCfg.cls += ' col-md-' + this.labelmd;
16918 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16921 if(this.labelsm > 0){
16922 labelCfg.cls += ' col-sm-' + this.labelsm;
16923 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16926 if(this.labelxs > 0){
16927 labelCfg.cls += ' col-xs-' + this.labelxs;
16928 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16932 } else if ( this.fieldLabel.length) {
16933 // Roo.log(" label");
16938 //cls : 'input-group-addon',
16939 html : this.fieldLabel
16944 if(this.indicatorpos == 'right'){
16948 //cls : 'input-group-addon',
16949 html : this.fieldLabel
16959 // Roo.log(" no label && no align");
16966 ['xs','sm','md','lg'].map(function(size){
16967 if (settings[size]) {
16968 cfg.cls += ' col-' + size + '-' + settings[size];
16976 _initEventsCalled : false,
16979 initEvents: function()
16981 if (this._initEventsCalled) { // as we call render... prevent looping...
16984 this._initEventsCalled = true;
16987 throw "can not find store for combo";
16990 this.indicator = this.indicatorEl();
16992 this.store = Roo.factory(this.store, Roo.data);
16993 this.store.parent = this;
16995 // if we are building from html. then this element is so complex, that we can not really
16996 // use the rendered HTML.
16997 // so we have to trash and replace the previous code.
16998 if (Roo.XComponent.build_from_html) {
16999 // remove this element....
17000 var e = this.el.dom, k=0;
17001 while (e ) { e = e.previousSibling; ++k;}
17006 this.rendered = false;
17008 this.render(this.parent().getChildContainer(true), k);
17011 if(Roo.isIOS && this.useNativeIOS){
17012 this.initIOSView();
17020 if(Roo.isTouch && this.mobileTouchView){
17021 this.initTouchView();
17026 this.initTickableEvents();
17030 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17032 if(this.hiddenName){
17034 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17036 this.hiddenField.dom.value =
17037 this.hiddenValue !== undefined ? this.hiddenValue :
17038 this.value !== undefined ? this.value : '';
17040 // prevent input submission
17041 this.el.dom.removeAttribute('name');
17042 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17047 // this.el.dom.setAttribute('autocomplete', 'off');
17050 var cls = 'x-combo-list';
17052 //this.list = new Roo.Layer({
17053 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17059 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17060 _this.list.setWidth(lw);
17063 this.list.on('mouseover', this.onViewOver, this);
17064 this.list.on('mousemove', this.onViewMove, this);
17065 this.list.on('scroll', this.onViewScroll, this);
17068 this.list.swallowEvent('mousewheel');
17069 this.assetHeight = 0;
17072 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17073 this.assetHeight += this.header.getHeight();
17076 this.innerList = this.list.createChild({cls:cls+'-inner'});
17077 this.innerList.on('mouseover', this.onViewOver, this);
17078 this.innerList.on('mousemove', this.onViewMove, this);
17079 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17081 if(this.allowBlank && !this.pageSize && !this.disableClear){
17082 this.footer = this.list.createChild({cls:cls+'-ft'});
17083 this.pageTb = new Roo.Toolbar(this.footer);
17087 this.footer = this.list.createChild({cls:cls+'-ft'});
17088 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17089 {pageSize: this.pageSize});
17093 if (this.pageTb && this.allowBlank && !this.disableClear) {
17095 this.pageTb.add(new Roo.Toolbar.Fill(), {
17096 cls: 'x-btn-icon x-btn-clear',
17098 handler: function()
17101 _this.clearValue();
17102 _this.onSelect(false, -1);
17107 this.assetHeight += this.footer.getHeight();
17112 this.tpl = Roo.bootstrap.version == 4 ?
17113 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17114 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17117 this.view = new Roo.View(this.list, this.tpl, {
17118 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17120 //this.view.wrapEl.setDisplayed(false);
17121 this.view.on('click', this.onViewClick, this);
17124 this.store.on('beforeload', this.onBeforeLoad, this);
17125 this.store.on('load', this.onLoad, this);
17126 this.store.on('loadexception', this.onLoadException, this);
17128 if(this.resizable){
17129 this.resizer = new Roo.Resizable(this.list, {
17130 pinned:true, handles:'se'
17132 this.resizer.on('resize', function(r, w, h){
17133 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17134 this.listWidth = w;
17135 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17136 this.restrictHeight();
17138 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17141 if(!this.editable){
17142 this.editable = true;
17143 this.setEditable(false);
17148 if (typeof(this.events.add.listeners) != 'undefined') {
17150 this.addicon = this.wrap.createChild(
17151 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17153 this.addicon.on('click', function(e) {
17154 this.fireEvent('add', this);
17157 if (typeof(this.events.edit.listeners) != 'undefined') {
17159 this.editicon = this.wrap.createChild(
17160 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17161 if (this.addicon) {
17162 this.editicon.setStyle('margin-left', '40px');
17164 this.editicon.on('click', function(e) {
17166 // we fire even if inothing is selected..
17167 this.fireEvent('edit', this, this.lastData );
17173 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17174 "up" : function(e){
17175 this.inKeyMode = true;
17179 "down" : function(e){
17180 if(!this.isExpanded()){
17181 this.onTriggerClick();
17183 this.inKeyMode = true;
17188 "enter" : function(e){
17189 // this.onViewClick();
17193 if(this.fireEvent("specialkey", this, e)){
17194 this.onViewClick(false);
17200 "esc" : function(e){
17204 "tab" : function(e){
17207 if(this.fireEvent("specialkey", this, e)){
17208 this.onViewClick(false);
17216 doRelay : function(foo, bar, hname){
17217 if(hname == 'down' || this.scope.isExpanded()){
17218 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17227 this.queryDelay = Math.max(this.queryDelay || 10,
17228 this.mode == 'local' ? 10 : 250);
17231 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17233 if(this.typeAhead){
17234 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17236 if(this.editable !== false){
17237 this.inputEl().on("keyup", this.onKeyUp, this);
17239 if(this.forceSelection){
17240 this.inputEl().on('blur', this.doForce, this);
17244 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17245 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17249 initTickableEvents: function()
17253 if(this.hiddenName){
17255 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17257 this.hiddenField.dom.value =
17258 this.hiddenValue !== undefined ? this.hiddenValue :
17259 this.value !== undefined ? this.value : '';
17261 // prevent input submission
17262 this.el.dom.removeAttribute('name');
17263 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17268 // this.list = this.el.select('ul.dropdown-menu',true).first();
17270 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17271 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17272 if(this.triggerList){
17273 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17276 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17277 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17279 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17280 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17282 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17283 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17285 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17286 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17287 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17290 this.cancelBtn.hide();
17295 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17296 _this.list.setWidth(lw);
17299 this.list.on('mouseover', this.onViewOver, this);
17300 this.list.on('mousemove', this.onViewMove, this);
17302 this.list.on('scroll', this.onViewScroll, this);
17305 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17306 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17309 this.view = new Roo.View(this.list, this.tpl, {
17314 selectedClass: this.selectedClass
17317 //this.view.wrapEl.setDisplayed(false);
17318 this.view.on('click', this.onViewClick, this);
17322 this.store.on('beforeload', this.onBeforeLoad, this);
17323 this.store.on('load', this.onLoad, this);
17324 this.store.on('loadexception', this.onLoadException, this);
17327 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17328 "up" : function(e){
17329 this.inKeyMode = true;
17333 "down" : function(e){
17334 this.inKeyMode = true;
17338 "enter" : function(e){
17339 if(this.fireEvent("specialkey", this, e)){
17340 this.onViewClick(false);
17346 "esc" : function(e){
17347 this.onTickableFooterButtonClick(e, false, false);
17350 "tab" : function(e){
17351 this.fireEvent("specialkey", this, e);
17353 this.onTickableFooterButtonClick(e, false, false);
17360 doRelay : function(e, fn, key){
17361 if(this.scope.isExpanded()){
17362 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17371 this.queryDelay = Math.max(this.queryDelay || 10,
17372 this.mode == 'local' ? 10 : 250);
17375 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17377 if(this.typeAhead){
17378 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17381 if(this.editable !== false){
17382 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17385 this.indicator = this.indicatorEl();
17387 if(this.indicator){
17388 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17389 this.indicator.hide();
17394 onDestroy : function(){
17396 this.view.setStore(null);
17397 this.view.el.removeAllListeners();
17398 this.view.el.remove();
17399 this.view.purgeListeners();
17402 this.list.dom.innerHTML = '';
17406 this.store.un('beforeload', this.onBeforeLoad, this);
17407 this.store.un('load', this.onLoad, this);
17408 this.store.un('loadexception', this.onLoadException, this);
17410 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17414 fireKey : function(e){
17415 if(e.isNavKeyPress() && !this.list.isVisible()){
17416 this.fireEvent("specialkey", this, e);
17421 onResize: function(w, h)
17425 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17427 // if(typeof w != 'number'){
17428 // // we do not handle it!?!?
17431 // var tw = this.trigger.getWidth();
17432 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17433 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17435 // this.inputEl().setWidth( this.adjustWidth('input', x));
17437 // //this.trigger.setStyle('left', x+'px');
17439 // if(this.list && this.listWidth === undefined){
17440 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17441 // this.list.setWidth(lw);
17442 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17450 * Allow or prevent the user from directly editing the field text. If false is passed,
17451 * the user will only be able to select from the items defined in the dropdown list. This method
17452 * is the runtime equivalent of setting the 'editable' config option at config time.
17453 * @param {Boolean} value True to allow the user to directly edit the field text
17455 setEditable : function(value){
17456 if(value == this.editable){
17459 this.editable = value;
17461 this.inputEl().dom.setAttribute('readOnly', true);
17462 this.inputEl().on('mousedown', this.onTriggerClick, this);
17463 this.inputEl().addClass('x-combo-noedit');
17465 this.inputEl().dom.removeAttribute('readOnly');
17466 this.inputEl().un('mousedown', this.onTriggerClick, this);
17467 this.inputEl().removeClass('x-combo-noedit');
17473 onBeforeLoad : function(combo,opts){
17474 if(!this.hasFocus){
17478 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17480 this.restrictHeight();
17481 this.selectedIndex = -1;
17485 onLoad : function(){
17487 this.hasQuery = false;
17489 if(!this.hasFocus){
17493 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17494 this.loading.hide();
17497 if(this.store.getCount() > 0){
17500 this.restrictHeight();
17501 if(this.lastQuery == this.allQuery){
17502 if(this.editable && !this.tickable){
17503 this.inputEl().dom.select();
17507 !this.selectByValue(this.value, true) &&
17510 !this.store.lastOptions ||
17511 typeof(this.store.lastOptions.add) == 'undefined' ||
17512 this.store.lastOptions.add != true
17515 this.select(0, true);
17518 if(this.autoFocus){
17521 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17522 this.taTask.delay(this.typeAheadDelay);
17526 this.onEmptyResults();
17532 onLoadException : function()
17534 this.hasQuery = false;
17536 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17537 this.loading.hide();
17540 if(this.tickable && this.editable){
17545 // only causes errors at present
17546 //Roo.log(this.store.reader.jsonData);
17547 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17549 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17555 onTypeAhead : function(){
17556 if(this.store.getCount() > 0){
17557 var r = this.store.getAt(0);
17558 var newValue = r.data[this.displayField];
17559 var len = newValue.length;
17560 var selStart = this.getRawValue().length;
17562 if(selStart != len){
17563 this.setRawValue(newValue);
17564 this.selectText(selStart, newValue.length);
17570 onSelect : function(record, index){
17572 if(this.fireEvent('beforeselect', this, record, index) !== false){
17574 this.setFromData(index > -1 ? record.data : false);
17577 this.fireEvent('select', this, record, index);
17582 * Returns the currently selected field value or empty string if no value is set.
17583 * @return {String} value The selected value
17585 getValue : function()
17587 if(Roo.isIOS && this.useNativeIOS){
17588 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17592 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17595 if(this.valueField){
17596 return typeof this.value != 'undefined' ? this.value : '';
17598 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17602 getRawValue : function()
17604 if(Roo.isIOS && this.useNativeIOS){
17605 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17608 var v = this.inputEl().getValue();
17614 * Clears any text/value currently set in the field
17616 clearValue : function(){
17618 if(this.hiddenField){
17619 this.hiddenField.dom.value = '';
17622 this.setRawValue('');
17623 this.lastSelectionText = '';
17624 this.lastData = false;
17626 var close = this.closeTriggerEl();
17637 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17638 * will be displayed in the field. If the value does not match the data value of an existing item,
17639 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17640 * Otherwise the field will be blank (although the value will still be set).
17641 * @param {String} value The value to match
17643 setValue : function(v)
17645 if(Roo.isIOS && this.useNativeIOS){
17646 this.setIOSValue(v);
17656 if(this.valueField){
17657 var r = this.findRecord(this.valueField, v);
17659 text = r.data[this.displayField];
17660 }else if(this.valueNotFoundText !== undefined){
17661 text = this.valueNotFoundText;
17664 this.lastSelectionText = text;
17665 if(this.hiddenField){
17666 this.hiddenField.dom.value = v;
17668 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17671 var close = this.closeTriggerEl();
17674 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17680 * @property {Object} the last set data for the element
17685 * Sets the value of the field based on a object which is related to the record format for the store.
17686 * @param {Object} value the value to set as. or false on reset?
17688 setFromData : function(o){
17695 var dv = ''; // display value
17696 var vv = ''; // value value..
17698 if (this.displayField) {
17699 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17701 // this is an error condition!!!
17702 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17705 if(this.valueField){
17706 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17709 var close = this.closeTriggerEl();
17712 if(dv.length || vv * 1 > 0){
17714 this.blockFocus=true;
17720 if(this.hiddenField){
17721 this.hiddenField.dom.value = vv;
17723 this.lastSelectionText = dv;
17724 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17728 // no hidden field.. - we store the value in 'value', but still display
17729 // display field!!!!
17730 this.lastSelectionText = dv;
17731 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17738 reset : function(){
17739 // overridden so that last data is reset..
17746 this.setValue(this.originalValue);
17747 //this.clearInvalid();
17748 this.lastData = false;
17750 this.view.clearSelections();
17756 findRecord : function(prop, value){
17758 if(this.store.getCount() > 0){
17759 this.store.each(function(r){
17760 if(r.data[prop] == value){
17770 getName: function()
17772 // returns hidden if it's set..
17773 if (!this.rendered) {return ''};
17774 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17778 onViewMove : function(e, t){
17779 this.inKeyMode = false;
17783 onViewOver : function(e, t){
17784 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17787 var item = this.view.findItemFromChild(t);
17790 var index = this.view.indexOf(item);
17791 this.select(index, false);
17796 onViewClick : function(view, doFocus, el, e)
17798 var index = this.view.getSelectedIndexes()[0];
17800 var r = this.store.getAt(index);
17804 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17811 Roo.each(this.tickItems, function(v,k){
17813 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17815 _this.tickItems.splice(k, 1);
17817 if(typeof(e) == 'undefined' && view == false){
17818 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17830 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17831 this.tickItems.push(r.data);
17834 if(typeof(e) == 'undefined' && view == false){
17835 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17842 this.onSelect(r, index);
17844 if(doFocus !== false && !this.blockFocus){
17845 this.inputEl().focus();
17850 restrictHeight : function(){
17851 //this.innerList.dom.style.height = '';
17852 //var inner = this.innerList.dom;
17853 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17854 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17855 //this.list.beginUpdate();
17856 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17857 this.list.alignTo(this.inputEl(), this.listAlign);
17858 this.list.alignTo(this.inputEl(), this.listAlign);
17859 //this.list.endUpdate();
17863 onEmptyResults : function(){
17865 if(this.tickable && this.editable){
17866 this.hasFocus = false;
17867 this.restrictHeight();
17875 * Returns true if the dropdown list is expanded, else false.
17877 isExpanded : function(){
17878 return this.list.isVisible();
17882 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17883 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17884 * @param {String} value The data value of the item to select
17885 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17886 * selected item if it is not currently in view (defaults to true)
17887 * @return {Boolean} True if the value matched an item in the list, else false
17889 selectByValue : function(v, scrollIntoView){
17890 if(v !== undefined && v !== null){
17891 var r = this.findRecord(this.valueField || this.displayField, v);
17893 this.select(this.store.indexOf(r), scrollIntoView);
17901 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17902 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17903 * @param {Number} index The zero-based index of the list item to select
17904 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17905 * selected item if it is not currently in view (defaults to true)
17907 select : function(index, scrollIntoView){
17908 this.selectedIndex = index;
17909 this.view.select(index);
17910 if(scrollIntoView !== false){
17911 var el = this.view.getNode(index);
17913 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17916 this.list.scrollChildIntoView(el, false);
17922 selectNext : function(){
17923 var ct = this.store.getCount();
17925 if(this.selectedIndex == -1){
17927 }else if(this.selectedIndex < ct-1){
17928 this.select(this.selectedIndex+1);
17934 selectPrev : function(){
17935 var ct = this.store.getCount();
17937 if(this.selectedIndex == -1){
17939 }else if(this.selectedIndex != 0){
17940 this.select(this.selectedIndex-1);
17946 onKeyUp : function(e){
17947 if(this.editable !== false && !e.isSpecialKey()){
17948 this.lastKey = e.getKey();
17949 this.dqTask.delay(this.queryDelay);
17954 validateBlur : function(){
17955 return !this.list || !this.list.isVisible();
17959 initQuery : function(){
17961 var v = this.getRawValue();
17963 if(this.tickable && this.editable){
17964 v = this.tickableInputEl().getValue();
17971 doForce : function(){
17972 if(this.inputEl().dom.value.length > 0){
17973 this.inputEl().dom.value =
17974 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17980 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17981 * query allowing the query action to be canceled if needed.
17982 * @param {String} query The SQL query to execute
17983 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17984 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17985 * saved in the current store (defaults to false)
17987 doQuery : function(q, forceAll){
17989 if(q === undefined || q === null){
17994 forceAll: forceAll,
17998 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18003 forceAll = qe.forceAll;
18004 if(forceAll === true || (q.length >= this.minChars)){
18006 this.hasQuery = true;
18008 if(this.lastQuery != q || this.alwaysQuery){
18009 this.lastQuery = q;
18010 if(this.mode == 'local'){
18011 this.selectedIndex = -1;
18013 this.store.clearFilter();
18016 if(this.specialFilter){
18017 this.fireEvent('specialfilter', this);
18022 this.store.filter(this.displayField, q);
18025 this.store.fireEvent("datachanged", this.store);
18032 this.store.baseParams[this.queryParam] = q;
18034 var options = {params : this.getParams(q)};
18037 options.add = true;
18038 options.params.start = this.page * this.pageSize;
18041 this.store.load(options);
18044 * this code will make the page width larger, at the beginning, the list not align correctly,
18045 * we should expand the list on onLoad
18046 * so command out it
18051 this.selectedIndex = -1;
18056 this.loadNext = false;
18060 getParams : function(q){
18062 //p[this.queryParam] = q;
18066 p.limit = this.pageSize;
18072 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18074 collapse : function(){
18075 if(!this.isExpanded()){
18081 this.hasFocus = false;
18085 this.cancelBtn.hide();
18086 this.trigger.show();
18089 this.tickableInputEl().dom.value = '';
18090 this.tickableInputEl().blur();
18095 Roo.get(document).un('mousedown', this.collapseIf, this);
18096 Roo.get(document).un('mousewheel', this.collapseIf, this);
18097 if (!this.editable) {
18098 Roo.get(document).un('keydown', this.listKeyPress, this);
18100 this.fireEvent('collapse', this);
18106 collapseIf : function(e){
18107 var in_combo = e.within(this.el);
18108 var in_list = e.within(this.list);
18109 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18111 if (in_combo || in_list || is_list) {
18112 //e.stopPropagation();
18117 this.onTickableFooterButtonClick(e, false, false);
18125 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18127 expand : function(){
18129 if(this.isExpanded() || !this.hasFocus){
18133 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18134 this.list.setWidth(lw);
18140 this.restrictHeight();
18144 this.tickItems = Roo.apply([], this.item);
18147 this.cancelBtn.show();
18148 this.trigger.hide();
18151 this.tickableInputEl().focus();
18156 Roo.get(document).on('mousedown', this.collapseIf, this);
18157 Roo.get(document).on('mousewheel', this.collapseIf, this);
18158 if (!this.editable) {
18159 Roo.get(document).on('keydown', this.listKeyPress, this);
18162 this.fireEvent('expand', this);
18166 // Implements the default empty TriggerField.onTriggerClick function
18167 onTriggerClick : function(e)
18169 Roo.log('trigger click');
18171 if(this.disabled || !this.triggerList){
18176 this.loadNext = false;
18178 if(this.isExpanded()){
18180 if (!this.blockFocus) {
18181 this.inputEl().focus();
18185 this.hasFocus = true;
18186 if(this.triggerAction == 'all') {
18187 this.doQuery(this.allQuery, true);
18189 this.doQuery(this.getRawValue());
18191 if (!this.blockFocus) {
18192 this.inputEl().focus();
18197 onTickableTriggerClick : function(e)
18204 this.loadNext = false;
18205 this.hasFocus = true;
18207 if(this.triggerAction == 'all') {
18208 this.doQuery(this.allQuery, true);
18210 this.doQuery(this.getRawValue());
18214 onSearchFieldClick : function(e)
18216 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18217 this.onTickableFooterButtonClick(e, false, false);
18221 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18226 this.loadNext = false;
18227 this.hasFocus = true;
18229 if(this.triggerAction == 'all') {
18230 this.doQuery(this.allQuery, true);
18232 this.doQuery(this.getRawValue());
18236 listKeyPress : function(e)
18238 //Roo.log('listkeypress');
18239 // scroll to first matching element based on key pres..
18240 if (e.isSpecialKey()) {
18243 var k = String.fromCharCode(e.getKey()).toUpperCase();
18246 var csel = this.view.getSelectedNodes();
18247 var cselitem = false;
18249 var ix = this.view.indexOf(csel[0]);
18250 cselitem = this.store.getAt(ix);
18251 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18257 this.store.each(function(v) {
18259 // start at existing selection.
18260 if (cselitem.id == v.id) {
18266 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18267 match = this.store.indexOf(v);
18273 if (match === false) {
18274 return true; // no more action?
18277 this.view.select(match);
18278 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18279 sn.scrollIntoView(sn.dom.parentNode, false);
18282 onViewScroll : function(e, t){
18284 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){
18288 this.hasQuery = true;
18290 this.loading = this.list.select('.loading', true).first();
18292 if(this.loading === null){
18293 this.list.createChild({
18295 cls: 'loading roo-select2-more-results roo-select2-active',
18296 html: 'Loading more results...'
18299 this.loading = this.list.select('.loading', true).first();
18301 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18303 this.loading.hide();
18306 this.loading.show();
18311 this.loadNext = true;
18313 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18318 addItem : function(o)
18320 var dv = ''; // display value
18322 if (this.displayField) {
18323 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18325 // this is an error condition!!!
18326 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18333 var choice = this.choices.createChild({
18335 cls: 'roo-select2-search-choice',
18344 cls: 'roo-select2-search-choice-close fa fa-times',
18349 }, this.searchField);
18351 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18353 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18361 this.inputEl().dom.value = '';
18366 onRemoveItem : function(e, _self, o)
18368 e.preventDefault();
18370 this.lastItem = Roo.apply([], this.item);
18372 var index = this.item.indexOf(o.data) * 1;
18375 Roo.log('not this item?!');
18379 this.item.splice(index, 1);
18384 this.fireEvent('remove', this, e);
18390 syncValue : function()
18392 if(!this.item.length){
18399 Roo.each(this.item, function(i){
18400 if(_this.valueField){
18401 value.push(i[_this.valueField]);
18408 this.value = value.join(',');
18410 if(this.hiddenField){
18411 this.hiddenField.dom.value = this.value;
18414 this.store.fireEvent("datachanged", this.store);
18419 clearItem : function()
18421 if(!this.multiple){
18427 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18435 if(this.tickable && !Roo.isTouch){
18436 this.view.refresh();
18440 inputEl: function ()
18442 if(Roo.isIOS && this.useNativeIOS){
18443 return this.el.select('select.roo-ios-select', true).first();
18446 if(Roo.isTouch && this.mobileTouchView){
18447 return this.el.select('input.form-control',true).first();
18451 return this.searchField;
18454 return this.el.select('input.form-control',true).first();
18457 onTickableFooterButtonClick : function(e, btn, el)
18459 e.preventDefault();
18461 this.lastItem = Roo.apply([], this.item);
18463 if(btn && btn.name == 'cancel'){
18464 this.tickItems = Roo.apply([], this.item);
18473 Roo.each(this.tickItems, function(o){
18481 validate : function()
18483 if(this.getVisibilityEl().hasClass('hidden')){
18487 var v = this.getRawValue();
18490 v = this.getValue();
18493 if(this.disabled || this.allowBlank || v.length){
18498 this.markInvalid();
18502 tickableInputEl : function()
18504 if(!this.tickable || !this.editable){
18505 return this.inputEl();
18508 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18512 getAutoCreateTouchView : function()
18517 cls: 'form-group' //input-group
18523 type : this.inputType,
18524 cls : 'form-control x-combo-noedit',
18525 autocomplete: 'new-password',
18526 placeholder : this.placeholder || '',
18531 input.name = this.name;
18535 input.cls += ' input-' + this.size;
18538 if (this.disabled) {
18539 input.disabled = true;
18543 cls : 'roo-combobox-wrap',
18550 inputblock.cls += ' input-group';
18552 inputblock.cn.unshift({
18554 cls : 'input-group-addon input-group-prepend input-group-text',
18559 if(this.removable && !this.multiple){
18560 inputblock.cls += ' roo-removable';
18562 inputblock.cn.push({
18565 cls : 'roo-combo-removable-btn close'
18569 if(this.hasFeedback && !this.allowBlank){
18571 inputblock.cls += ' has-feedback';
18573 inputblock.cn.push({
18575 cls: 'glyphicon form-control-feedback'
18582 inputblock.cls += (this.before) ? '' : ' input-group';
18584 inputblock.cn.push({
18586 cls : 'input-group-addon input-group-append input-group-text',
18592 var ibwrap = inputblock;
18597 cls: 'roo-select2-choices',
18601 cls: 'roo-select2-search-field',
18614 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18619 cls: 'form-hidden-field'
18625 if(!this.multiple && this.showToggleBtn){
18631 if (this.caret != false) {
18634 cls: 'fa fa-' + this.caret
18641 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18643 Roo.bootstrap.version == 3 ? caret : '',
18646 cls: 'combobox-clear',
18660 combobox.cls += ' roo-select2-container-multi';
18663 var required = this.allowBlank ? {
18665 style: 'display: none'
18668 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18669 tooltip : 'This field is required'
18672 var align = this.labelAlign || this.parentLabelAlign();
18674 if (align ==='left' && this.fieldLabel.length) {
18680 cls : 'control-label col-form-label',
18681 html : this.fieldLabel
18685 cls : 'roo-combobox-wrap ',
18692 var labelCfg = cfg.cn[1];
18693 var contentCfg = cfg.cn[2];
18696 if(this.indicatorpos == 'right'){
18701 cls : 'control-label col-form-label',
18705 html : this.fieldLabel
18711 cls : "roo-combobox-wrap ",
18719 labelCfg = cfg.cn[0];
18720 contentCfg = cfg.cn[1];
18725 if(this.labelWidth > 12){
18726 labelCfg.style = "width: " + this.labelWidth + 'px';
18729 if(this.labelWidth < 13 && this.labelmd == 0){
18730 this.labelmd = this.labelWidth;
18733 if(this.labellg > 0){
18734 labelCfg.cls += ' col-lg-' + this.labellg;
18735 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18738 if(this.labelmd > 0){
18739 labelCfg.cls += ' col-md-' + this.labelmd;
18740 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18743 if(this.labelsm > 0){
18744 labelCfg.cls += ' col-sm-' + this.labelsm;
18745 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18748 if(this.labelxs > 0){
18749 labelCfg.cls += ' col-xs-' + this.labelxs;
18750 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18754 } else if ( this.fieldLabel.length) {
18759 cls : 'control-label',
18760 html : this.fieldLabel
18771 if(this.indicatorpos == 'right'){
18775 cls : 'control-label',
18776 html : this.fieldLabel,
18794 var settings = this;
18796 ['xs','sm','md','lg'].map(function(size){
18797 if (settings[size]) {
18798 cfg.cls += ' col-' + size + '-' + settings[size];
18805 initTouchView : function()
18807 this.renderTouchView();
18809 this.touchViewEl.on('scroll', function(){
18810 this.el.dom.scrollTop = 0;
18813 this.originalValue = this.getValue();
18815 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18817 this.inputEl().on("click", this.showTouchView, this);
18818 if (this.triggerEl) {
18819 this.triggerEl.on("click", this.showTouchView, this);
18823 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18824 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18826 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18828 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18829 this.store.on('load', this.onTouchViewLoad, this);
18830 this.store.on('loadexception', this.onTouchViewLoadException, this);
18832 if(this.hiddenName){
18834 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18836 this.hiddenField.dom.value =
18837 this.hiddenValue !== undefined ? this.hiddenValue :
18838 this.value !== undefined ? this.value : '';
18840 this.el.dom.removeAttribute('name');
18841 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18845 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18846 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18849 if(this.removable && !this.multiple){
18850 var close = this.closeTriggerEl();
18852 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18853 close.on('click', this.removeBtnClick, this, close);
18857 * fix the bug in Safari iOS8
18859 this.inputEl().on("focus", function(e){
18860 document.activeElement.blur();
18863 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18870 renderTouchView : function()
18872 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18873 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18875 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18876 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18878 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18879 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18880 this.touchViewBodyEl.setStyle('overflow', 'auto');
18882 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18883 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18885 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18886 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18890 showTouchView : function()
18896 this.touchViewHeaderEl.hide();
18898 if(this.modalTitle.length){
18899 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18900 this.touchViewHeaderEl.show();
18903 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18904 this.touchViewEl.show();
18906 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18908 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18909 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18911 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18913 if(this.modalTitle.length){
18914 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18917 this.touchViewBodyEl.setHeight(bodyHeight);
18921 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18923 this.touchViewEl.addClass(['in','show']);
18926 if(this._touchViewMask){
18927 Roo.get(document.body).addClass("x-body-masked");
18928 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18929 this._touchViewMask.setStyle('z-index', 10000);
18930 this._touchViewMask.addClass('show');
18933 this.doTouchViewQuery();
18937 hideTouchView : function()
18939 this.touchViewEl.removeClass(['in','show']);
18943 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18945 this.touchViewEl.setStyle('display', 'none');
18948 if(this._touchViewMask){
18949 this._touchViewMask.removeClass('show');
18950 Roo.get(document.body).removeClass("x-body-masked");
18954 setTouchViewValue : function()
18961 Roo.each(this.tickItems, function(o){
18966 this.hideTouchView();
18969 doTouchViewQuery : function()
18978 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18982 if(!this.alwaysQuery || this.mode == 'local'){
18983 this.onTouchViewLoad();
18990 onTouchViewBeforeLoad : function(combo,opts)
18996 onTouchViewLoad : function()
18998 if(this.store.getCount() < 1){
18999 this.onTouchViewEmptyResults();
19003 this.clearTouchView();
19005 var rawValue = this.getRawValue();
19007 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19009 this.tickItems = [];
19011 this.store.data.each(function(d, rowIndex){
19012 var row = this.touchViewListGroup.createChild(template);
19014 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19015 row.addClass(d.data.cls);
19018 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19021 html : d.data[this.displayField]
19024 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19025 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19028 row.removeClass('selected');
19029 if(!this.multiple && this.valueField &&
19030 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19033 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19034 row.addClass('selected');
19037 if(this.multiple && this.valueField &&
19038 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19042 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19043 this.tickItems.push(d.data);
19046 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19050 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19052 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19054 if(this.modalTitle.length){
19055 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19058 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19060 if(this.mobile_restrict_height && listHeight < bodyHeight){
19061 this.touchViewBodyEl.setHeight(listHeight);
19066 if(firstChecked && listHeight > bodyHeight){
19067 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19072 onTouchViewLoadException : function()
19074 this.hideTouchView();
19077 onTouchViewEmptyResults : function()
19079 this.clearTouchView();
19081 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19083 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19087 clearTouchView : function()
19089 this.touchViewListGroup.dom.innerHTML = '';
19092 onTouchViewClick : function(e, el, o)
19094 e.preventDefault();
19097 var rowIndex = o.rowIndex;
19099 var r = this.store.getAt(rowIndex);
19101 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19103 if(!this.multiple){
19104 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19105 c.dom.removeAttribute('checked');
19108 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19110 this.setFromData(r.data);
19112 var close = this.closeTriggerEl();
19118 this.hideTouchView();
19120 this.fireEvent('select', this, r, rowIndex);
19125 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19126 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19127 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19131 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19132 this.addItem(r.data);
19133 this.tickItems.push(r.data);
19137 getAutoCreateNativeIOS : function()
19140 cls: 'form-group' //input-group,
19145 cls : 'roo-ios-select'
19149 combobox.name = this.name;
19152 if (this.disabled) {
19153 combobox.disabled = true;
19156 var settings = this;
19158 ['xs','sm','md','lg'].map(function(size){
19159 if (settings[size]) {
19160 cfg.cls += ' col-' + size + '-' + settings[size];
19170 initIOSView : function()
19172 this.store.on('load', this.onIOSViewLoad, this);
19177 onIOSViewLoad : function()
19179 if(this.store.getCount() < 1){
19183 this.clearIOSView();
19185 if(this.allowBlank) {
19187 var default_text = '-- SELECT --';
19189 if(this.placeholder.length){
19190 default_text = this.placeholder;
19193 if(this.emptyTitle.length){
19194 default_text += ' - ' + this.emptyTitle + ' -';
19197 var opt = this.inputEl().createChild({
19200 html : default_text
19204 o[this.valueField] = 0;
19205 o[this.displayField] = default_text;
19207 this.ios_options.push({
19214 this.store.data.each(function(d, rowIndex){
19218 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19219 html = d.data[this.displayField];
19224 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19225 value = d.data[this.valueField];
19234 if(this.value == d.data[this.valueField]){
19235 option['selected'] = true;
19238 var opt = this.inputEl().createChild(option);
19240 this.ios_options.push({
19247 this.inputEl().on('change', function(){
19248 this.fireEvent('select', this);
19253 clearIOSView: function()
19255 this.inputEl().dom.innerHTML = '';
19257 this.ios_options = [];
19260 setIOSValue: function(v)
19264 if(!this.ios_options){
19268 Roo.each(this.ios_options, function(opts){
19270 opts.el.dom.removeAttribute('selected');
19272 if(opts.data[this.valueField] != v){
19276 opts.el.dom.setAttribute('selected', true);
19282 * @cfg {Boolean} grow
19286 * @cfg {Number} growMin
19290 * @cfg {Number} growMax
19299 Roo.apply(Roo.bootstrap.ComboBox, {
19303 cls: 'modal-header',
19325 cls: 'list-group-item',
19329 cls: 'roo-combobox-list-group-item-value'
19333 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19347 listItemCheckbox : {
19349 cls: 'list-group-item',
19353 cls: 'roo-combobox-list-group-item-value'
19357 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19373 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19378 cls: 'modal-footer',
19386 cls: 'col-xs-6 text-left',
19389 cls: 'btn btn-danger roo-touch-view-cancel',
19395 cls: 'col-xs-6 text-right',
19398 cls: 'btn btn-success roo-touch-view-ok',
19409 Roo.apply(Roo.bootstrap.ComboBox, {
19411 touchViewTemplate : {
19413 cls: 'modal fade roo-combobox-touch-view',
19417 cls: 'modal-dialog',
19418 style : 'position:fixed', // we have to fix position....
19422 cls: 'modal-content',
19424 Roo.bootstrap.ComboBox.header,
19425 Roo.bootstrap.ComboBox.body,
19426 Roo.bootstrap.ComboBox.footer
19435 * Ext JS Library 1.1.1
19436 * Copyright(c) 2006-2007, Ext JS, LLC.
19438 * Originally Released Under LGPL - original licence link has changed is not relivant.
19441 * <script type="text/javascript">
19446 * @extends Roo.util.Observable
19447 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19448 * This class also supports single and multi selection modes. <br>
19449 * Create a data model bound view:
19451 var store = new Roo.data.Store(...);
19453 var view = new Roo.View({
19455 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19457 singleSelect: true,
19458 selectedClass: "ydataview-selected",
19462 // listen for node click?
19463 view.on("click", function(vw, index, node, e){
19464 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19468 dataModel.load("foobar.xml");
19470 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19472 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19473 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19475 * Note: old style constructor is still suported (container, template, config)
19478 * Create a new View
19479 * @param {Object} config The config object
19482 Roo.View = function(config, depreciated_tpl, depreciated_config){
19484 this.parent = false;
19486 if (typeof(depreciated_tpl) == 'undefined') {
19487 // new way.. - universal constructor.
19488 Roo.apply(this, config);
19489 this.el = Roo.get(this.el);
19492 this.el = Roo.get(config);
19493 this.tpl = depreciated_tpl;
19494 Roo.apply(this, depreciated_config);
19496 this.wrapEl = this.el.wrap().wrap();
19497 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19500 if(typeof(this.tpl) == "string"){
19501 this.tpl = new Roo.Template(this.tpl);
19503 // support xtype ctors..
19504 this.tpl = new Roo.factory(this.tpl, Roo);
19508 this.tpl.compile();
19513 * @event beforeclick
19514 * Fires before a click is processed. Returns false to cancel the default action.
19515 * @param {Roo.View} this
19516 * @param {Number} index The index of the target node
19517 * @param {HTMLElement} node The target node
19518 * @param {Roo.EventObject} e The raw event object
19520 "beforeclick" : true,
19523 * Fires when a template node is clicked.
19524 * @param {Roo.View} this
19525 * @param {Number} index The index of the target node
19526 * @param {HTMLElement} node The target node
19527 * @param {Roo.EventObject} e The raw event object
19532 * Fires when a template node is double clicked.
19533 * @param {Roo.View} this
19534 * @param {Number} index The index of the target node
19535 * @param {HTMLElement} node The target node
19536 * @param {Roo.EventObject} e The raw event object
19540 * @event contextmenu
19541 * Fires when a template node is right clicked.
19542 * @param {Roo.View} this
19543 * @param {Number} index The index of the target node
19544 * @param {HTMLElement} node The target node
19545 * @param {Roo.EventObject} e The raw event object
19547 "contextmenu" : true,
19549 * @event selectionchange
19550 * Fires when the selected nodes change.
19551 * @param {Roo.View} this
19552 * @param {Array} selections Array of the selected nodes
19554 "selectionchange" : true,
19557 * @event beforeselect
19558 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19559 * @param {Roo.View} this
19560 * @param {HTMLElement} node The node to be selected
19561 * @param {Array} selections Array of currently selected nodes
19563 "beforeselect" : true,
19565 * @event preparedata
19566 * Fires on every row to render, to allow you to change the data.
19567 * @param {Roo.View} this
19568 * @param {Object} data to be rendered (change this)
19570 "preparedata" : true
19578 "click": this.onClick,
19579 "dblclick": this.onDblClick,
19580 "contextmenu": this.onContextMenu,
19584 this.selections = [];
19586 this.cmp = new Roo.CompositeElementLite([]);
19588 this.store = Roo.factory(this.store, Roo.data);
19589 this.setStore(this.store, true);
19592 if ( this.footer && this.footer.xtype) {
19594 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19596 this.footer.dataSource = this.store;
19597 this.footer.container = fctr;
19598 this.footer = Roo.factory(this.footer, Roo);
19599 fctr.insertFirst(this.el);
19601 // this is a bit insane - as the paging toolbar seems to detach the el..
19602 // dom.parentNode.parentNode.parentNode
19603 // they get detached?
19607 Roo.View.superclass.constructor.call(this);
19612 Roo.extend(Roo.View, Roo.util.Observable, {
19615 * @cfg {Roo.data.Store} store Data store to load data from.
19620 * @cfg {String|Roo.Element} el The container element.
19625 * @cfg {String|Roo.Template} tpl The template used by this View
19629 * @cfg {String} dataName the named area of the template to use as the data area
19630 * Works with domtemplates roo-name="name"
19634 * @cfg {String} selectedClass The css class to add to selected nodes
19636 selectedClass : "x-view-selected",
19638 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19643 * @cfg {String} text to display on mask (default Loading)
19647 * @cfg {Boolean} multiSelect Allow multiple selection
19649 multiSelect : false,
19651 * @cfg {Boolean} singleSelect Allow single selection
19653 singleSelect: false,
19656 * @cfg {Boolean} toggleSelect - selecting
19658 toggleSelect : false,
19661 * @cfg {Boolean} tickable - selecting
19666 * Returns the element this view is bound to.
19667 * @return {Roo.Element}
19669 getEl : function(){
19670 return this.wrapEl;
19676 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19678 refresh : function(){
19679 //Roo.log('refresh');
19682 // if we are using something like 'domtemplate', then
19683 // the what gets used is:
19684 // t.applySubtemplate(NAME, data, wrapping data..)
19685 // the outer template then get' applied with
19686 // the store 'extra data'
19687 // and the body get's added to the
19688 // roo-name="data" node?
19689 // <span class='roo-tpl-{name}'></span> ?????
19693 this.clearSelections();
19694 this.el.update("");
19696 var records = this.store.getRange();
19697 if(records.length < 1) {
19699 // is this valid?? = should it render a template??
19701 this.el.update(this.emptyText);
19705 if (this.dataName) {
19706 this.el.update(t.apply(this.store.meta)); //????
19707 el = this.el.child('.roo-tpl-' + this.dataName);
19710 for(var i = 0, len = records.length; i < len; i++){
19711 var data = this.prepareData(records[i].data, i, records[i]);
19712 this.fireEvent("preparedata", this, data, i, records[i]);
19714 var d = Roo.apply({}, data);
19717 Roo.apply(d, {'roo-id' : Roo.id()});
19721 Roo.each(this.parent.item, function(item){
19722 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19725 Roo.apply(d, {'roo-data-checked' : 'checked'});
19729 html[html.length] = Roo.util.Format.trim(
19731 t.applySubtemplate(this.dataName, d, this.store.meta) :
19738 el.update(html.join(""));
19739 this.nodes = el.dom.childNodes;
19740 this.updateIndexes(0);
19745 * Function to override to reformat the data that is sent to
19746 * the template for each node.
19747 * DEPRICATED - use the preparedata event handler.
19748 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19749 * a JSON object for an UpdateManager bound view).
19751 prepareData : function(data, index, record)
19753 this.fireEvent("preparedata", this, data, index, record);
19757 onUpdate : function(ds, record){
19758 // Roo.log('on update');
19759 this.clearSelections();
19760 var index = this.store.indexOf(record);
19761 var n = this.nodes[index];
19762 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19763 n.parentNode.removeChild(n);
19764 this.updateIndexes(index, index);
19770 onAdd : function(ds, records, index)
19772 //Roo.log(['on Add', ds, records, index] );
19773 this.clearSelections();
19774 if(this.nodes.length == 0){
19778 var n = this.nodes[index];
19779 for(var i = 0, len = records.length; i < len; i++){
19780 var d = this.prepareData(records[i].data, i, records[i]);
19782 this.tpl.insertBefore(n, d);
19785 this.tpl.append(this.el, d);
19788 this.updateIndexes(index);
19791 onRemove : function(ds, record, index){
19792 // Roo.log('onRemove');
19793 this.clearSelections();
19794 var el = this.dataName ?
19795 this.el.child('.roo-tpl-' + this.dataName) :
19798 el.dom.removeChild(this.nodes[index]);
19799 this.updateIndexes(index);
19803 * Refresh an individual node.
19804 * @param {Number} index
19806 refreshNode : function(index){
19807 this.onUpdate(this.store, this.store.getAt(index));
19810 updateIndexes : function(startIndex, endIndex){
19811 var ns = this.nodes;
19812 startIndex = startIndex || 0;
19813 endIndex = endIndex || ns.length - 1;
19814 for(var i = startIndex; i <= endIndex; i++){
19815 ns[i].nodeIndex = i;
19820 * Changes the data store this view uses and refresh the view.
19821 * @param {Store} store
19823 setStore : function(store, initial){
19824 if(!initial && this.store){
19825 this.store.un("datachanged", this.refresh);
19826 this.store.un("add", this.onAdd);
19827 this.store.un("remove", this.onRemove);
19828 this.store.un("update", this.onUpdate);
19829 this.store.un("clear", this.refresh);
19830 this.store.un("beforeload", this.onBeforeLoad);
19831 this.store.un("load", this.onLoad);
19832 this.store.un("loadexception", this.onLoad);
19836 store.on("datachanged", this.refresh, this);
19837 store.on("add", this.onAdd, this);
19838 store.on("remove", this.onRemove, this);
19839 store.on("update", this.onUpdate, this);
19840 store.on("clear", this.refresh, this);
19841 store.on("beforeload", this.onBeforeLoad, this);
19842 store.on("load", this.onLoad, this);
19843 store.on("loadexception", this.onLoad, this);
19851 * onbeforeLoad - masks the loading area.
19854 onBeforeLoad : function(store,opts)
19856 //Roo.log('onBeforeLoad');
19858 this.el.update("");
19860 this.el.mask(this.mask ? this.mask : "Loading" );
19862 onLoad : function ()
19869 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19870 * @param {HTMLElement} node
19871 * @return {HTMLElement} The template node
19873 findItemFromChild : function(node){
19874 var el = this.dataName ?
19875 this.el.child('.roo-tpl-' + this.dataName,true) :
19878 if(!node || node.parentNode == el){
19881 var p = node.parentNode;
19882 while(p && p != el){
19883 if(p.parentNode == el){
19892 onClick : function(e){
19893 var item = this.findItemFromChild(e.getTarget());
19895 var index = this.indexOf(item);
19896 if(this.onItemClick(item, index, e) !== false){
19897 this.fireEvent("click", this, index, item, e);
19900 this.clearSelections();
19905 onContextMenu : function(e){
19906 var item = this.findItemFromChild(e.getTarget());
19908 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19913 onDblClick : function(e){
19914 var item = this.findItemFromChild(e.getTarget());
19916 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19920 onItemClick : function(item, index, e)
19922 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19925 if (this.toggleSelect) {
19926 var m = this.isSelected(item) ? 'unselect' : 'select';
19929 _t[m](item, true, false);
19932 if(this.multiSelect || this.singleSelect){
19933 if(this.multiSelect && e.shiftKey && this.lastSelection){
19934 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19936 this.select(item, this.multiSelect && e.ctrlKey);
19937 this.lastSelection = item;
19940 if(!this.tickable){
19941 e.preventDefault();
19949 * Get the number of selected nodes.
19952 getSelectionCount : function(){
19953 return this.selections.length;
19957 * Get the currently selected nodes.
19958 * @return {Array} An array of HTMLElements
19960 getSelectedNodes : function(){
19961 return this.selections;
19965 * Get the indexes of the selected nodes.
19968 getSelectedIndexes : function(){
19969 var indexes = [], s = this.selections;
19970 for(var i = 0, len = s.length; i < len; i++){
19971 indexes.push(s[i].nodeIndex);
19977 * Clear all selections
19978 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19980 clearSelections : function(suppressEvent){
19981 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19982 this.cmp.elements = this.selections;
19983 this.cmp.removeClass(this.selectedClass);
19984 this.selections = [];
19985 if(!suppressEvent){
19986 this.fireEvent("selectionchange", this, this.selections);
19992 * Returns true if the passed node is selected
19993 * @param {HTMLElement/Number} node The node or node index
19994 * @return {Boolean}
19996 isSelected : function(node){
19997 var s = this.selections;
20001 node = this.getNode(node);
20002 return s.indexOf(node) !== -1;
20007 * @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
20008 * @param {Boolean} keepExisting (optional) true to keep existing selections
20009 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20011 select : function(nodeInfo, keepExisting, suppressEvent){
20012 if(nodeInfo instanceof Array){
20014 this.clearSelections(true);
20016 for(var i = 0, len = nodeInfo.length; i < len; i++){
20017 this.select(nodeInfo[i], true, true);
20021 var node = this.getNode(nodeInfo);
20022 if(!node || this.isSelected(node)){
20023 return; // already selected.
20026 this.clearSelections(true);
20029 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20030 Roo.fly(node).addClass(this.selectedClass);
20031 this.selections.push(node);
20032 if(!suppressEvent){
20033 this.fireEvent("selectionchange", this, this.selections);
20041 * @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
20042 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20043 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20045 unselect : function(nodeInfo, keepExisting, suppressEvent)
20047 if(nodeInfo instanceof Array){
20048 Roo.each(this.selections, function(s) {
20049 this.unselect(s, nodeInfo);
20053 var node = this.getNode(nodeInfo);
20054 if(!node || !this.isSelected(node)){
20055 //Roo.log("not selected");
20056 return; // not selected.
20060 Roo.each(this.selections, function(s) {
20062 Roo.fly(node).removeClass(this.selectedClass);
20069 this.selections= ns;
20070 this.fireEvent("selectionchange", this, this.selections);
20074 * Gets a template node.
20075 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20076 * @return {HTMLElement} The node or null if it wasn't found
20078 getNode : function(nodeInfo){
20079 if(typeof nodeInfo == "string"){
20080 return document.getElementById(nodeInfo);
20081 }else if(typeof nodeInfo == "number"){
20082 return this.nodes[nodeInfo];
20088 * Gets a range template nodes.
20089 * @param {Number} startIndex
20090 * @param {Number} endIndex
20091 * @return {Array} An array of nodes
20093 getNodes : function(start, end){
20094 var ns = this.nodes;
20095 start = start || 0;
20096 end = typeof end == "undefined" ? ns.length - 1 : end;
20099 for(var i = start; i <= end; i++){
20103 for(var i = start; i >= end; i--){
20111 * Finds the index of the passed node
20112 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20113 * @return {Number} The index of the node or -1
20115 indexOf : function(node){
20116 node = this.getNode(node);
20117 if(typeof node.nodeIndex == "number"){
20118 return node.nodeIndex;
20120 var ns = this.nodes;
20121 for(var i = 0, len = ns.length; i < len; i++){
20132 * based on jquery fullcalendar
20136 Roo.bootstrap = Roo.bootstrap || {};
20138 * @class Roo.bootstrap.Calendar
20139 * @extends Roo.bootstrap.Component
20140 * Bootstrap Calendar class
20141 * @cfg {Boolean} loadMask (true|false) default false
20142 * @cfg {Object} header generate the user specific header of the calendar, default false
20145 * Create a new Container
20146 * @param {Object} config The config object
20151 Roo.bootstrap.Calendar = function(config){
20152 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20156 * Fires when a date is selected
20157 * @param {DatePicker} this
20158 * @param {Date} date The selected date
20162 * @event monthchange
20163 * Fires when the displayed month changes
20164 * @param {DatePicker} this
20165 * @param {Date} date The selected month
20167 'monthchange': true,
20169 * @event evententer
20170 * Fires when mouse over an event
20171 * @param {Calendar} this
20172 * @param {event} Event
20174 'evententer': true,
20176 * @event eventleave
20177 * Fires when the mouse leaves an
20178 * @param {Calendar} this
20181 'eventleave': true,
20183 * @event eventclick
20184 * Fires when the mouse click an
20185 * @param {Calendar} this
20194 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20197 * @cfg {Number} startDay
20198 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20206 getAutoCreate : function(){
20209 var fc_button = function(name, corner, style, content ) {
20210 return Roo.apply({},{
20212 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20214 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20217 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20228 style : 'width:100%',
20235 cls : 'fc-header-left',
20237 fc_button('prev', 'left', 'arrow', '‹' ),
20238 fc_button('next', 'right', 'arrow', '›' ),
20239 { tag: 'span', cls: 'fc-header-space' },
20240 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20248 cls : 'fc-header-center',
20252 cls: 'fc-header-title',
20255 html : 'month / year'
20263 cls : 'fc-header-right',
20265 /* fc_button('month', 'left', '', 'month' ),
20266 fc_button('week', '', '', 'week' ),
20267 fc_button('day', 'right', '', 'day' )
20279 header = this.header;
20282 var cal_heads = function() {
20284 // fixme - handle this.
20286 for (var i =0; i < Date.dayNames.length; i++) {
20287 var d = Date.dayNames[i];
20290 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20291 html : d.substring(0,3)
20295 ret[0].cls += ' fc-first';
20296 ret[6].cls += ' fc-last';
20299 var cal_cell = function(n) {
20302 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20307 cls: 'fc-day-number',
20311 cls: 'fc-day-content',
20315 style: 'position: relative;' // height: 17px;
20327 var cal_rows = function() {
20330 for (var r = 0; r < 6; r++) {
20337 for (var i =0; i < Date.dayNames.length; i++) {
20338 var d = Date.dayNames[i];
20339 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20342 row.cn[0].cls+=' fc-first';
20343 row.cn[0].cn[0].style = 'min-height:90px';
20344 row.cn[6].cls+=' fc-last';
20348 ret[0].cls += ' fc-first';
20349 ret[4].cls += ' fc-prev-last';
20350 ret[5].cls += ' fc-last';
20357 cls: 'fc-border-separate',
20358 style : 'width:100%',
20366 cls : 'fc-first fc-last',
20384 cls : 'fc-content',
20385 style : "position: relative;",
20388 cls : 'fc-view fc-view-month fc-grid',
20389 style : 'position: relative',
20390 unselectable : 'on',
20393 cls : 'fc-event-container',
20394 style : 'position:absolute;z-index:8;top:0;left:0;'
20412 initEvents : function()
20415 throw "can not find store for calendar";
20421 style: "text-align:center",
20425 style: "background-color:white;width:50%;margin:250 auto",
20429 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20440 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20442 var size = this.el.select('.fc-content', true).first().getSize();
20443 this.maskEl.setSize(size.width, size.height);
20444 this.maskEl.enableDisplayMode("block");
20445 if(!this.loadMask){
20446 this.maskEl.hide();
20449 this.store = Roo.factory(this.store, Roo.data);
20450 this.store.on('load', this.onLoad, this);
20451 this.store.on('beforeload', this.onBeforeLoad, this);
20455 this.cells = this.el.select('.fc-day',true);
20456 //Roo.log(this.cells);
20457 this.textNodes = this.el.query('.fc-day-number');
20458 this.cells.addClassOnOver('fc-state-hover');
20460 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20461 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20462 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20463 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20465 this.on('monthchange', this.onMonthChange, this);
20467 this.update(new Date().clearTime());
20470 resize : function() {
20471 var sz = this.el.getSize();
20473 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20474 this.el.select('.fc-day-content div',true).setHeight(34);
20479 showPrevMonth : function(e){
20480 this.update(this.activeDate.add("mo", -1));
20482 showToday : function(e){
20483 this.update(new Date().clearTime());
20486 showNextMonth : function(e){
20487 this.update(this.activeDate.add("mo", 1));
20491 showPrevYear : function(){
20492 this.update(this.activeDate.add("y", -1));
20496 showNextYear : function(){
20497 this.update(this.activeDate.add("y", 1));
20502 update : function(date)
20504 var vd = this.activeDate;
20505 this.activeDate = date;
20506 // if(vd && this.el){
20507 // var t = date.getTime();
20508 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20509 // Roo.log('using add remove');
20511 // this.fireEvent('monthchange', this, date);
20513 // this.cells.removeClass("fc-state-highlight");
20514 // this.cells.each(function(c){
20515 // if(c.dateValue == t){
20516 // c.addClass("fc-state-highlight");
20517 // setTimeout(function(){
20518 // try{c.dom.firstChild.focus();}catch(e){}
20528 var days = date.getDaysInMonth();
20530 var firstOfMonth = date.getFirstDateOfMonth();
20531 var startingPos = firstOfMonth.getDay()-this.startDay;
20533 if(startingPos < this.startDay){
20537 var pm = date.add(Date.MONTH, -1);
20538 var prevStart = pm.getDaysInMonth()-startingPos;
20540 this.cells = this.el.select('.fc-day',true);
20541 this.textNodes = this.el.query('.fc-day-number');
20542 this.cells.addClassOnOver('fc-state-hover');
20544 var cells = this.cells.elements;
20545 var textEls = this.textNodes;
20547 Roo.each(cells, function(cell){
20548 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20551 days += startingPos;
20553 // convert everything to numbers so it's fast
20554 var day = 86400000;
20555 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20558 //Roo.log(prevStart);
20560 var today = new Date().clearTime().getTime();
20561 var sel = date.clearTime().getTime();
20562 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20563 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20564 var ddMatch = this.disabledDatesRE;
20565 var ddText = this.disabledDatesText;
20566 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20567 var ddaysText = this.disabledDaysText;
20568 var format = this.format;
20570 var setCellClass = function(cal, cell){
20574 //Roo.log('set Cell Class');
20576 var t = d.getTime();
20580 cell.dateValue = t;
20582 cell.className += " fc-today";
20583 cell.className += " fc-state-highlight";
20584 cell.title = cal.todayText;
20587 // disable highlight in other month..
20588 //cell.className += " fc-state-highlight";
20593 cell.className = " fc-state-disabled";
20594 cell.title = cal.minText;
20598 cell.className = " fc-state-disabled";
20599 cell.title = cal.maxText;
20603 if(ddays.indexOf(d.getDay()) != -1){
20604 cell.title = ddaysText;
20605 cell.className = " fc-state-disabled";
20608 if(ddMatch && format){
20609 var fvalue = d.dateFormat(format);
20610 if(ddMatch.test(fvalue)){
20611 cell.title = ddText.replace("%0", fvalue);
20612 cell.className = " fc-state-disabled";
20616 if (!cell.initialClassName) {
20617 cell.initialClassName = cell.dom.className;
20620 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20625 for(; i < startingPos; i++) {
20626 textEls[i].innerHTML = (++prevStart);
20627 d.setDate(d.getDate()+1);
20629 cells[i].className = "fc-past fc-other-month";
20630 setCellClass(this, cells[i]);
20635 for(; i < days; i++){
20636 intDay = i - startingPos + 1;
20637 textEls[i].innerHTML = (intDay);
20638 d.setDate(d.getDate()+1);
20640 cells[i].className = ''; // "x-date-active";
20641 setCellClass(this, cells[i]);
20645 for(; i < 42; i++) {
20646 textEls[i].innerHTML = (++extraDays);
20647 d.setDate(d.getDate()+1);
20649 cells[i].className = "fc-future fc-other-month";
20650 setCellClass(this, cells[i]);
20653 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20655 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20657 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20658 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20660 if(totalRows != 6){
20661 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20662 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20665 this.fireEvent('monthchange', this, date);
20669 if(!this.internalRender){
20670 var main = this.el.dom.firstChild;
20671 var w = main.offsetWidth;
20672 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20673 Roo.fly(main).setWidth(w);
20674 this.internalRender = true;
20675 // opera does not respect the auto grow header center column
20676 // then, after it gets a width opera refuses to recalculate
20677 // without a second pass
20678 if(Roo.isOpera && !this.secondPass){
20679 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20680 this.secondPass = true;
20681 this.update.defer(10, this, [date]);
20688 findCell : function(dt) {
20689 dt = dt.clearTime().getTime();
20691 this.cells.each(function(c){
20692 //Roo.log("check " +c.dateValue + '?=' + dt);
20693 if(c.dateValue == dt){
20703 findCells : function(ev) {
20704 var s = ev.start.clone().clearTime().getTime();
20706 var e= ev.end.clone().clearTime().getTime();
20709 this.cells.each(function(c){
20710 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20712 if(c.dateValue > e){
20715 if(c.dateValue < s){
20724 // findBestRow: function(cells)
20728 // for (var i =0 ; i < cells.length;i++) {
20729 // ret = Math.max(cells[i].rows || 0,ret);
20736 addItem : function(ev)
20738 // look for vertical location slot in
20739 var cells = this.findCells(ev);
20741 // ev.row = this.findBestRow(cells);
20743 // work out the location.
20747 for(var i =0; i < cells.length; i++) {
20749 cells[i].row = cells[0].row;
20752 cells[i].row = cells[i].row + 1;
20762 if (crow.start.getY() == cells[i].getY()) {
20764 crow.end = cells[i];
20781 cells[0].events.push(ev);
20783 this.calevents.push(ev);
20786 clearEvents: function() {
20788 if(!this.calevents){
20792 Roo.each(this.cells.elements, function(c){
20798 Roo.each(this.calevents, function(e) {
20799 Roo.each(e.els, function(el) {
20800 el.un('mouseenter' ,this.onEventEnter, this);
20801 el.un('mouseleave' ,this.onEventLeave, this);
20806 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20812 renderEvents: function()
20816 this.cells.each(function(c) {
20825 if(c.row != c.events.length){
20826 r = 4 - (4 - (c.row - c.events.length));
20829 c.events = ev.slice(0, r);
20830 c.more = ev.slice(r);
20832 if(c.more.length && c.more.length == 1){
20833 c.events.push(c.more.pop());
20836 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20840 this.cells.each(function(c) {
20842 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20845 for (var e = 0; e < c.events.length; e++){
20846 var ev = c.events[e];
20847 var rows = ev.rows;
20849 for(var i = 0; i < rows.length; i++) {
20851 // how many rows should it span..
20854 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20855 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20857 unselectable : "on",
20860 cls: 'fc-event-inner',
20864 // cls: 'fc-event-time',
20865 // html : cells.length > 1 ? '' : ev.time
20869 cls: 'fc-event-title',
20870 html : String.format('{0}', ev.title)
20877 cls: 'ui-resizable-handle ui-resizable-e',
20878 html : '  '
20885 cfg.cls += ' fc-event-start';
20887 if ((i+1) == rows.length) {
20888 cfg.cls += ' fc-event-end';
20891 var ctr = _this.el.select('.fc-event-container',true).first();
20892 var cg = ctr.createChild(cfg);
20894 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20895 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20897 var r = (c.more.length) ? 1 : 0;
20898 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20899 cg.setWidth(ebox.right - sbox.x -2);
20901 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20902 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20903 cg.on('click', _this.onEventClick, _this, ev);
20914 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20915 style : 'position: absolute',
20916 unselectable : "on",
20919 cls: 'fc-event-inner',
20923 cls: 'fc-event-title',
20931 cls: 'ui-resizable-handle ui-resizable-e',
20932 html : '  '
20938 var ctr = _this.el.select('.fc-event-container',true).first();
20939 var cg = ctr.createChild(cfg);
20941 var sbox = c.select('.fc-day-content',true).first().getBox();
20942 var ebox = c.select('.fc-day-content',true).first().getBox();
20944 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20945 cg.setWidth(ebox.right - sbox.x -2);
20947 cg.on('click', _this.onMoreEventClick, _this, c.more);
20957 onEventEnter: function (e, el,event,d) {
20958 this.fireEvent('evententer', this, el, event);
20961 onEventLeave: function (e, el,event,d) {
20962 this.fireEvent('eventleave', this, el, event);
20965 onEventClick: function (e, el,event,d) {
20966 this.fireEvent('eventclick', this, el, event);
20969 onMonthChange: function () {
20973 onMoreEventClick: function(e, el, more)
20977 this.calpopover.placement = 'right';
20978 this.calpopover.setTitle('More');
20980 this.calpopover.setContent('');
20982 var ctr = this.calpopover.el.select('.popover-content', true).first();
20984 Roo.each(more, function(m){
20986 cls : 'fc-event-hori fc-event-draggable',
20989 var cg = ctr.createChild(cfg);
20991 cg.on('click', _this.onEventClick, _this, m);
20994 this.calpopover.show(el);
20999 onLoad: function ()
21001 this.calevents = [];
21004 if(this.store.getCount() > 0){
21005 this.store.data.each(function(d){
21008 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21009 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21010 time : d.data.start_time,
21011 title : d.data.title,
21012 description : d.data.description,
21013 venue : d.data.venue
21018 this.renderEvents();
21020 if(this.calevents.length && this.loadMask){
21021 this.maskEl.hide();
21025 onBeforeLoad: function()
21027 this.clearEvents();
21029 this.maskEl.show();
21043 * @class Roo.bootstrap.Popover
21044 * @extends Roo.bootstrap.Component
21045 * Bootstrap Popover class
21046 * @cfg {String} html contents of the popover (or false to use children..)
21047 * @cfg {String} title of popover (or false to hide)
21048 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21049 * @cfg {String} trigger click || hover (or false to trigger manually)
21050 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21051 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21052 * - if false and it has a 'parent' then it will be automatically added to that element
21053 * - if string - Roo.get will be called
21054 * @cfg {Number} delay - delay before showing
21057 * Create a new Popover
21058 * @param {Object} config The config object
21061 Roo.bootstrap.Popover = function(config){
21062 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21068 * After the popover show
21070 * @param {Roo.bootstrap.Popover} this
21075 * After the popover hide
21077 * @param {Roo.bootstrap.Popover} this
21083 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21088 placement : 'right',
21089 trigger : 'hover', // hover
21095 can_build_overlaid : false,
21097 maskEl : false, // the mask element
21100 alignEl : false, // when show is called with an element - this get's stored.
21102 getChildContainer : function()
21104 return this.contentEl;
21107 getPopoverHeader : function()
21109 this.title = true; // flag not to hide it..
21110 this.headerEl.addClass('p-0');
21111 return this.headerEl
21115 getAutoCreate : function(){
21118 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21119 style: 'display:block',
21125 cls : 'popover-inner ',
21129 cls: 'popover-title popover-header',
21130 html : this.title === false ? '' : this.title
21133 cls : 'popover-content popover-body ' + (this.cls || ''),
21134 html : this.html || ''
21145 * @param {string} the title
21147 setTitle: function(str)
21151 this.headerEl.dom.innerHTML = str;
21156 * @param {string} the body content
21158 setContent: function(str)
21161 if (this.contentEl) {
21162 this.contentEl.dom.innerHTML = str;
21166 // as it get's added to the bottom of the page.
21167 onRender : function(ct, position)
21169 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21174 var cfg = Roo.apply({}, this.getAutoCreate());
21178 cfg.cls += ' ' + this.cls;
21181 cfg.style = this.style;
21183 //Roo.log("adding to ");
21184 this.el = Roo.get(document.body).createChild(cfg, position);
21185 // Roo.log(this.el);
21188 this.contentEl = this.el.select('.popover-content',true).first();
21189 this.headerEl = this.el.select('.popover-title',true).first();
21192 if(typeof(this.items) != 'undefined'){
21193 var items = this.items;
21196 for(var i =0;i < items.length;i++) {
21197 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21201 this.items = nitems;
21203 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21204 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21211 resizeMask : function()
21213 this.maskEl.setSize(
21214 Roo.lib.Dom.getViewWidth(true),
21215 Roo.lib.Dom.getViewHeight(true)
21219 initEvents : function()
21223 Roo.bootstrap.Popover.register(this);
21226 this.arrowEl = this.el.select('.arrow',true).first();
21227 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21228 this.el.enableDisplayMode('block');
21232 if (this.over === false && !this.parent()) {
21235 if (this.triggers === false) {
21240 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21241 var triggers = this.trigger ? this.trigger.split(' ') : [];
21242 Roo.each(triggers, function(trigger) {
21244 if (trigger == 'click') {
21245 on_el.on('click', this.toggle, this);
21246 } else if (trigger != 'manual') {
21247 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21248 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21250 on_el.on(eventIn ,this.enter, this);
21251 on_el.on(eventOut, this.leave, this);
21261 toggle : function () {
21262 this.hoverState == 'in' ? this.leave() : this.enter();
21265 enter : function () {
21267 clearTimeout(this.timeout);
21269 this.hoverState = 'in';
21271 if (!this.delay || !this.delay.show) {
21276 this.timeout = setTimeout(function () {
21277 if (_t.hoverState == 'in') {
21280 }, this.delay.show)
21283 leave : function() {
21284 clearTimeout(this.timeout);
21286 this.hoverState = 'out';
21288 if (!this.delay || !this.delay.hide) {
21293 this.timeout = setTimeout(function () {
21294 if (_t.hoverState == 'out') {
21297 }, this.delay.hide)
21301 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21302 * @param {string} (left|right|top|bottom) position
21304 show : function (on_el, placement)
21306 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21307 on_el = on_el || false; // default to false
21310 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21311 on_el = this.parent().el;
21312 } else if (this.over) {
21313 on_el = Roo.get(this.over);
21318 this.alignEl = Roo.get( on_el );
21321 this.render(document.body);
21327 if (this.title === false) {
21328 this.headerEl.hide();
21333 this.el.dom.style.display = 'block';
21336 if (this.alignEl) {
21337 this.updatePosition(this.placement, true);
21340 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21341 var es = this.el.getSize();
21342 var x = Roo.lib.Dom.getViewWidth()/2;
21343 var y = Roo.lib.Dom.getViewHeight()/2;
21344 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21349 //var arrow = this.el.select('.arrow',true).first();
21350 //arrow.set(align[2],
21352 this.el.addClass('in');
21356 this.hoverState = 'in';
21359 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21360 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21361 this.maskEl.dom.style.display = 'block';
21362 this.maskEl.addClass('show');
21364 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21366 this.fireEvent('show', this);
21370 * fire this manually after loading a grid in the table for example
21371 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21372 * @param {Boolean} try and move it if we cant get right position.
21374 updatePosition : function(placement, try_move)
21376 // allow for calling with no parameters
21377 placement = placement ? placement : this.placement;
21378 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21380 this.el.removeClass([
21381 'fade','top','bottom', 'left', 'right','in',
21382 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21384 this.el.addClass(placement + ' bs-popover-' + placement);
21386 if (!this.alignEl ) {
21390 switch (placement) {
21392 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21393 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21394 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21395 //normal display... or moved up/down.
21396 this.el.setXY(offset);
21397 var xy = this.alignEl.getAnchorXY('tr', false);
21399 this.arrowEl.setXY(xy);
21402 // continue through...
21403 return this.updatePosition('left', false);
21407 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21408 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21409 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21410 //normal display... or moved up/down.
21411 this.el.setXY(offset);
21412 var xy = this.alignEl.getAnchorXY('tl', false);
21413 xy[0]-=10;xy[1]+=5; // << fix me
21414 this.arrowEl.setXY(xy);
21418 return this.updatePosition('right', false);
21421 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21422 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21423 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21424 //normal display... or moved up/down.
21425 this.el.setXY(offset);
21426 var xy = this.alignEl.getAnchorXY('t', false);
21427 xy[1]-=10; // << fix me
21428 this.arrowEl.setXY(xy);
21432 return this.updatePosition('bottom', false);
21435 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21436 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21437 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21438 //normal display... or moved up/down.
21439 this.el.setXY(offset);
21440 var xy = this.alignEl.getAnchorXY('b', false);
21441 xy[1]+=2; // << fix me
21442 this.arrowEl.setXY(xy);
21446 return this.updatePosition('top', false);
21457 this.el.setXY([0,0]);
21458 this.el.removeClass('in');
21460 this.hoverState = null;
21461 this.maskEl.hide(); // always..
21462 this.fireEvent('hide', this);
21468 Roo.apply(Roo.bootstrap.Popover, {
21471 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21472 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21473 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21474 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21479 clickHander : false,
21483 onMouseDown : function(e)
21485 if (this.popups.length && !e.getTarget(".roo-popover")) {
21486 /// what is nothing is showing..
21495 register : function(popup)
21497 if (!Roo.bootstrap.Popover.clickHandler) {
21498 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21500 // hide other popups.
21501 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21502 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21503 this.hideAll(); //<< why?
21504 //this.popups.push(popup);
21506 hideAll : function()
21508 this.popups.forEach(function(p) {
21512 onShow : function() {
21513 Roo.bootstrap.Popover.popups.push(this);
21515 onHide : function() {
21516 Roo.bootstrap.Popover.popups.remove(this);
21522 * Card header - holder for the card header elements.
21527 * @class Roo.bootstrap.PopoverNav
21528 * @extends Roo.bootstrap.NavGroup
21529 * Bootstrap Popover header navigation class
21531 * Create a new Popover Header Navigation
21532 * @param {Object} config The config object
21535 Roo.bootstrap.PopoverNav = function(config){
21536 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21539 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21542 container_method : 'getPopoverHeader'
21560 * @class Roo.bootstrap.Progress
21561 * @extends Roo.bootstrap.Component
21562 * Bootstrap Progress class
21563 * @cfg {Boolean} striped striped of the progress bar
21564 * @cfg {Boolean} active animated of the progress bar
21568 * Create a new Progress
21569 * @param {Object} config The config object
21572 Roo.bootstrap.Progress = function(config){
21573 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21576 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21581 getAutoCreate : function(){
21589 cfg.cls += ' progress-striped';
21593 cfg.cls += ' active';
21612 * @class Roo.bootstrap.ProgressBar
21613 * @extends Roo.bootstrap.Component
21614 * Bootstrap ProgressBar class
21615 * @cfg {Number} aria_valuenow aria-value now
21616 * @cfg {Number} aria_valuemin aria-value min
21617 * @cfg {Number} aria_valuemax aria-value max
21618 * @cfg {String} label label for the progress bar
21619 * @cfg {String} panel (success | info | warning | danger )
21620 * @cfg {String} role role of the progress bar
21621 * @cfg {String} sr_only text
21625 * Create a new ProgressBar
21626 * @param {Object} config The config object
21629 Roo.bootstrap.ProgressBar = function(config){
21630 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21633 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21637 aria_valuemax : 100,
21643 getAutoCreate : function()
21648 cls: 'progress-bar',
21649 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21661 cfg.role = this.role;
21664 if(this.aria_valuenow){
21665 cfg['aria-valuenow'] = this.aria_valuenow;
21668 if(this.aria_valuemin){
21669 cfg['aria-valuemin'] = this.aria_valuemin;
21672 if(this.aria_valuemax){
21673 cfg['aria-valuemax'] = this.aria_valuemax;
21676 if(this.label && !this.sr_only){
21677 cfg.html = this.label;
21681 cfg.cls += ' progress-bar-' + this.panel;
21687 update : function(aria_valuenow)
21689 this.aria_valuenow = aria_valuenow;
21691 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21706 * @class Roo.bootstrap.TabGroup
21707 * @extends Roo.bootstrap.Column
21708 * Bootstrap Column class
21709 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21710 * @cfg {Boolean} carousel true to make the group behave like a carousel
21711 * @cfg {Boolean} bullets show bullets for the panels
21712 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21713 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21714 * @cfg {Boolean} showarrow (true|false) show arrow default true
21717 * Create a new TabGroup
21718 * @param {Object} config The config object
21721 Roo.bootstrap.TabGroup = function(config){
21722 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21724 this.navId = Roo.id();
21727 Roo.bootstrap.TabGroup.register(this);
21731 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21734 transition : false,
21739 slideOnTouch : false,
21742 getAutoCreate : function()
21744 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21746 cfg.cls += ' tab-content';
21748 if (this.carousel) {
21749 cfg.cls += ' carousel slide';
21752 cls : 'carousel-inner',
21756 if(this.bullets && !Roo.isTouch){
21759 cls : 'carousel-bullets',
21763 if(this.bullets_cls){
21764 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21771 cfg.cn[0].cn.push(bullets);
21774 if(this.showarrow){
21775 cfg.cn[0].cn.push({
21777 class : 'carousel-arrow',
21781 class : 'carousel-prev',
21785 class : 'fa fa-chevron-left'
21791 class : 'carousel-next',
21795 class : 'fa fa-chevron-right'
21808 initEvents: function()
21810 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21811 // this.el.on("touchstart", this.onTouchStart, this);
21814 if(this.autoslide){
21817 this.slideFn = window.setInterval(function() {
21818 _this.showPanelNext();
21822 if(this.showarrow){
21823 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21824 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21830 // onTouchStart : function(e, el, o)
21832 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21836 // this.showPanelNext();
21840 getChildContainer : function()
21842 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21846 * register a Navigation item
21847 * @param {Roo.bootstrap.NavItem} the navitem to add
21849 register : function(item)
21851 this.tabs.push( item);
21852 item.navId = this.navId; // not really needed..
21857 getActivePanel : function()
21860 Roo.each(this.tabs, function(t) {
21870 getPanelByName : function(n)
21873 Roo.each(this.tabs, function(t) {
21874 if (t.tabId == n) {
21882 indexOfPanel : function(p)
21885 Roo.each(this.tabs, function(t,i) {
21886 if (t.tabId == p.tabId) {
21895 * show a specific panel
21896 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21897 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21899 showPanel : function (pan)
21901 if(this.transition || typeof(pan) == 'undefined'){
21902 Roo.log("waiting for the transitionend");
21906 if (typeof(pan) == 'number') {
21907 pan = this.tabs[pan];
21910 if (typeof(pan) == 'string') {
21911 pan = this.getPanelByName(pan);
21914 var cur = this.getActivePanel();
21917 Roo.log('pan or acitve pan is undefined');
21921 if (pan.tabId == this.getActivePanel().tabId) {
21925 if (false === cur.fireEvent('beforedeactivate')) {
21929 if(this.bullets > 0 && !Roo.isTouch){
21930 this.setActiveBullet(this.indexOfPanel(pan));
21933 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21935 //class="carousel-item carousel-item-next carousel-item-left"
21937 this.transition = true;
21938 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21939 var lr = dir == 'next' ? 'left' : 'right';
21940 pan.el.addClass(dir); // or prev
21941 pan.el.addClass('carousel-item-' + dir); // or prev
21942 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21943 cur.el.addClass(lr); // or right
21944 pan.el.addClass(lr);
21945 cur.el.addClass('carousel-item-' +lr); // or right
21946 pan.el.addClass('carousel-item-' +lr);
21950 cur.el.on('transitionend', function() {
21951 Roo.log("trans end?");
21953 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21954 pan.setActive(true);
21956 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21957 cur.setActive(false);
21959 _this.transition = false;
21961 }, this, { single: true } );
21966 cur.setActive(false);
21967 pan.setActive(true);
21972 showPanelNext : function()
21974 var i = this.indexOfPanel(this.getActivePanel());
21976 if (i >= this.tabs.length - 1 && !this.autoslide) {
21980 if (i >= this.tabs.length - 1 && this.autoslide) {
21984 this.showPanel(this.tabs[i+1]);
21987 showPanelPrev : function()
21989 var i = this.indexOfPanel(this.getActivePanel());
21991 if (i < 1 && !this.autoslide) {
21995 if (i < 1 && this.autoslide) {
21996 i = this.tabs.length;
21999 this.showPanel(this.tabs[i-1]);
22003 addBullet: function()
22005 if(!this.bullets || Roo.isTouch){
22008 var ctr = this.el.select('.carousel-bullets',true).first();
22009 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22010 var bullet = ctr.createChild({
22011 cls : 'bullet bullet-' + i
22012 },ctr.dom.lastChild);
22017 bullet.on('click', (function(e, el, o, ii, t){
22019 e.preventDefault();
22021 this.showPanel(ii);
22023 if(this.autoslide && this.slideFn){
22024 clearInterval(this.slideFn);
22025 this.slideFn = window.setInterval(function() {
22026 _this.showPanelNext();
22030 }).createDelegate(this, [i, bullet], true));
22035 setActiveBullet : function(i)
22041 Roo.each(this.el.select('.bullet', true).elements, function(el){
22042 el.removeClass('selected');
22045 var bullet = this.el.select('.bullet-' + i, true).first();
22051 bullet.addClass('selected');
22062 Roo.apply(Roo.bootstrap.TabGroup, {
22066 * register a Navigation Group
22067 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22069 register : function(navgrp)
22071 this.groups[navgrp.navId] = navgrp;
22075 * fetch a Navigation Group based on the navigation ID
22076 * if one does not exist , it will get created.
22077 * @param {string} the navgroup to add
22078 * @returns {Roo.bootstrap.NavGroup} the navgroup
22080 get: function(navId) {
22081 if (typeof(this.groups[navId]) == 'undefined') {
22082 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22084 return this.groups[navId] ;
22099 * @class Roo.bootstrap.TabPanel
22100 * @extends Roo.bootstrap.Component
22101 * Bootstrap TabPanel class
22102 * @cfg {Boolean} active panel active
22103 * @cfg {String} html panel content
22104 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22105 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22106 * @cfg {String} href click to link..
22107 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22111 * Create a new TabPanel
22112 * @param {Object} config The config object
22115 Roo.bootstrap.TabPanel = function(config){
22116 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22120 * Fires when the active status changes
22121 * @param {Roo.bootstrap.TabPanel} this
22122 * @param {Boolean} state the new state
22127 * @event beforedeactivate
22128 * Fires before a tab is de-activated - can be used to do validation on a form.
22129 * @param {Roo.bootstrap.TabPanel} this
22130 * @return {Boolean} false if there is an error
22133 'beforedeactivate': true
22136 this.tabId = this.tabId || Roo.id();
22140 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22147 touchSlide : false,
22148 getAutoCreate : function(){
22153 // item is needed for carousel - not sure if it has any effect otherwise
22154 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22155 html: this.html || ''
22159 cfg.cls += ' active';
22163 cfg.tabId = this.tabId;
22171 initEvents: function()
22173 var p = this.parent();
22175 this.navId = this.navId || p.navId;
22177 if (typeof(this.navId) != 'undefined') {
22178 // not really needed.. but just in case.. parent should be a NavGroup.
22179 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22183 var i = tg.tabs.length - 1;
22185 if(this.active && tg.bullets > 0 && i < tg.bullets){
22186 tg.setActiveBullet(i);
22190 this.el.on('click', this.onClick, this);
22192 if(Roo.isTouch && this.touchSlide){
22193 this.el.on("touchstart", this.onTouchStart, this);
22194 this.el.on("touchmove", this.onTouchMove, this);
22195 this.el.on("touchend", this.onTouchEnd, this);
22200 onRender : function(ct, position)
22202 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22205 setActive : function(state)
22207 Roo.log("panel - set active " + this.tabId + "=" + state);
22209 this.active = state;
22211 this.el.removeClass('active');
22213 } else if (!this.el.hasClass('active')) {
22214 this.el.addClass('active');
22217 this.fireEvent('changed', this, state);
22220 onClick : function(e)
22222 e.preventDefault();
22224 if(!this.href.length){
22228 window.location.href = this.href;
22237 onTouchStart : function(e)
22239 this.swiping = false;
22241 this.startX = e.browserEvent.touches[0].clientX;
22242 this.startY = e.browserEvent.touches[0].clientY;
22245 onTouchMove : function(e)
22247 this.swiping = true;
22249 this.endX = e.browserEvent.touches[0].clientX;
22250 this.endY = e.browserEvent.touches[0].clientY;
22253 onTouchEnd : function(e)
22260 var tabGroup = this.parent();
22262 if(this.endX > this.startX){ // swiping right
22263 tabGroup.showPanelPrev();
22267 if(this.startX > this.endX){ // swiping left
22268 tabGroup.showPanelNext();
22287 * @class Roo.bootstrap.DateField
22288 * @extends Roo.bootstrap.Input
22289 * Bootstrap DateField class
22290 * @cfg {Number} weekStart default 0
22291 * @cfg {String} viewMode default empty, (months|years)
22292 * @cfg {String} minViewMode default empty, (months|years)
22293 * @cfg {Number} startDate default -Infinity
22294 * @cfg {Number} endDate default Infinity
22295 * @cfg {Boolean} todayHighlight default false
22296 * @cfg {Boolean} todayBtn default false
22297 * @cfg {Boolean} calendarWeeks default false
22298 * @cfg {Object} daysOfWeekDisabled default empty
22299 * @cfg {Boolean} singleMode default false (true | false)
22301 * @cfg {Boolean} keyboardNavigation default true
22302 * @cfg {String} language default en
22305 * Create a new DateField
22306 * @param {Object} config The config object
22309 Roo.bootstrap.DateField = function(config){
22310 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22314 * Fires when this field show.
22315 * @param {Roo.bootstrap.DateField} this
22316 * @param {Mixed} date The date value
22321 * Fires when this field hide.
22322 * @param {Roo.bootstrap.DateField} this
22323 * @param {Mixed} date The date value
22328 * Fires when select a date.
22329 * @param {Roo.bootstrap.DateField} this
22330 * @param {Mixed} date The date value
22334 * @event beforeselect
22335 * Fires when before select a date.
22336 * @param {Roo.bootstrap.DateField} this
22337 * @param {Mixed} date The date value
22339 beforeselect : true
22343 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22346 * @cfg {String} format
22347 * The default date format string which can be overriden for localization support. The format must be
22348 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22352 * @cfg {String} altFormats
22353 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22354 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22356 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22364 todayHighlight : false,
22370 keyboardNavigation: true,
22372 calendarWeeks: false,
22374 startDate: -Infinity,
22378 daysOfWeekDisabled: [],
22382 singleMode : false,
22384 UTCDate: function()
22386 return new Date(Date.UTC.apply(Date, arguments));
22389 UTCToday: function()
22391 var today = new Date();
22392 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22395 getDate: function() {
22396 var d = this.getUTCDate();
22397 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22400 getUTCDate: function() {
22404 setDate: function(d) {
22405 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22408 setUTCDate: function(d) {
22410 this.setValue(this.formatDate(this.date));
22413 onRender: function(ct, position)
22416 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22418 this.language = this.language || 'en';
22419 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22420 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22422 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22423 this.format = this.format || 'm/d/y';
22424 this.isInline = false;
22425 this.isInput = true;
22426 this.component = this.el.select('.add-on', true).first() || false;
22427 this.component = (this.component && this.component.length === 0) ? false : this.component;
22428 this.hasInput = this.component && this.inputEl().length;
22430 if (typeof(this.minViewMode === 'string')) {
22431 switch (this.minViewMode) {
22433 this.minViewMode = 1;
22436 this.minViewMode = 2;
22439 this.minViewMode = 0;
22444 if (typeof(this.viewMode === 'string')) {
22445 switch (this.viewMode) {
22458 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22460 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22462 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22464 this.picker().on('mousedown', this.onMousedown, this);
22465 this.picker().on('click', this.onClick, this);
22467 this.picker().addClass('datepicker-dropdown');
22469 this.startViewMode = this.viewMode;
22471 if(this.singleMode){
22472 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22473 v.setVisibilityMode(Roo.Element.DISPLAY);
22477 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22478 v.setStyle('width', '189px');
22482 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22483 if(!this.calendarWeeks){
22488 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22489 v.attr('colspan', function(i, val){
22490 return parseInt(val) + 1;
22495 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22497 this.setStartDate(this.startDate);
22498 this.setEndDate(this.endDate);
22500 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22507 if(this.isInline) {
22512 picker : function()
22514 return this.pickerEl;
22515 // return this.el.select('.datepicker', true).first();
22518 fillDow: function()
22520 var dowCnt = this.weekStart;
22529 if(this.calendarWeeks){
22537 while (dowCnt < this.weekStart + 7) {
22541 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22545 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22548 fillMonths: function()
22551 var months = this.picker().select('>.datepicker-months td', true).first();
22553 months.dom.innerHTML = '';
22559 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22562 months.createChild(month);
22569 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;
22571 if (this.date < this.startDate) {
22572 this.viewDate = new Date(this.startDate);
22573 } else if (this.date > this.endDate) {
22574 this.viewDate = new Date(this.endDate);
22576 this.viewDate = new Date(this.date);
22584 var d = new Date(this.viewDate),
22585 year = d.getUTCFullYear(),
22586 month = d.getUTCMonth(),
22587 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22588 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22589 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22590 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22591 currentDate = this.date && this.date.valueOf(),
22592 today = this.UTCToday();
22594 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22596 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22598 // this.picker.select('>tfoot th.today').
22599 // .text(dates[this.language].today)
22600 // .toggle(this.todayBtn !== false);
22602 this.updateNavArrows();
22605 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22607 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22609 prevMonth.setUTCDate(day);
22611 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22613 var nextMonth = new Date(prevMonth);
22615 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22617 nextMonth = nextMonth.valueOf();
22619 var fillMonths = false;
22621 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22623 while(prevMonth.valueOf() <= nextMonth) {
22626 if (prevMonth.getUTCDay() === this.weekStart) {
22628 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22636 if(this.calendarWeeks){
22637 // ISO 8601: First week contains first thursday.
22638 // ISO also states week starts on Monday, but we can be more abstract here.
22640 // Start of current week: based on weekstart/current date
22641 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22642 // Thursday of this week
22643 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22644 // First Thursday of year, year from thursday
22645 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22646 // Calendar week: ms between thursdays, div ms per day, div 7 days
22647 calWeek = (th - yth) / 864e5 / 7 + 1;
22649 fillMonths.cn.push({
22657 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22659 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22662 if (this.todayHighlight &&
22663 prevMonth.getUTCFullYear() == today.getFullYear() &&
22664 prevMonth.getUTCMonth() == today.getMonth() &&
22665 prevMonth.getUTCDate() == today.getDate()) {
22666 clsName += ' today';
22669 if (currentDate && prevMonth.valueOf() === currentDate) {
22670 clsName += ' active';
22673 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22674 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22675 clsName += ' disabled';
22678 fillMonths.cn.push({
22680 cls: 'day ' + clsName,
22681 html: prevMonth.getDate()
22684 prevMonth.setDate(prevMonth.getDate()+1);
22687 var currentYear = this.date && this.date.getUTCFullYear();
22688 var currentMonth = this.date && this.date.getUTCMonth();
22690 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22692 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22693 v.removeClass('active');
22695 if(currentYear === year && k === currentMonth){
22696 v.addClass('active');
22699 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22700 v.addClass('disabled');
22706 year = parseInt(year/10, 10) * 10;
22708 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22710 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22713 for (var i = -1; i < 11; i++) {
22714 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22716 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22724 showMode: function(dir)
22727 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22730 Roo.each(this.picker().select('>div',true).elements, function(v){
22731 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22734 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22739 if(this.isInline) {
22743 this.picker().removeClass(['bottom', 'top']);
22745 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22747 * place to the top of element!
22751 this.picker().addClass('top');
22752 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22757 this.picker().addClass('bottom');
22759 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22762 parseDate : function(value)
22764 if(!value || value instanceof Date){
22767 var v = Date.parseDate(value, this.format);
22768 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22769 v = Date.parseDate(value, 'Y-m-d');
22771 if(!v && this.altFormats){
22772 if(!this.altFormatsArray){
22773 this.altFormatsArray = this.altFormats.split("|");
22775 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22776 v = Date.parseDate(value, this.altFormatsArray[i]);
22782 formatDate : function(date, fmt)
22784 return (!date || !(date instanceof Date)) ?
22785 date : date.dateFormat(fmt || this.format);
22788 onFocus : function()
22790 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22794 onBlur : function()
22796 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22798 var d = this.inputEl().getValue();
22805 showPopup : function()
22807 this.picker().show();
22811 this.fireEvent('showpopup', this, this.date);
22814 hidePopup : function()
22816 if(this.isInline) {
22819 this.picker().hide();
22820 this.viewMode = this.startViewMode;
22823 this.fireEvent('hidepopup', this, this.date);
22827 onMousedown: function(e)
22829 e.stopPropagation();
22830 e.preventDefault();
22835 Roo.bootstrap.DateField.superclass.keyup.call(this);
22839 setValue: function(v)
22841 if(this.fireEvent('beforeselect', this, v) !== false){
22842 var d = new Date(this.parseDate(v) ).clearTime();
22844 if(isNaN(d.getTime())){
22845 this.date = this.viewDate = '';
22846 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22850 v = this.formatDate(d);
22852 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22854 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22858 this.fireEvent('select', this, this.date);
22862 getValue: function()
22864 return this.formatDate(this.date);
22867 fireKey: function(e)
22869 if (!this.picker().isVisible()){
22870 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22876 var dateChanged = false,
22878 newDate, newViewDate;
22883 e.preventDefault();
22887 if (!this.keyboardNavigation) {
22890 dir = e.keyCode == 37 ? -1 : 1;
22893 newDate = this.moveYear(this.date, dir);
22894 newViewDate = this.moveYear(this.viewDate, dir);
22895 } else if (e.shiftKey){
22896 newDate = this.moveMonth(this.date, dir);
22897 newViewDate = this.moveMonth(this.viewDate, dir);
22899 newDate = new Date(this.date);
22900 newDate.setUTCDate(this.date.getUTCDate() + dir);
22901 newViewDate = new Date(this.viewDate);
22902 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22904 if (this.dateWithinRange(newDate)){
22905 this.date = newDate;
22906 this.viewDate = newViewDate;
22907 this.setValue(this.formatDate(this.date));
22909 e.preventDefault();
22910 dateChanged = true;
22915 if (!this.keyboardNavigation) {
22918 dir = e.keyCode == 38 ? -1 : 1;
22920 newDate = this.moveYear(this.date, dir);
22921 newViewDate = this.moveYear(this.viewDate, dir);
22922 } else if (e.shiftKey){
22923 newDate = this.moveMonth(this.date, dir);
22924 newViewDate = this.moveMonth(this.viewDate, dir);
22926 newDate = new Date(this.date);
22927 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22928 newViewDate = new Date(this.viewDate);
22929 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22931 if (this.dateWithinRange(newDate)){
22932 this.date = newDate;
22933 this.viewDate = newViewDate;
22934 this.setValue(this.formatDate(this.date));
22936 e.preventDefault();
22937 dateChanged = true;
22941 this.setValue(this.formatDate(this.date));
22943 e.preventDefault();
22946 this.setValue(this.formatDate(this.date));
22960 onClick: function(e)
22962 e.stopPropagation();
22963 e.preventDefault();
22965 var target = e.getTarget();
22967 if(target.nodeName.toLowerCase() === 'i'){
22968 target = Roo.get(target).dom.parentNode;
22971 var nodeName = target.nodeName;
22972 var className = target.className;
22973 var html = target.innerHTML;
22974 //Roo.log(nodeName);
22976 switch(nodeName.toLowerCase()) {
22978 switch(className) {
22984 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22985 switch(this.viewMode){
22987 this.viewDate = this.moveMonth(this.viewDate, dir);
22991 this.viewDate = this.moveYear(this.viewDate, dir);
22997 var date = new Date();
22998 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23000 this.setValue(this.formatDate(this.date));
23007 if (className.indexOf('disabled') < 0) {
23008 if (!this.viewDate) {
23009 this.viewDate = new Date();
23011 this.viewDate.setUTCDate(1);
23012 if (className.indexOf('month') > -1) {
23013 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23015 var year = parseInt(html, 10) || 0;
23016 this.viewDate.setUTCFullYear(year);
23020 if(this.singleMode){
23021 this.setValue(this.formatDate(this.viewDate));
23032 //Roo.log(className);
23033 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23034 var day = parseInt(html, 10) || 1;
23035 var year = (this.viewDate || new Date()).getUTCFullYear(),
23036 month = (this.viewDate || new Date()).getUTCMonth();
23038 if (className.indexOf('old') > -1) {
23045 } else if (className.indexOf('new') > -1) {
23053 //Roo.log([year,month,day]);
23054 this.date = this.UTCDate(year, month, day,0,0,0,0);
23055 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23057 //Roo.log(this.formatDate(this.date));
23058 this.setValue(this.formatDate(this.date));
23065 setStartDate: function(startDate)
23067 this.startDate = startDate || -Infinity;
23068 if (this.startDate !== -Infinity) {
23069 this.startDate = this.parseDate(this.startDate);
23072 this.updateNavArrows();
23075 setEndDate: function(endDate)
23077 this.endDate = endDate || Infinity;
23078 if (this.endDate !== Infinity) {
23079 this.endDate = this.parseDate(this.endDate);
23082 this.updateNavArrows();
23085 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23087 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23088 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23089 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23091 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23092 return parseInt(d, 10);
23095 this.updateNavArrows();
23098 updateNavArrows: function()
23100 if(this.singleMode){
23104 var d = new Date(this.viewDate),
23105 year = d.getUTCFullYear(),
23106 month = d.getUTCMonth();
23108 Roo.each(this.picker().select('.prev', true).elements, function(v){
23110 switch (this.viewMode) {
23113 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23119 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23126 Roo.each(this.picker().select('.next', true).elements, function(v){
23128 switch (this.viewMode) {
23131 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23137 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23145 moveMonth: function(date, dir)
23150 var new_date = new Date(date.valueOf()),
23151 day = new_date.getUTCDate(),
23152 month = new_date.getUTCMonth(),
23153 mag = Math.abs(dir),
23155 dir = dir > 0 ? 1 : -1;
23158 // If going back one month, make sure month is not current month
23159 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23161 return new_date.getUTCMonth() == month;
23163 // If going forward one month, make sure month is as expected
23164 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23166 return new_date.getUTCMonth() != new_month;
23168 new_month = month + dir;
23169 new_date.setUTCMonth(new_month);
23170 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23171 if (new_month < 0 || new_month > 11) {
23172 new_month = (new_month + 12) % 12;
23175 // For magnitudes >1, move one month at a time...
23176 for (var i=0; i<mag; i++) {
23177 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23178 new_date = this.moveMonth(new_date, dir);
23180 // ...then reset the day, keeping it in the new month
23181 new_month = new_date.getUTCMonth();
23182 new_date.setUTCDate(day);
23184 return new_month != new_date.getUTCMonth();
23187 // Common date-resetting loop -- if date is beyond end of month, make it
23190 new_date.setUTCDate(--day);
23191 new_date.setUTCMonth(new_month);
23196 moveYear: function(date, dir)
23198 return this.moveMonth(date, dir*12);
23201 dateWithinRange: function(date)
23203 return date >= this.startDate && date <= this.endDate;
23209 this.picker().remove();
23212 validateValue : function(value)
23214 if(this.getVisibilityEl().hasClass('hidden')){
23218 if(value.length < 1) {
23219 if(this.allowBlank){
23225 if(value.length < this.minLength){
23228 if(value.length > this.maxLength){
23232 var vt = Roo.form.VTypes;
23233 if(!vt[this.vtype](value, this)){
23237 if(typeof this.validator == "function"){
23238 var msg = this.validator(value);
23244 if(this.regex && !this.regex.test(value)){
23248 if(typeof(this.parseDate(value)) == 'undefined'){
23252 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23256 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23266 this.date = this.viewDate = '';
23268 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23273 Roo.apply(Roo.bootstrap.DateField, {
23284 html: '<i class="fa fa-arrow-left"/>'
23294 html: '<i class="fa fa-arrow-right"/>'
23336 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23337 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23338 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23339 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23340 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23353 navFnc: 'FullYear',
23358 navFnc: 'FullYear',
23363 Roo.apply(Roo.bootstrap.DateField, {
23367 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23371 cls: 'datepicker-days',
23375 cls: 'table-condensed',
23377 Roo.bootstrap.DateField.head,
23381 Roo.bootstrap.DateField.footer
23388 cls: 'datepicker-months',
23392 cls: 'table-condensed',
23394 Roo.bootstrap.DateField.head,
23395 Roo.bootstrap.DateField.content,
23396 Roo.bootstrap.DateField.footer
23403 cls: 'datepicker-years',
23407 cls: 'table-condensed',
23409 Roo.bootstrap.DateField.head,
23410 Roo.bootstrap.DateField.content,
23411 Roo.bootstrap.DateField.footer
23430 * @class Roo.bootstrap.TimeField
23431 * @extends Roo.bootstrap.Input
23432 * Bootstrap DateField class
23436 * Create a new TimeField
23437 * @param {Object} config The config object
23440 Roo.bootstrap.TimeField = function(config){
23441 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23445 * Fires when this field show.
23446 * @param {Roo.bootstrap.DateField} thisthis
23447 * @param {Mixed} date The date value
23452 * Fires when this field hide.
23453 * @param {Roo.bootstrap.DateField} this
23454 * @param {Mixed} date The date value
23459 * Fires when select a date.
23460 * @param {Roo.bootstrap.DateField} this
23461 * @param {Mixed} date The date value
23467 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23470 * @cfg {String} format
23471 * The default time format string which can be overriden for localization support. The format must be
23472 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23476 getAutoCreate : function()
23478 this.after = '<i class="fa far fa-clock"></i>';
23479 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23483 onRender: function(ct, position)
23486 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23488 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23490 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23492 this.pop = this.picker().select('>.datepicker-time',true).first();
23493 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23495 this.picker().on('mousedown', this.onMousedown, this);
23496 this.picker().on('click', this.onClick, this);
23498 this.picker().addClass('datepicker-dropdown');
23503 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23504 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23505 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23506 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23507 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23508 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23512 fireKey: function(e){
23513 if (!this.picker().isVisible()){
23514 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23520 e.preventDefault();
23528 this.onTogglePeriod();
23531 this.onIncrementMinutes();
23534 this.onDecrementMinutes();
23543 onClick: function(e) {
23544 e.stopPropagation();
23545 e.preventDefault();
23548 picker : function()
23550 return this.pickerEl;
23553 fillTime: function()
23555 var time = this.pop.select('tbody', true).first();
23557 time.dom.innerHTML = '';
23572 cls: 'hours-up fa fas fa-chevron-up'
23592 cls: 'minutes-up fa fas fa-chevron-up'
23613 cls: 'timepicker-hour',
23628 cls: 'timepicker-minute',
23643 cls: 'btn btn-primary period',
23665 cls: 'hours-down fa fas fa-chevron-down'
23685 cls: 'minutes-down fa fas fa-chevron-down'
23703 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23710 var hours = this.time.getHours();
23711 var minutes = this.time.getMinutes();
23724 hours = hours - 12;
23728 hours = '0' + hours;
23732 minutes = '0' + minutes;
23735 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23736 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23737 this.pop.select('button', true).first().dom.innerHTML = period;
23743 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23745 var cls = ['bottom'];
23747 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23754 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23758 //this.picker().setXY(20000,20000);
23759 this.picker().addClass(cls.join('-'));
23763 Roo.each(cls, function(c){
23768 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23769 //_this.picker().setTop(_this.inputEl().getHeight());
23773 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23775 //_this.picker().setTop(0 - _this.picker().getHeight());
23780 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23784 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23792 onFocus : function()
23794 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23798 onBlur : function()
23800 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23806 this.picker().show();
23811 this.fireEvent('show', this, this.date);
23816 this.picker().hide();
23819 this.fireEvent('hide', this, this.date);
23822 setTime : function()
23825 this.setValue(this.time.format(this.format));
23827 this.fireEvent('select', this, this.date);
23832 onMousedown: function(e){
23833 e.stopPropagation();
23834 e.preventDefault();
23837 onIncrementHours: function()
23839 Roo.log('onIncrementHours');
23840 this.time = this.time.add(Date.HOUR, 1);
23845 onDecrementHours: function()
23847 Roo.log('onDecrementHours');
23848 this.time = this.time.add(Date.HOUR, -1);
23852 onIncrementMinutes: function()
23854 Roo.log('onIncrementMinutes');
23855 this.time = this.time.add(Date.MINUTE, 1);
23859 onDecrementMinutes: function()
23861 Roo.log('onDecrementMinutes');
23862 this.time = this.time.add(Date.MINUTE, -1);
23866 onTogglePeriod: function()
23868 Roo.log('onTogglePeriod');
23869 this.time = this.time.add(Date.HOUR, 12);
23877 Roo.apply(Roo.bootstrap.TimeField, {
23881 cls: 'datepicker dropdown-menu',
23885 cls: 'datepicker-time',
23889 cls: 'table-condensed',
23918 cls: 'btn btn-info ok',
23946 * @class Roo.bootstrap.MonthField
23947 * @extends Roo.bootstrap.Input
23948 * Bootstrap MonthField class
23950 * @cfg {String} language default en
23953 * Create a new MonthField
23954 * @param {Object} config The config object
23957 Roo.bootstrap.MonthField = function(config){
23958 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23963 * Fires when this field show.
23964 * @param {Roo.bootstrap.MonthField} this
23965 * @param {Mixed} date The date value
23970 * Fires when this field hide.
23971 * @param {Roo.bootstrap.MonthField} this
23972 * @param {Mixed} date The date value
23977 * Fires when select a date.
23978 * @param {Roo.bootstrap.MonthField} this
23979 * @param {String} oldvalue The old value
23980 * @param {String} newvalue The new value
23986 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
23988 onRender: function(ct, position)
23991 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23993 this.language = this.language || 'en';
23994 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23995 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23997 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
23998 this.isInline = false;
23999 this.isInput = true;
24000 this.component = this.el.select('.add-on', true).first() || false;
24001 this.component = (this.component && this.component.length === 0) ? false : this.component;
24002 this.hasInput = this.component && this.inputEL().length;
24004 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24006 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24008 this.picker().on('mousedown', this.onMousedown, this);
24009 this.picker().on('click', this.onClick, this);
24011 this.picker().addClass('datepicker-dropdown');
24013 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24014 v.setStyle('width', '189px');
24021 if(this.isInline) {
24027 setValue: function(v, suppressEvent)
24029 var o = this.getValue();
24031 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24035 if(suppressEvent !== true){
24036 this.fireEvent('select', this, o, v);
24041 getValue: function()
24046 onClick: function(e)
24048 e.stopPropagation();
24049 e.preventDefault();
24051 var target = e.getTarget();
24053 if(target.nodeName.toLowerCase() === 'i'){
24054 target = Roo.get(target).dom.parentNode;
24057 var nodeName = target.nodeName;
24058 var className = target.className;
24059 var html = target.innerHTML;
24061 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24065 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24067 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24073 picker : function()
24075 return this.pickerEl;
24078 fillMonths: function()
24081 var months = this.picker().select('>.datepicker-months td', true).first();
24083 months.dom.innerHTML = '';
24089 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24092 months.createChild(month);
24101 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24102 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24105 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24106 e.removeClass('active');
24108 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24109 e.addClass('active');
24116 if(this.isInline) {
24120 this.picker().removeClass(['bottom', 'top']);
24122 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24124 * place to the top of element!
24128 this.picker().addClass('top');
24129 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24134 this.picker().addClass('bottom');
24136 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24139 onFocus : function()
24141 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24145 onBlur : function()
24147 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24149 var d = this.inputEl().getValue();
24158 this.picker().show();
24159 this.picker().select('>.datepicker-months', true).first().show();
24163 this.fireEvent('show', this, this.date);
24168 if(this.isInline) {
24171 this.picker().hide();
24172 this.fireEvent('hide', this, this.date);
24176 onMousedown: function(e)
24178 e.stopPropagation();
24179 e.preventDefault();
24184 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24188 fireKey: function(e)
24190 if (!this.picker().isVisible()){
24191 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24202 e.preventDefault();
24206 dir = e.keyCode == 37 ? -1 : 1;
24208 this.vIndex = this.vIndex + dir;
24210 if(this.vIndex < 0){
24214 if(this.vIndex > 11){
24218 if(isNaN(this.vIndex)){
24222 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24228 dir = e.keyCode == 38 ? -1 : 1;
24230 this.vIndex = this.vIndex + dir * 4;
24232 if(this.vIndex < 0){
24236 if(this.vIndex > 11){
24240 if(isNaN(this.vIndex)){
24244 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24249 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24250 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24254 e.preventDefault();
24257 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24258 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24274 this.picker().remove();
24279 Roo.apply(Roo.bootstrap.MonthField, {
24298 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24299 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24304 Roo.apply(Roo.bootstrap.MonthField, {
24308 cls: 'datepicker dropdown-menu roo-dynamic',
24312 cls: 'datepicker-months',
24316 cls: 'table-condensed',
24318 Roo.bootstrap.DateField.content
24338 * @class Roo.bootstrap.CheckBox
24339 * @extends Roo.bootstrap.Input
24340 * Bootstrap CheckBox class
24342 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24343 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24344 * @cfg {String} boxLabel The text that appears beside the checkbox
24345 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24346 * @cfg {Boolean} checked initnal the element
24347 * @cfg {Boolean} inline inline the element (default false)
24348 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24349 * @cfg {String} tooltip label tooltip
24352 * Create a new CheckBox
24353 * @param {Object} config The config object
24356 Roo.bootstrap.CheckBox = function(config){
24357 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24362 * Fires when the element is checked or unchecked.
24363 * @param {Roo.bootstrap.CheckBox} this This input
24364 * @param {Boolean} checked The new checked value
24369 * Fires when the element is click.
24370 * @param {Roo.bootstrap.CheckBox} this This input
24377 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24379 inputType: 'checkbox',
24388 // checkbox success does not make any sense really..
24393 getAutoCreate : function()
24395 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24401 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24404 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24410 type : this.inputType,
24411 value : this.inputValue,
24412 cls : 'roo-' + this.inputType, //'form-box',
24413 placeholder : this.placeholder || ''
24417 if(this.inputType != 'radio'){
24421 cls : 'roo-hidden-value',
24422 value : this.checked ? this.inputValue : this.valueOff
24427 if (this.weight) { // Validity check?
24428 cfg.cls += " " + this.inputType + "-" + this.weight;
24431 if (this.disabled) {
24432 input.disabled=true;
24436 input.checked = this.checked;
24441 input.name = this.name;
24443 if(this.inputType != 'radio'){
24444 hidden.name = this.name;
24445 input.name = '_hidden_' + this.name;
24450 input.cls += ' input-' + this.size;
24455 ['xs','sm','md','lg'].map(function(size){
24456 if (settings[size]) {
24457 cfg.cls += ' col-' + size + '-' + settings[size];
24461 var inputblock = input;
24463 if (this.before || this.after) {
24466 cls : 'input-group',
24471 inputblock.cn.push({
24473 cls : 'input-group-addon',
24478 inputblock.cn.push(input);
24480 if(this.inputType != 'radio'){
24481 inputblock.cn.push(hidden);
24485 inputblock.cn.push({
24487 cls : 'input-group-addon',
24493 var boxLabelCfg = false;
24499 //'for': id, // box label is handled by onclick - so no for...
24501 html: this.boxLabel
24504 boxLabelCfg.tooltip = this.tooltip;
24510 if (align ==='left' && this.fieldLabel.length) {
24511 // Roo.log("left and has label");
24516 cls : 'control-label',
24517 html : this.fieldLabel
24528 cfg.cn[1].cn.push(boxLabelCfg);
24531 if(this.labelWidth > 12){
24532 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24535 if(this.labelWidth < 13 && this.labelmd == 0){
24536 this.labelmd = this.labelWidth;
24539 if(this.labellg > 0){
24540 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24541 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24544 if(this.labelmd > 0){
24545 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24546 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24549 if(this.labelsm > 0){
24550 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24551 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24554 if(this.labelxs > 0){
24555 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24556 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24559 } else if ( this.fieldLabel.length) {
24560 // Roo.log(" label");
24564 tag: this.boxLabel ? 'span' : 'label',
24566 cls: 'control-label box-input-label',
24567 //cls : 'input-group-addon',
24568 html : this.fieldLabel
24575 cfg.cn.push(boxLabelCfg);
24580 // Roo.log(" no label && no align");
24581 cfg.cn = [ inputblock ] ;
24583 cfg.cn.push(boxLabelCfg);
24591 if(this.inputType != 'radio'){
24592 cfg.cn.push(hidden);
24600 * return the real input element.
24602 inputEl: function ()
24604 return this.el.select('input.roo-' + this.inputType,true).first();
24606 hiddenEl: function ()
24608 return this.el.select('input.roo-hidden-value',true).first();
24611 labelEl: function()
24613 return this.el.select('label.control-label',true).first();
24615 /* depricated... */
24619 return this.labelEl();
24622 boxLabelEl: function()
24624 return this.el.select('label.box-label',true).first();
24627 initEvents : function()
24629 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24631 this.inputEl().on('click', this.onClick, this);
24633 if (this.boxLabel) {
24634 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24637 this.startValue = this.getValue();
24640 Roo.bootstrap.CheckBox.register(this);
24644 onClick : function(e)
24646 if(this.fireEvent('click', this, e) !== false){
24647 this.setChecked(!this.checked);
24652 setChecked : function(state,suppressEvent)
24654 this.startValue = this.getValue();
24656 if(this.inputType == 'radio'){
24658 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24659 e.dom.checked = false;
24662 this.inputEl().dom.checked = true;
24664 this.inputEl().dom.value = this.inputValue;
24666 if(suppressEvent !== true){
24667 this.fireEvent('check', this, true);
24675 this.checked = state;
24677 this.inputEl().dom.checked = state;
24680 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24682 if(suppressEvent !== true){
24683 this.fireEvent('check', this, state);
24689 getValue : function()
24691 if(this.inputType == 'radio'){
24692 return this.getGroupValue();
24695 return this.hiddenEl().dom.value;
24699 getGroupValue : function()
24701 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24705 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24708 setValue : function(v,suppressEvent)
24710 if(this.inputType == 'radio'){
24711 this.setGroupValue(v, suppressEvent);
24715 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24720 setGroupValue : function(v, suppressEvent)
24722 this.startValue = this.getValue();
24724 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24725 e.dom.checked = false;
24727 if(e.dom.value == v){
24728 e.dom.checked = true;
24732 if(suppressEvent !== true){
24733 this.fireEvent('check', this, true);
24741 validate : function()
24743 if(this.getVisibilityEl().hasClass('hidden')){
24749 (this.inputType == 'radio' && this.validateRadio()) ||
24750 (this.inputType == 'checkbox' && this.validateCheckbox())
24756 this.markInvalid();
24760 validateRadio : function()
24762 if(this.getVisibilityEl().hasClass('hidden')){
24766 if(this.allowBlank){
24772 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24773 if(!e.dom.checked){
24785 validateCheckbox : function()
24788 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24789 //return (this.getValue() == this.inputValue) ? true : false;
24792 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24800 for(var i in group){
24801 if(group[i].el.isVisible(true)){
24809 for(var i in group){
24814 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24821 * Mark this field as valid
24823 markValid : function()
24827 this.fireEvent('valid', this);
24829 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24832 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24839 if(this.inputType == 'radio'){
24840 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24841 var fg = e.findParent('.form-group', false, true);
24842 if (Roo.bootstrap.version == 3) {
24843 fg.removeClass([_this.invalidClass, _this.validClass]);
24844 fg.addClass(_this.validClass);
24846 fg.removeClass(['is-valid', 'is-invalid']);
24847 fg.addClass('is-valid');
24855 var fg = this.el.findParent('.form-group', false, true);
24856 if (Roo.bootstrap.version == 3) {
24857 fg.removeClass([this.invalidClass, this.validClass]);
24858 fg.addClass(this.validClass);
24860 fg.removeClass(['is-valid', 'is-invalid']);
24861 fg.addClass('is-valid');
24866 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24872 for(var i in group){
24873 var fg = group[i].el.findParent('.form-group', false, true);
24874 if (Roo.bootstrap.version == 3) {
24875 fg.removeClass([this.invalidClass, this.validClass]);
24876 fg.addClass(this.validClass);
24878 fg.removeClass(['is-valid', 'is-invalid']);
24879 fg.addClass('is-valid');
24885 * Mark this field as invalid
24886 * @param {String} msg The validation message
24888 markInvalid : function(msg)
24890 if(this.allowBlank){
24896 this.fireEvent('invalid', this, msg);
24898 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24901 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24905 label.markInvalid();
24908 if(this.inputType == 'radio'){
24910 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24911 var fg = e.findParent('.form-group', false, true);
24912 if (Roo.bootstrap.version == 3) {
24913 fg.removeClass([_this.invalidClass, _this.validClass]);
24914 fg.addClass(_this.invalidClass);
24916 fg.removeClass(['is-invalid', 'is-valid']);
24917 fg.addClass('is-invalid');
24925 var fg = this.el.findParent('.form-group', false, true);
24926 if (Roo.bootstrap.version == 3) {
24927 fg.removeClass([_this.invalidClass, _this.validClass]);
24928 fg.addClass(_this.invalidClass);
24930 fg.removeClass(['is-invalid', 'is-valid']);
24931 fg.addClass('is-invalid');
24936 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24942 for(var i in group){
24943 var fg = group[i].el.findParent('.form-group', false, true);
24944 if (Roo.bootstrap.version == 3) {
24945 fg.removeClass([_this.invalidClass, _this.validClass]);
24946 fg.addClass(_this.invalidClass);
24948 fg.removeClass(['is-invalid', 'is-valid']);
24949 fg.addClass('is-invalid');
24955 clearInvalid : function()
24957 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24959 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24961 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24963 if (label && label.iconEl) {
24964 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24965 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24969 disable : function()
24971 if(this.inputType != 'radio'){
24972 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24979 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24980 _this.getActionEl().addClass(this.disabledClass);
24981 e.dom.disabled = true;
24985 this.disabled = true;
24986 this.fireEvent("disable", this);
24990 enable : function()
24992 if(this.inputType != 'radio'){
24993 Roo.bootstrap.CheckBox.superclass.enable.call(this);
25000 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25001 _this.getActionEl().removeClass(this.disabledClass);
25002 e.dom.disabled = false;
25006 this.disabled = false;
25007 this.fireEvent("enable", this);
25011 setBoxLabel : function(v)
25016 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25022 Roo.apply(Roo.bootstrap.CheckBox, {
25027 * register a CheckBox Group
25028 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25030 register : function(checkbox)
25032 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25033 this.groups[checkbox.groupId] = {};
25036 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25040 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25044 * fetch a CheckBox Group based on the group ID
25045 * @param {string} the group ID
25046 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25048 get: function(groupId) {
25049 if (typeof(this.groups[groupId]) == 'undefined') {
25053 return this.groups[groupId] ;
25066 * @class Roo.bootstrap.Radio
25067 * @extends Roo.bootstrap.Component
25068 * Bootstrap Radio class
25069 * @cfg {String} boxLabel - the label associated
25070 * @cfg {String} value - the value of radio
25073 * Create a new Radio
25074 * @param {Object} config The config object
25076 Roo.bootstrap.Radio = function(config){
25077 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25081 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25087 getAutoCreate : function()
25091 cls : 'form-group radio',
25096 html : this.boxLabel
25104 initEvents : function()
25106 this.parent().register(this);
25108 this.el.on('click', this.onClick, this);
25112 onClick : function(e)
25114 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25115 this.setChecked(true);
25119 setChecked : function(state, suppressEvent)
25121 this.parent().setValue(this.value, suppressEvent);
25125 setBoxLabel : function(v)
25130 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25145 * @class Roo.bootstrap.SecurePass
25146 * @extends Roo.bootstrap.Input
25147 * Bootstrap SecurePass class
25151 * Create a new SecurePass
25152 * @param {Object} config The config object
25155 Roo.bootstrap.SecurePass = function (config) {
25156 // these go here, so the translation tool can replace them..
25158 PwdEmpty: "Please type a password, and then retype it to confirm.",
25159 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25160 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25161 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25162 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25163 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25164 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25165 TooWeak: "Your password is Too Weak."
25167 this.meterLabel = "Password strength:";
25168 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25169 this.meterClass = [
25170 "roo-password-meter-tooweak",
25171 "roo-password-meter-weak",
25172 "roo-password-meter-medium",
25173 "roo-password-meter-strong",
25174 "roo-password-meter-grey"
25179 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25182 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25184 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25186 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25187 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25188 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25189 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25190 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25191 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25192 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25202 * @cfg {String/Object} Label for the strength meter (defaults to
25203 * 'Password strength:')
25208 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25209 * ['Weak', 'Medium', 'Strong'])
25212 pwdStrengths: false,
25225 initEvents: function ()
25227 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25229 if (this.el.is('input[type=password]') && Roo.isSafari) {
25230 this.el.on('keydown', this.SafariOnKeyDown, this);
25233 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25236 onRender: function (ct, position)
25238 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25239 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25240 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25242 this.trigger.createChild({
25247 cls: 'roo-password-meter-grey col-xs-12',
25250 //width: this.meterWidth + 'px'
25254 cls: 'roo-password-meter-text'
25260 if (this.hideTrigger) {
25261 this.trigger.setDisplayed(false);
25263 this.setSize(this.width || '', this.height || '');
25266 onDestroy: function ()
25268 if (this.trigger) {
25269 this.trigger.removeAllListeners();
25270 this.trigger.remove();
25273 this.wrap.remove();
25275 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25278 checkStrength: function ()
25280 var pwd = this.inputEl().getValue();
25281 if (pwd == this._lastPwd) {
25286 if (this.ClientSideStrongPassword(pwd)) {
25288 } else if (this.ClientSideMediumPassword(pwd)) {
25290 } else if (this.ClientSideWeakPassword(pwd)) {
25296 Roo.log('strength1: ' + strength);
25298 //var pm = this.trigger.child('div/div/div').dom;
25299 var pm = this.trigger.child('div/div');
25300 pm.removeClass(this.meterClass);
25301 pm.addClass(this.meterClass[strength]);
25304 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25306 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25308 this._lastPwd = pwd;
25312 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25314 this._lastPwd = '';
25316 var pm = this.trigger.child('div/div');
25317 pm.removeClass(this.meterClass);
25318 pm.addClass('roo-password-meter-grey');
25321 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25324 this.inputEl().dom.type='password';
25327 validateValue: function (value)
25329 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25332 if (value.length == 0) {
25333 if (this.allowBlank) {
25334 this.clearInvalid();
25338 this.markInvalid(this.errors.PwdEmpty);
25339 this.errorMsg = this.errors.PwdEmpty;
25347 if (!value.match(/[\x21-\x7e]+/)) {
25348 this.markInvalid(this.errors.PwdBadChar);
25349 this.errorMsg = this.errors.PwdBadChar;
25352 if (value.length < 6) {
25353 this.markInvalid(this.errors.PwdShort);
25354 this.errorMsg = this.errors.PwdShort;
25357 if (value.length > 16) {
25358 this.markInvalid(this.errors.PwdLong);
25359 this.errorMsg = this.errors.PwdLong;
25363 if (this.ClientSideStrongPassword(value)) {
25365 } else if (this.ClientSideMediumPassword(value)) {
25367 } else if (this.ClientSideWeakPassword(value)) {
25374 if (strength < 2) {
25375 //this.markInvalid(this.errors.TooWeak);
25376 this.errorMsg = this.errors.TooWeak;
25381 console.log('strength2: ' + strength);
25383 //var pm = this.trigger.child('div/div/div').dom;
25385 var pm = this.trigger.child('div/div');
25386 pm.removeClass(this.meterClass);
25387 pm.addClass(this.meterClass[strength]);
25389 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25391 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25393 this.errorMsg = '';
25397 CharacterSetChecks: function (type)
25400 this.fResult = false;
25403 isctype: function (character, type)
25406 case this.kCapitalLetter:
25407 if (character >= 'A' && character <= 'Z') {
25412 case this.kSmallLetter:
25413 if (character >= 'a' && character <= 'z') {
25419 if (character >= '0' && character <= '9') {
25424 case this.kPunctuation:
25425 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25436 IsLongEnough: function (pwd, size)
25438 return !(pwd == null || isNaN(size) || pwd.length < size);
25441 SpansEnoughCharacterSets: function (word, nb)
25443 if (!this.IsLongEnough(word, nb))
25448 var characterSetChecks = new Array(
25449 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25450 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25453 for (var index = 0; index < word.length; ++index) {
25454 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25455 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25456 characterSetChecks[nCharSet].fResult = true;
25463 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25464 if (characterSetChecks[nCharSet].fResult) {
25469 if (nCharSets < nb) {
25475 ClientSideStrongPassword: function (pwd)
25477 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25480 ClientSideMediumPassword: function (pwd)
25482 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25485 ClientSideWeakPassword: function (pwd)
25487 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25490 })//<script type="text/javascript">
25493 * Based Ext JS Library 1.1.1
25494 * Copyright(c) 2006-2007, Ext JS, LLC.
25500 * @class Roo.HtmlEditorCore
25501 * @extends Roo.Component
25502 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25504 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25507 Roo.HtmlEditorCore = function(config){
25510 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25515 * @event initialize
25516 * Fires when the editor is fully initialized (including the iframe)
25517 * @param {Roo.HtmlEditorCore} this
25522 * Fires when the editor is first receives the focus. Any insertion must wait
25523 * until after this event.
25524 * @param {Roo.HtmlEditorCore} this
25528 * @event beforesync
25529 * Fires before the textarea is updated with content from the editor iframe. Return false
25530 * to cancel the sync.
25531 * @param {Roo.HtmlEditorCore} this
25532 * @param {String} html
25536 * @event beforepush
25537 * Fires before the iframe editor is updated with content from the textarea. Return false
25538 * to cancel the push.
25539 * @param {Roo.HtmlEditorCore} this
25540 * @param {String} html
25545 * Fires when the textarea is updated with content from the editor iframe.
25546 * @param {Roo.HtmlEditorCore} this
25547 * @param {String} html
25552 * Fires when the iframe editor is updated with content from the textarea.
25553 * @param {Roo.HtmlEditorCore} this
25554 * @param {String} html
25559 * @event editorevent
25560 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25561 * @param {Roo.HtmlEditorCore} this
25567 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25569 // defaults : white / black...
25570 this.applyBlacklists();
25577 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25581 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25587 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25592 * @cfg {Number} height (in pixels)
25596 * @cfg {Number} width (in pixels)
25601 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25604 stylesheets: false,
25609 // private properties
25610 validationEvent : false,
25612 initialized : false,
25614 sourceEditMode : false,
25615 onFocus : Roo.emptyFn,
25617 hideMode:'offsets',
25621 // blacklist + whitelisted elements..
25628 * Protected method that will not generally be called directly. It
25629 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25630 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25632 getDocMarkup : function(){
25636 // inherit styels from page...??
25637 if (this.stylesheets === false) {
25639 Roo.get(document.head).select('style').each(function(node) {
25640 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25643 Roo.get(document.head).select('link').each(function(node) {
25644 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25647 } else if (!this.stylesheets.length) {
25649 st = '<style type="text/css">' +
25650 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25653 for (var i in this.stylesheets) {
25654 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25659 st += '<style type="text/css">' +
25660 'IMG { cursor: pointer } ' +
25663 var cls = 'roo-htmleditor-body';
25665 if(this.bodyCls.length){
25666 cls += ' ' + this.bodyCls;
25669 return '<html><head>' + st +
25670 //<style type="text/css">' +
25671 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25673 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25677 onRender : function(ct, position)
25680 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25681 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25684 this.el.dom.style.border = '0 none';
25685 this.el.dom.setAttribute('tabIndex', -1);
25686 this.el.addClass('x-hidden hide');
25690 if(Roo.isIE){ // fix IE 1px bogus margin
25691 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25695 this.frameId = Roo.id();
25699 var iframe = this.owner.wrap.createChild({
25701 cls: 'form-control', // bootstrap..
25703 name: this.frameId,
25704 frameBorder : 'no',
25705 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25710 this.iframe = iframe.dom;
25712 this.assignDocWin();
25714 this.doc.designMode = 'on';
25717 this.doc.write(this.getDocMarkup());
25721 var task = { // must defer to wait for browser to be ready
25723 //console.log("run task?" + this.doc.readyState);
25724 this.assignDocWin();
25725 if(this.doc.body || this.doc.readyState == 'complete'){
25727 this.doc.designMode="on";
25731 Roo.TaskMgr.stop(task);
25732 this.initEditor.defer(10, this);
25739 Roo.TaskMgr.start(task);
25744 onResize : function(w, h)
25746 Roo.log('resize: ' +w + ',' + h );
25747 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25751 if(typeof w == 'number'){
25753 this.iframe.style.width = w + 'px';
25755 if(typeof h == 'number'){
25757 this.iframe.style.height = h + 'px';
25759 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25766 * Toggles the editor between standard and source edit mode.
25767 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25769 toggleSourceEdit : function(sourceEditMode){
25771 this.sourceEditMode = sourceEditMode === true;
25773 if(this.sourceEditMode){
25775 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25778 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25779 //this.iframe.className = '';
25782 //this.setSize(this.owner.wrap.getSize());
25783 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25790 * Protected method that will not generally be called directly. If you need/want
25791 * custom HTML cleanup, this is the method you should override.
25792 * @param {String} html The HTML to be cleaned
25793 * return {String} The cleaned HTML
25795 cleanHtml : function(html){
25796 html = String(html);
25797 if(html.length > 5){
25798 if(Roo.isSafari){ // strip safari nonsense
25799 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25802 if(html == ' '){
25809 * HTML Editor -> Textarea
25810 * Protected method that will not generally be called directly. Syncs the contents
25811 * of the editor iframe with the textarea.
25813 syncValue : function(){
25814 if(this.initialized){
25815 var bd = (this.doc.body || this.doc.documentElement);
25816 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25817 var html = bd.innerHTML;
25819 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25820 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25822 html = '<div style="'+m[0]+'">' + html + '</div>';
25825 html = this.cleanHtml(html);
25826 // fix up the special chars.. normaly like back quotes in word...
25827 // however we do not want to do this with chinese..
25828 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25830 var cc = match.charCodeAt();
25832 // Get the character value, handling surrogate pairs
25833 if (match.length == 2) {
25834 // It's a surrogate pair, calculate the Unicode code point
25835 var high = match.charCodeAt(0) - 0xD800;
25836 var low = match.charCodeAt(1) - 0xDC00;
25837 cc = (high * 0x400) + low + 0x10000;
25839 (cc >= 0x4E00 && cc < 0xA000 ) ||
25840 (cc >= 0x3400 && cc < 0x4E00 ) ||
25841 (cc >= 0xf900 && cc < 0xfb00 )
25846 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25847 return "&#" + cc + ";";
25854 if(this.owner.fireEvent('beforesync', this, html) !== false){
25855 this.el.dom.value = html;
25856 this.owner.fireEvent('sync', this, html);
25862 * Protected method that will not generally be called directly. Pushes the value of the textarea
25863 * into the iframe editor.
25865 pushValue : function(){
25866 if(this.initialized){
25867 var v = this.el.dom.value.trim();
25869 // if(v.length < 1){
25873 if(this.owner.fireEvent('beforepush', this, v) !== false){
25874 var d = (this.doc.body || this.doc.documentElement);
25876 this.cleanUpPaste();
25877 this.el.dom.value = d.innerHTML;
25878 this.owner.fireEvent('push', this, v);
25884 deferFocus : function(){
25885 this.focus.defer(10, this);
25889 focus : function(){
25890 if(this.win && !this.sourceEditMode){
25897 assignDocWin: function()
25899 var iframe = this.iframe;
25902 this.doc = iframe.contentWindow.document;
25903 this.win = iframe.contentWindow;
25905 // if (!Roo.get(this.frameId)) {
25908 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25909 // this.win = Roo.get(this.frameId).dom.contentWindow;
25911 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25915 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25916 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25921 initEditor : function(){
25922 //console.log("INIT EDITOR");
25923 this.assignDocWin();
25927 this.doc.designMode="on";
25929 this.doc.write(this.getDocMarkup());
25932 var dbody = (this.doc.body || this.doc.documentElement);
25933 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25934 // this copies styles from the containing element into thsi one..
25935 // not sure why we need all of this..
25936 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25938 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25939 //ss['background-attachment'] = 'fixed'; // w3c
25940 dbody.bgProperties = 'fixed'; // ie
25941 //Roo.DomHelper.applyStyles(dbody, ss);
25942 Roo.EventManager.on(this.doc, {
25943 //'mousedown': this.onEditorEvent,
25944 'mouseup': this.onEditorEvent,
25945 'dblclick': this.onEditorEvent,
25946 'click': this.onEditorEvent,
25947 'keyup': this.onEditorEvent,
25952 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25954 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25955 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25957 this.initialized = true;
25959 this.owner.fireEvent('initialize', this);
25964 onDestroy : function(){
25970 //for (var i =0; i < this.toolbars.length;i++) {
25971 // // fixme - ask toolbars for heights?
25972 // this.toolbars[i].onDestroy();
25975 //this.wrap.dom.innerHTML = '';
25976 //this.wrap.remove();
25981 onFirstFocus : function(){
25983 this.assignDocWin();
25986 this.activated = true;
25989 if(Roo.isGecko){ // prevent silly gecko errors
25991 var s = this.win.getSelection();
25992 if(!s.focusNode || s.focusNode.nodeType != 3){
25993 var r = s.getRangeAt(0);
25994 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25999 this.execCmd('useCSS', true);
26000 this.execCmd('styleWithCSS', false);
26003 this.owner.fireEvent('activate', this);
26007 adjustFont: function(btn){
26008 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26009 //if(Roo.isSafari){ // safari
26012 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26013 if(Roo.isSafari){ // safari
26014 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26015 v = (v < 10) ? 10 : v;
26016 v = (v > 48) ? 48 : v;
26017 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26022 v = Math.max(1, v+adjust);
26024 this.execCmd('FontSize', v );
26027 onEditorEvent : function(e)
26029 this.owner.fireEvent('editorevent', this, e);
26030 // this.updateToolbar();
26031 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26034 insertTag : function(tg)
26036 // could be a bit smarter... -> wrap the current selected tRoo..
26037 if (tg.toLowerCase() == 'span' ||
26038 tg.toLowerCase() == 'code' ||
26039 tg.toLowerCase() == 'sup' ||
26040 tg.toLowerCase() == 'sub'
26043 range = this.createRange(this.getSelection());
26044 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26045 wrappingNode.appendChild(range.extractContents());
26046 range.insertNode(wrappingNode);
26053 this.execCmd("formatblock", tg);
26057 insertText : function(txt)
26061 var range = this.createRange();
26062 range.deleteContents();
26063 //alert(Sender.getAttribute('label'));
26065 range.insertNode(this.doc.createTextNode(txt));
26071 * Executes a Midas editor command on the editor document and performs necessary focus and
26072 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26073 * @param {String} cmd The Midas command
26074 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26076 relayCmd : function(cmd, value){
26078 this.execCmd(cmd, value);
26079 this.owner.fireEvent('editorevent', this);
26080 //this.updateToolbar();
26081 this.owner.deferFocus();
26085 * Executes a Midas editor command directly on the editor document.
26086 * For visual commands, you should use {@link #relayCmd} instead.
26087 * <b>This should only be called after the editor is initialized.</b>
26088 * @param {String} cmd The Midas command
26089 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26091 execCmd : function(cmd, value){
26092 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26099 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26101 * @param {String} text | dom node..
26103 insertAtCursor : function(text)
26106 if(!this.activated){
26112 var r = this.doc.selection.createRange();
26123 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26127 // from jquery ui (MIT licenced)
26129 var win = this.win;
26131 if (win.getSelection && win.getSelection().getRangeAt) {
26132 range = win.getSelection().getRangeAt(0);
26133 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26134 range.insertNode(node);
26135 } else if (win.document.selection && win.document.selection.createRange) {
26136 // no firefox support
26137 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26138 win.document.selection.createRange().pasteHTML(txt);
26140 // no firefox support
26141 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26142 this.execCmd('InsertHTML', txt);
26151 mozKeyPress : function(e){
26153 var c = e.getCharCode(), cmd;
26156 c = String.fromCharCode(c).toLowerCase();
26170 this.cleanUpPaste.defer(100, this);
26178 e.preventDefault();
26186 fixKeys : function(){ // load time branching for fastest keydown performance
26188 return function(e){
26189 var k = e.getKey(), r;
26192 r = this.doc.selection.createRange();
26195 r.pasteHTML('    ');
26202 r = this.doc.selection.createRange();
26204 var target = r.parentElement();
26205 if(!target || target.tagName.toLowerCase() != 'li'){
26207 r.pasteHTML('<br />');
26213 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26214 this.cleanUpPaste.defer(100, this);
26220 }else if(Roo.isOpera){
26221 return function(e){
26222 var k = e.getKey();
26226 this.execCmd('InsertHTML','    ');
26229 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26230 this.cleanUpPaste.defer(100, this);
26235 }else if(Roo.isSafari){
26236 return function(e){
26237 var k = e.getKey();
26241 this.execCmd('InsertText','\t');
26245 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26246 this.cleanUpPaste.defer(100, this);
26254 getAllAncestors: function()
26256 var p = this.getSelectedNode();
26259 a.push(p); // push blank onto stack..
26260 p = this.getParentElement();
26264 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26268 a.push(this.doc.body);
26272 lastSelNode : false,
26275 getSelection : function()
26277 this.assignDocWin();
26278 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26281 getSelectedNode: function()
26283 // this may only work on Gecko!!!
26285 // should we cache this!!!!
26290 var range = this.createRange(this.getSelection()).cloneRange();
26293 var parent = range.parentElement();
26295 var testRange = range.duplicate();
26296 testRange.moveToElementText(parent);
26297 if (testRange.inRange(range)) {
26300 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26303 parent = parent.parentElement;
26308 // is ancestor a text element.
26309 var ac = range.commonAncestorContainer;
26310 if (ac.nodeType == 3) {
26311 ac = ac.parentNode;
26314 var ar = ac.childNodes;
26317 var other_nodes = [];
26318 var has_other_nodes = false;
26319 for (var i=0;i<ar.length;i++) {
26320 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26323 // fullly contained node.
26325 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26330 // probably selected..
26331 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26332 other_nodes.push(ar[i]);
26336 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26341 has_other_nodes = true;
26343 if (!nodes.length && other_nodes.length) {
26344 nodes= other_nodes;
26346 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26352 createRange: function(sel)
26354 // this has strange effects when using with
26355 // top toolbar - not sure if it's a great idea.
26356 //this.editor.contentWindow.focus();
26357 if (typeof sel != "undefined") {
26359 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26361 return this.doc.createRange();
26364 return this.doc.createRange();
26367 getParentElement: function()
26370 this.assignDocWin();
26371 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26373 var range = this.createRange(sel);
26376 var p = range.commonAncestorContainer;
26377 while (p.nodeType == 3) { // text node
26388 * Range intersection.. the hard stuff...
26392 * [ -- selected range --- ]
26396 * if end is before start or hits it. fail.
26397 * if start is after end or hits it fail.
26399 * if either hits (but other is outside. - then it's not
26405 // @see http://www.thismuchiknow.co.uk/?p=64.
26406 rangeIntersectsNode : function(range, node)
26408 var nodeRange = node.ownerDocument.createRange();
26410 nodeRange.selectNode(node);
26412 nodeRange.selectNodeContents(node);
26415 var rangeStartRange = range.cloneRange();
26416 rangeStartRange.collapse(true);
26418 var rangeEndRange = range.cloneRange();
26419 rangeEndRange.collapse(false);
26421 var nodeStartRange = nodeRange.cloneRange();
26422 nodeStartRange.collapse(true);
26424 var nodeEndRange = nodeRange.cloneRange();
26425 nodeEndRange.collapse(false);
26427 return rangeStartRange.compareBoundaryPoints(
26428 Range.START_TO_START, nodeEndRange) == -1 &&
26429 rangeEndRange.compareBoundaryPoints(
26430 Range.START_TO_START, nodeStartRange) == 1;
26434 rangeCompareNode : function(range, node)
26436 var nodeRange = node.ownerDocument.createRange();
26438 nodeRange.selectNode(node);
26440 nodeRange.selectNodeContents(node);
26444 range.collapse(true);
26446 nodeRange.collapse(true);
26448 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26449 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26451 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26453 var nodeIsBefore = ss == 1;
26454 var nodeIsAfter = ee == -1;
26456 if (nodeIsBefore && nodeIsAfter) {
26459 if (!nodeIsBefore && nodeIsAfter) {
26460 return 1; //right trailed.
26463 if (nodeIsBefore && !nodeIsAfter) {
26464 return 2; // left trailed.
26470 // private? - in a new class?
26471 cleanUpPaste : function()
26473 // cleans up the whole document..
26474 Roo.log('cleanuppaste');
26476 this.cleanUpChildren(this.doc.body);
26477 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26478 if (clean != this.doc.body.innerHTML) {
26479 this.doc.body.innerHTML = clean;
26484 cleanWordChars : function(input) {// change the chars to hex code
26485 var he = Roo.HtmlEditorCore;
26487 var output = input;
26488 Roo.each(he.swapCodes, function(sw) {
26489 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26491 output = output.replace(swapper, sw[1]);
26498 cleanUpChildren : function (n)
26500 if (!n.childNodes.length) {
26503 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26504 this.cleanUpChild(n.childNodes[i]);
26511 cleanUpChild : function (node)
26514 //console.log(node);
26515 if (node.nodeName == "#text") {
26516 // clean up silly Windows -- stuff?
26519 if (node.nodeName == "#comment") {
26520 node.parentNode.removeChild(node);
26521 // clean up silly Windows -- stuff?
26524 var lcname = node.tagName.toLowerCase();
26525 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26526 // whitelist of tags..
26528 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26530 node.parentNode.removeChild(node);
26535 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26537 // spans with no attributes - just remove them..
26538 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26539 remove_keep_children = true;
26542 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26543 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26545 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26546 // remove_keep_children = true;
26549 if (remove_keep_children) {
26550 this.cleanUpChildren(node);
26551 // inserts everything just before this node...
26552 while (node.childNodes.length) {
26553 var cn = node.childNodes[0];
26554 node.removeChild(cn);
26555 node.parentNode.insertBefore(cn, node);
26557 node.parentNode.removeChild(node);
26561 if (!node.attributes || !node.attributes.length) {
26566 this.cleanUpChildren(node);
26570 function cleanAttr(n,v)
26573 if (v.match(/^\./) || v.match(/^\//)) {
26576 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26579 if (v.match(/^#/)) {
26582 if (v.match(/^\{/)) { // allow template editing.
26585 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26586 node.removeAttribute(n);
26590 var cwhite = this.cwhite;
26591 var cblack = this.cblack;
26593 function cleanStyle(n,v)
26595 if (v.match(/expression/)) { //XSS?? should we even bother..
26596 node.removeAttribute(n);
26600 var parts = v.split(/;/);
26603 Roo.each(parts, function(p) {
26604 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26608 var l = p.split(':').shift().replace(/\s+/g,'');
26609 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26611 if ( cwhite.length && cblack.indexOf(l) > -1) {
26612 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26613 //node.removeAttribute(n);
26617 // only allow 'c whitelisted system attributes'
26618 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26619 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26620 //node.removeAttribute(n);
26630 if (clean.length) {
26631 node.setAttribute(n, clean.join(';'));
26633 node.removeAttribute(n);
26639 for (var i = node.attributes.length-1; i > -1 ; i--) {
26640 var a = node.attributes[i];
26643 if (a.name.toLowerCase().substr(0,2)=='on') {
26644 node.removeAttribute(a.name);
26647 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26648 node.removeAttribute(a.name);
26651 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26652 cleanAttr(a.name,a.value); // fixme..
26655 if (a.name == 'style') {
26656 cleanStyle(a.name,a.value);
26659 /// clean up MS crap..
26660 // tecnically this should be a list of valid class'es..
26663 if (a.name == 'class') {
26664 if (a.value.match(/^Mso/)) {
26665 node.removeAttribute('class');
26668 if (a.value.match(/^body$/)) {
26669 node.removeAttribute('class');
26680 this.cleanUpChildren(node);
26686 * Clean up MS wordisms...
26688 cleanWord : function(node)
26691 this.cleanWord(this.doc.body);
26696 node.nodeName == 'SPAN' &&
26697 !node.hasAttributes() &&
26698 node.childNodes.length == 1 &&
26699 node.firstChild.nodeName == "#text"
26701 var textNode = node.firstChild;
26702 node.removeChild(textNode);
26703 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26704 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26706 node.parentNode.insertBefore(textNode, node);
26707 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26708 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26710 node.parentNode.removeChild(node);
26713 if (node.nodeName == "#text") {
26714 // clean up silly Windows -- stuff?
26717 if (node.nodeName == "#comment") {
26718 node.parentNode.removeChild(node);
26719 // clean up silly Windows -- stuff?
26723 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26724 node.parentNode.removeChild(node);
26727 //Roo.log(node.tagName);
26728 // remove - but keep children..
26729 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26730 //Roo.log('-- removed');
26731 while (node.childNodes.length) {
26732 var cn = node.childNodes[0];
26733 node.removeChild(cn);
26734 node.parentNode.insertBefore(cn, node);
26735 // move node to parent - and clean it..
26736 this.cleanWord(cn);
26738 node.parentNode.removeChild(node);
26739 /// no need to iterate chidlren = it's got none..
26740 //this.iterateChildren(node, this.cleanWord);
26744 if (node.className.length) {
26746 var cn = node.className.split(/\W+/);
26748 Roo.each(cn, function(cls) {
26749 if (cls.match(/Mso[a-zA-Z]+/)) {
26754 node.className = cna.length ? cna.join(' ') : '';
26756 node.removeAttribute("class");
26760 if (node.hasAttribute("lang")) {
26761 node.removeAttribute("lang");
26764 if (node.hasAttribute("style")) {
26766 var styles = node.getAttribute("style").split(";");
26768 Roo.each(styles, function(s) {
26769 if (!s.match(/:/)) {
26772 var kv = s.split(":");
26773 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26776 // what ever is left... we allow.
26779 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26780 if (!nstyle.length) {
26781 node.removeAttribute('style');
26784 this.iterateChildren(node, this.cleanWord);
26790 * iterateChildren of a Node, calling fn each time, using this as the scole..
26791 * @param {DomNode} node node to iterate children of.
26792 * @param {Function} fn method of this class to call on each item.
26794 iterateChildren : function(node, fn)
26796 if (!node.childNodes.length) {
26799 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26800 fn.call(this, node.childNodes[i])
26806 * cleanTableWidths.
26808 * Quite often pasting from word etc.. results in tables with column and widths.
26809 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26812 cleanTableWidths : function(node)
26817 this.cleanTableWidths(this.doc.body);
26822 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26825 Roo.log(node.tagName);
26826 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26827 this.iterateChildren(node, this.cleanTableWidths);
26830 if (node.hasAttribute('width')) {
26831 node.removeAttribute('width');
26835 if (node.hasAttribute("style")) {
26838 var styles = node.getAttribute("style").split(";");
26840 Roo.each(styles, function(s) {
26841 if (!s.match(/:/)) {
26844 var kv = s.split(":");
26845 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26848 // what ever is left... we allow.
26851 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26852 if (!nstyle.length) {
26853 node.removeAttribute('style');
26857 this.iterateChildren(node, this.cleanTableWidths);
26865 domToHTML : function(currentElement, depth, nopadtext) {
26867 depth = depth || 0;
26868 nopadtext = nopadtext || false;
26870 if (!currentElement) {
26871 return this.domToHTML(this.doc.body);
26874 //Roo.log(currentElement);
26876 var allText = false;
26877 var nodeName = currentElement.nodeName;
26878 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26880 if (nodeName == '#text') {
26882 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26887 if (nodeName != 'BODY') {
26890 // Prints the node tagName, such as <A>, <IMG>, etc
26893 for(i = 0; i < currentElement.attributes.length;i++) {
26895 var aname = currentElement.attributes.item(i).name;
26896 if (!currentElement.attributes.item(i).value.length) {
26899 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26902 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26911 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26914 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26919 // Traverse the tree
26921 var currentElementChild = currentElement.childNodes.item(i);
26922 var allText = true;
26923 var innerHTML = '';
26925 while (currentElementChild) {
26926 // Formatting code (indent the tree so it looks nice on the screen)
26927 var nopad = nopadtext;
26928 if (lastnode == 'SPAN') {
26932 if (currentElementChild.nodeName == '#text') {
26933 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26934 toadd = nopadtext ? toadd : toadd.trim();
26935 if (!nopad && toadd.length > 80) {
26936 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26938 innerHTML += toadd;
26941 currentElementChild = currentElement.childNodes.item(i);
26947 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26949 // Recursively traverse the tree structure of the child node
26950 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26951 lastnode = currentElementChild.nodeName;
26953 currentElementChild=currentElement.childNodes.item(i);
26959 // The remaining code is mostly for formatting the tree
26960 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26965 ret+= "</"+tagName+">";
26971 applyBlacklists : function()
26973 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26974 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26978 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26979 if (b.indexOf(tag) > -1) {
26982 this.white.push(tag);
26986 Roo.each(w, function(tag) {
26987 if (b.indexOf(tag) > -1) {
26990 if (this.white.indexOf(tag) > -1) {
26993 this.white.push(tag);
26998 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26999 if (w.indexOf(tag) > -1) {
27002 this.black.push(tag);
27006 Roo.each(b, function(tag) {
27007 if (w.indexOf(tag) > -1) {
27010 if (this.black.indexOf(tag) > -1) {
27013 this.black.push(tag);
27018 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27019 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27023 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27024 if (b.indexOf(tag) > -1) {
27027 this.cwhite.push(tag);
27031 Roo.each(w, function(tag) {
27032 if (b.indexOf(tag) > -1) {
27035 if (this.cwhite.indexOf(tag) > -1) {
27038 this.cwhite.push(tag);
27043 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27044 if (w.indexOf(tag) > -1) {
27047 this.cblack.push(tag);
27051 Roo.each(b, function(tag) {
27052 if (w.indexOf(tag) > -1) {
27055 if (this.cblack.indexOf(tag) > -1) {
27058 this.cblack.push(tag);
27063 setStylesheets : function(stylesheets)
27065 if(typeof(stylesheets) == 'string'){
27066 Roo.get(this.iframe.contentDocument.head).createChild({
27068 rel : 'stylesheet',
27077 Roo.each(stylesheets, function(s) {
27082 Roo.get(_this.iframe.contentDocument.head).createChild({
27084 rel : 'stylesheet',
27093 removeStylesheets : function()
27097 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27102 setStyle : function(style)
27104 Roo.get(this.iframe.contentDocument.head).createChild({
27113 // hide stuff that is not compatible
27127 * @event specialkey
27131 * @cfg {String} fieldClass @hide
27134 * @cfg {String} focusClass @hide
27137 * @cfg {String} autoCreate @hide
27140 * @cfg {String} inputType @hide
27143 * @cfg {String} invalidClass @hide
27146 * @cfg {String} invalidText @hide
27149 * @cfg {String} msgFx @hide
27152 * @cfg {String} validateOnBlur @hide
27156 Roo.HtmlEditorCore.white = [
27157 'area', 'br', 'img', 'input', 'hr', 'wbr',
27159 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27160 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27161 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27162 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27163 'table', 'ul', 'xmp',
27165 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27168 'dir', 'menu', 'ol', 'ul', 'dl',
27174 Roo.HtmlEditorCore.black = [
27175 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27177 'base', 'basefont', 'bgsound', 'blink', 'body',
27178 'frame', 'frameset', 'head', 'html', 'ilayer',
27179 'iframe', 'layer', 'link', 'meta', 'object',
27180 'script', 'style' ,'title', 'xml' // clean later..
27182 Roo.HtmlEditorCore.clean = [
27183 'script', 'style', 'title', 'xml'
27185 Roo.HtmlEditorCore.remove = [
27190 Roo.HtmlEditorCore.ablack = [
27194 Roo.HtmlEditorCore.aclean = [
27195 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27199 Roo.HtmlEditorCore.pwhite= [
27200 'http', 'https', 'mailto'
27203 // white listed style attributes.
27204 Roo.HtmlEditorCore.cwhite= [
27205 // 'text-align', /// default is to allow most things..
27211 // black listed style attributes.
27212 Roo.HtmlEditorCore.cblack= [
27213 // 'font-size' -- this can be set by the project
27217 Roo.HtmlEditorCore.swapCodes =[
27218 [ 8211, "–" ],
27219 [ 8212, "—" ],
27236 * @class Roo.bootstrap.HtmlEditor
27237 * @extends Roo.bootstrap.TextArea
27238 * Bootstrap HtmlEditor class
27241 * Create a new HtmlEditor
27242 * @param {Object} config The config object
27245 Roo.bootstrap.HtmlEditor = function(config){
27246 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27247 if (!this.toolbars) {
27248 this.toolbars = [];
27251 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27254 * @event initialize
27255 * Fires when the editor is fully initialized (including the iframe)
27256 * @param {HtmlEditor} this
27261 * Fires when the editor is first receives the focus. Any insertion must wait
27262 * until after this event.
27263 * @param {HtmlEditor} this
27267 * @event beforesync
27268 * Fires before the textarea is updated with content from the editor iframe. Return false
27269 * to cancel the sync.
27270 * @param {HtmlEditor} this
27271 * @param {String} html
27275 * @event beforepush
27276 * Fires before the iframe editor is updated with content from the textarea. Return false
27277 * to cancel the push.
27278 * @param {HtmlEditor} this
27279 * @param {String} html
27284 * Fires when the textarea is updated with content from the editor iframe.
27285 * @param {HtmlEditor} this
27286 * @param {String} html
27291 * Fires when the iframe editor is updated with content from the textarea.
27292 * @param {HtmlEditor} this
27293 * @param {String} html
27297 * @event editmodechange
27298 * Fires when the editor switches edit modes
27299 * @param {HtmlEditor} this
27300 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27302 editmodechange: true,
27304 * @event editorevent
27305 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27306 * @param {HtmlEditor} this
27310 * @event firstfocus
27311 * Fires when on first focus - needed by toolbars..
27312 * @param {HtmlEditor} this
27317 * Auto save the htmlEditor value as a file into Events
27318 * @param {HtmlEditor} this
27322 * @event savedpreview
27323 * preview the saved version of htmlEditor
27324 * @param {HtmlEditor} this
27331 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27335 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27340 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27345 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27350 * @cfg {Number} height (in pixels)
27354 * @cfg {Number} width (in pixels)
27359 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27362 stylesheets: false,
27367 // private properties
27368 validationEvent : false,
27370 initialized : false,
27373 onFocus : Roo.emptyFn,
27375 hideMode:'offsets',
27377 tbContainer : false,
27381 toolbarContainer :function() {
27382 return this.wrap.select('.x-html-editor-tb',true).first();
27386 * Protected method that will not generally be called directly. It
27387 * is called when the editor creates its toolbar. Override this method if you need to
27388 * add custom toolbar buttons.
27389 * @param {HtmlEditor} editor
27391 createToolbar : function(){
27392 Roo.log('renewing');
27393 Roo.log("create toolbars");
27395 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27396 this.toolbars[0].render(this.toolbarContainer());
27400 // if (!editor.toolbars || !editor.toolbars.length) {
27401 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27404 // for (var i =0 ; i < editor.toolbars.length;i++) {
27405 // editor.toolbars[i] = Roo.factory(
27406 // typeof(editor.toolbars[i]) == 'string' ?
27407 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27408 // Roo.bootstrap.HtmlEditor);
27409 // editor.toolbars[i].init(editor);
27415 onRender : function(ct, position)
27417 // Roo.log("Call onRender: " + this.xtype);
27419 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27421 this.wrap = this.inputEl().wrap({
27422 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27425 this.editorcore.onRender(ct, position);
27427 if (this.resizable) {
27428 this.resizeEl = new Roo.Resizable(this.wrap, {
27432 minHeight : this.height,
27433 height: this.height,
27434 handles : this.resizable,
27437 resize : function(r, w, h) {
27438 _t.onResize(w,h); // -something
27444 this.createToolbar(this);
27447 if(!this.width && this.resizable){
27448 this.setSize(this.wrap.getSize());
27450 if (this.resizeEl) {
27451 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27452 // should trigger onReize..
27458 onResize : function(w, h)
27460 Roo.log('resize: ' +w + ',' + h );
27461 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27465 if(this.inputEl() ){
27466 if(typeof w == 'number'){
27467 var aw = w - this.wrap.getFrameWidth('lr');
27468 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27471 if(typeof h == 'number'){
27472 var tbh = -11; // fixme it needs to tool bar size!
27473 for (var i =0; i < this.toolbars.length;i++) {
27474 // fixme - ask toolbars for heights?
27475 tbh += this.toolbars[i].el.getHeight();
27476 //if (this.toolbars[i].footer) {
27477 // tbh += this.toolbars[i].footer.el.getHeight();
27485 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27486 ah -= 5; // knock a few pixes off for look..
27487 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27491 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27492 this.editorcore.onResize(ew,eh);
27497 * Toggles the editor between standard and source edit mode.
27498 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27500 toggleSourceEdit : function(sourceEditMode)
27502 this.editorcore.toggleSourceEdit(sourceEditMode);
27504 if(this.editorcore.sourceEditMode){
27505 Roo.log('editor - showing textarea');
27508 // Roo.log(this.syncValue());
27510 this.inputEl().removeClass(['hide', 'x-hidden']);
27511 this.inputEl().dom.removeAttribute('tabIndex');
27512 this.inputEl().focus();
27514 Roo.log('editor - hiding textarea');
27516 // Roo.log(this.pushValue());
27519 this.inputEl().addClass(['hide', 'x-hidden']);
27520 this.inputEl().dom.setAttribute('tabIndex', -1);
27521 //this.deferFocus();
27524 if(this.resizable){
27525 this.setSize(this.wrap.getSize());
27528 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27531 // private (for BoxComponent)
27532 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27534 // private (for BoxComponent)
27535 getResizeEl : function(){
27539 // private (for BoxComponent)
27540 getPositionEl : function(){
27545 initEvents : function(){
27546 this.originalValue = this.getValue();
27550 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27553 // markInvalid : Roo.emptyFn,
27555 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27558 // clearInvalid : Roo.emptyFn,
27560 setValue : function(v){
27561 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27562 this.editorcore.pushValue();
27567 deferFocus : function(){
27568 this.focus.defer(10, this);
27572 focus : function(){
27573 this.editorcore.focus();
27579 onDestroy : function(){
27585 for (var i =0; i < this.toolbars.length;i++) {
27586 // fixme - ask toolbars for heights?
27587 this.toolbars[i].onDestroy();
27590 this.wrap.dom.innerHTML = '';
27591 this.wrap.remove();
27596 onFirstFocus : function(){
27597 //Roo.log("onFirstFocus");
27598 this.editorcore.onFirstFocus();
27599 for (var i =0; i < this.toolbars.length;i++) {
27600 this.toolbars[i].onFirstFocus();
27606 syncValue : function()
27608 this.editorcore.syncValue();
27611 pushValue : function()
27613 this.editorcore.pushValue();
27617 // hide stuff that is not compatible
27631 * @event specialkey
27635 * @cfg {String} fieldClass @hide
27638 * @cfg {String} focusClass @hide
27641 * @cfg {String} autoCreate @hide
27644 * @cfg {String} inputType @hide
27648 * @cfg {String} invalidText @hide
27651 * @cfg {String} msgFx @hide
27654 * @cfg {String} validateOnBlur @hide
27663 Roo.namespace('Roo.bootstrap.htmleditor');
27665 * @class Roo.bootstrap.HtmlEditorToolbar1
27671 new Roo.bootstrap.HtmlEditor({
27674 new Roo.bootstrap.HtmlEditorToolbar1({
27675 disable : { fonts: 1 , format: 1, ..., ... , ...],
27681 * @cfg {Object} disable List of elements to disable..
27682 * @cfg {Array} btns List of additional buttons.
27686 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27689 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27692 Roo.apply(this, config);
27694 // default disabled, based on 'good practice'..
27695 this.disable = this.disable || {};
27696 Roo.applyIf(this.disable, {
27699 specialElements : true
27701 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27703 this.editor = config.editor;
27704 this.editorcore = config.editor.editorcore;
27706 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27708 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27709 // dont call parent... till later.
27711 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27716 editorcore : false,
27721 "h1","h2","h3","h4","h5","h6",
27723 "abbr", "acronym", "address", "cite", "samp", "var",
27727 onRender : function(ct, position)
27729 // Roo.log("Call onRender: " + this.xtype);
27731 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27733 this.el.dom.style.marginBottom = '0';
27735 var editorcore = this.editorcore;
27736 var editor= this.editor;
27739 var btn = function(id,cmd , toggle, handler, html){
27741 var event = toggle ? 'toggle' : 'click';
27746 xns: Roo.bootstrap,
27750 enableToggle:toggle !== false,
27752 pressed : toggle ? false : null,
27755 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27756 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27762 // var cb_box = function...
27767 xns: Roo.bootstrap,
27772 xns: Roo.bootstrap,
27776 Roo.each(this.formats, function(f) {
27777 style.menu.items.push({
27779 xns: Roo.bootstrap,
27780 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27785 editorcore.insertTag(this.tagname);
27792 children.push(style);
27794 btn('bold',false,true);
27795 btn('italic',false,true);
27796 btn('align-left', 'justifyleft',true);
27797 btn('align-center', 'justifycenter',true);
27798 btn('align-right' , 'justifyright',true);
27799 btn('link', false, false, function(btn) {
27800 //Roo.log("create link?");
27801 var url = prompt(this.createLinkText, this.defaultLinkValue);
27802 if(url && url != 'http:/'+'/'){
27803 this.editorcore.relayCmd('createlink', url);
27806 btn('list','insertunorderedlist',true);
27807 btn('pencil', false,true, function(btn){
27809 this.toggleSourceEdit(btn.pressed);
27812 if (this.editor.btns.length > 0) {
27813 for (var i = 0; i<this.editor.btns.length; i++) {
27814 children.push(this.editor.btns[i]);
27822 xns: Roo.bootstrap,
27827 xns: Roo.bootstrap,
27832 cog.menu.items.push({
27834 xns: Roo.bootstrap,
27835 html : Clean styles,
27840 editorcore.insertTag(this.tagname);
27849 this.xtype = 'NavSimplebar';
27851 for(var i=0;i< children.length;i++) {
27853 this.buttons.add(this.addxtypeChild(children[i]));
27857 editor.on('editorevent', this.updateToolbar, this);
27859 onBtnClick : function(id)
27861 this.editorcore.relayCmd(id);
27862 this.editorcore.focus();
27866 * Protected method that will not generally be called directly. It triggers
27867 * a toolbar update by reading the markup state of the current selection in the editor.
27869 updateToolbar: function(){
27871 if(!this.editorcore.activated){
27872 this.editor.onFirstFocus(); // is this neeed?
27876 var btns = this.buttons;
27877 var doc = this.editorcore.doc;
27878 btns.get('bold').setActive(doc.queryCommandState('bold'));
27879 btns.get('italic').setActive(doc.queryCommandState('italic'));
27880 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27882 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27883 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27884 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27886 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27887 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27890 var ans = this.editorcore.getAllAncestors();
27891 if (this.formatCombo) {
27894 var store = this.formatCombo.store;
27895 this.formatCombo.setValue("");
27896 for (var i =0; i < ans.length;i++) {
27897 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27899 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27907 // hides menus... - so this cant be on a menu...
27908 Roo.bootstrap.MenuMgr.hideAll();
27910 Roo.bootstrap.MenuMgr.hideAll();
27911 //this.editorsyncValue();
27913 onFirstFocus: function() {
27914 this.buttons.each(function(item){
27918 toggleSourceEdit : function(sourceEditMode){
27921 if(sourceEditMode){
27922 Roo.log("disabling buttons");
27923 this.buttons.each( function(item){
27924 if(item.cmd != 'pencil'){
27930 Roo.log("enabling buttons");
27931 if(this.editorcore.initialized){
27932 this.buttons.each( function(item){
27938 Roo.log("calling toggole on editor");
27939 // tell the editor that it's been pressed..
27940 this.editor.toggleSourceEdit(sourceEditMode);
27954 * @class Roo.bootstrap.Markdown
27955 * @extends Roo.bootstrap.TextArea
27956 * Bootstrap Showdown editable area
27957 * @cfg {string} content
27960 * Create a new Showdown
27963 Roo.bootstrap.Markdown = function(config){
27964 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27968 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27972 initEvents : function()
27975 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27976 this.markdownEl = this.el.createChild({
27977 cls : 'roo-markdown-area'
27979 this.inputEl().addClass('d-none');
27980 if (this.getValue() == '') {
27981 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27984 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27986 this.markdownEl.on('click', this.toggleTextEdit, this);
27987 this.on('blur', this.toggleTextEdit, this);
27988 this.on('specialkey', this.resizeTextArea, this);
27991 toggleTextEdit : function()
27993 var sh = this.markdownEl.getHeight();
27994 this.inputEl().addClass('d-none');
27995 this.markdownEl.addClass('d-none');
27996 if (!this.editing) {
27998 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
27999 this.inputEl().removeClass('d-none');
28000 this.inputEl().focus();
28001 this.editing = true;
28004 // show showdown...
28005 this.updateMarkdown();
28006 this.markdownEl.removeClass('d-none');
28007 this.editing = false;
28010 updateMarkdown : function()
28012 if (this.getValue() == '') {
28013 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28017 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28020 resizeTextArea: function () {
28023 Roo.log([sh, this.getValue().split("\n").length * 30]);
28024 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28026 setValue : function(val)
28028 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28029 if (!this.editing) {
28030 this.updateMarkdown();
28036 if (!this.editing) {
28037 this.toggleTextEdit();
28045 * Ext JS Library 1.1.1
28046 * Copyright(c) 2006-2007, Ext JS, LLC.
28048 * Originally Released Under LGPL - original licence link has changed is not relivant.
28051 * <script type="text/javascript">
28055 * @class Roo.bootstrap.PagingToolbar
28056 * @extends Roo.bootstrap.NavSimplebar
28057 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28059 * Create a new PagingToolbar
28060 * @param {Object} config The config object
28061 * @param {Roo.data.Store} store
28063 Roo.bootstrap.PagingToolbar = function(config)
28065 // old args format still supported... - xtype is prefered..
28066 // created from xtype...
28068 this.ds = config.dataSource;
28070 if (config.store && !this.ds) {
28071 this.store= Roo.factory(config.store, Roo.data);
28072 this.ds = this.store;
28073 this.ds.xmodule = this.xmodule || false;
28076 this.toolbarItems = [];
28077 if (config.items) {
28078 this.toolbarItems = config.items;
28081 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28086 this.bind(this.ds);
28089 if (Roo.bootstrap.version == 4) {
28090 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28092 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28097 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28099 * @cfg {Roo.data.Store} dataSource
28100 * The underlying data store providing the paged data
28103 * @cfg {String/HTMLElement/Element} container
28104 * container The id or element that will contain the toolbar
28107 * @cfg {Boolean} displayInfo
28108 * True to display the displayMsg (defaults to false)
28111 * @cfg {Number} pageSize
28112 * The number of records to display per page (defaults to 20)
28116 * @cfg {String} displayMsg
28117 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28119 displayMsg : 'Displaying {0} - {1} of {2}',
28121 * @cfg {String} emptyMsg
28122 * The message to display when no records are found (defaults to "No data to display")
28124 emptyMsg : 'No data to display',
28126 * Customizable piece of the default paging text (defaults to "Page")
28129 beforePageText : "Page",
28131 * Customizable piece of the default paging text (defaults to "of %0")
28134 afterPageText : "of {0}",
28136 * Customizable piece of the default paging text (defaults to "First Page")
28139 firstText : "First Page",
28141 * Customizable piece of the default paging text (defaults to "Previous Page")
28144 prevText : "Previous Page",
28146 * Customizable piece of the default paging text (defaults to "Next Page")
28149 nextText : "Next Page",
28151 * Customizable piece of the default paging text (defaults to "Last Page")
28154 lastText : "Last Page",
28156 * Customizable piece of the default paging text (defaults to "Refresh")
28159 refreshText : "Refresh",
28163 onRender : function(ct, position)
28165 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28166 this.navgroup.parentId = this.id;
28167 this.navgroup.onRender(this.el, null);
28168 // add the buttons to the navgroup
28170 if(this.displayInfo){
28171 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28172 this.displayEl = this.el.select('.x-paging-info', true).first();
28173 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28174 // this.displayEl = navel.el.select('span',true).first();
28180 Roo.each(_this.buttons, function(e){ // this might need to use render????
28181 Roo.factory(e).render(_this.el);
28185 Roo.each(_this.toolbarItems, function(e) {
28186 _this.navgroup.addItem(e);
28190 this.first = this.navgroup.addItem({
28191 tooltip: this.firstText,
28192 cls: "prev btn-outline-secondary",
28193 html : ' <i class="fa fa-step-backward"></i>',
28195 preventDefault: true,
28196 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28199 this.prev = this.navgroup.addItem({
28200 tooltip: this.prevText,
28201 cls: "prev btn-outline-secondary",
28202 html : ' <i class="fa fa-backward"></i>',
28204 preventDefault: true,
28205 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28207 //this.addSeparator();
28210 var field = this.navgroup.addItem( {
28212 cls : 'x-paging-position btn-outline-secondary',
28214 html : this.beforePageText +
28215 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28216 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28219 this.field = field.el.select('input', true).first();
28220 this.field.on("keydown", this.onPagingKeydown, this);
28221 this.field.on("focus", function(){this.dom.select();});
28224 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28225 //this.field.setHeight(18);
28226 //this.addSeparator();
28227 this.next = this.navgroup.addItem({
28228 tooltip: this.nextText,
28229 cls: "next btn-outline-secondary",
28230 html : ' <i class="fa fa-forward"></i>',
28232 preventDefault: true,
28233 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28235 this.last = this.navgroup.addItem({
28236 tooltip: this.lastText,
28237 html : ' <i class="fa fa-step-forward"></i>',
28238 cls: "next btn-outline-secondary",
28240 preventDefault: true,
28241 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28243 //this.addSeparator();
28244 this.loading = this.navgroup.addItem({
28245 tooltip: this.refreshText,
28246 cls: "btn-outline-secondary",
28247 html : ' <i class="fa fa-refresh"></i>',
28248 preventDefault: true,
28249 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28255 updateInfo : function(){
28256 if(this.displayEl){
28257 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28258 var msg = count == 0 ?
28262 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28264 this.displayEl.update(msg);
28269 onLoad : function(ds, r, o)
28271 this.cursor = o.params && o.params.start ? o.params.start : 0;
28273 var d = this.getPageData(),
28278 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28279 this.field.dom.value = ap;
28280 this.first.setDisabled(ap == 1);
28281 this.prev.setDisabled(ap == 1);
28282 this.next.setDisabled(ap == ps);
28283 this.last.setDisabled(ap == ps);
28284 this.loading.enable();
28289 getPageData : function(){
28290 var total = this.ds.getTotalCount();
28293 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28294 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28299 onLoadError : function(){
28300 this.loading.enable();
28304 onPagingKeydown : function(e){
28305 var k = e.getKey();
28306 var d = this.getPageData();
28308 var v = this.field.dom.value, pageNum;
28309 if(!v || isNaN(pageNum = parseInt(v, 10))){
28310 this.field.dom.value = d.activePage;
28313 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28314 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28317 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))
28319 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28320 this.field.dom.value = pageNum;
28321 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28324 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28326 var v = this.field.dom.value, pageNum;
28327 var increment = (e.shiftKey) ? 10 : 1;
28328 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28331 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28332 this.field.dom.value = d.activePage;
28335 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28337 this.field.dom.value = parseInt(v, 10) + increment;
28338 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28339 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28346 beforeLoad : function(){
28348 this.loading.disable();
28353 onClick : function(which){
28362 ds.load({params:{start: 0, limit: this.pageSize}});
28365 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28368 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28371 var total = ds.getTotalCount();
28372 var extra = total % this.pageSize;
28373 var lastStart = extra ? (total - extra) : total-this.pageSize;
28374 ds.load({params:{start: lastStart, limit: this.pageSize}});
28377 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28383 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28384 * @param {Roo.data.Store} store The data store to unbind
28386 unbind : function(ds){
28387 ds.un("beforeload", this.beforeLoad, this);
28388 ds.un("load", this.onLoad, this);
28389 ds.un("loadexception", this.onLoadError, this);
28390 ds.un("remove", this.updateInfo, this);
28391 ds.un("add", this.updateInfo, this);
28392 this.ds = undefined;
28396 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28397 * @param {Roo.data.Store} store The data store to bind
28399 bind : function(ds){
28400 ds.on("beforeload", this.beforeLoad, this);
28401 ds.on("load", this.onLoad, this);
28402 ds.on("loadexception", this.onLoadError, this);
28403 ds.on("remove", this.updateInfo, this);
28404 ds.on("add", this.updateInfo, this);
28415 * @class Roo.bootstrap.MessageBar
28416 * @extends Roo.bootstrap.Component
28417 * Bootstrap MessageBar class
28418 * @cfg {String} html contents of the MessageBar
28419 * @cfg {String} weight (info | success | warning | danger) default info
28420 * @cfg {String} beforeClass insert the bar before the given class
28421 * @cfg {Boolean} closable (true | false) default false
28422 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28425 * Create a new Element
28426 * @param {Object} config The config object
28429 Roo.bootstrap.MessageBar = function(config){
28430 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28433 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28439 beforeClass: 'bootstrap-sticky-wrap',
28441 getAutoCreate : function(){
28445 cls: 'alert alert-dismissable alert-' + this.weight,
28450 html: this.html || ''
28456 cfg.cls += ' alert-messages-fixed';
28470 onRender : function(ct, position)
28472 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28475 var cfg = Roo.apply({}, this.getAutoCreate());
28479 cfg.cls += ' ' + this.cls;
28482 cfg.style = this.style;
28484 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28486 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28489 this.el.select('>button.close').on('click', this.hide, this);
28495 if (!this.rendered) {
28501 this.fireEvent('show', this);
28507 if (!this.rendered) {
28513 this.fireEvent('hide', this);
28516 update : function()
28518 // var e = this.el.dom.firstChild;
28520 // if(this.closable){
28521 // e = e.nextSibling;
28524 // e.data = this.html || '';
28526 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28542 * @class Roo.bootstrap.Graph
28543 * @extends Roo.bootstrap.Component
28544 * Bootstrap Graph class
28548 @cfg {String} graphtype bar | vbar | pie
28549 @cfg {number} g_x coodinator | centre x (pie)
28550 @cfg {number} g_y coodinator | centre y (pie)
28551 @cfg {number} g_r radius (pie)
28552 @cfg {number} g_height height of the chart (respected by all elements in the set)
28553 @cfg {number} g_width width of the chart (respected by all elements in the set)
28554 @cfg {Object} title The title of the chart
28557 -opts (object) options for the chart
28559 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28560 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28562 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.
28563 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28565 o stretch (boolean)
28567 -opts (object) options for the pie
28570 o startAngle (number)
28571 o endAngle (number)
28575 * Create a new Input
28576 * @param {Object} config The config object
28579 Roo.bootstrap.Graph = function(config){
28580 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28586 * The img click event for the img.
28587 * @param {Roo.EventObject} e
28593 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28604 //g_colors: this.colors,
28611 getAutoCreate : function(){
28622 onRender : function(ct,position){
28625 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28627 if (typeof(Raphael) == 'undefined') {
28628 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28632 this.raphael = Raphael(this.el.dom);
28634 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28635 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28636 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28637 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28639 r.text(160, 10, "Single Series Chart").attr(txtattr);
28640 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28641 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28642 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28644 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28645 r.barchart(330, 10, 300, 220, data1);
28646 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28647 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28650 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28651 // r.barchart(30, 30, 560, 250, xdata, {
28652 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28653 // axis : "0 0 1 1",
28654 // axisxlabels : xdata
28655 // //yvalues : cols,
28658 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28660 // this.load(null,xdata,{
28661 // axis : "0 0 1 1",
28662 // axisxlabels : xdata
28667 load : function(graphtype,xdata,opts)
28669 this.raphael.clear();
28671 graphtype = this.graphtype;
28676 var r = this.raphael,
28677 fin = function () {
28678 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28680 fout = function () {
28681 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28683 pfin = function() {
28684 this.sector.stop();
28685 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28688 this.label[0].stop();
28689 this.label[0].attr({ r: 7.5 });
28690 this.label[1].attr({ "font-weight": 800 });
28693 pfout = function() {
28694 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28697 this.label[0].animate({ r: 5 }, 500, "bounce");
28698 this.label[1].attr({ "font-weight": 400 });
28704 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28707 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28710 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28711 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28713 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28720 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28725 setTitle: function(o)
28730 initEvents: function() {
28733 this.el.on('click', this.onClick, this);
28737 onClick : function(e)
28739 Roo.log('img onclick');
28740 this.fireEvent('click', this, e);
28752 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28755 * @class Roo.bootstrap.dash.NumberBox
28756 * @extends Roo.bootstrap.Component
28757 * Bootstrap NumberBox class
28758 * @cfg {String} headline Box headline
28759 * @cfg {String} content Box content
28760 * @cfg {String} icon Box icon
28761 * @cfg {String} footer Footer text
28762 * @cfg {String} fhref Footer href
28765 * Create a new NumberBox
28766 * @param {Object} config The config object
28770 Roo.bootstrap.dash.NumberBox = function(config){
28771 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28775 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28784 getAutoCreate : function(){
28788 cls : 'small-box ',
28796 cls : 'roo-headline',
28797 html : this.headline
28801 cls : 'roo-content',
28802 html : this.content
28816 cls : 'ion ' + this.icon
28825 cls : 'small-box-footer',
28826 href : this.fhref || '#',
28830 cfg.cn.push(footer);
28837 onRender : function(ct,position){
28838 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28845 setHeadline: function (value)
28847 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28850 setFooter: function (value, href)
28852 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28855 this.el.select('a.small-box-footer',true).first().attr('href', href);
28860 setContent: function (value)
28862 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28865 initEvents: function()
28879 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28882 * @class Roo.bootstrap.dash.TabBox
28883 * @extends Roo.bootstrap.Component
28884 * Bootstrap TabBox class
28885 * @cfg {String} title Title of the TabBox
28886 * @cfg {String} icon Icon of the TabBox
28887 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28888 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28891 * Create a new TabBox
28892 * @param {Object} config The config object
28896 Roo.bootstrap.dash.TabBox = function(config){
28897 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28902 * When a pane is added
28903 * @param {Roo.bootstrap.dash.TabPane} pane
28907 * @event activatepane
28908 * When a pane is activated
28909 * @param {Roo.bootstrap.dash.TabPane} pane
28911 "activatepane" : true
28919 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28924 tabScrollable : false,
28926 getChildContainer : function()
28928 return this.el.select('.tab-content', true).first();
28931 getAutoCreate : function(){
28935 cls: 'pull-left header',
28943 cls: 'fa ' + this.icon
28949 cls: 'nav nav-tabs pull-right',
28955 if(this.tabScrollable){
28962 cls: 'nav nav-tabs pull-right',
28973 cls: 'nav-tabs-custom',
28978 cls: 'tab-content no-padding',
28986 initEvents : function()
28988 //Roo.log('add add pane handler');
28989 this.on('addpane', this.onAddPane, this);
28992 * Updates the box title
28993 * @param {String} html to set the title to.
28995 setTitle : function(value)
28997 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28999 onAddPane : function(pane)
29001 this.panes.push(pane);
29002 //Roo.log('addpane');
29004 // tabs are rendere left to right..
29005 if(!this.showtabs){
29009 var ctr = this.el.select('.nav-tabs', true).first();
29012 var existing = ctr.select('.nav-tab',true);
29013 var qty = existing.getCount();;
29016 var tab = ctr.createChild({
29018 cls : 'nav-tab' + (qty ? '' : ' active'),
29026 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29029 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29031 pane.el.addClass('active');
29036 onTabClick : function(ev,un,ob,pane)
29038 //Roo.log('tab - prev default');
29039 ev.preventDefault();
29042 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29043 pane.tab.addClass('active');
29044 //Roo.log(pane.title);
29045 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29046 // technically we should have a deactivate event.. but maybe add later.
29047 // and it should not de-activate the selected tab...
29048 this.fireEvent('activatepane', pane);
29049 pane.el.addClass('active');
29050 pane.fireEvent('activate');
29055 getActivePane : function()
29058 Roo.each(this.panes, function(p) {
29059 if(p.el.hasClass('active')){
29080 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29082 * @class Roo.bootstrap.TabPane
29083 * @extends Roo.bootstrap.Component
29084 * Bootstrap TabPane class
29085 * @cfg {Boolean} active (false | true) Default false
29086 * @cfg {String} title title of panel
29090 * Create a new TabPane
29091 * @param {Object} config The config object
29094 Roo.bootstrap.dash.TabPane = function(config){
29095 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29101 * When a pane is activated
29102 * @param {Roo.bootstrap.dash.TabPane} pane
29109 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29114 // the tabBox that this is attached to.
29117 getAutoCreate : function()
29125 cfg.cls += ' active';
29130 initEvents : function()
29132 //Roo.log('trigger add pane handler');
29133 this.parent().fireEvent('addpane', this)
29137 * Updates the tab title
29138 * @param {String} html to set the title to.
29140 setTitle: function(str)
29146 this.tab.select('a', true).first().dom.innerHTML = str;
29163 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29166 * @class Roo.bootstrap.menu.Menu
29167 * @extends Roo.bootstrap.Component
29168 * Bootstrap Menu class - container for Menu
29169 * @cfg {String} html Text of the menu
29170 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29171 * @cfg {String} icon Font awesome icon
29172 * @cfg {String} pos Menu align to (top | bottom) default bottom
29176 * Create a new Menu
29177 * @param {Object} config The config object
29181 Roo.bootstrap.menu.Menu = function(config){
29182 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29186 * @event beforeshow
29187 * Fires before this menu is displayed
29188 * @param {Roo.bootstrap.menu.Menu} this
29192 * @event beforehide
29193 * Fires before this menu is hidden
29194 * @param {Roo.bootstrap.menu.Menu} this
29199 * Fires after this menu is displayed
29200 * @param {Roo.bootstrap.menu.Menu} this
29205 * Fires after this menu is hidden
29206 * @param {Roo.bootstrap.menu.Menu} this
29211 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29212 * @param {Roo.bootstrap.menu.Menu} this
29213 * @param {Roo.EventObject} e
29220 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29224 weight : 'default',
29229 getChildContainer : function() {
29230 if(this.isSubMenu){
29234 return this.el.select('ul.dropdown-menu', true).first();
29237 getAutoCreate : function()
29242 cls : 'roo-menu-text',
29250 cls : 'fa ' + this.icon
29261 cls : 'dropdown-button btn btn-' + this.weight,
29266 cls : 'dropdown-toggle btn btn-' + this.weight,
29276 cls : 'dropdown-menu'
29282 if(this.pos == 'top'){
29283 cfg.cls += ' dropup';
29286 if(this.isSubMenu){
29289 cls : 'dropdown-menu'
29296 onRender : function(ct, position)
29298 this.isSubMenu = ct.hasClass('dropdown-submenu');
29300 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29303 initEvents : function()
29305 if(this.isSubMenu){
29309 this.hidden = true;
29311 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29312 this.triggerEl.on('click', this.onTriggerPress, this);
29314 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29315 this.buttonEl.on('click', this.onClick, this);
29321 if(this.isSubMenu){
29325 return this.el.select('ul.dropdown-menu', true).first();
29328 onClick : function(e)
29330 this.fireEvent("click", this, e);
29333 onTriggerPress : function(e)
29335 if (this.isVisible()) {
29342 isVisible : function(){
29343 return !this.hidden;
29348 this.fireEvent("beforeshow", this);
29350 this.hidden = false;
29351 this.el.addClass('open');
29353 Roo.get(document).on("mouseup", this.onMouseUp, this);
29355 this.fireEvent("show", this);
29362 this.fireEvent("beforehide", this);
29364 this.hidden = true;
29365 this.el.removeClass('open');
29367 Roo.get(document).un("mouseup", this.onMouseUp);
29369 this.fireEvent("hide", this);
29372 onMouseUp : function()
29386 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29389 * @class Roo.bootstrap.menu.Item
29390 * @extends Roo.bootstrap.Component
29391 * Bootstrap MenuItem class
29392 * @cfg {Boolean} submenu (true | false) default false
29393 * @cfg {String} html text of the item
29394 * @cfg {String} href the link
29395 * @cfg {Boolean} disable (true | false) default false
29396 * @cfg {Boolean} preventDefault (true | false) default true
29397 * @cfg {String} icon Font awesome icon
29398 * @cfg {String} pos Submenu align to (left | right) default right
29402 * Create a new Item
29403 * @param {Object} config The config object
29407 Roo.bootstrap.menu.Item = function(config){
29408 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29412 * Fires when the mouse is hovering over this menu
29413 * @param {Roo.bootstrap.menu.Item} this
29414 * @param {Roo.EventObject} e
29419 * Fires when the mouse exits this menu
29420 * @param {Roo.bootstrap.menu.Item} this
29421 * @param {Roo.EventObject} e
29427 * The raw click event for the entire grid.
29428 * @param {Roo.EventObject} e
29434 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29439 preventDefault: true,
29444 getAutoCreate : function()
29449 cls : 'roo-menu-item-text',
29457 cls : 'fa ' + this.icon
29466 href : this.href || '#',
29473 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29477 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29479 if(this.pos == 'left'){
29480 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29487 initEvents : function()
29489 this.el.on('mouseover', this.onMouseOver, this);
29490 this.el.on('mouseout', this.onMouseOut, this);
29492 this.el.select('a', true).first().on('click', this.onClick, this);
29496 onClick : function(e)
29498 if(this.preventDefault){
29499 e.preventDefault();
29502 this.fireEvent("click", this, e);
29505 onMouseOver : function(e)
29507 if(this.submenu && this.pos == 'left'){
29508 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29511 this.fireEvent("mouseover", this, e);
29514 onMouseOut : function(e)
29516 this.fireEvent("mouseout", this, e);
29528 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29531 * @class Roo.bootstrap.menu.Separator
29532 * @extends Roo.bootstrap.Component
29533 * Bootstrap Separator class
29536 * Create a new Separator
29537 * @param {Object} config The config object
29541 Roo.bootstrap.menu.Separator = function(config){
29542 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29545 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29547 getAutoCreate : function(){
29550 cls: 'dropdown-divider divider'
29568 * @class Roo.bootstrap.Tooltip
29569 * Bootstrap Tooltip class
29570 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29571 * to determine which dom element triggers the tooltip.
29573 * It needs to add support for additional attributes like tooltip-position
29576 * Create a new Toolti
29577 * @param {Object} config The config object
29580 Roo.bootstrap.Tooltip = function(config){
29581 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29583 this.alignment = Roo.bootstrap.Tooltip.alignment;
29585 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29586 this.alignment = config.alignment;
29591 Roo.apply(Roo.bootstrap.Tooltip, {
29593 * @function init initialize tooltip monitoring.
29597 currentTip : false,
29598 currentRegion : false,
29604 Roo.get(document).on('mouseover', this.enter ,this);
29605 Roo.get(document).on('mouseout', this.leave, this);
29608 this.currentTip = new Roo.bootstrap.Tooltip();
29611 enter : function(ev)
29613 var dom = ev.getTarget();
29615 //Roo.log(['enter',dom]);
29616 var el = Roo.fly(dom);
29617 if (this.currentEl) {
29619 //Roo.log(this.currentEl);
29620 //Roo.log(this.currentEl.contains(dom));
29621 if (this.currentEl == el) {
29624 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29630 if (this.currentTip.el) {
29631 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29635 if(!el || el.dom == document){
29641 if (!el.attr('tooltip')) {
29642 pel = el.findParent("[tooltip]");
29644 bindEl = Roo.get(pel);
29650 // you can not look for children, as if el is the body.. then everythign is the child..
29651 if (!pel && !el.attr('tooltip')) { //
29652 if (!el.select("[tooltip]").elements.length) {
29655 // is the mouse over this child...?
29656 bindEl = el.select("[tooltip]").first();
29657 var xy = ev.getXY();
29658 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29659 //Roo.log("not in region.");
29662 //Roo.log("child element over..");
29665 this.currentEl = el;
29666 this.currentTip.bind(bindEl);
29667 this.currentRegion = Roo.lib.Region.getRegion(dom);
29668 this.currentTip.enter();
29671 leave : function(ev)
29673 var dom = ev.getTarget();
29674 //Roo.log(['leave',dom]);
29675 if (!this.currentEl) {
29680 if (dom != this.currentEl.dom) {
29683 var xy = ev.getXY();
29684 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29687 // only activate leave if mouse cursor is outside... bounding box..
29692 if (this.currentTip) {
29693 this.currentTip.leave();
29695 //Roo.log('clear currentEl');
29696 this.currentEl = false;
29701 'left' : ['r-l', [-2,0], 'right'],
29702 'right' : ['l-r', [2,0], 'left'],
29703 'bottom' : ['t-b', [0,2], 'top'],
29704 'top' : [ 'b-t', [0,-2], 'bottom']
29710 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29715 delay : null, // can be { show : 300 , hide: 500}
29719 hoverState : null, //???
29721 placement : 'bottom',
29725 getAutoCreate : function(){
29732 cls : 'tooltip-arrow arrow'
29735 cls : 'tooltip-inner'
29742 bind : function(el)
29747 initEvents : function()
29749 this.arrowEl = this.el.select('.arrow', true).first();
29750 this.innerEl = this.el.select('.tooltip-inner', true).first();
29753 enter : function () {
29755 if (this.timeout != null) {
29756 clearTimeout(this.timeout);
29759 this.hoverState = 'in';
29760 //Roo.log("enter - show");
29761 if (!this.delay || !this.delay.show) {
29766 this.timeout = setTimeout(function () {
29767 if (_t.hoverState == 'in') {
29770 }, this.delay.show);
29774 clearTimeout(this.timeout);
29776 this.hoverState = 'out';
29777 if (!this.delay || !this.delay.hide) {
29783 this.timeout = setTimeout(function () {
29784 //Roo.log("leave - timeout");
29786 if (_t.hoverState == 'out') {
29788 Roo.bootstrap.Tooltip.currentEl = false;
29793 show : function (msg)
29796 this.render(document.body);
29799 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29801 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29803 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29805 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29806 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29808 var placement = typeof this.placement == 'function' ?
29809 this.placement.call(this, this.el, on_el) :
29812 var autoToken = /\s?auto?\s?/i;
29813 var autoPlace = autoToken.test(placement);
29815 placement = placement.replace(autoToken, '') || 'top';
29819 //this.el.setXY([0,0]);
29821 //this.el.dom.style.display='block';
29823 //this.el.appendTo(on_el);
29825 var p = this.getPosition();
29826 var box = this.el.getBox();
29832 var align = this.alignment[placement];
29834 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29836 if(placement == 'top' || placement == 'bottom'){
29838 placement = 'right';
29841 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29842 placement = 'left';
29845 var scroll = Roo.select('body', true).first().getScroll();
29847 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29851 align = this.alignment[placement];
29853 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29857 var elems = document.getElementsByTagName('div');
29858 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29859 for (var i = 0; i < elems.length; i++) {
29860 var zindex = Number.parseInt(
29861 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29864 if (zindex > highest) {
29871 this.el.dom.style.zIndex = highest;
29873 this.el.alignTo(this.bindEl, align[0],align[1]);
29874 //var arrow = this.el.select('.arrow',true).first();
29875 //arrow.set(align[2],
29877 this.el.addClass(placement);
29878 this.el.addClass("bs-tooltip-"+ placement);
29880 this.el.addClass('in fade show');
29882 this.hoverState = null;
29884 if (this.el.hasClass('fade')) {
29899 //this.el.setXY([0,0]);
29900 this.el.removeClass(['show', 'in']);
29916 * @class Roo.bootstrap.LocationPicker
29917 * @extends Roo.bootstrap.Component
29918 * Bootstrap LocationPicker class
29919 * @cfg {Number} latitude Position when init default 0
29920 * @cfg {Number} longitude Position when init default 0
29921 * @cfg {Number} zoom default 15
29922 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29923 * @cfg {Boolean} mapTypeControl default false
29924 * @cfg {Boolean} disableDoubleClickZoom default false
29925 * @cfg {Boolean} scrollwheel default true
29926 * @cfg {Boolean} streetViewControl default false
29927 * @cfg {Number} radius default 0
29928 * @cfg {String} locationName
29929 * @cfg {Boolean} draggable default true
29930 * @cfg {Boolean} enableAutocomplete default false
29931 * @cfg {Boolean} enableReverseGeocode default true
29932 * @cfg {String} markerTitle
29935 * Create a new LocationPicker
29936 * @param {Object} config The config object
29940 Roo.bootstrap.LocationPicker = function(config){
29942 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29947 * Fires when the picker initialized.
29948 * @param {Roo.bootstrap.LocationPicker} this
29949 * @param {Google Location} location
29953 * @event positionchanged
29954 * Fires when the picker position changed.
29955 * @param {Roo.bootstrap.LocationPicker} this
29956 * @param {Google Location} location
29958 positionchanged : true,
29961 * Fires when the map resize.
29962 * @param {Roo.bootstrap.LocationPicker} this
29967 * Fires when the map show.
29968 * @param {Roo.bootstrap.LocationPicker} this
29973 * Fires when the map hide.
29974 * @param {Roo.bootstrap.LocationPicker} this
29979 * Fires when click the map.
29980 * @param {Roo.bootstrap.LocationPicker} this
29981 * @param {Map event} e
29985 * @event mapRightClick
29986 * Fires when right click the map.
29987 * @param {Roo.bootstrap.LocationPicker} this
29988 * @param {Map event} e
29990 mapRightClick : true,
29992 * @event markerClick
29993 * Fires when click the marker.
29994 * @param {Roo.bootstrap.LocationPicker} this
29995 * @param {Map event} e
29997 markerClick : true,
29999 * @event markerRightClick
30000 * Fires when right click the marker.
30001 * @param {Roo.bootstrap.LocationPicker} this
30002 * @param {Map event} e
30004 markerRightClick : true,
30006 * @event OverlayViewDraw
30007 * Fires when OverlayView Draw
30008 * @param {Roo.bootstrap.LocationPicker} this
30010 OverlayViewDraw : true,
30012 * @event OverlayViewOnAdd
30013 * Fires when OverlayView Draw
30014 * @param {Roo.bootstrap.LocationPicker} this
30016 OverlayViewOnAdd : true,
30018 * @event OverlayViewOnRemove
30019 * Fires when OverlayView Draw
30020 * @param {Roo.bootstrap.LocationPicker} this
30022 OverlayViewOnRemove : true,
30024 * @event OverlayViewShow
30025 * Fires when OverlayView Draw
30026 * @param {Roo.bootstrap.LocationPicker} this
30027 * @param {Pixel} cpx
30029 OverlayViewShow : true,
30031 * @event OverlayViewHide
30032 * Fires when OverlayView Draw
30033 * @param {Roo.bootstrap.LocationPicker} this
30035 OverlayViewHide : true,
30037 * @event loadexception
30038 * Fires when load google lib failed.
30039 * @param {Roo.bootstrap.LocationPicker} this
30041 loadexception : true
30046 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30048 gMapContext: false,
30054 mapTypeControl: false,
30055 disableDoubleClickZoom: false,
30057 streetViewControl: false,
30061 enableAutocomplete: false,
30062 enableReverseGeocode: true,
30065 getAutoCreate: function()
30070 cls: 'roo-location-picker'
30076 initEvents: function(ct, position)
30078 if(!this.el.getWidth() || this.isApplied()){
30082 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30087 initial: function()
30089 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30090 this.fireEvent('loadexception', this);
30094 if(!this.mapTypeId){
30095 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30098 this.gMapContext = this.GMapContext();
30100 this.initOverlayView();
30102 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30106 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30107 _this.setPosition(_this.gMapContext.marker.position);
30110 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30111 _this.fireEvent('mapClick', this, event);
30115 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30116 _this.fireEvent('mapRightClick', this, event);
30120 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30121 _this.fireEvent('markerClick', this, event);
30125 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30126 _this.fireEvent('markerRightClick', this, event);
30130 this.setPosition(this.gMapContext.location);
30132 this.fireEvent('initial', this, this.gMapContext.location);
30135 initOverlayView: function()
30139 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30143 _this.fireEvent('OverlayViewDraw', _this);
30148 _this.fireEvent('OverlayViewOnAdd', _this);
30151 onRemove: function()
30153 _this.fireEvent('OverlayViewOnRemove', _this);
30156 show: function(cpx)
30158 _this.fireEvent('OverlayViewShow', _this, cpx);
30163 _this.fireEvent('OverlayViewHide', _this);
30169 fromLatLngToContainerPixel: function(event)
30171 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30174 isApplied: function()
30176 return this.getGmapContext() == false ? false : true;
30179 getGmapContext: function()
30181 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30184 GMapContext: function()
30186 var position = new google.maps.LatLng(this.latitude, this.longitude);
30188 var _map = new google.maps.Map(this.el.dom, {
30191 mapTypeId: this.mapTypeId,
30192 mapTypeControl: this.mapTypeControl,
30193 disableDoubleClickZoom: this.disableDoubleClickZoom,
30194 scrollwheel: this.scrollwheel,
30195 streetViewControl: this.streetViewControl,
30196 locationName: this.locationName,
30197 draggable: this.draggable,
30198 enableAutocomplete: this.enableAutocomplete,
30199 enableReverseGeocode: this.enableReverseGeocode
30202 var _marker = new google.maps.Marker({
30203 position: position,
30205 title: this.markerTitle,
30206 draggable: this.draggable
30213 location: position,
30214 radius: this.radius,
30215 locationName: this.locationName,
30216 addressComponents: {
30217 formatted_address: null,
30218 addressLine1: null,
30219 addressLine2: null,
30221 streetNumber: null,
30225 stateOrProvince: null
30228 domContainer: this.el.dom,
30229 geodecoder: new google.maps.Geocoder()
30233 drawCircle: function(center, radius, options)
30235 if (this.gMapContext.circle != null) {
30236 this.gMapContext.circle.setMap(null);
30240 options = Roo.apply({}, options, {
30241 strokeColor: "#0000FF",
30242 strokeOpacity: .35,
30244 fillColor: "#0000FF",
30248 options.map = this.gMapContext.map;
30249 options.radius = radius;
30250 options.center = center;
30251 this.gMapContext.circle = new google.maps.Circle(options);
30252 return this.gMapContext.circle;
30258 setPosition: function(location)
30260 this.gMapContext.location = location;
30261 this.gMapContext.marker.setPosition(location);
30262 this.gMapContext.map.panTo(location);
30263 this.drawCircle(location, this.gMapContext.radius, {});
30267 if (this.gMapContext.settings.enableReverseGeocode) {
30268 this.gMapContext.geodecoder.geocode({
30269 latLng: this.gMapContext.location
30270 }, function(results, status) {
30272 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30273 _this.gMapContext.locationName = results[0].formatted_address;
30274 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30276 _this.fireEvent('positionchanged', this, location);
30283 this.fireEvent('positionchanged', this, location);
30288 google.maps.event.trigger(this.gMapContext.map, "resize");
30290 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30292 this.fireEvent('resize', this);
30295 setPositionByLatLng: function(latitude, longitude)
30297 this.setPosition(new google.maps.LatLng(latitude, longitude));
30300 getCurrentPosition: function()
30303 latitude: this.gMapContext.location.lat(),
30304 longitude: this.gMapContext.location.lng()
30308 getAddressName: function()
30310 return this.gMapContext.locationName;
30313 getAddressComponents: function()
30315 return this.gMapContext.addressComponents;
30318 address_component_from_google_geocode: function(address_components)
30322 for (var i = 0; i < address_components.length; i++) {
30323 var component = address_components[i];
30324 if (component.types.indexOf("postal_code") >= 0) {
30325 result.postalCode = component.short_name;
30326 } else if (component.types.indexOf("street_number") >= 0) {
30327 result.streetNumber = component.short_name;
30328 } else if (component.types.indexOf("route") >= 0) {
30329 result.streetName = component.short_name;
30330 } else if (component.types.indexOf("neighborhood") >= 0) {
30331 result.city = component.short_name;
30332 } else if (component.types.indexOf("locality") >= 0) {
30333 result.city = component.short_name;
30334 } else if (component.types.indexOf("sublocality") >= 0) {
30335 result.district = component.short_name;
30336 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30337 result.stateOrProvince = component.short_name;
30338 } else if (component.types.indexOf("country") >= 0) {
30339 result.country = component.short_name;
30343 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30344 result.addressLine2 = "";
30348 setZoomLevel: function(zoom)
30350 this.gMapContext.map.setZoom(zoom);
30363 this.fireEvent('show', this);
30374 this.fireEvent('hide', this);
30379 Roo.apply(Roo.bootstrap.LocationPicker, {
30381 OverlayView : function(map, options)
30383 options = options || {};
30390 * @class Roo.bootstrap.Alert
30391 * @extends Roo.bootstrap.Component
30392 * Bootstrap Alert class - shows an alert area box
30394 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30395 Enter a valid email address
30398 * @cfg {String} title The title of alert
30399 * @cfg {String} html The content of alert
30400 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30401 * @cfg {String} fa font-awesomeicon
30402 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30403 * @cfg {Boolean} close true to show a x closer
30407 * Create a new alert
30408 * @param {Object} config The config object
30412 Roo.bootstrap.Alert = function(config){
30413 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30417 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30423 faicon: false, // BC
30427 getAutoCreate : function()
30439 style : this.close ? '' : 'display:none'
30443 cls : 'roo-alert-icon'
30448 cls : 'roo-alert-title',
30453 cls : 'roo-alert-text',
30460 cfg.cn[0].cls += ' fa ' + this.faicon;
30463 cfg.cn[0].cls += ' fa ' + this.fa;
30467 cfg.cls += ' alert-' + this.weight;
30473 initEvents: function()
30475 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30476 this.titleEl = this.el.select('.roo-alert-title',true).first();
30477 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30478 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30479 if (this.seconds > 0) {
30480 this.hide.defer(this.seconds, this);
30484 * Set the Title Message HTML
30485 * @param {String} html
30487 setTitle : function(str)
30489 this.titleEl.dom.innerHTML = str;
30493 * Set the Body Message HTML
30494 * @param {String} html
30496 setHtml : function(str)
30498 this.htmlEl.dom.innerHTML = str;
30501 * Set the Weight of the alert
30502 * @param {String} (success|info|warning|danger) weight
30505 setWeight : function(weight)
30508 this.el.removeClass('alert-' + this.weight);
30511 this.weight = weight;
30513 this.el.addClass('alert-' + this.weight);
30516 * Set the Icon of the alert
30517 * @param {String} see fontawsome names (name without the 'fa-' bit)
30519 setIcon : function(icon)
30522 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30525 this.faicon = icon;
30527 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30552 * @class Roo.bootstrap.UploadCropbox
30553 * @extends Roo.bootstrap.Component
30554 * Bootstrap UploadCropbox class
30555 * @cfg {String} emptyText show when image has been loaded
30556 * @cfg {String} rotateNotify show when image too small to rotate
30557 * @cfg {Number} errorTimeout default 3000
30558 * @cfg {Number} minWidth default 300
30559 * @cfg {Number} minHeight default 300
30560 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30561 * @cfg {Boolean} isDocument (true|false) default false
30562 * @cfg {String} url action url
30563 * @cfg {String} paramName default 'imageUpload'
30564 * @cfg {String} method default POST
30565 * @cfg {Boolean} loadMask (true|false) default true
30566 * @cfg {Boolean} loadingText default 'Loading...'
30569 * Create a new UploadCropbox
30570 * @param {Object} config The config object
30573 Roo.bootstrap.UploadCropbox = function(config){
30574 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30578 * @event beforeselectfile
30579 * Fire before select file
30580 * @param {Roo.bootstrap.UploadCropbox} this
30582 "beforeselectfile" : true,
30585 * Fire after initEvent
30586 * @param {Roo.bootstrap.UploadCropbox} this
30591 * Fire after initEvent
30592 * @param {Roo.bootstrap.UploadCropbox} this
30593 * @param {String} data
30598 * Fire when preparing the file data
30599 * @param {Roo.bootstrap.UploadCropbox} this
30600 * @param {Object} file
30605 * Fire when get exception
30606 * @param {Roo.bootstrap.UploadCropbox} this
30607 * @param {XMLHttpRequest} xhr
30609 "exception" : true,
30611 * @event beforeloadcanvas
30612 * Fire before load the canvas
30613 * @param {Roo.bootstrap.UploadCropbox} this
30614 * @param {String} src
30616 "beforeloadcanvas" : true,
30619 * Fire when trash image
30620 * @param {Roo.bootstrap.UploadCropbox} this
30625 * Fire when download the image
30626 * @param {Roo.bootstrap.UploadCropbox} this
30630 * @event footerbuttonclick
30631 * Fire when footerbuttonclick
30632 * @param {Roo.bootstrap.UploadCropbox} this
30633 * @param {String} type
30635 "footerbuttonclick" : true,
30639 * @param {Roo.bootstrap.UploadCropbox} this
30644 * Fire when rotate the image
30645 * @param {Roo.bootstrap.UploadCropbox} this
30646 * @param {String} pos
30651 * Fire when inspect the file
30652 * @param {Roo.bootstrap.UploadCropbox} this
30653 * @param {Object} file
30658 * Fire when xhr upload the file
30659 * @param {Roo.bootstrap.UploadCropbox} this
30660 * @param {Object} data
30665 * Fire when arrange the file data
30666 * @param {Roo.bootstrap.UploadCropbox} this
30667 * @param {Object} formData
30672 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30675 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30677 emptyText : 'Click to upload image',
30678 rotateNotify : 'Image is too small to rotate',
30679 errorTimeout : 3000,
30693 cropType : 'image/jpeg',
30695 canvasLoaded : false,
30696 isDocument : false,
30698 paramName : 'imageUpload',
30700 loadingText : 'Loading...',
30703 getAutoCreate : function()
30707 cls : 'roo-upload-cropbox',
30711 cls : 'roo-upload-cropbox-selector',
30716 cls : 'roo-upload-cropbox-body',
30717 style : 'cursor:pointer',
30721 cls : 'roo-upload-cropbox-preview'
30725 cls : 'roo-upload-cropbox-thumb'
30729 cls : 'roo-upload-cropbox-empty-notify',
30730 html : this.emptyText
30734 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30735 html : this.rotateNotify
30741 cls : 'roo-upload-cropbox-footer',
30744 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30754 onRender : function(ct, position)
30756 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30758 if (this.buttons.length) {
30760 Roo.each(this.buttons, function(bb) {
30762 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30764 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30770 this.maskEl = this.el;
30774 initEvents : function()
30776 this.urlAPI = (window.createObjectURL && window) ||
30777 (window.URL && URL.revokeObjectURL && URL) ||
30778 (window.webkitURL && webkitURL);
30780 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30781 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30783 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30784 this.selectorEl.hide();
30786 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30787 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30789 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30790 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30791 this.thumbEl.hide();
30793 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30794 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30796 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30797 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30798 this.errorEl.hide();
30800 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30801 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30802 this.footerEl.hide();
30804 this.setThumbBoxSize();
30810 this.fireEvent('initial', this);
30817 window.addEventListener("resize", function() { _this.resize(); } );
30819 this.bodyEl.on('click', this.beforeSelectFile, this);
30822 this.bodyEl.on('touchstart', this.onTouchStart, this);
30823 this.bodyEl.on('touchmove', this.onTouchMove, this);
30824 this.bodyEl.on('touchend', this.onTouchEnd, this);
30828 this.bodyEl.on('mousedown', this.onMouseDown, this);
30829 this.bodyEl.on('mousemove', this.onMouseMove, this);
30830 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30831 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30832 Roo.get(document).on('mouseup', this.onMouseUp, this);
30835 this.selectorEl.on('change', this.onFileSelected, this);
30841 this.baseScale = 1;
30843 this.baseRotate = 1;
30844 this.dragable = false;
30845 this.pinching = false;
30848 this.cropData = false;
30849 this.notifyEl.dom.innerHTML = this.emptyText;
30851 this.selectorEl.dom.value = '';
30855 resize : function()
30857 if(this.fireEvent('resize', this) != false){
30858 this.setThumbBoxPosition();
30859 this.setCanvasPosition();
30863 onFooterButtonClick : function(e, el, o, type)
30866 case 'rotate-left' :
30867 this.onRotateLeft(e);
30869 case 'rotate-right' :
30870 this.onRotateRight(e);
30873 this.beforeSelectFile(e);
30888 this.fireEvent('footerbuttonclick', this, type);
30891 beforeSelectFile : function(e)
30893 e.preventDefault();
30895 if(this.fireEvent('beforeselectfile', this) != false){
30896 this.selectorEl.dom.click();
30900 onFileSelected : function(e)
30902 e.preventDefault();
30904 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30908 var file = this.selectorEl.dom.files[0];
30910 if(this.fireEvent('inspect', this, file) != false){
30911 this.prepare(file);
30916 trash : function(e)
30918 this.fireEvent('trash', this);
30921 download : function(e)
30923 this.fireEvent('download', this);
30926 loadCanvas : function(src)
30928 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30932 this.imageEl = document.createElement('img');
30936 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30938 this.imageEl.src = src;
30942 onLoadCanvas : function()
30944 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30945 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30947 this.bodyEl.un('click', this.beforeSelectFile, this);
30949 this.notifyEl.hide();
30950 this.thumbEl.show();
30951 this.footerEl.show();
30953 this.baseRotateLevel();
30955 if(this.isDocument){
30956 this.setThumbBoxSize();
30959 this.setThumbBoxPosition();
30961 this.baseScaleLevel();
30967 this.canvasLoaded = true;
30970 this.maskEl.unmask();
30975 setCanvasPosition : function()
30977 if(!this.canvasEl){
30981 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30982 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30984 this.previewEl.setLeft(pw);
30985 this.previewEl.setTop(ph);
30989 onMouseDown : function(e)
30993 this.dragable = true;
30994 this.pinching = false;
30996 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30997 this.dragable = false;
31001 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31002 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31006 onMouseMove : function(e)
31010 if(!this.canvasLoaded){
31014 if (!this.dragable){
31018 var minX = Math.ceil(this.thumbEl.getLeft(true));
31019 var minY = Math.ceil(this.thumbEl.getTop(true));
31021 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31022 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31024 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31025 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31027 x = x - this.mouseX;
31028 y = y - this.mouseY;
31030 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31031 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31033 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31034 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31036 this.previewEl.setLeft(bgX);
31037 this.previewEl.setTop(bgY);
31039 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31040 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31043 onMouseUp : function(e)
31047 this.dragable = false;
31050 onMouseWheel : function(e)
31054 this.startScale = this.scale;
31056 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31058 if(!this.zoomable()){
31059 this.scale = this.startScale;
31068 zoomable : function()
31070 var minScale = this.thumbEl.getWidth() / this.minWidth;
31072 if(this.minWidth < this.minHeight){
31073 minScale = this.thumbEl.getHeight() / this.minHeight;
31076 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31077 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31081 (this.rotate == 0 || this.rotate == 180) &&
31083 width > this.imageEl.OriginWidth ||
31084 height > this.imageEl.OriginHeight ||
31085 (width < this.minWidth && height < this.minHeight)
31093 (this.rotate == 90 || this.rotate == 270) &&
31095 width > this.imageEl.OriginWidth ||
31096 height > this.imageEl.OriginHeight ||
31097 (width < this.minHeight && height < this.minWidth)
31104 !this.isDocument &&
31105 (this.rotate == 0 || this.rotate == 180) &&
31107 width < this.minWidth ||
31108 width > this.imageEl.OriginWidth ||
31109 height < this.minHeight ||
31110 height > this.imageEl.OriginHeight
31117 !this.isDocument &&
31118 (this.rotate == 90 || this.rotate == 270) &&
31120 width < this.minHeight ||
31121 width > this.imageEl.OriginWidth ||
31122 height < this.minWidth ||
31123 height > this.imageEl.OriginHeight
31133 onRotateLeft : function(e)
31135 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31137 var minScale = this.thumbEl.getWidth() / this.minWidth;
31139 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31140 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31142 this.startScale = this.scale;
31144 while (this.getScaleLevel() < minScale){
31146 this.scale = this.scale + 1;
31148 if(!this.zoomable()){
31153 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31154 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31159 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31166 this.scale = this.startScale;
31168 this.onRotateFail();
31173 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31175 if(this.isDocument){
31176 this.setThumbBoxSize();
31177 this.setThumbBoxPosition();
31178 this.setCanvasPosition();
31183 this.fireEvent('rotate', this, 'left');
31187 onRotateRight : function(e)
31189 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31191 var minScale = this.thumbEl.getWidth() / this.minWidth;
31193 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31194 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31196 this.startScale = this.scale;
31198 while (this.getScaleLevel() < minScale){
31200 this.scale = this.scale + 1;
31202 if(!this.zoomable()){
31207 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31208 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31213 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31220 this.scale = this.startScale;
31222 this.onRotateFail();
31227 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31229 if(this.isDocument){
31230 this.setThumbBoxSize();
31231 this.setThumbBoxPosition();
31232 this.setCanvasPosition();
31237 this.fireEvent('rotate', this, 'right');
31240 onRotateFail : function()
31242 this.errorEl.show(true);
31246 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31251 this.previewEl.dom.innerHTML = '';
31253 var canvasEl = document.createElement("canvas");
31255 var contextEl = canvasEl.getContext("2d");
31257 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31258 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31259 var center = this.imageEl.OriginWidth / 2;
31261 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31262 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31263 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31264 center = this.imageEl.OriginHeight / 2;
31267 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31269 contextEl.translate(center, center);
31270 contextEl.rotate(this.rotate * Math.PI / 180);
31272 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31274 this.canvasEl = document.createElement("canvas");
31276 this.contextEl = this.canvasEl.getContext("2d");
31278 switch (this.rotate) {
31281 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31282 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31284 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31289 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31290 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31292 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31293 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);
31297 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31302 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31303 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31305 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31306 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);
31310 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);
31315 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31316 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31318 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31319 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31323 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);
31330 this.previewEl.appendChild(this.canvasEl);
31332 this.setCanvasPosition();
31337 if(!this.canvasLoaded){
31341 var imageCanvas = document.createElement("canvas");
31343 var imageContext = imageCanvas.getContext("2d");
31345 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31346 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31348 var center = imageCanvas.width / 2;
31350 imageContext.translate(center, center);
31352 imageContext.rotate(this.rotate * Math.PI / 180);
31354 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31356 var canvas = document.createElement("canvas");
31358 var context = canvas.getContext("2d");
31360 canvas.width = this.minWidth;
31361 canvas.height = this.minHeight;
31363 switch (this.rotate) {
31366 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31367 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31369 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31370 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31372 var targetWidth = this.minWidth - 2 * x;
31373 var targetHeight = this.minHeight - 2 * y;
31377 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31378 scale = targetWidth / width;
31381 if(x > 0 && y == 0){
31382 scale = targetHeight / height;
31385 if(x > 0 && y > 0){
31386 scale = targetWidth / width;
31388 if(width < height){
31389 scale = targetHeight / height;
31393 context.scale(scale, scale);
31395 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31396 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31398 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31399 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31401 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31406 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31407 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31409 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31410 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31412 var targetWidth = this.minWidth - 2 * x;
31413 var targetHeight = this.minHeight - 2 * y;
31417 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31418 scale = targetWidth / width;
31421 if(x > 0 && y == 0){
31422 scale = targetHeight / height;
31425 if(x > 0 && y > 0){
31426 scale = targetWidth / width;
31428 if(width < height){
31429 scale = targetHeight / height;
31433 context.scale(scale, scale);
31435 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31436 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31438 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31439 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31441 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31443 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31448 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31449 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31451 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31452 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31454 var targetWidth = this.minWidth - 2 * x;
31455 var targetHeight = this.minHeight - 2 * y;
31459 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31460 scale = targetWidth / width;
31463 if(x > 0 && y == 0){
31464 scale = targetHeight / height;
31467 if(x > 0 && y > 0){
31468 scale = targetWidth / width;
31470 if(width < height){
31471 scale = targetHeight / height;
31475 context.scale(scale, scale);
31477 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31478 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31480 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31481 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31483 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31484 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31486 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31491 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31492 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31494 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31495 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31497 var targetWidth = this.minWidth - 2 * x;
31498 var targetHeight = this.minHeight - 2 * y;
31502 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31503 scale = targetWidth / width;
31506 if(x > 0 && y == 0){
31507 scale = targetHeight / height;
31510 if(x > 0 && y > 0){
31511 scale = targetWidth / width;
31513 if(width < height){
31514 scale = targetHeight / height;
31518 context.scale(scale, scale);
31520 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31521 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31523 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31524 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31526 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31528 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31535 this.cropData = canvas.toDataURL(this.cropType);
31537 if(this.fireEvent('crop', this, this.cropData) !== false){
31538 this.process(this.file, this.cropData);
31545 setThumbBoxSize : function()
31549 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31550 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31551 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31553 this.minWidth = width;
31554 this.minHeight = height;
31556 if(this.rotate == 90 || this.rotate == 270){
31557 this.minWidth = height;
31558 this.minHeight = width;
31563 width = Math.ceil(this.minWidth * height / this.minHeight);
31565 if(this.minWidth > this.minHeight){
31567 height = Math.ceil(this.minHeight * width / this.minWidth);
31570 this.thumbEl.setStyle({
31571 width : width + 'px',
31572 height : height + 'px'
31579 setThumbBoxPosition : function()
31581 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31582 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31584 this.thumbEl.setLeft(x);
31585 this.thumbEl.setTop(y);
31589 baseRotateLevel : function()
31591 this.baseRotate = 1;
31594 typeof(this.exif) != 'undefined' &&
31595 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31596 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31598 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31601 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31605 baseScaleLevel : function()
31609 if(this.isDocument){
31611 if(this.baseRotate == 6 || this.baseRotate == 8){
31613 height = this.thumbEl.getHeight();
31614 this.baseScale = height / this.imageEl.OriginWidth;
31616 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31617 width = this.thumbEl.getWidth();
31618 this.baseScale = width / this.imageEl.OriginHeight;
31624 height = this.thumbEl.getHeight();
31625 this.baseScale = height / this.imageEl.OriginHeight;
31627 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31628 width = this.thumbEl.getWidth();
31629 this.baseScale = width / this.imageEl.OriginWidth;
31635 if(this.baseRotate == 6 || this.baseRotate == 8){
31637 width = this.thumbEl.getHeight();
31638 this.baseScale = width / this.imageEl.OriginHeight;
31640 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31641 height = this.thumbEl.getWidth();
31642 this.baseScale = height / this.imageEl.OriginHeight;
31645 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31646 height = this.thumbEl.getWidth();
31647 this.baseScale = height / this.imageEl.OriginHeight;
31649 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31650 width = this.thumbEl.getHeight();
31651 this.baseScale = width / this.imageEl.OriginWidth;
31658 width = this.thumbEl.getWidth();
31659 this.baseScale = width / this.imageEl.OriginWidth;
31661 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31662 height = this.thumbEl.getHeight();
31663 this.baseScale = height / this.imageEl.OriginHeight;
31666 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31668 height = this.thumbEl.getHeight();
31669 this.baseScale = height / this.imageEl.OriginHeight;
31671 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31672 width = this.thumbEl.getWidth();
31673 this.baseScale = width / this.imageEl.OriginWidth;
31681 getScaleLevel : function()
31683 return this.baseScale * Math.pow(1.1, this.scale);
31686 onTouchStart : function(e)
31688 if(!this.canvasLoaded){
31689 this.beforeSelectFile(e);
31693 var touches = e.browserEvent.touches;
31699 if(touches.length == 1){
31700 this.onMouseDown(e);
31704 if(touches.length != 2){
31710 for(var i = 0, finger; finger = touches[i]; i++){
31711 coords.push(finger.pageX, finger.pageY);
31714 var x = Math.pow(coords[0] - coords[2], 2);
31715 var y = Math.pow(coords[1] - coords[3], 2);
31717 this.startDistance = Math.sqrt(x + y);
31719 this.startScale = this.scale;
31721 this.pinching = true;
31722 this.dragable = false;
31726 onTouchMove : function(e)
31728 if(!this.pinching && !this.dragable){
31732 var touches = e.browserEvent.touches;
31739 this.onMouseMove(e);
31745 for(var i = 0, finger; finger = touches[i]; i++){
31746 coords.push(finger.pageX, finger.pageY);
31749 var x = Math.pow(coords[0] - coords[2], 2);
31750 var y = Math.pow(coords[1] - coords[3], 2);
31752 this.endDistance = Math.sqrt(x + y);
31754 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31756 if(!this.zoomable()){
31757 this.scale = this.startScale;
31765 onTouchEnd : function(e)
31767 this.pinching = false;
31768 this.dragable = false;
31772 process : function(file, crop)
31775 this.maskEl.mask(this.loadingText);
31778 this.xhr = new XMLHttpRequest();
31780 file.xhr = this.xhr;
31782 this.xhr.open(this.method, this.url, true);
31785 "Accept": "application/json",
31786 "Cache-Control": "no-cache",
31787 "X-Requested-With": "XMLHttpRequest"
31790 for (var headerName in headers) {
31791 var headerValue = headers[headerName];
31793 this.xhr.setRequestHeader(headerName, headerValue);
31799 this.xhr.onload = function()
31801 _this.xhrOnLoad(_this.xhr);
31804 this.xhr.onerror = function()
31806 _this.xhrOnError(_this.xhr);
31809 var formData = new FormData();
31811 formData.append('returnHTML', 'NO');
31814 formData.append('crop', crop);
31817 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31818 formData.append(this.paramName, file, file.name);
31821 if(typeof(file.filename) != 'undefined'){
31822 formData.append('filename', file.filename);
31825 if(typeof(file.mimetype) != 'undefined'){
31826 formData.append('mimetype', file.mimetype);
31829 if(this.fireEvent('arrange', this, formData) != false){
31830 this.xhr.send(formData);
31834 xhrOnLoad : function(xhr)
31837 this.maskEl.unmask();
31840 if (xhr.readyState !== 4) {
31841 this.fireEvent('exception', this, xhr);
31845 var response = Roo.decode(xhr.responseText);
31847 if(!response.success){
31848 this.fireEvent('exception', this, xhr);
31852 var response = Roo.decode(xhr.responseText);
31854 this.fireEvent('upload', this, response);
31858 xhrOnError : function()
31861 this.maskEl.unmask();
31864 Roo.log('xhr on error');
31866 var response = Roo.decode(xhr.responseText);
31872 prepare : function(file)
31875 this.maskEl.mask(this.loadingText);
31881 if(typeof(file) === 'string'){
31882 this.loadCanvas(file);
31886 if(!file || !this.urlAPI){
31891 this.cropType = file.type;
31895 if(this.fireEvent('prepare', this, this.file) != false){
31897 var reader = new FileReader();
31899 reader.onload = function (e) {
31900 if (e.target.error) {
31901 Roo.log(e.target.error);
31905 var buffer = e.target.result,
31906 dataView = new DataView(buffer),
31908 maxOffset = dataView.byteLength - 4,
31912 if (dataView.getUint16(0) === 0xffd8) {
31913 while (offset < maxOffset) {
31914 markerBytes = dataView.getUint16(offset);
31916 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31917 markerLength = dataView.getUint16(offset + 2) + 2;
31918 if (offset + markerLength > dataView.byteLength) {
31919 Roo.log('Invalid meta data: Invalid segment size.');
31923 if(markerBytes == 0xffe1){
31924 _this.parseExifData(
31931 offset += markerLength;
31941 var url = _this.urlAPI.createObjectURL(_this.file);
31943 _this.loadCanvas(url);
31948 reader.readAsArrayBuffer(this.file);
31954 parseExifData : function(dataView, offset, length)
31956 var tiffOffset = offset + 10,
31960 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31961 // No Exif data, might be XMP data instead
31965 // Check for the ASCII code for "Exif" (0x45786966):
31966 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31967 // No Exif data, might be XMP data instead
31970 if (tiffOffset + 8 > dataView.byteLength) {
31971 Roo.log('Invalid Exif data: Invalid segment size.');
31974 // Check for the two null bytes:
31975 if (dataView.getUint16(offset + 8) !== 0x0000) {
31976 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31979 // Check the byte alignment:
31980 switch (dataView.getUint16(tiffOffset)) {
31982 littleEndian = true;
31985 littleEndian = false;
31988 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31991 // Check for the TIFF tag marker (0x002A):
31992 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31993 Roo.log('Invalid Exif data: Missing TIFF marker.');
31996 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31997 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31999 this.parseExifTags(
32002 tiffOffset + dirOffset,
32007 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32012 if (dirOffset + 6 > dataView.byteLength) {
32013 Roo.log('Invalid Exif data: Invalid directory offset.');
32016 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32017 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32018 if (dirEndOffset + 4 > dataView.byteLength) {
32019 Roo.log('Invalid Exif data: Invalid directory size.');
32022 for (i = 0; i < tagsNumber; i += 1) {
32026 dirOffset + 2 + 12 * i, // tag offset
32030 // Return the offset to the next directory:
32031 return dataView.getUint32(dirEndOffset, littleEndian);
32034 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32036 var tag = dataView.getUint16(offset, littleEndian);
32038 this.exif[tag] = this.getExifValue(
32042 dataView.getUint16(offset + 2, littleEndian), // tag type
32043 dataView.getUint32(offset + 4, littleEndian), // tag length
32048 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32050 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32059 Roo.log('Invalid Exif data: Invalid tag type.');
32063 tagSize = tagType.size * length;
32064 // Determine if the value is contained in the dataOffset bytes,
32065 // or if the value at the dataOffset is a pointer to the actual data:
32066 dataOffset = tagSize > 4 ?
32067 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32068 if (dataOffset + tagSize > dataView.byteLength) {
32069 Roo.log('Invalid Exif data: Invalid data offset.');
32072 if (length === 1) {
32073 return tagType.getValue(dataView, dataOffset, littleEndian);
32076 for (i = 0; i < length; i += 1) {
32077 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32080 if (tagType.ascii) {
32082 // Concatenate the chars:
32083 for (i = 0; i < values.length; i += 1) {
32085 // Ignore the terminating NULL byte(s):
32086 if (c === '\u0000') {
32098 Roo.apply(Roo.bootstrap.UploadCropbox, {
32100 'Orientation': 0x0112
32104 1: 0, //'top-left',
32106 3: 180, //'bottom-right',
32107 // 4: 'bottom-left',
32109 6: 90, //'right-top',
32110 // 7: 'right-bottom',
32111 8: 270 //'left-bottom'
32115 // byte, 8-bit unsigned int:
32117 getValue: function (dataView, dataOffset) {
32118 return dataView.getUint8(dataOffset);
32122 // ascii, 8-bit byte:
32124 getValue: function (dataView, dataOffset) {
32125 return String.fromCharCode(dataView.getUint8(dataOffset));
32130 // short, 16 bit int:
32132 getValue: function (dataView, dataOffset, littleEndian) {
32133 return dataView.getUint16(dataOffset, littleEndian);
32137 // long, 32 bit int:
32139 getValue: function (dataView, dataOffset, littleEndian) {
32140 return dataView.getUint32(dataOffset, littleEndian);
32144 // rational = two long values, first is numerator, second is denominator:
32146 getValue: function (dataView, dataOffset, littleEndian) {
32147 return dataView.getUint32(dataOffset, littleEndian) /
32148 dataView.getUint32(dataOffset + 4, littleEndian);
32152 // slong, 32 bit signed int:
32154 getValue: function (dataView, dataOffset, littleEndian) {
32155 return dataView.getInt32(dataOffset, littleEndian);
32159 // srational, two slongs, first is numerator, second is denominator:
32161 getValue: function (dataView, dataOffset, littleEndian) {
32162 return dataView.getInt32(dataOffset, littleEndian) /
32163 dataView.getInt32(dataOffset + 4, littleEndian);
32173 cls : 'btn-group roo-upload-cropbox-rotate-left',
32174 action : 'rotate-left',
32178 cls : 'btn btn-default',
32179 html : '<i class="fa fa-undo"></i>'
32185 cls : 'btn-group roo-upload-cropbox-picture',
32186 action : 'picture',
32190 cls : 'btn btn-default',
32191 html : '<i class="fa fa-picture-o"></i>'
32197 cls : 'btn-group roo-upload-cropbox-rotate-right',
32198 action : 'rotate-right',
32202 cls : 'btn btn-default',
32203 html : '<i class="fa fa-repeat"></i>'
32211 cls : 'btn-group roo-upload-cropbox-rotate-left',
32212 action : 'rotate-left',
32216 cls : 'btn btn-default',
32217 html : '<i class="fa fa-undo"></i>'
32223 cls : 'btn-group roo-upload-cropbox-download',
32224 action : 'download',
32228 cls : 'btn btn-default',
32229 html : '<i class="fa fa-download"></i>'
32235 cls : 'btn-group roo-upload-cropbox-crop',
32240 cls : 'btn btn-default',
32241 html : '<i class="fa fa-crop"></i>'
32247 cls : 'btn-group roo-upload-cropbox-trash',
32252 cls : 'btn btn-default',
32253 html : '<i class="fa fa-trash"></i>'
32259 cls : 'btn-group roo-upload-cropbox-rotate-right',
32260 action : 'rotate-right',
32264 cls : 'btn btn-default',
32265 html : '<i class="fa fa-repeat"></i>'
32273 cls : 'btn-group roo-upload-cropbox-rotate-left',
32274 action : 'rotate-left',
32278 cls : 'btn btn-default',
32279 html : '<i class="fa fa-undo"></i>'
32285 cls : 'btn-group roo-upload-cropbox-rotate-right',
32286 action : 'rotate-right',
32290 cls : 'btn btn-default',
32291 html : '<i class="fa fa-repeat"></i>'
32304 * @class Roo.bootstrap.DocumentManager
32305 * @extends Roo.bootstrap.Component
32306 * Bootstrap DocumentManager class
32307 * @cfg {String} paramName default 'imageUpload'
32308 * @cfg {String} toolTipName default 'filename'
32309 * @cfg {String} method default POST
32310 * @cfg {String} url action url
32311 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32312 * @cfg {Boolean} multiple multiple upload default true
32313 * @cfg {Number} thumbSize default 300
32314 * @cfg {String} fieldLabel
32315 * @cfg {Number} labelWidth default 4
32316 * @cfg {String} labelAlign (left|top) default left
32317 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32318 * @cfg {Number} labellg set the width of label (1-12)
32319 * @cfg {Number} labelmd set the width of label (1-12)
32320 * @cfg {Number} labelsm set the width of label (1-12)
32321 * @cfg {Number} labelxs set the width of label (1-12)
32324 * Create a new DocumentManager
32325 * @param {Object} config The config object
32328 Roo.bootstrap.DocumentManager = function(config){
32329 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32332 this.delegates = [];
32337 * Fire when initial the DocumentManager
32338 * @param {Roo.bootstrap.DocumentManager} this
32343 * inspect selected file
32344 * @param {Roo.bootstrap.DocumentManager} this
32345 * @param {File} file
32350 * Fire when xhr load exception
32351 * @param {Roo.bootstrap.DocumentManager} this
32352 * @param {XMLHttpRequest} xhr
32354 "exception" : true,
32356 * @event afterupload
32357 * Fire when xhr load exception
32358 * @param {Roo.bootstrap.DocumentManager} this
32359 * @param {XMLHttpRequest} xhr
32361 "afterupload" : true,
32364 * prepare the form data
32365 * @param {Roo.bootstrap.DocumentManager} this
32366 * @param {Object} formData
32371 * Fire when remove the file
32372 * @param {Roo.bootstrap.DocumentManager} this
32373 * @param {Object} file
32378 * Fire after refresh the file
32379 * @param {Roo.bootstrap.DocumentManager} this
32384 * Fire after click the image
32385 * @param {Roo.bootstrap.DocumentManager} this
32386 * @param {Object} file
32391 * Fire when upload a image and editable set to true
32392 * @param {Roo.bootstrap.DocumentManager} this
32393 * @param {Object} file
32397 * @event beforeselectfile
32398 * Fire before select file
32399 * @param {Roo.bootstrap.DocumentManager} this
32401 "beforeselectfile" : true,
32404 * Fire before process file
32405 * @param {Roo.bootstrap.DocumentManager} this
32406 * @param {Object} file
32410 * @event previewrendered
32411 * Fire when preview rendered
32412 * @param {Roo.bootstrap.DocumentManager} this
32413 * @param {Object} file
32415 "previewrendered" : true,
32418 "previewResize" : true
32423 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32432 paramName : 'imageUpload',
32433 toolTipName : 'filename',
32436 labelAlign : 'left',
32446 getAutoCreate : function()
32448 var managerWidget = {
32450 cls : 'roo-document-manager',
32454 cls : 'roo-document-manager-selector',
32459 cls : 'roo-document-manager-uploader',
32463 cls : 'roo-document-manager-upload-btn',
32464 html : '<i class="fa fa-plus"></i>'
32475 cls : 'column col-md-12',
32480 if(this.fieldLabel.length){
32485 cls : 'column col-md-12',
32486 html : this.fieldLabel
32490 cls : 'column col-md-12',
32495 if(this.labelAlign == 'left'){
32500 html : this.fieldLabel
32509 if(this.labelWidth > 12){
32510 content[0].style = "width: " + this.labelWidth + 'px';
32513 if(this.labelWidth < 13 && this.labelmd == 0){
32514 this.labelmd = this.labelWidth;
32517 if(this.labellg > 0){
32518 content[0].cls += ' col-lg-' + this.labellg;
32519 content[1].cls += ' col-lg-' + (12 - this.labellg);
32522 if(this.labelmd > 0){
32523 content[0].cls += ' col-md-' + this.labelmd;
32524 content[1].cls += ' col-md-' + (12 - this.labelmd);
32527 if(this.labelsm > 0){
32528 content[0].cls += ' col-sm-' + this.labelsm;
32529 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32532 if(this.labelxs > 0){
32533 content[0].cls += ' col-xs-' + this.labelxs;
32534 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32542 cls : 'row clearfix',
32550 initEvents : function()
32552 this.managerEl = this.el.select('.roo-document-manager', true).first();
32553 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32555 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32556 this.selectorEl.hide();
32559 this.selectorEl.attr('multiple', 'multiple');
32562 this.selectorEl.on('change', this.onFileSelected, this);
32564 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32565 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32567 this.uploader.on('click', this.onUploaderClick, this);
32569 this.renderProgressDialog();
32573 window.addEventListener("resize", function() { _this.refresh(); } );
32575 this.fireEvent('initial', this);
32578 renderProgressDialog : function()
32582 this.progressDialog = new Roo.bootstrap.Modal({
32583 cls : 'roo-document-manager-progress-dialog',
32584 allow_close : false,
32595 btnclick : function() {
32596 _this.uploadCancel();
32602 this.progressDialog.render(Roo.get(document.body));
32604 this.progress = new Roo.bootstrap.Progress({
32605 cls : 'roo-document-manager-progress',
32610 this.progress.render(this.progressDialog.getChildContainer());
32612 this.progressBar = new Roo.bootstrap.ProgressBar({
32613 cls : 'roo-document-manager-progress-bar',
32616 aria_valuemax : 12,
32620 this.progressBar.render(this.progress.getChildContainer());
32623 onUploaderClick : function(e)
32625 e.preventDefault();
32627 if(this.fireEvent('beforeselectfile', this) != false){
32628 this.selectorEl.dom.click();
32633 onFileSelected : function(e)
32635 e.preventDefault();
32637 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32641 Roo.each(this.selectorEl.dom.files, function(file){
32642 if(this.fireEvent('inspect', this, file) != false){
32643 this.files.push(file);
32653 this.selectorEl.dom.value = '';
32655 if(!this.files || !this.files.length){
32659 if(this.boxes > 0 && this.files.length > this.boxes){
32660 this.files = this.files.slice(0, this.boxes);
32663 this.uploader.show();
32665 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32666 this.uploader.hide();
32675 Roo.each(this.files, function(file){
32677 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32678 var f = this.renderPreview(file);
32683 if(file.type.indexOf('image') != -1){
32684 this.delegates.push(
32686 _this.process(file);
32687 }).createDelegate(this)
32695 _this.process(file);
32696 }).createDelegate(this)
32701 this.files = files;
32703 this.delegates = this.delegates.concat(docs);
32705 if(!this.delegates.length){
32710 this.progressBar.aria_valuemax = this.delegates.length;
32717 arrange : function()
32719 if(!this.delegates.length){
32720 this.progressDialog.hide();
32725 var delegate = this.delegates.shift();
32727 this.progressDialog.show();
32729 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32731 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32736 refresh : function()
32738 this.uploader.show();
32740 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32741 this.uploader.hide();
32744 Roo.isTouch ? this.closable(false) : this.closable(true);
32746 this.fireEvent('refresh', this);
32749 onRemove : function(e, el, o)
32751 e.preventDefault();
32753 this.fireEvent('remove', this, o);
32757 remove : function(o)
32761 Roo.each(this.files, function(file){
32762 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32771 this.files = files;
32778 Roo.each(this.files, function(file){
32783 file.target.remove();
32792 onClick : function(e, el, o)
32794 e.preventDefault();
32796 this.fireEvent('click', this, o);
32800 closable : function(closable)
32802 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32804 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32816 xhrOnLoad : function(xhr)
32818 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32822 if (xhr.readyState !== 4) {
32824 this.fireEvent('exception', this, xhr);
32828 var response = Roo.decode(xhr.responseText);
32830 if(!response.success){
32832 this.fireEvent('exception', this, xhr);
32836 var file = this.renderPreview(response.data);
32838 this.files.push(file);
32842 this.fireEvent('afterupload', this, xhr);
32846 xhrOnError : function(xhr)
32848 Roo.log('xhr on error');
32850 var response = Roo.decode(xhr.responseText);
32857 process : function(file)
32859 if(this.fireEvent('process', this, file) !== false){
32860 if(this.editable && file.type.indexOf('image') != -1){
32861 this.fireEvent('edit', this, file);
32865 this.uploadStart(file, false);
32872 uploadStart : function(file, crop)
32874 this.xhr = new XMLHttpRequest();
32876 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32881 file.xhr = this.xhr;
32883 this.managerEl.createChild({
32885 cls : 'roo-document-manager-loading',
32889 tooltip : file.name,
32890 cls : 'roo-document-manager-thumb',
32891 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32897 this.xhr.open(this.method, this.url, true);
32900 "Accept": "application/json",
32901 "Cache-Control": "no-cache",
32902 "X-Requested-With": "XMLHttpRequest"
32905 for (var headerName in headers) {
32906 var headerValue = headers[headerName];
32908 this.xhr.setRequestHeader(headerName, headerValue);
32914 this.xhr.onload = function()
32916 _this.xhrOnLoad(_this.xhr);
32919 this.xhr.onerror = function()
32921 _this.xhrOnError(_this.xhr);
32924 var formData = new FormData();
32926 formData.append('returnHTML', 'NO');
32929 formData.append('crop', crop);
32932 formData.append(this.paramName, file, file.name);
32939 if(this.fireEvent('prepare', this, formData, options) != false){
32941 if(options.manually){
32945 this.xhr.send(formData);
32949 this.uploadCancel();
32952 uploadCancel : function()
32958 this.delegates = [];
32960 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32967 renderPreview : function(file)
32969 if(typeof(file.target) != 'undefined' && file.target){
32973 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32975 var previewEl = this.managerEl.createChild({
32977 cls : 'roo-document-manager-preview',
32981 tooltip : file[this.toolTipName],
32982 cls : 'roo-document-manager-thumb',
32983 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32988 html : '<i class="fa fa-times-circle"></i>'
32993 var close = previewEl.select('button.close', true).first();
32995 close.on('click', this.onRemove, this, file);
32997 file.target = previewEl;
32999 var image = previewEl.select('img', true).first();
33003 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33005 image.on('click', this.onClick, this, file);
33007 this.fireEvent('previewrendered', this, file);
33013 onPreviewLoad : function(file, image)
33015 if(typeof(file.target) == 'undefined' || !file.target){
33019 var width = image.dom.naturalWidth || image.dom.width;
33020 var height = image.dom.naturalHeight || image.dom.height;
33022 if(!this.previewResize) {
33026 if(width > height){
33027 file.target.addClass('wide');
33031 file.target.addClass('tall');
33036 uploadFromSource : function(file, crop)
33038 this.xhr = new XMLHttpRequest();
33040 this.managerEl.createChild({
33042 cls : 'roo-document-manager-loading',
33046 tooltip : file.name,
33047 cls : 'roo-document-manager-thumb',
33048 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33054 this.xhr.open(this.method, this.url, true);
33057 "Accept": "application/json",
33058 "Cache-Control": "no-cache",
33059 "X-Requested-With": "XMLHttpRequest"
33062 for (var headerName in headers) {
33063 var headerValue = headers[headerName];
33065 this.xhr.setRequestHeader(headerName, headerValue);
33071 this.xhr.onload = function()
33073 _this.xhrOnLoad(_this.xhr);
33076 this.xhr.onerror = function()
33078 _this.xhrOnError(_this.xhr);
33081 var formData = new FormData();
33083 formData.append('returnHTML', 'NO');
33085 formData.append('crop', crop);
33087 if(typeof(file.filename) != 'undefined'){
33088 formData.append('filename', file.filename);
33091 if(typeof(file.mimetype) != 'undefined'){
33092 formData.append('mimetype', file.mimetype);
33097 if(this.fireEvent('prepare', this, formData) != false){
33098 this.xhr.send(formData);
33108 * @class Roo.bootstrap.DocumentViewer
33109 * @extends Roo.bootstrap.Component
33110 * Bootstrap DocumentViewer class
33111 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33112 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33115 * Create a new DocumentViewer
33116 * @param {Object} config The config object
33119 Roo.bootstrap.DocumentViewer = function(config){
33120 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33125 * Fire after initEvent
33126 * @param {Roo.bootstrap.DocumentViewer} this
33132 * @param {Roo.bootstrap.DocumentViewer} this
33137 * Fire after download button
33138 * @param {Roo.bootstrap.DocumentViewer} this
33143 * Fire after trash button
33144 * @param {Roo.bootstrap.DocumentViewer} this
33151 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33153 showDownload : true,
33157 getAutoCreate : function()
33161 cls : 'roo-document-viewer',
33165 cls : 'roo-document-viewer-body',
33169 cls : 'roo-document-viewer-thumb',
33173 cls : 'roo-document-viewer-image'
33181 cls : 'roo-document-viewer-footer',
33184 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33188 cls : 'btn-group roo-document-viewer-download',
33192 cls : 'btn btn-default',
33193 html : '<i class="fa fa-download"></i>'
33199 cls : 'btn-group roo-document-viewer-trash',
33203 cls : 'btn btn-default',
33204 html : '<i class="fa fa-trash"></i>'
33217 initEvents : function()
33219 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33220 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33222 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33223 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33225 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33226 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33228 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33229 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33231 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33232 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33234 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33235 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33237 this.bodyEl.on('click', this.onClick, this);
33238 this.downloadBtn.on('click', this.onDownload, this);
33239 this.trashBtn.on('click', this.onTrash, this);
33241 this.downloadBtn.hide();
33242 this.trashBtn.hide();
33244 if(this.showDownload){
33245 this.downloadBtn.show();
33248 if(this.showTrash){
33249 this.trashBtn.show();
33252 if(!this.showDownload && !this.showTrash) {
33253 this.footerEl.hide();
33258 initial : function()
33260 this.fireEvent('initial', this);
33264 onClick : function(e)
33266 e.preventDefault();
33268 this.fireEvent('click', this);
33271 onDownload : function(e)
33273 e.preventDefault();
33275 this.fireEvent('download', this);
33278 onTrash : function(e)
33280 e.preventDefault();
33282 this.fireEvent('trash', this);
33294 * @class Roo.bootstrap.NavProgressBar
33295 * @extends Roo.bootstrap.Component
33296 * Bootstrap NavProgressBar class
33299 * Create a new nav progress bar
33300 * @param {Object} config The config object
33303 Roo.bootstrap.NavProgressBar = function(config){
33304 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33306 this.bullets = this.bullets || [];
33308 // Roo.bootstrap.NavProgressBar.register(this);
33312 * Fires when the active item changes
33313 * @param {Roo.bootstrap.NavProgressBar} this
33314 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33315 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33322 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33327 getAutoCreate : function()
33329 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33333 cls : 'roo-navigation-bar-group',
33337 cls : 'roo-navigation-top-bar'
33341 cls : 'roo-navigation-bullets-bar',
33345 cls : 'roo-navigation-bar'
33352 cls : 'roo-navigation-bottom-bar'
33362 initEvents: function()
33367 onRender : function(ct, position)
33369 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33371 if(this.bullets.length){
33372 Roo.each(this.bullets, function(b){
33381 addItem : function(cfg)
33383 var item = new Roo.bootstrap.NavProgressItem(cfg);
33385 item.parentId = this.id;
33386 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33389 var top = new Roo.bootstrap.Element({
33391 cls : 'roo-navigation-bar-text'
33394 var bottom = new Roo.bootstrap.Element({
33396 cls : 'roo-navigation-bar-text'
33399 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33400 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33402 var topText = new Roo.bootstrap.Element({
33404 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33407 var bottomText = new Roo.bootstrap.Element({
33409 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33412 topText.onRender(top.el, null);
33413 bottomText.onRender(bottom.el, null);
33416 item.bottomEl = bottom;
33419 this.barItems.push(item);
33424 getActive : function()
33426 var active = false;
33428 Roo.each(this.barItems, function(v){
33430 if (!v.isActive()) {
33442 setActiveItem : function(item)
33446 Roo.each(this.barItems, function(v){
33447 if (v.rid == item.rid) {
33451 if (v.isActive()) {
33452 v.setActive(false);
33457 item.setActive(true);
33459 this.fireEvent('changed', this, item, prev);
33462 getBarItem: function(rid)
33466 Roo.each(this.barItems, function(e) {
33467 if (e.rid != rid) {
33478 indexOfItem : function(item)
33482 Roo.each(this.barItems, function(v, i){
33484 if (v.rid != item.rid) {
33495 setActiveNext : function()
33497 var i = this.indexOfItem(this.getActive());
33499 if (i > this.barItems.length) {
33503 this.setActiveItem(this.barItems[i+1]);
33506 setActivePrev : function()
33508 var i = this.indexOfItem(this.getActive());
33514 this.setActiveItem(this.barItems[i-1]);
33517 format : function()
33519 if(!this.barItems.length){
33523 var width = 100 / this.barItems.length;
33525 Roo.each(this.barItems, function(i){
33526 i.el.setStyle('width', width + '%');
33527 i.topEl.el.setStyle('width', width + '%');
33528 i.bottomEl.el.setStyle('width', width + '%');
33537 * Nav Progress Item
33542 * @class Roo.bootstrap.NavProgressItem
33543 * @extends Roo.bootstrap.Component
33544 * Bootstrap NavProgressItem class
33545 * @cfg {String} rid the reference id
33546 * @cfg {Boolean} active (true|false) Is item active default false
33547 * @cfg {Boolean} disabled (true|false) Is item active default false
33548 * @cfg {String} html
33549 * @cfg {String} position (top|bottom) text position default bottom
33550 * @cfg {String} icon show icon instead of number
33553 * Create a new NavProgressItem
33554 * @param {Object} config The config object
33556 Roo.bootstrap.NavProgressItem = function(config){
33557 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33562 * The raw click event for the entire grid.
33563 * @param {Roo.bootstrap.NavProgressItem} this
33564 * @param {Roo.EventObject} e
33571 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33577 position : 'bottom',
33580 getAutoCreate : function()
33582 var iconCls = 'roo-navigation-bar-item-icon';
33584 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33588 cls: 'roo-navigation-bar-item',
33598 cfg.cls += ' active';
33601 cfg.cls += ' disabled';
33607 disable : function()
33609 this.setDisabled(true);
33612 enable : function()
33614 this.setDisabled(false);
33617 initEvents: function()
33619 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33621 this.iconEl.on('click', this.onClick, this);
33624 onClick : function(e)
33626 e.preventDefault();
33632 if(this.fireEvent('click', this, e) === false){
33636 this.parent().setActiveItem(this);
33639 isActive: function ()
33641 return this.active;
33644 setActive : function(state)
33646 if(this.active == state){
33650 this.active = state;
33653 this.el.addClass('active');
33657 this.el.removeClass('active');
33662 setDisabled : function(state)
33664 if(this.disabled == state){
33668 this.disabled = state;
33671 this.el.addClass('disabled');
33675 this.el.removeClass('disabled');
33678 tooltipEl : function()
33680 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33693 * @class Roo.bootstrap.FieldLabel
33694 * @extends Roo.bootstrap.Component
33695 * Bootstrap FieldLabel class
33696 * @cfg {String} html contents of the element
33697 * @cfg {String} tag tag of the element default label
33698 * @cfg {String} cls class of the element
33699 * @cfg {String} target label target
33700 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33701 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33702 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33703 * @cfg {String} iconTooltip default "This field is required"
33704 * @cfg {String} indicatorpos (left|right) default left
33707 * Create a new FieldLabel
33708 * @param {Object} config The config object
33711 Roo.bootstrap.FieldLabel = function(config){
33712 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33717 * Fires after the field has been marked as invalid.
33718 * @param {Roo.form.FieldLabel} this
33719 * @param {String} msg The validation message
33724 * Fires after the field has been validated with no errors.
33725 * @param {Roo.form.FieldLabel} this
33731 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33738 invalidClass : 'has-warning',
33739 validClass : 'has-success',
33740 iconTooltip : 'This field is required',
33741 indicatorpos : 'left',
33743 getAutoCreate : function(){
33746 if (!this.allowBlank) {
33752 cls : 'roo-bootstrap-field-label ' + this.cls,
33757 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33758 tooltip : this.iconTooltip
33767 if(this.indicatorpos == 'right'){
33770 cls : 'roo-bootstrap-field-label ' + this.cls,
33779 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33780 tooltip : this.iconTooltip
33789 initEvents: function()
33791 Roo.bootstrap.Element.superclass.initEvents.call(this);
33793 this.indicator = this.indicatorEl();
33795 if(this.indicator){
33796 this.indicator.removeClass('visible');
33797 this.indicator.addClass('invisible');
33800 Roo.bootstrap.FieldLabel.register(this);
33803 indicatorEl : function()
33805 var indicator = this.el.select('i.roo-required-indicator',true).first();
33816 * Mark this field as valid
33818 markValid : function()
33820 if(this.indicator){
33821 this.indicator.removeClass('visible');
33822 this.indicator.addClass('invisible');
33824 if (Roo.bootstrap.version == 3) {
33825 this.el.removeClass(this.invalidClass);
33826 this.el.addClass(this.validClass);
33828 this.el.removeClass('is-invalid');
33829 this.el.addClass('is-valid');
33833 this.fireEvent('valid', this);
33837 * Mark this field as invalid
33838 * @param {String} msg The validation message
33840 markInvalid : function(msg)
33842 if(this.indicator){
33843 this.indicator.removeClass('invisible');
33844 this.indicator.addClass('visible');
33846 if (Roo.bootstrap.version == 3) {
33847 this.el.removeClass(this.validClass);
33848 this.el.addClass(this.invalidClass);
33850 this.el.removeClass('is-valid');
33851 this.el.addClass('is-invalid');
33855 this.fireEvent('invalid', this, msg);
33861 Roo.apply(Roo.bootstrap.FieldLabel, {
33866 * register a FieldLabel Group
33867 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33869 register : function(label)
33871 if(this.groups.hasOwnProperty(label.target)){
33875 this.groups[label.target] = label;
33879 * fetch a FieldLabel Group based on the target
33880 * @param {string} target
33881 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33883 get: function(target) {
33884 if (typeof(this.groups[target]) == 'undefined') {
33888 return this.groups[target] ;
33897 * page DateSplitField.
33903 * @class Roo.bootstrap.DateSplitField
33904 * @extends Roo.bootstrap.Component
33905 * Bootstrap DateSplitField class
33906 * @cfg {string} fieldLabel - the label associated
33907 * @cfg {Number} labelWidth set the width of label (0-12)
33908 * @cfg {String} labelAlign (top|left)
33909 * @cfg {Boolean} dayAllowBlank (true|false) default false
33910 * @cfg {Boolean} monthAllowBlank (true|false) default false
33911 * @cfg {Boolean} yearAllowBlank (true|false) default false
33912 * @cfg {string} dayPlaceholder
33913 * @cfg {string} monthPlaceholder
33914 * @cfg {string} yearPlaceholder
33915 * @cfg {string} dayFormat default 'd'
33916 * @cfg {string} monthFormat default 'm'
33917 * @cfg {string} yearFormat default 'Y'
33918 * @cfg {Number} labellg set the width of label (1-12)
33919 * @cfg {Number} labelmd set the width of label (1-12)
33920 * @cfg {Number} labelsm set the width of label (1-12)
33921 * @cfg {Number} labelxs set the width of label (1-12)
33925 * Create a new DateSplitField
33926 * @param {Object} config The config object
33929 Roo.bootstrap.DateSplitField = function(config){
33930 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33936 * getting the data of years
33937 * @param {Roo.bootstrap.DateSplitField} this
33938 * @param {Object} years
33943 * getting the data of days
33944 * @param {Roo.bootstrap.DateSplitField} this
33945 * @param {Object} days
33950 * Fires after the field has been marked as invalid.
33951 * @param {Roo.form.Field} this
33952 * @param {String} msg The validation message
33957 * Fires after the field has been validated with no errors.
33958 * @param {Roo.form.Field} this
33964 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33967 labelAlign : 'top',
33969 dayAllowBlank : false,
33970 monthAllowBlank : false,
33971 yearAllowBlank : false,
33972 dayPlaceholder : '',
33973 monthPlaceholder : '',
33974 yearPlaceholder : '',
33978 isFormField : true,
33984 getAutoCreate : function()
33988 cls : 'row roo-date-split-field-group',
33993 cls : 'form-hidden-field roo-date-split-field-group-value',
33999 var labelCls = 'col-md-12';
34000 var contentCls = 'col-md-4';
34002 if(this.fieldLabel){
34006 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34010 html : this.fieldLabel
34015 if(this.labelAlign == 'left'){
34017 if(this.labelWidth > 12){
34018 label.style = "width: " + this.labelWidth + 'px';
34021 if(this.labelWidth < 13 && this.labelmd == 0){
34022 this.labelmd = this.labelWidth;
34025 if(this.labellg > 0){
34026 labelCls = ' col-lg-' + this.labellg;
34027 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34030 if(this.labelmd > 0){
34031 labelCls = ' col-md-' + this.labelmd;
34032 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34035 if(this.labelsm > 0){
34036 labelCls = ' col-sm-' + this.labelsm;
34037 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34040 if(this.labelxs > 0){
34041 labelCls = ' col-xs-' + this.labelxs;
34042 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34046 label.cls += ' ' + labelCls;
34048 cfg.cn.push(label);
34051 Roo.each(['day', 'month', 'year'], function(t){
34054 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34061 inputEl: function ()
34063 return this.el.select('.roo-date-split-field-group-value', true).first();
34066 onRender : function(ct, position)
34070 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34072 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34074 this.dayField = new Roo.bootstrap.ComboBox({
34075 allowBlank : this.dayAllowBlank,
34076 alwaysQuery : true,
34077 displayField : 'value',
34080 forceSelection : true,
34082 placeholder : this.dayPlaceholder,
34083 selectOnFocus : true,
34084 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34085 triggerAction : 'all',
34087 valueField : 'value',
34088 store : new Roo.data.SimpleStore({
34089 data : (function() {
34091 _this.fireEvent('days', _this, days);
34094 fields : [ 'value' ]
34097 select : function (_self, record, index)
34099 _this.setValue(_this.getValue());
34104 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34106 this.monthField = new Roo.bootstrap.MonthField({
34107 after : '<i class=\"fa fa-calendar\"></i>',
34108 allowBlank : this.monthAllowBlank,
34109 placeholder : this.monthPlaceholder,
34112 render : function (_self)
34114 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34115 e.preventDefault();
34119 select : function (_self, oldvalue, newvalue)
34121 _this.setValue(_this.getValue());
34126 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34128 this.yearField = new Roo.bootstrap.ComboBox({
34129 allowBlank : this.yearAllowBlank,
34130 alwaysQuery : true,
34131 displayField : 'value',
34134 forceSelection : true,
34136 placeholder : this.yearPlaceholder,
34137 selectOnFocus : true,
34138 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34139 triggerAction : 'all',
34141 valueField : 'value',
34142 store : new Roo.data.SimpleStore({
34143 data : (function() {
34145 _this.fireEvent('years', _this, years);
34148 fields : [ 'value' ]
34151 select : function (_self, record, index)
34153 _this.setValue(_this.getValue());
34158 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34161 setValue : function(v, format)
34163 this.inputEl.dom.value = v;
34165 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34167 var d = Date.parseDate(v, f);
34174 this.setDay(d.format(this.dayFormat));
34175 this.setMonth(d.format(this.monthFormat));
34176 this.setYear(d.format(this.yearFormat));
34183 setDay : function(v)
34185 this.dayField.setValue(v);
34186 this.inputEl.dom.value = this.getValue();
34191 setMonth : function(v)
34193 this.monthField.setValue(v, true);
34194 this.inputEl.dom.value = this.getValue();
34199 setYear : function(v)
34201 this.yearField.setValue(v);
34202 this.inputEl.dom.value = this.getValue();
34207 getDay : function()
34209 return this.dayField.getValue();
34212 getMonth : function()
34214 return this.monthField.getValue();
34217 getYear : function()
34219 return this.yearField.getValue();
34222 getValue : function()
34224 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34226 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34236 this.inputEl.dom.value = '';
34241 validate : function()
34243 var d = this.dayField.validate();
34244 var m = this.monthField.validate();
34245 var y = this.yearField.validate();
34250 (!this.dayAllowBlank && !d) ||
34251 (!this.monthAllowBlank && !m) ||
34252 (!this.yearAllowBlank && !y)
34257 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34266 this.markInvalid();
34271 markValid : function()
34274 var label = this.el.select('label', true).first();
34275 var icon = this.el.select('i.fa-star', true).first();
34281 this.fireEvent('valid', this);
34285 * Mark this field as invalid
34286 * @param {String} msg The validation message
34288 markInvalid : function(msg)
34291 var label = this.el.select('label', true).first();
34292 var icon = this.el.select('i.fa-star', true).first();
34294 if(label && !icon){
34295 this.el.select('.roo-date-split-field-label', true).createChild({
34297 cls : 'text-danger fa fa-lg fa-star',
34298 tooltip : 'This field is required',
34299 style : 'margin-right:5px;'
34303 this.fireEvent('invalid', this, msg);
34306 clearInvalid : function()
34308 var label = this.el.select('label', true).first();
34309 var icon = this.el.select('i.fa-star', true).first();
34315 this.fireEvent('valid', this);
34318 getName: function()
34328 * http://masonry.desandro.com
34330 * The idea is to render all the bricks based on vertical width...
34332 * The original code extends 'outlayer' - we might need to use that....
34338 * @class Roo.bootstrap.LayoutMasonry
34339 * @extends Roo.bootstrap.Component
34340 * Bootstrap Layout Masonry class
34343 * Create a new Element
34344 * @param {Object} config The config object
34347 Roo.bootstrap.LayoutMasonry = function(config){
34349 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34353 Roo.bootstrap.LayoutMasonry.register(this);
34359 * Fire after layout the items
34360 * @param {Roo.bootstrap.LayoutMasonry} this
34361 * @param {Roo.EventObject} e
34368 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34371 * @cfg {Boolean} isLayoutInstant = no animation?
34373 isLayoutInstant : false, // needed?
34376 * @cfg {Number} boxWidth width of the columns
34381 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34386 * @cfg {Number} padWidth padding below box..
34391 * @cfg {Number} gutter gutter width..
34396 * @cfg {Number} maxCols maximum number of columns
34402 * @cfg {Boolean} isAutoInitial defalut true
34404 isAutoInitial : true,
34409 * @cfg {Boolean} isHorizontal defalut false
34411 isHorizontal : false,
34413 currentSize : null,
34419 bricks: null, //CompositeElement
34423 _isLayoutInited : false,
34425 // isAlternative : false, // only use for vertical layout...
34428 * @cfg {Number} alternativePadWidth padding below box..
34430 alternativePadWidth : 50,
34432 selectedBrick : [],
34434 getAutoCreate : function(){
34436 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34440 cls: 'blog-masonary-wrapper ' + this.cls,
34442 cls : 'mas-boxes masonary'
34449 getChildContainer: function( )
34451 if (this.boxesEl) {
34452 return this.boxesEl;
34455 this.boxesEl = this.el.select('.mas-boxes').first();
34457 return this.boxesEl;
34461 initEvents : function()
34465 if(this.isAutoInitial){
34466 Roo.log('hook children rendered');
34467 this.on('childrenrendered', function() {
34468 Roo.log('children rendered');
34474 initial : function()
34476 this.selectedBrick = [];
34478 this.currentSize = this.el.getBox(true);
34480 Roo.EventManager.onWindowResize(this.resize, this);
34482 if(!this.isAutoInitial){
34490 //this.layout.defer(500,this);
34494 resize : function()
34496 var cs = this.el.getBox(true);
34499 this.currentSize.width == cs.width &&
34500 this.currentSize.x == cs.x &&
34501 this.currentSize.height == cs.height &&
34502 this.currentSize.y == cs.y
34504 Roo.log("no change in with or X or Y");
34508 this.currentSize = cs;
34514 layout : function()
34516 this._resetLayout();
34518 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34520 this.layoutItems( isInstant );
34522 this._isLayoutInited = true;
34524 this.fireEvent('layout', this);
34528 _resetLayout : function()
34530 if(this.isHorizontal){
34531 this.horizontalMeasureColumns();
34535 this.verticalMeasureColumns();
34539 verticalMeasureColumns : function()
34541 this.getContainerWidth();
34543 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34544 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34548 var boxWidth = this.boxWidth + this.padWidth;
34550 if(this.containerWidth < this.boxWidth){
34551 boxWidth = this.containerWidth
34554 var containerWidth = this.containerWidth;
34556 var cols = Math.floor(containerWidth / boxWidth);
34558 this.cols = Math.max( cols, 1 );
34560 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34562 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34564 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34566 this.colWidth = boxWidth + avail - this.padWidth;
34568 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34569 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34572 horizontalMeasureColumns : function()
34574 this.getContainerWidth();
34576 var boxWidth = this.boxWidth;
34578 if(this.containerWidth < boxWidth){
34579 boxWidth = this.containerWidth;
34582 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34584 this.el.setHeight(boxWidth);
34588 getContainerWidth : function()
34590 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34593 layoutItems : function( isInstant )
34595 Roo.log(this.bricks);
34597 var items = Roo.apply([], this.bricks);
34599 if(this.isHorizontal){
34600 this._horizontalLayoutItems( items , isInstant );
34604 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34605 // this._verticalAlternativeLayoutItems( items , isInstant );
34609 this._verticalLayoutItems( items , isInstant );
34613 _verticalLayoutItems : function ( items , isInstant)
34615 if ( !items || !items.length ) {
34620 ['xs', 'xs', 'xs', 'tall'],
34621 ['xs', 'xs', 'tall'],
34622 ['xs', 'xs', 'sm'],
34623 ['xs', 'xs', 'xs'],
34629 ['sm', 'xs', 'xs'],
34633 ['tall', 'xs', 'xs', 'xs'],
34634 ['tall', 'xs', 'xs'],
34646 Roo.each(items, function(item, k){
34648 switch (item.size) {
34649 // these layouts take up a full box,
34660 boxes.push([item]);
34683 var filterPattern = function(box, length)
34691 var pattern = box.slice(0, length);
34695 Roo.each(pattern, function(i){
34696 format.push(i.size);
34699 Roo.each(standard, function(s){
34701 if(String(s) != String(format)){
34710 if(!match && length == 1){
34715 filterPattern(box, length - 1);
34719 queue.push(pattern);
34721 box = box.slice(length, box.length);
34723 filterPattern(box, 4);
34729 Roo.each(boxes, function(box, k){
34735 if(box.length == 1){
34740 filterPattern(box, 4);
34744 this._processVerticalLayoutQueue( queue, isInstant );
34748 // _verticalAlternativeLayoutItems : function( items , isInstant )
34750 // if ( !items || !items.length ) {
34754 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34758 _horizontalLayoutItems : function ( items , isInstant)
34760 if ( !items || !items.length || items.length < 3) {
34766 var eItems = items.slice(0, 3);
34768 items = items.slice(3, items.length);
34771 ['xs', 'xs', 'xs', 'wide'],
34772 ['xs', 'xs', 'wide'],
34773 ['xs', 'xs', 'sm'],
34774 ['xs', 'xs', 'xs'],
34780 ['sm', 'xs', 'xs'],
34784 ['wide', 'xs', 'xs', 'xs'],
34785 ['wide', 'xs', 'xs'],
34798 Roo.each(items, function(item, k){
34800 switch (item.size) {
34811 boxes.push([item]);
34835 var filterPattern = function(box, length)
34843 var pattern = box.slice(0, length);
34847 Roo.each(pattern, function(i){
34848 format.push(i.size);
34851 Roo.each(standard, function(s){
34853 if(String(s) != String(format)){
34862 if(!match && length == 1){
34867 filterPattern(box, length - 1);
34871 queue.push(pattern);
34873 box = box.slice(length, box.length);
34875 filterPattern(box, 4);
34881 Roo.each(boxes, function(box, k){
34887 if(box.length == 1){
34892 filterPattern(box, 4);
34899 var pos = this.el.getBox(true);
34903 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34905 var hit_end = false;
34907 Roo.each(queue, function(box){
34911 Roo.each(box, function(b){
34913 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34923 Roo.each(box, function(b){
34925 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34928 mx = Math.max(mx, b.x);
34932 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34936 Roo.each(box, function(b){
34938 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34952 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34955 /** Sets position of item in DOM
34956 * @param {Element} item
34957 * @param {Number} x - horizontal position
34958 * @param {Number} y - vertical position
34959 * @param {Boolean} isInstant - disables transitions
34961 _processVerticalLayoutQueue : function( queue, isInstant )
34963 var pos = this.el.getBox(true);
34968 for (var i = 0; i < this.cols; i++){
34972 Roo.each(queue, function(box, k){
34974 var col = k % this.cols;
34976 Roo.each(box, function(b,kk){
34978 b.el.position('absolute');
34980 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34981 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34983 if(b.size == 'md-left' || b.size == 'md-right'){
34984 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34985 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34988 b.el.setWidth(width);
34989 b.el.setHeight(height);
34991 b.el.select('iframe',true).setSize(width,height);
34995 for (var i = 0; i < this.cols; i++){
34997 if(maxY[i] < maxY[col]){
35002 col = Math.min(col, i);
35006 x = pos.x + col * (this.colWidth + this.padWidth);
35010 var positions = [];
35012 switch (box.length){
35014 positions = this.getVerticalOneBoxColPositions(x, y, box);
35017 positions = this.getVerticalTwoBoxColPositions(x, y, box);
35020 positions = this.getVerticalThreeBoxColPositions(x, y, box);
35023 positions = this.getVerticalFourBoxColPositions(x, y, box);
35029 Roo.each(box, function(b,kk){
35031 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35033 var sz = b.el.getSize();
35035 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35043 for (var i = 0; i < this.cols; i++){
35044 mY = Math.max(mY, maxY[i]);
35047 this.el.setHeight(mY - pos.y);
35051 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35053 // var pos = this.el.getBox(true);
35056 // var maxX = pos.right;
35058 // var maxHeight = 0;
35060 // Roo.each(items, function(item, k){
35064 // item.el.position('absolute');
35066 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35068 // item.el.setWidth(width);
35070 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35072 // item.el.setHeight(height);
35075 // item.el.setXY([x, y], isInstant ? false : true);
35077 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35080 // y = y + height + this.alternativePadWidth;
35082 // maxHeight = maxHeight + height + this.alternativePadWidth;
35086 // this.el.setHeight(maxHeight);
35090 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35092 var pos = this.el.getBox(true);
35097 var maxX = pos.right;
35099 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35101 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35103 Roo.each(queue, function(box, k){
35105 Roo.each(box, function(b, kk){
35107 b.el.position('absolute');
35109 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35110 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35112 if(b.size == 'md-left' || b.size == 'md-right'){
35113 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35114 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35117 b.el.setWidth(width);
35118 b.el.setHeight(height);
35126 var positions = [];
35128 switch (box.length){
35130 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35133 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35136 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35139 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35145 Roo.each(box, function(b,kk){
35147 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35149 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35157 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35159 Roo.each(eItems, function(b,k){
35161 b.size = (k == 0) ? 'sm' : 'xs';
35162 b.x = (k == 0) ? 2 : 1;
35163 b.y = (k == 0) ? 2 : 1;
35165 b.el.position('absolute');
35167 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35169 b.el.setWidth(width);
35171 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35173 b.el.setHeight(height);
35177 var positions = [];
35180 x : maxX - this.unitWidth * 2 - this.gutter,
35185 x : maxX - this.unitWidth,
35186 y : minY + (this.unitWidth + this.gutter) * 2
35190 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35194 Roo.each(eItems, function(b,k){
35196 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35202 getVerticalOneBoxColPositions : function(x, y, box)
35206 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35208 if(box[0].size == 'md-left'){
35212 if(box[0].size == 'md-right'){
35217 x : x + (this.unitWidth + this.gutter) * rand,
35224 getVerticalTwoBoxColPositions : function(x, y, box)
35228 if(box[0].size == 'xs'){
35232 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35236 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35250 x : x + (this.unitWidth + this.gutter) * 2,
35251 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35258 getVerticalThreeBoxColPositions : function(x, y, box)
35262 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35270 x : x + (this.unitWidth + this.gutter) * 1,
35275 x : x + (this.unitWidth + this.gutter) * 2,
35283 if(box[0].size == 'xs' && box[1].size == 'xs'){
35292 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35296 x : x + (this.unitWidth + this.gutter) * 1,
35310 x : x + (this.unitWidth + this.gutter) * 2,
35315 x : x + (this.unitWidth + this.gutter) * 2,
35316 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35323 getVerticalFourBoxColPositions : function(x, y, box)
35327 if(box[0].size == 'xs'){
35336 y : y + (this.unitHeight + this.gutter) * 1
35341 y : y + (this.unitHeight + this.gutter) * 2
35345 x : x + (this.unitWidth + this.gutter) * 1,
35359 x : x + (this.unitWidth + this.gutter) * 2,
35364 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35365 y : y + (this.unitHeight + this.gutter) * 1
35369 x : x + (this.unitWidth + this.gutter) * 2,
35370 y : y + (this.unitWidth + this.gutter) * 2
35377 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35381 if(box[0].size == 'md-left'){
35383 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35390 if(box[0].size == 'md-right'){
35392 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35393 y : minY + (this.unitWidth + this.gutter) * 1
35399 var rand = Math.floor(Math.random() * (4 - box[0].y));
35402 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35403 y : minY + (this.unitWidth + this.gutter) * rand
35410 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35414 if(box[0].size == 'xs'){
35417 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35422 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35423 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35431 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35436 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35437 y : minY + (this.unitWidth + this.gutter) * 2
35444 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35448 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35451 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35456 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35457 y : minY + (this.unitWidth + this.gutter) * 1
35461 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35462 y : minY + (this.unitWidth + this.gutter) * 2
35469 if(box[0].size == 'xs' && box[1].size == 'xs'){
35472 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35477 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35482 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35483 y : minY + (this.unitWidth + this.gutter) * 1
35491 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35496 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35497 y : minY + (this.unitWidth + this.gutter) * 2
35501 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35502 y : minY + (this.unitWidth + this.gutter) * 2
35509 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35513 if(box[0].size == 'xs'){
35516 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35521 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35526 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),
35531 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35532 y : minY + (this.unitWidth + this.gutter) * 1
35540 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35545 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35546 y : minY + (this.unitWidth + this.gutter) * 2
35550 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35551 y : minY + (this.unitWidth + this.gutter) * 2
35555 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),
35556 y : minY + (this.unitWidth + this.gutter) * 2
35564 * remove a Masonry Brick
35565 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35567 removeBrick : function(brick_id)
35573 for (var i = 0; i<this.bricks.length; i++) {
35574 if (this.bricks[i].id == brick_id) {
35575 this.bricks.splice(i,1);
35576 this.el.dom.removeChild(Roo.get(brick_id).dom);
35583 * adds a Masonry Brick
35584 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35586 addBrick : function(cfg)
35588 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35589 //this.register(cn);
35590 cn.parentId = this.id;
35591 cn.render(this.el);
35596 * register a Masonry Brick
35597 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35600 register : function(brick)
35602 this.bricks.push(brick);
35603 brick.masonryId = this.id;
35607 * clear all the Masonry Brick
35609 clearAll : function()
35612 //this.getChildContainer().dom.innerHTML = "";
35613 this.el.dom.innerHTML = '';
35616 getSelected : function()
35618 if (!this.selectedBrick) {
35622 return this.selectedBrick;
35626 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35630 * register a Masonry Layout
35631 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35634 register : function(layout)
35636 this.groups[layout.id] = layout;
35639 * fetch a Masonry Layout based on the masonry layout ID
35640 * @param {string} the masonry layout to add
35641 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35644 get: function(layout_id) {
35645 if (typeof(this.groups[layout_id]) == 'undefined') {
35648 return this.groups[layout_id] ;
35660 * http://masonry.desandro.com
35662 * The idea is to render all the bricks based on vertical width...
35664 * The original code extends 'outlayer' - we might need to use that....
35670 * @class Roo.bootstrap.LayoutMasonryAuto
35671 * @extends Roo.bootstrap.Component
35672 * Bootstrap Layout Masonry class
35675 * Create a new Element
35676 * @param {Object} config The config object
35679 Roo.bootstrap.LayoutMasonryAuto = function(config){
35680 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35683 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35686 * @cfg {Boolean} isFitWidth - resize the width..
35688 isFitWidth : false, // options..
35690 * @cfg {Boolean} isOriginLeft = left align?
35692 isOriginLeft : true,
35694 * @cfg {Boolean} isOriginTop = top align?
35696 isOriginTop : false,
35698 * @cfg {Boolean} isLayoutInstant = no animation?
35700 isLayoutInstant : false, // needed?
35702 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35704 isResizingContainer : true,
35706 * @cfg {Number} columnWidth width of the columns
35712 * @cfg {Number} maxCols maximum number of columns
35717 * @cfg {Number} padHeight padding below box..
35723 * @cfg {Boolean} isAutoInitial defalut true
35726 isAutoInitial : true,
35732 initialColumnWidth : 0,
35733 currentSize : null,
35735 colYs : null, // array.
35742 bricks: null, //CompositeElement
35743 cols : 0, // array?
35744 // element : null, // wrapped now this.el
35745 _isLayoutInited : null,
35748 getAutoCreate : function(){
35752 cls: 'blog-masonary-wrapper ' + this.cls,
35754 cls : 'mas-boxes masonary'
35761 getChildContainer: function( )
35763 if (this.boxesEl) {
35764 return this.boxesEl;
35767 this.boxesEl = this.el.select('.mas-boxes').first();
35769 return this.boxesEl;
35773 initEvents : function()
35777 if(this.isAutoInitial){
35778 Roo.log('hook children rendered');
35779 this.on('childrenrendered', function() {
35780 Roo.log('children rendered');
35787 initial : function()
35789 this.reloadItems();
35791 this.currentSize = this.el.getBox(true);
35793 /// was window resize... - let's see if this works..
35794 Roo.EventManager.onWindowResize(this.resize, this);
35796 if(!this.isAutoInitial){
35801 this.layout.defer(500,this);
35804 reloadItems: function()
35806 this.bricks = this.el.select('.masonry-brick', true);
35808 this.bricks.each(function(b) {
35809 //Roo.log(b.getSize());
35810 if (!b.attr('originalwidth')) {
35811 b.attr('originalwidth', b.getSize().width);
35816 Roo.log(this.bricks.elements.length);
35819 resize : function()
35822 var cs = this.el.getBox(true);
35824 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35825 Roo.log("no change in with or X");
35828 this.currentSize = cs;
35832 layout : function()
35835 this._resetLayout();
35836 //this._manageStamps();
35838 // don't animate first layout
35839 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35840 this.layoutItems( isInstant );
35842 // flag for initalized
35843 this._isLayoutInited = true;
35846 layoutItems : function( isInstant )
35848 //var items = this._getItemsForLayout( this.items );
35849 // original code supports filtering layout items.. we just ignore it..
35851 this._layoutItems( this.bricks , isInstant );
35853 this._postLayout();
35855 _layoutItems : function ( items , isInstant)
35857 //this.fireEvent( 'layout', this, items );
35860 if ( !items || !items.elements.length ) {
35861 // no items, emit event with empty array
35866 items.each(function(item) {
35867 Roo.log("layout item");
35869 // get x/y object from method
35870 var position = this._getItemLayoutPosition( item );
35872 position.item = item;
35873 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35874 queue.push( position );
35877 this._processLayoutQueue( queue );
35879 /** Sets position of item in DOM
35880 * @param {Element} item
35881 * @param {Number} x - horizontal position
35882 * @param {Number} y - vertical position
35883 * @param {Boolean} isInstant - disables transitions
35885 _processLayoutQueue : function( queue )
35887 for ( var i=0, len = queue.length; i < len; i++ ) {
35888 var obj = queue[i];
35889 obj.item.position('absolute');
35890 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35896 * Any logic you want to do after each layout,
35897 * i.e. size the container
35899 _postLayout : function()
35901 this.resizeContainer();
35904 resizeContainer : function()
35906 if ( !this.isResizingContainer ) {
35909 var size = this._getContainerSize();
35911 this.el.setSize(size.width,size.height);
35912 this.boxesEl.setSize(size.width,size.height);
35918 _resetLayout : function()
35920 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35921 this.colWidth = this.el.getWidth();
35922 //this.gutter = this.el.getWidth();
35924 this.measureColumns();
35930 this.colYs.push( 0 );
35936 measureColumns : function()
35938 this.getContainerWidth();
35939 // if columnWidth is 0, default to outerWidth of first item
35940 if ( !this.columnWidth ) {
35941 var firstItem = this.bricks.first();
35942 Roo.log(firstItem);
35943 this.columnWidth = this.containerWidth;
35944 if (firstItem && firstItem.attr('originalwidth') ) {
35945 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35947 // columnWidth fall back to item of first element
35948 Roo.log("set column width?");
35949 this.initialColumnWidth = this.columnWidth ;
35951 // if first elem has no width, default to size of container
35956 if (this.initialColumnWidth) {
35957 this.columnWidth = this.initialColumnWidth;
35962 // column width is fixed at the top - however if container width get's smaller we should
35965 // this bit calcs how man columns..
35967 var columnWidth = this.columnWidth += this.gutter;
35969 // calculate columns
35970 var containerWidth = this.containerWidth + this.gutter;
35972 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35973 // fix rounding errors, typically with gutters
35974 var excess = columnWidth - containerWidth % columnWidth;
35977 // if overshoot is less than a pixel, round up, otherwise floor it
35978 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35979 cols = Math[ mathMethod ]( cols );
35980 this.cols = Math.max( cols, 1 );
35981 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35983 // padding positioning..
35984 var totalColWidth = this.cols * this.columnWidth;
35985 var padavail = this.containerWidth - totalColWidth;
35986 // so for 2 columns - we need 3 'pads'
35988 var padNeeded = (1+this.cols) * this.padWidth;
35990 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35992 this.columnWidth += padExtra
35993 //this.padWidth = Math.floor(padavail / ( this.cols));
35995 // adjust colum width so that padding is fixed??
35997 // we have 3 columns ... total = width * 3
35998 // we have X left over... that should be used by
36000 //if (this.expandC) {
36008 getContainerWidth : function()
36010 /* // container is parent if fit width
36011 var container = this.isFitWidth ? this.element.parentNode : this.element;
36012 // check that this.size and size are there
36013 // IE8 triggers resize on body size change, so they might not be
36015 var size = getSize( container ); //FIXME
36016 this.containerWidth = size && size.innerWidth; //FIXME
36019 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36023 _getItemLayoutPosition : function( item ) // what is item?
36025 // we resize the item to our columnWidth..
36027 item.setWidth(this.columnWidth);
36028 item.autoBoxAdjust = false;
36030 var sz = item.getSize();
36032 // how many columns does this brick span
36033 var remainder = this.containerWidth % this.columnWidth;
36035 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36036 // round if off by 1 pixel, otherwise use ceil
36037 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
36038 colSpan = Math.min( colSpan, this.cols );
36040 // normally this should be '1' as we dont' currently allow multi width columns..
36042 var colGroup = this._getColGroup( colSpan );
36043 // get the minimum Y value from the columns
36044 var minimumY = Math.min.apply( Math, colGroup );
36045 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36047 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
36049 // position the brick
36051 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36052 y: this.currentSize.y + minimumY + this.padHeight
36056 // apply setHeight to necessary columns
36057 var setHeight = minimumY + sz.height + this.padHeight;
36058 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36060 var setSpan = this.cols + 1 - colGroup.length;
36061 for ( var i = 0; i < setSpan; i++ ) {
36062 this.colYs[ shortColIndex + i ] = setHeight ;
36069 * @param {Number} colSpan - number of columns the element spans
36070 * @returns {Array} colGroup
36072 _getColGroup : function( colSpan )
36074 if ( colSpan < 2 ) {
36075 // if brick spans only one column, use all the column Ys
36080 // how many different places could this brick fit horizontally
36081 var groupCount = this.cols + 1 - colSpan;
36082 // for each group potential horizontal position
36083 for ( var i = 0; i < groupCount; i++ ) {
36084 // make an array of colY values for that one group
36085 var groupColYs = this.colYs.slice( i, i + colSpan );
36086 // and get the max value of the array
36087 colGroup[i] = Math.max.apply( Math, groupColYs );
36092 _manageStamp : function( stamp )
36094 var stampSize = stamp.getSize();
36095 var offset = stamp.getBox();
36096 // get the columns that this stamp affects
36097 var firstX = this.isOriginLeft ? offset.x : offset.right;
36098 var lastX = firstX + stampSize.width;
36099 var firstCol = Math.floor( firstX / this.columnWidth );
36100 firstCol = Math.max( 0, firstCol );
36102 var lastCol = Math.floor( lastX / this.columnWidth );
36103 // lastCol should not go over if multiple of columnWidth #425
36104 lastCol -= lastX % this.columnWidth ? 0 : 1;
36105 lastCol = Math.min( this.cols - 1, lastCol );
36107 // set colYs to bottom of the stamp
36108 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36111 for ( var i = firstCol; i <= lastCol; i++ ) {
36112 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36117 _getContainerSize : function()
36119 this.maxY = Math.max.apply( Math, this.colYs );
36124 if ( this.isFitWidth ) {
36125 size.width = this._getContainerFitWidth();
36131 _getContainerFitWidth : function()
36133 var unusedCols = 0;
36134 // count unused columns
36137 if ( this.colYs[i] !== 0 ) {
36142 // fit container to columns that have been used
36143 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36146 needsResizeLayout : function()
36148 var previousWidth = this.containerWidth;
36149 this.getContainerWidth();
36150 return previousWidth !== this.containerWidth;
36165 * @class Roo.bootstrap.MasonryBrick
36166 * @extends Roo.bootstrap.Component
36167 * Bootstrap MasonryBrick class
36170 * Create a new MasonryBrick
36171 * @param {Object} config The config object
36174 Roo.bootstrap.MasonryBrick = function(config){
36176 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36178 Roo.bootstrap.MasonryBrick.register(this);
36184 * When a MasonryBrick is clcik
36185 * @param {Roo.bootstrap.MasonryBrick} this
36186 * @param {Roo.EventObject} e
36192 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36195 * @cfg {String} title
36199 * @cfg {String} html
36203 * @cfg {String} bgimage
36207 * @cfg {String} videourl
36211 * @cfg {String} cls
36215 * @cfg {String} href
36219 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36224 * @cfg {String} placetitle (center|bottom)
36229 * @cfg {Boolean} isFitContainer defalut true
36231 isFitContainer : true,
36234 * @cfg {Boolean} preventDefault defalut false
36236 preventDefault : false,
36239 * @cfg {Boolean} inverse defalut false
36241 maskInverse : false,
36243 getAutoCreate : function()
36245 if(!this.isFitContainer){
36246 return this.getSplitAutoCreate();
36249 var cls = 'masonry-brick masonry-brick-full';
36251 if(this.href.length){
36252 cls += ' masonry-brick-link';
36255 if(this.bgimage.length){
36256 cls += ' masonry-brick-image';
36259 if(this.maskInverse){
36260 cls += ' mask-inverse';
36263 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36264 cls += ' enable-mask';
36268 cls += ' masonry-' + this.size + '-brick';
36271 if(this.placetitle.length){
36273 switch (this.placetitle) {
36275 cls += ' masonry-center-title';
36278 cls += ' masonry-bottom-title';
36285 if(!this.html.length && !this.bgimage.length){
36286 cls += ' masonry-center-title';
36289 if(!this.html.length && this.bgimage.length){
36290 cls += ' masonry-bottom-title';
36295 cls += ' ' + this.cls;
36299 tag: (this.href.length) ? 'a' : 'div',
36304 cls: 'masonry-brick-mask'
36308 cls: 'masonry-brick-paragraph',
36314 if(this.href.length){
36315 cfg.href = this.href;
36318 var cn = cfg.cn[1].cn;
36320 if(this.title.length){
36323 cls: 'masonry-brick-title',
36328 if(this.html.length){
36331 cls: 'masonry-brick-text',
36336 if (!this.title.length && !this.html.length) {
36337 cfg.cn[1].cls += ' hide';
36340 if(this.bgimage.length){
36343 cls: 'masonry-brick-image-view',
36348 if(this.videourl.length){
36349 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36350 // youtube support only?
36353 cls: 'masonry-brick-image-view',
36356 allowfullscreen : true
36364 getSplitAutoCreate : function()
36366 var cls = 'masonry-brick masonry-brick-split';
36368 if(this.href.length){
36369 cls += ' masonry-brick-link';
36372 if(this.bgimage.length){
36373 cls += ' masonry-brick-image';
36377 cls += ' masonry-' + this.size + '-brick';
36380 switch (this.placetitle) {
36382 cls += ' masonry-center-title';
36385 cls += ' masonry-bottom-title';
36388 if(!this.bgimage.length){
36389 cls += ' masonry-center-title';
36392 if(this.bgimage.length){
36393 cls += ' masonry-bottom-title';
36399 cls += ' ' + this.cls;
36403 tag: (this.href.length) ? 'a' : 'div',
36408 cls: 'masonry-brick-split-head',
36412 cls: 'masonry-brick-paragraph',
36419 cls: 'masonry-brick-split-body',
36425 if(this.href.length){
36426 cfg.href = this.href;
36429 if(this.title.length){
36430 cfg.cn[0].cn[0].cn.push({
36432 cls: 'masonry-brick-title',
36437 if(this.html.length){
36438 cfg.cn[1].cn.push({
36440 cls: 'masonry-brick-text',
36445 if(this.bgimage.length){
36446 cfg.cn[0].cn.push({
36448 cls: 'masonry-brick-image-view',
36453 if(this.videourl.length){
36454 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36455 // youtube support only?
36456 cfg.cn[0].cn.cn.push({
36458 cls: 'masonry-brick-image-view',
36461 allowfullscreen : true
36468 initEvents: function()
36470 switch (this.size) {
36503 this.el.on('touchstart', this.onTouchStart, this);
36504 this.el.on('touchmove', this.onTouchMove, this);
36505 this.el.on('touchend', this.onTouchEnd, this);
36506 this.el.on('contextmenu', this.onContextMenu, this);
36508 this.el.on('mouseenter' ,this.enter, this);
36509 this.el.on('mouseleave', this.leave, this);
36510 this.el.on('click', this.onClick, this);
36513 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36514 this.parent().bricks.push(this);
36519 onClick: function(e, el)
36521 var time = this.endTimer - this.startTimer;
36522 // Roo.log(e.preventDefault());
36525 e.preventDefault();
36530 if(!this.preventDefault){
36534 e.preventDefault();
36536 if (this.activeClass != '') {
36537 this.selectBrick();
36540 this.fireEvent('click', this, e);
36543 enter: function(e, el)
36545 e.preventDefault();
36547 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36551 if(this.bgimage.length && this.html.length){
36552 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36556 leave: function(e, el)
36558 e.preventDefault();
36560 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36564 if(this.bgimage.length && this.html.length){
36565 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36569 onTouchStart: function(e, el)
36571 // e.preventDefault();
36573 this.touchmoved = false;
36575 if(!this.isFitContainer){
36579 if(!this.bgimage.length || !this.html.length){
36583 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36585 this.timer = new Date().getTime();
36589 onTouchMove: function(e, el)
36591 this.touchmoved = true;
36594 onContextMenu : function(e,el)
36596 e.preventDefault();
36597 e.stopPropagation();
36601 onTouchEnd: function(e, el)
36603 // e.preventDefault();
36605 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36612 if(!this.bgimage.length || !this.html.length){
36614 if(this.href.length){
36615 window.location.href = this.href;
36621 if(!this.isFitContainer){
36625 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36627 window.location.href = this.href;
36630 //selection on single brick only
36631 selectBrick : function() {
36633 if (!this.parentId) {
36637 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36638 var index = m.selectedBrick.indexOf(this.id);
36641 m.selectedBrick.splice(index,1);
36642 this.el.removeClass(this.activeClass);
36646 for(var i = 0; i < m.selectedBrick.length; i++) {
36647 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36648 b.el.removeClass(b.activeClass);
36651 m.selectedBrick = [];
36653 m.selectedBrick.push(this.id);
36654 this.el.addClass(this.activeClass);
36658 isSelected : function(){
36659 return this.el.hasClass(this.activeClass);
36664 Roo.apply(Roo.bootstrap.MasonryBrick, {
36667 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36669 * register a Masonry Brick
36670 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36673 register : function(brick)
36675 //this.groups[brick.id] = brick;
36676 this.groups.add(brick.id, brick);
36679 * fetch a masonry brick based on the masonry brick ID
36680 * @param {string} the masonry brick to add
36681 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36684 get: function(brick_id)
36686 // if (typeof(this.groups[brick_id]) == 'undefined') {
36689 // return this.groups[brick_id] ;
36691 if(this.groups.key(brick_id)) {
36692 return this.groups.key(brick_id);
36710 * @class Roo.bootstrap.Brick
36711 * @extends Roo.bootstrap.Component
36712 * Bootstrap Brick class
36715 * Create a new Brick
36716 * @param {Object} config The config object
36719 Roo.bootstrap.Brick = function(config){
36720 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36726 * When a Brick is click
36727 * @param {Roo.bootstrap.Brick} this
36728 * @param {Roo.EventObject} e
36734 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36737 * @cfg {String} title
36741 * @cfg {String} html
36745 * @cfg {String} bgimage
36749 * @cfg {String} cls
36753 * @cfg {String} href
36757 * @cfg {String} video
36761 * @cfg {Boolean} square
36765 getAutoCreate : function()
36767 var cls = 'roo-brick';
36769 if(this.href.length){
36770 cls += ' roo-brick-link';
36773 if(this.bgimage.length){
36774 cls += ' roo-brick-image';
36777 if(!this.html.length && !this.bgimage.length){
36778 cls += ' roo-brick-center-title';
36781 if(!this.html.length && this.bgimage.length){
36782 cls += ' roo-brick-bottom-title';
36786 cls += ' ' + this.cls;
36790 tag: (this.href.length) ? 'a' : 'div',
36795 cls: 'roo-brick-paragraph',
36801 if(this.href.length){
36802 cfg.href = this.href;
36805 var cn = cfg.cn[0].cn;
36807 if(this.title.length){
36810 cls: 'roo-brick-title',
36815 if(this.html.length){
36818 cls: 'roo-brick-text',
36825 if(this.bgimage.length){
36828 cls: 'roo-brick-image-view',
36836 initEvents: function()
36838 if(this.title.length || this.html.length){
36839 this.el.on('mouseenter' ,this.enter, this);
36840 this.el.on('mouseleave', this.leave, this);
36843 Roo.EventManager.onWindowResize(this.resize, this);
36845 if(this.bgimage.length){
36846 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36847 this.imageEl.on('load', this.onImageLoad, this);
36854 onImageLoad : function()
36859 resize : function()
36861 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36863 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36865 if(this.bgimage.length){
36866 var image = this.el.select('.roo-brick-image-view', true).first();
36868 image.setWidth(paragraph.getWidth());
36871 image.setHeight(paragraph.getWidth());
36874 this.el.setHeight(image.getHeight());
36875 paragraph.setHeight(image.getHeight());
36881 enter: function(e, el)
36883 e.preventDefault();
36885 if(this.bgimage.length){
36886 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36887 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36891 leave: function(e, el)
36893 e.preventDefault();
36895 if(this.bgimage.length){
36896 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36897 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36912 * @class Roo.bootstrap.NumberField
36913 * @extends Roo.bootstrap.Input
36914 * Bootstrap NumberField class
36920 * Create a new NumberField
36921 * @param {Object} config The config object
36924 Roo.bootstrap.NumberField = function(config){
36925 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36928 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36931 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36933 allowDecimals : true,
36935 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36937 decimalSeparator : ".",
36939 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36941 decimalPrecision : 2,
36943 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36945 allowNegative : true,
36948 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36952 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36954 minValue : Number.NEGATIVE_INFINITY,
36956 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36958 maxValue : Number.MAX_VALUE,
36960 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36962 minText : "The minimum value for this field is {0}",
36964 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36966 maxText : "The maximum value for this field is {0}",
36968 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36969 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36971 nanText : "{0} is not a valid number",
36973 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36975 thousandsDelimiter : false,
36977 * @cfg {String} valueAlign alignment of value
36979 valueAlign : "left",
36981 getAutoCreate : function()
36983 var hiddenInput = {
36987 cls: 'hidden-number-input'
36991 hiddenInput.name = this.name;
36996 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36998 this.name = hiddenInput.name;
37000 if(cfg.cn.length > 0) {
37001 cfg.cn.push(hiddenInput);
37008 initEvents : function()
37010 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37012 var allowed = "0123456789";
37014 if(this.allowDecimals){
37015 allowed += this.decimalSeparator;
37018 if(this.allowNegative){
37022 if(this.thousandsDelimiter) {
37026 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37028 var keyPress = function(e){
37030 var k = e.getKey();
37032 var c = e.getCharCode();
37035 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37036 allowed.indexOf(String.fromCharCode(c)) === -1
37042 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37046 if(allowed.indexOf(String.fromCharCode(c)) === -1){
37051 this.el.on("keypress", keyPress, this);
37054 validateValue : function(value)
37057 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37061 var num = this.parseValue(value);
37064 this.markInvalid(String.format(this.nanText, value));
37068 if(num < this.minValue){
37069 this.markInvalid(String.format(this.minText, this.minValue));
37073 if(num > this.maxValue){
37074 this.markInvalid(String.format(this.maxText, this.maxValue));
37081 getValue : function()
37083 var v = this.hiddenEl().getValue();
37085 return this.fixPrecision(this.parseValue(v));
37088 parseValue : function(value)
37090 if(this.thousandsDelimiter) {
37092 r = new RegExp(",", "g");
37093 value = value.replace(r, "");
37096 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37097 return isNaN(value) ? '' : value;
37100 fixPrecision : function(value)
37102 if(this.thousandsDelimiter) {
37104 r = new RegExp(",", "g");
37105 value = value.replace(r, "");
37108 var nan = isNaN(value);
37110 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37111 return nan ? '' : value;
37113 return parseFloat(value).toFixed(this.decimalPrecision);
37116 setValue : function(v)
37118 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37124 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37126 this.inputEl().dom.value = (v == '') ? '' :
37127 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37129 if(!this.allowZero && v === '0') {
37130 this.hiddenEl().dom.value = '';
37131 this.inputEl().dom.value = '';
37138 decimalPrecisionFcn : function(v)
37140 return Math.floor(v);
37143 beforeBlur : function()
37145 var v = this.parseValue(this.getRawValue());
37147 if(v || v === 0 || v === ''){
37152 hiddenEl : function()
37154 return this.el.select('input.hidden-number-input',true).first();
37166 * @class Roo.bootstrap.DocumentSlider
37167 * @extends Roo.bootstrap.Component
37168 * Bootstrap DocumentSlider class
37171 * Create a new DocumentViewer
37172 * @param {Object} config The config object
37175 Roo.bootstrap.DocumentSlider = function(config){
37176 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37183 * Fire after initEvent
37184 * @param {Roo.bootstrap.DocumentSlider} this
37189 * Fire after update
37190 * @param {Roo.bootstrap.DocumentSlider} this
37196 * @param {Roo.bootstrap.DocumentSlider} this
37202 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37208 getAutoCreate : function()
37212 cls : 'roo-document-slider',
37216 cls : 'roo-document-slider-header',
37220 cls : 'roo-document-slider-header-title'
37226 cls : 'roo-document-slider-body',
37230 cls : 'roo-document-slider-prev',
37234 cls : 'fa fa-chevron-left'
37240 cls : 'roo-document-slider-thumb',
37244 cls : 'roo-document-slider-image'
37250 cls : 'roo-document-slider-next',
37254 cls : 'fa fa-chevron-right'
37266 initEvents : function()
37268 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37269 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37271 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37272 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37274 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37275 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37277 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37278 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37280 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37281 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37283 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37284 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37286 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37287 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37289 this.thumbEl.on('click', this.onClick, this);
37291 this.prevIndicator.on('click', this.prev, this);
37293 this.nextIndicator.on('click', this.next, this);
37297 initial : function()
37299 if(this.files.length){
37300 this.indicator = 1;
37304 this.fireEvent('initial', this);
37307 update : function()
37309 this.imageEl.attr('src', this.files[this.indicator - 1]);
37311 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37313 this.prevIndicator.show();
37315 if(this.indicator == 1){
37316 this.prevIndicator.hide();
37319 this.nextIndicator.show();
37321 if(this.indicator == this.files.length){
37322 this.nextIndicator.hide();
37325 this.thumbEl.scrollTo('top');
37327 this.fireEvent('update', this);
37330 onClick : function(e)
37332 e.preventDefault();
37334 this.fireEvent('click', this);
37339 e.preventDefault();
37341 this.indicator = Math.max(1, this.indicator - 1);
37348 e.preventDefault();
37350 this.indicator = Math.min(this.files.length, this.indicator + 1);
37364 * @class Roo.bootstrap.RadioSet
37365 * @extends Roo.bootstrap.Input
37366 * Bootstrap RadioSet class
37367 * @cfg {String} indicatorpos (left|right) default left
37368 * @cfg {Boolean} inline (true|false) inline the element (default true)
37369 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37371 * Create a new RadioSet
37372 * @param {Object} config The config object
37375 Roo.bootstrap.RadioSet = function(config){
37377 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37381 Roo.bootstrap.RadioSet.register(this);
37386 * Fires when the element is checked or unchecked.
37387 * @param {Roo.bootstrap.RadioSet} this This radio
37388 * @param {Roo.bootstrap.Radio} item The checked item
37393 * Fires when the element is click.
37394 * @param {Roo.bootstrap.RadioSet} this This radio set
37395 * @param {Roo.bootstrap.Radio} item The checked item
37396 * @param {Roo.EventObject} e The event object
37403 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37411 indicatorpos : 'left',
37413 getAutoCreate : function()
37417 cls : 'roo-radio-set-label',
37421 html : this.fieldLabel
37425 if (Roo.bootstrap.version == 3) {
37428 if(this.indicatorpos == 'left'){
37431 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37432 tooltip : 'This field is required'
37437 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37438 tooltip : 'This field is required'
37444 cls : 'roo-radio-set-items'
37447 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37449 if (align === 'left' && this.fieldLabel.length) {
37452 cls : "roo-radio-set-right",
37458 if(this.labelWidth > 12){
37459 label.style = "width: " + this.labelWidth + 'px';
37462 if(this.labelWidth < 13 && this.labelmd == 0){
37463 this.labelmd = this.labelWidth;
37466 if(this.labellg > 0){
37467 label.cls += ' col-lg-' + this.labellg;
37468 items.cls += ' col-lg-' + (12 - this.labellg);
37471 if(this.labelmd > 0){
37472 label.cls += ' col-md-' + this.labelmd;
37473 items.cls += ' col-md-' + (12 - this.labelmd);
37476 if(this.labelsm > 0){
37477 label.cls += ' col-sm-' + this.labelsm;
37478 items.cls += ' col-sm-' + (12 - this.labelsm);
37481 if(this.labelxs > 0){
37482 label.cls += ' col-xs-' + this.labelxs;
37483 items.cls += ' col-xs-' + (12 - this.labelxs);
37489 cls : 'roo-radio-set',
37493 cls : 'roo-radio-set-input',
37496 value : this.value ? this.value : ''
37503 if(this.weight.length){
37504 cfg.cls += ' roo-radio-' + this.weight;
37508 cfg.cls += ' roo-radio-set-inline';
37512 ['xs','sm','md','lg'].map(function(size){
37513 if (settings[size]) {
37514 cfg.cls += ' col-' + size + '-' + settings[size];
37522 initEvents : function()
37524 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37525 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37527 if(!this.fieldLabel.length){
37528 this.labelEl.hide();
37531 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37532 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37534 this.indicator = this.indicatorEl();
37536 if(this.indicator){
37537 this.indicator.addClass('invisible');
37540 this.originalValue = this.getValue();
37544 inputEl: function ()
37546 return this.el.select('.roo-radio-set-input', true).first();
37549 getChildContainer : function()
37551 return this.itemsEl;
37554 register : function(item)
37556 this.radioes.push(item);
37560 validate : function()
37562 if(this.getVisibilityEl().hasClass('hidden')){
37568 Roo.each(this.radioes, function(i){
37577 if(this.allowBlank) {
37581 if(this.disabled || valid){
37586 this.markInvalid();
37591 markValid : function()
37593 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37594 this.indicatorEl().removeClass('visible');
37595 this.indicatorEl().addClass('invisible');
37599 if (Roo.bootstrap.version == 3) {
37600 this.el.removeClass([this.invalidClass, this.validClass]);
37601 this.el.addClass(this.validClass);
37603 this.el.removeClass(['is-invalid','is-valid']);
37604 this.el.addClass(['is-valid']);
37606 this.fireEvent('valid', this);
37609 markInvalid : function(msg)
37611 if(this.allowBlank || this.disabled){
37615 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37616 this.indicatorEl().removeClass('invisible');
37617 this.indicatorEl().addClass('visible');
37619 if (Roo.bootstrap.version == 3) {
37620 this.el.removeClass([this.invalidClass, this.validClass]);
37621 this.el.addClass(this.invalidClass);
37623 this.el.removeClass(['is-invalid','is-valid']);
37624 this.el.addClass(['is-invalid']);
37627 this.fireEvent('invalid', this, msg);
37631 setValue : function(v, suppressEvent)
37633 if(this.value === v){
37640 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37643 Roo.each(this.radioes, function(i){
37645 i.el.removeClass('checked');
37648 Roo.each(this.radioes, function(i){
37650 if(i.value === v || i.value.toString() === v.toString()){
37652 i.el.addClass('checked');
37654 if(suppressEvent !== true){
37655 this.fireEvent('check', this, i);
37666 clearInvalid : function(){
37668 if(!this.el || this.preventMark){
37672 this.el.removeClass([this.invalidClass]);
37674 this.fireEvent('valid', this);
37679 Roo.apply(Roo.bootstrap.RadioSet, {
37683 register : function(set)
37685 this.groups[set.name] = set;
37688 get: function(name)
37690 if (typeof(this.groups[name]) == 'undefined') {
37694 return this.groups[name] ;
37700 * Ext JS Library 1.1.1
37701 * Copyright(c) 2006-2007, Ext JS, LLC.
37703 * Originally Released Under LGPL - original licence link has changed is not relivant.
37706 * <script type="text/javascript">
37711 * @class Roo.bootstrap.SplitBar
37712 * @extends Roo.util.Observable
37713 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37717 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37718 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37719 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37720 split.minSize = 100;
37721 split.maxSize = 600;
37722 split.animate = true;
37723 split.on('moved', splitterMoved);
37726 * Create a new SplitBar
37727 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37728 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37729 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37730 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37731 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37732 position of the SplitBar).
37734 Roo.bootstrap.SplitBar = function(cfg){
37739 // dragElement : elm
37740 // resizingElement: el,
37742 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37743 // placement : Roo.bootstrap.SplitBar.LEFT ,
37744 // existingProxy ???
37747 this.el = Roo.get(cfg.dragElement, true);
37748 this.el.dom.unselectable = "on";
37750 this.resizingEl = Roo.get(cfg.resizingElement, true);
37754 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37755 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37758 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37761 * The minimum size of the resizing element. (Defaults to 0)
37767 * The maximum size of the resizing element. (Defaults to 2000)
37770 this.maxSize = 2000;
37773 * Whether to animate the transition to the new size
37776 this.animate = false;
37779 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37782 this.useShim = false;
37787 if(!cfg.existingProxy){
37789 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37791 this.proxy = Roo.get(cfg.existingProxy).dom;
37794 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37797 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37800 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37803 this.dragSpecs = {};
37806 * @private The adapter to use to positon and resize elements
37808 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37809 this.adapter.init(this);
37811 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37813 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37814 this.el.addClass("roo-splitbar-h");
37817 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37818 this.el.addClass("roo-splitbar-v");
37824 * Fires when the splitter is moved (alias for {@link #event-moved})
37825 * @param {Roo.bootstrap.SplitBar} this
37826 * @param {Number} newSize the new width or height
37831 * Fires when the splitter is moved
37832 * @param {Roo.bootstrap.SplitBar} this
37833 * @param {Number} newSize the new width or height
37837 * @event beforeresize
37838 * Fires before the splitter is dragged
37839 * @param {Roo.bootstrap.SplitBar} this
37841 "beforeresize" : true,
37843 "beforeapply" : true
37846 Roo.util.Observable.call(this);
37849 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37850 onStartProxyDrag : function(x, y){
37851 this.fireEvent("beforeresize", this);
37853 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37855 o.enableDisplayMode("block");
37856 // all splitbars share the same overlay
37857 Roo.bootstrap.SplitBar.prototype.overlay = o;
37859 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37860 this.overlay.show();
37861 Roo.get(this.proxy).setDisplayed("block");
37862 var size = this.adapter.getElementSize(this);
37863 this.activeMinSize = this.getMinimumSize();;
37864 this.activeMaxSize = this.getMaximumSize();;
37865 var c1 = size - this.activeMinSize;
37866 var c2 = Math.max(this.activeMaxSize - size, 0);
37867 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37868 this.dd.resetConstraints();
37869 this.dd.setXConstraint(
37870 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37871 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37873 this.dd.setYConstraint(0, 0);
37875 this.dd.resetConstraints();
37876 this.dd.setXConstraint(0, 0);
37877 this.dd.setYConstraint(
37878 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37879 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37882 this.dragSpecs.startSize = size;
37883 this.dragSpecs.startPoint = [x, y];
37884 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37888 * @private Called after the drag operation by the DDProxy
37890 onEndProxyDrag : function(e){
37891 Roo.get(this.proxy).setDisplayed(false);
37892 var endPoint = Roo.lib.Event.getXY(e);
37894 this.overlay.hide();
37897 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37898 newSize = this.dragSpecs.startSize +
37899 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37900 endPoint[0] - this.dragSpecs.startPoint[0] :
37901 this.dragSpecs.startPoint[0] - endPoint[0]
37904 newSize = this.dragSpecs.startSize +
37905 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37906 endPoint[1] - this.dragSpecs.startPoint[1] :
37907 this.dragSpecs.startPoint[1] - endPoint[1]
37910 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37911 if(newSize != this.dragSpecs.startSize){
37912 if(this.fireEvent('beforeapply', this, newSize) !== false){
37913 this.adapter.setElementSize(this, newSize);
37914 this.fireEvent("moved", this, newSize);
37915 this.fireEvent("resize", this, newSize);
37921 * Get the adapter this SplitBar uses
37922 * @return The adapter object
37924 getAdapter : function(){
37925 return this.adapter;
37929 * Set the adapter this SplitBar uses
37930 * @param {Object} adapter A SplitBar adapter object
37932 setAdapter : function(adapter){
37933 this.adapter = adapter;
37934 this.adapter.init(this);
37938 * Gets the minimum size for the resizing element
37939 * @return {Number} The minimum size
37941 getMinimumSize : function(){
37942 return this.minSize;
37946 * Sets the minimum size for the resizing element
37947 * @param {Number} minSize The minimum size
37949 setMinimumSize : function(minSize){
37950 this.minSize = minSize;
37954 * Gets the maximum size for the resizing element
37955 * @return {Number} The maximum size
37957 getMaximumSize : function(){
37958 return this.maxSize;
37962 * Sets the maximum size for the resizing element
37963 * @param {Number} maxSize The maximum size
37965 setMaximumSize : function(maxSize){
37966 this.maxSize = maxSize;
37970 * Sets the initialize size for the resizing element
37971 * @param {Number} size The initial size
37973 setCurrentSize : function(size){
37974 var oldAnimate = this.animate;
37975 this.animate = false;
37976 this.adapter.setElementSize(this, size);
37977 this.animate = oldAnimate;
37981 * Destroy this splitbar.
37982 * @param {Boolean} removeEl True to remove the element
37984 destroy : function(removeEl){
37986 this.shim.remove();
37989 this.proxy.parentNode.removeChild(this.proxy);
37997 * @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.
37999 Roo.bootstrap.SplitBar.createProxy = function(dir){
38000 var proxy = new Roo.Element(document.createElement("div"));
38001 proxy.unselectable();
38002 var cls = 'roo-splitbar-proxy';
38003 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38004 document.body.appendChild(proxy.dom);
38009 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38010 * Default Adapter. It assumes the splitter and resizing element are not positioned
38011 * elements and only gets/sets the width of the element. Generally used for table based layouts.
38013 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38016 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38017 // do nothing for now
38018 init : function(s){
38022 * Called before drag operations to get the current size of the resizing element.
38023 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38025 getElementSize : function(s){
38026 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38027 return s.resizingEl.getWidth();
38029 return s.resizingEl.getHeight();
38034 * Called after drag operations to set the size of the resizing element.
38035 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38036 * @param {Number} newSize The new size to set
38037 * @param {Function} onComplete A function to be invoked when resizing is complete
38039 setElementSize : function(s, newSize, onComplete){
38040 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38042 s.resizingEl.setWidth(newSize);
38044 onComplete(s, newSize);
38047 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38052 s.resizingEl.setHeight(newSize);
38054 onComplete(s, newSize);
38057 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38064 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38065 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38066 * Adapter that moves the splitter element to align with the resized sizing element.
38067 * Used with an absolute positioned SplitBar.
38068 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38069 * document.body, make sure you assign an id to the body element.
38071 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38072 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38073 this.container = Roo.get(container);
38076 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38077 init : function(s){
38078 this.basic.init(s);
38081 getElementSize : function(s){
38082 return this.basic.getElementSize(s);
38085 setElementSize : function(s, newSize, onComplete){
38086 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38089 moveSplitter : function(s){
38090 var yes = Roo.bootstrap.SplitBar;
38091 switch(s.placement){
38093 s.el.setX(s.resizingEl.getRight());
38096 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38099 s.el.setY(s.resizingEl.getBottom());
38102 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38109 * Orientation constant - Create a vertical SplitBar
38113 Roo.bootstrap.SplitBar.VERTICAL = 1;
38116 * Orientation constant - Create a horizontal SplitBar
38120 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38123 * Placement constant - The resizing element is to the left of the splitter element
38127 Roo.bootstrap.SplitBar.LEFT = 1;
38130 * Placement constant - The resizing element is to the right of the splitter element
38134 Roo.bootstrap.SplitBar.RIGHT = 2;
38137 * Placement constant - The resizing element is positioned above the splitter element
38141 Roo.bootstrap.SplitBar.TOP = 3;
38144 * Placement constant - The resizing element is positioned under splitter element
38148 Roo.bootstrap.SplitBar.BOTTOM = 4;
38149 Roo.namespace("Roo.bootstrap.layout");/*
38151 * Ext JS Library 1.1.1
38152 * Copyright(c) 2006-2007, Ext JS, LLC.
38154 * Originally Released Under LGPL - original licence link has changed is not relivant.
38157 * <script type="text/javascript">
38161 * @class Roo.bootstrap.layout.Manager
38162 * @extends Roo.bootstrap.Component
38163 * Base class for layout managers.
38165 Roo.bootstrap.layout.Manager = function(config)
38167 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38173 /** false to disable window resize monitoring @type Boolean */
38174 this.monitorWindowResize = true;
38179 * Fires when a layout is performed.
38180 * @param {Roo.LayoutManager} this
38184 * @event regionresized
38185 * Fires when the user resizes a region.
38186 * @param {Roo.LayoutRegion} region The resized region
38187 * @param {Number} newSize The new size (width for east/west, height for north/south)
38189 "regionresized" : true,
38191 * @event regioncollapsed
38192 * Fires when a region is collapsed.
38193 * @param {Roo.LayoutRegion} region The collapsed region
38195 "regioncollapsed" : true,
38197 * @event regionexpanded
38198 * Fires when a region is expanded.
38199 * @param {Roo.LayoutRegion} region The expanded region
38201 "regionexpanded" : true
38203 this.updating = false;
38206 this.el = Roo.get(config.el);
38212 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38217 monitorWindowResize : true,
38223 onRender : function(ct, position)
38226 this.el = Roo.get(ct);
38229 //this.fireEvent('render',this);
38233 initEvents: function()
38237 // ie scrollbar fix
38238 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38239 document.body.scroll = "no";
38240 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38241 this.el.position('relative');
38243 this.id = this.el.id;
38244 this.el.addClass("roo-layout-container");
38245 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38246 if(this.el.dom != document.body ) {
38247 this.el.on('resize', this.layout,this);
38248 this.el.on('show', this.layout,this);
38254 * Returns true if this layout is currently being updated
38255 * @return {Boolean}
38257 isUpdating : function(){
38258 return this.updating;
38262 * Suspend the LayoutManager from doing auto-layouts while
38263 * making multiple add or remove calls
38265 beginUpdate : function(){
38266 this.updating = true;
38270 * Restore auto-layouts and optionally disable the manager from performing a layout
38271 * @param {Boolean} noLayout true to disable a layout update
38273 endUpdate : function(noLayout){
38274 this.updating = false;
38280 layout: function(){
38284 onRegionResized : function(region, newSize){
38285 this.fireEvent("regionresized", region, newSize);
38289 onRegionCollapsed : function(region){
38290 this.fireEvent("regioncollapsed", region);
38293 onRegionExpanded : function(region){
38294 this.fireEvent("regionexpanded", region);
38298 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38299 * performs box-model adjustments.
38300 * @return {Object} The size as an object {width: (the width), height: (the height)}
38302 getViewSize : function()
38305 if(this.el.dom != document.body){
38306 size = this.el.getSize();
38308 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38310 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38311 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38316 * Returns the Element this layout is bound to.
38317 * @return {Roo.Element}
38319 getEl : function(){
38324 * Returns the specified region.
38325 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38326 * @return {Roo.LayoutRegion}
38328 getRegion : function(target){
38329 return this.regions[target.toLowerCase()];
38332 onWindowResize : function(){
38333 if(this.monitorWindowResize){
38340 * Ext JS Library 1.1.1
38341 * Copyright(c) 2006-2007, Ext JS, LLC.
38343 * Originally Released Under LGPL - original licence link has changed is not relivant.
38346 * <script type="text/javascript">
38349 * @class Roo.bootstrap.layout.Border
38350 * @extends Roo.bootstrap.layout.Manager
38351 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38352 * please see: examples/bootstrap/nested.html<br><br>
38354 <b>The container the layout is rendered into can be either the body element or any other element.
38355 If it is not the body element, the container needs to either be an absolute positioned element,
38356 or you will need to add "position:relative" to the css of the container. You will also need to specify
38357 the container size if it is not the body element.</b>
38360 * Create a new Border
38361 * @param {Object} config Configuration options
38363 Roo.bootstrap.layout.Border = function(config){
38364 config = config || {};
38365 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38369 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38370 if(config[region]){
38371 config[region].region = region;
38372 this.addRegion(config[region]);
38378 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38380 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38382 parent : false, // this might point to a 'nest' or a ???
38385 * Creates and adds a new region if it doesn't already exist.
38386 * @param {String} target The target region key (north, south, east, west or center).
38387 * @param {Object} config The regions config object
38388 * @return {BorderLayoutRegion} The new region
38390 addRegion : function(config)
38392 if(!this.regions[config.region]){
38393 var r = this.factory(config);
38394 this.bindRegion(r);
38396 return this.regions[config.region];
38400 bindRegion : function(r){
38401 this.regions[r.config.region] = r;
38403 r.on("visibilitychange", this.layout, this);
38404 r.on("paneladded", this.layout, this);
38405 r.on("panelremoved", this.layout, this);
38406 r.on("invalidated", this.layout, this);
38407 r.on("resized", this.onRegionResized, this);
38408 r.on("collapsed", this.onRegionCollapsed, this);
38409 r.on("expanded", this.onRegionExpanded, this);
38413 * Performs a layout update.
38415 layout : function()
38417 if(this.updating) {
38421 // render all the rebions if they have not been done alreayd?
38422 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38423 if(this.regions[region] && !this.regions[region].bodyEl){
38424 this.regions[region].onRender(this.el)
38428 var size = this.getViewSize();
38429 var w = size.width;
38430 var h = size.height;
38435 //var x = 0, y = 0;
38437 var rs = this.regions;
38438 var north = rs["north"];
38439 var south = rs["south"];
38440 var west = rs["west"];
38441 var east = rs["east"];
38442 var center = rs["center"];
38443 //if(this.hideOnLayout){ // not supported anymore
38444 //c.el.setStyle("display", "none");
38446 if(north && north.isVisible()){
38447 var b = north.getBox();
38448 var m = north.getMargins();
38449 b.width = w - (m.left+m.right);
38452 centerY = b.height + b.y + m.bottom;
38453 centerH -= centerY;
38454 north.updateBox(this.safeBox(b));
38456 if(south && south.isVisible()){
38457 var b = south.getBox();
38458 var m = south.getMargins();
38459 b.width = w - (m.left+m.right);
38461 var totalHeight = (b.height + m.top + m.bottom);
38462 b.y = h - totalHeight + m.top;
38463 centerH -= totalHeight;
38464 south.updateBox(this.safeBox(b));
38466 if(west && west.isVisible()){
38467 var b = west.getBox();
38468 var m = west.getMargins();
38469 b.height = centerH - (m.top+m.bottom);
38471 b.y = centerY + m.top;
38472 var totalWidth = (b.width + m.left + m.right);
38473 centerX += totalWidth;
38474 centerW -= totalWidth;
38475 west.updateBox(this.safeBox(b));
38477 if(east && east.isVisible()){
38478 var b = east.getBox();
38479 var m = east.getMargins();
38480 b.height = centerH - (m.top+m.bottom);
38481 var totalWidth = (b.width + m.left + m.right);
38482 b.x = w - totalWidth + m.left;
38483 b.y = centerY + m.top;
38484 centerW -= totalWidth;
38485 east.updateBox(this.safeBox(b));
38488 var m = center.getMargins();
38490 x: centerX + m.left,
38491 y: centerY + m.top,
38492 width: centerW - (m.left+m.right),
38493 height: centerH - (m.top+m.bottom)
38495 //if(this.hideOnLayout){
38496 //center.el.setStyle("display", "block");
38498 center.updateBox(this.safeBox(centerBox));
38501 this.fireEvent("layout", this);
38505 safeBox : function(box){
38506 box.width = Math.max(0, box.width);
38507 box.height = Math.max(0, box.height);
38512 * Adds a ContentPanel (or subclass) to this layout.
38513 * @param {String} target The target region key (north, south, east, west or center).
38514 * @param {Roo.ContentPanel} panel The panel to add
38515 * @return {Roo.ContentPanel} The added panel
38517 add : function(target, panel){
38519 target = target.toLowerCase();
38520 return this.regions[target].add(panel);
38524 * Remove a ContentPanel (or subclass) to this layout.
38525 * @param {String} target The target region key (north, south, east, west or center).
38526 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38527 * @return {Roo.ContentPanel} The removed panel
38529 remove : function(target, panel){
38530 target = target.toLowerCase();
38531 return this.regions[target].remove(panel);
38535 * Searches all regions for a panel with the specified id
38536 * @param {String} panelId
38537 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38539 findPanel : function(panelId){
38540 var rs = this.regions;
38541 for(var target in rs){
38542 if(typeof rs[target] != "function"){
38543 var p = rs[target].getPanel(panelId);
38553 * Searches all regions for a panel with the specified id and activates (shows) it.
38554 * @param {String/ContentPanel} panelId The panels id or the panel itself
38555 * @return {Roo.ContentPanel} The shown panel or null
38557 showPanel : function(panelId) {
38558 var rs = this.regions;
38559 for(var target in rs){
38560 var r = rs[target];
38561 if(typeof r != "function"){
38562 if(r.hasPanel(panelId)){
38563 return r.showPanel(panelId);
38571 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38572 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38575 restoreState : function(provider){
38577 provider = Roo.state.Manager;
38579 var sm = new Roo.LayoutStateManager();
38580 sm.init(this, provider);
38586 * Adds a xtype elements to the layout.
38590 xtype : 'ContentPanel',
38597 xtype : 'NestedLayoutPanel',
38603 items : [ ... list of content panels or nested layout panels.. ]
38607 * @param {Object} cfg Xtype definition of item to add.
38609 addxtype : function(cfg)
38611 // basically accepts a pannel...
38612 // can accept a layout region..!?!?
38613 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38616 // theory? children can only be panels??
38618 //if (!cfg.xtype.match(/Panel$/)) {
38623 if (typeof(cfg.region) == 'undefined') {
38624 Roo.log("Failed to add Panel, region was not set");
38628 var region = cfg.region;
38634 xitems = cfg.items;
38639 if ( region == 'center') {
38640 Roo.log("Center: " + cfg.title);
38646 case 'Content': // ContentPanel (el, cfg)
38647 case 'Scroll': // ContentPanel (el, cfg)
38649 cfg.autoCreate = cfg.autoCreate || true;
38650 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38652 // var el = this.el.createChild();
38653 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38656 this.add(region, ret);
38660 case 'TreePanel': // our new panel!
38661 cfg.el = this.el.createChild();
38662 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38663 this.add(region, ret);
38668 // create a new Layout (which is a Border Layout...
38670 var clayout = cfg.layout;
38671 clayout.el = this.el.createChild();
38672 clayout.items = clayout.items || [];
38676 // replace this exitems with the clayout ones..
38677 xitems = clayout.items;
38679 // force background off if it's in center...
38680 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38681 cfg.background = false;
38683 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38686 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38687 //console.log('adding nested layout panel ' + cfg.toSource());
38688 this.add(region, ret);
38689 nb = {}; /// find first...
38694 // needs grid and region
38696 //var el = this.getRegion(region).el.createChild();
38698 *var el = this.el.createChild();
38699 // create the grid first...
38700 cfg.grid.container = el;
38701 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38704 if (region == 'center' && this.active ) {
38705 cfg.background = false;
38708 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38710 this.add(region, ret);
38712 if (cfg.background) {
38713 // render grid on panel activation (if panel background)
38714 ret.on('activate', function(gp) {
38715 if (!gp.grid.rendered) {
38716 // gp.grid.render(el);
38720 // cfg.grid.render(el);
38726 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38727 // it was the old xcomponent building that caused this before.
38728 // espeically if border is the top element in the tree.
38738 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38740 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38741 this.add(region, ret);
38745 throw "Can not add '" + cfg.xtype + "' to Border";
38751 this.beginUpdate();
38755 Roo.each(xitems, function(i) {
38756 region = nb && i.region ? i.region : false;
38758 var add = ret.addxtype(i);
38761 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38762 if (!i.background) {
38763 abn[region] = nb[region] ;
38770 // make the last non-background panel active..
38771 //if (nb) { Roo.log(abn); }
38774 for(var r in abn) {
38775 region = this.getRegion(r);
38777 // tried using nb[r], but it does not work..
38779 region.showPanel(abn[r]);
38790 factory : function(cfg)
38793 var validRegions = Roo.bootstrap.layout.Border.regions;
38795 var target = cfg.region;
38798 var r = Roo.bootstrap.layout;
38802 return new r.North(cfg);
38804 return new r.South(cfg);
38806 return new r.East(cfg);
38808 return new r.West(cfg);
38810 return new r.Center(cfg);
38812 throw 'Layout region "'+target+'" not supported.';
38819 * Ext JS Library 1.1.1
38820 * Copyright(c) 2006-2007, Ext JS, LLC.
38822 * Originally Released Under LGPL - original licence link has changed is not relivant.
38825 * <script type="text/javascript">
38829 * @class Roo.bootstrap.layout.Basic
38830 * @extends Roo.util.Observable
38831 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38832 * and does not have a titlebar, tabs or any other features. All it does is size and position
38833 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38834 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38835 * @cfg {string} region the region that it inhabits..
38836 * @cfg {bool} skipConfig skip config?
38840 Roo.bootstrap.layout.Basic = function(config){
38842 this.mgr = config.mgr;
38844 this.position = config.region;
38846 var skipConfig = config.skipConfig;
38850 * @scope Roo.BasicLayoutRegion
38854 * @event beforeremove
38855 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38856 * @param {Roo.LayoutRegion} this
38857 * @param {Roo.ContentPanel} panel The panel
38858 * @param {Object} e The cancel event object
38860 "beforeremove" : true,
38862 * @event invalidated
38863 * Fires when the layout for this region is changed.
38864 * @param {Roo.LayoutRegion} this
38866 "invalidated" : true,
38868 * @event visibilitychange
38869 * Fires when this region is shown or hidden
38870 * @param {Roo.LayoutRegion} this
38871 * @param {Boolean} visibility true or false
38873 "visibilitychange" : true,
38875 * @event paneladded
38876 * Fires when a panel is added.
38877 * @param {Roo.LayoutRegion} this
38878 * @param {Roo.ContentPanel} panel The panel
38880 "paneladded" : true,
38882 * @event panelremoved
38883 * Fires when a panel is removed.
38884 * @param {Roo.LayoutRegion} this
38885 * @param {Roo.ContentPanel} panel The panel
38887 "panelremoved" : true,
38889 * @event beforecollapse
38890 * Fires when this region before collapse.
38891 * @param {Roo.LayoutRegion} this
38893 "beforecollapse" : true,
38896 * Fires when this region is collapsed.
38897 * @param {Roo.LayoutRegion} this
38899 "collapsed" : true,
38902 * Fires when this region is expanded.
38903 * @param {Roo.LayoutRegion} this
38908 * Fires when this region is slid into view.
38909 * @param {Roo.LayoutRegion} this
38911 "slideshow" : true,
38914 * Fires when this region slides out of view.
38915 * @param {Roo.LayoutRegion} this
38917 "slidehide" : true,
38919 * @event panelactivated
38920 * Fires when a panel is activated.
38921 * @param {Roo.LayoutRegion} this
38922 * @param {Roo.ContentPanel} panel The activated panel
38924 "panelactivated" : true,
38927 * Fires when the user resizes this region.
38928 * @param {Roo.LayoutRegion} this
38929 * @param {Number} newSize The new size (width for east/west, height for north/south)
38933 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38934 this.panels = new Roo.util.MixedCollection();
38935 this.panels.getKey = this.getPanelId.createDelegate(this);
38937 this.activePanel = null;
38938 // ensure listeners are added...
38940 if (config.listeners || config.events) {
38941 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38942 listeners : config.listeners || {},
38943 events : config.events || {}
38947 if(skipConfig !== true){
38948 this.applyConfig(config);
38952 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38954 getPanelId : function(p){
38958 applyConfig : function(config){
38959 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38960 this.config = config;
38965 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38966 * the width, for horizontal (north, south) the height.
38967 * @param {Number} newSize The new width or height
38969 resizeTo : function(newSize){
38970 var el = this.el ? this.el :
38971 (this.activePanel ? this.activePanel.getEl() : null);
38973 switch(this.position){
38976 el.setWidth(newSize);
38977 this.fireEvent("resized", this, newSize);
38981 el.setHeight(newSize);
38982 this.fireEvent("resized", this, newSize);
38988 getBox : function(){
38989 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38992 getMargins : function(){
38993 return this.margins;
38996 updateBox : function(box){
38998 var el = this.activePanel.getEl();
38999 el.dom.style.left = box.x + "px";
39000 el.dom.style.top = box.y + "px";
39001 this.activePanel.setSize(box.width, box.height);
39005 * Returns the container element for this region.
39006 * @return {Roo.Element}
39008 getEl : function(){
39009 return this.activePanel;
39013 * Returns true if this region is currently visible.
39014 * @return {Boolean}
39016 isVisible : function(){
39017 return this.activePanel ? true : false;
39020 setActivePanel : function(panel){
39021 panel = this.getPanel(panel);
39022 if(this.activePanel && this.activePanel != panel){
39023 this.activePanel.setActiveState(false);
39024 this.activePanel.getEl().setLeftTop(-10000,-10000);
39026 this.activePanel = panel;
39027 panel.setActiveState(true);
39029 panel.setSize(this.box.width, this.box.height);
39031 this.fireEvent("panelactivated", this, panel);
39032 this.fireEvent("invalidated");
39036 * Show the specified panel.
39037 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39038 * @return {Roo.ContentPanel} The shown panel or null
39040 showPanel : function(panel){
39041 panel = this.getPanel(panel);
39043 this.setActivePanel(panel);
39049 * Get the active panel for this region.
39050 * @return {Roo.ContentPanel} The active panel or null
39052 getActivePanel : function(){
39053 return this.activePanel;
39057 * Add the passed ContentPanel(s)
39058 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39059 * @return {Roo.ContentPanel} The panel added (if only one was added)
39061 add : function(panel){
39062 if(arguments.length > 1){
39063 for(var i = 0, len = arguments.length; i < len; i++) {
39064 this.add(arguments[i]);
39068 if(this.hasPanel(panel)){
39069 this.showPanel(panel);
39072 var el = panel.getEl();
39073 if(el.dom.parentNode != this.mgr.el.dom){
39074 this.mgr.el.dom.appendChild(el.dom);
39076 if(panel.setRegion){
39077 panel.setRegion(this);
39079 this.panels.add(panel);
39080 el.setStyle("position", "absolute");
39081 if(!panel.background){
39082 this.setActivePanel(panel);
39083 if(this.config.initialSize && this.panels.getCount()==1){
39084 this.resizeTo(this.config.initialSize);
39087 this.fireEvent("paneladded", this, panel);
39092 * Returns true if the panel is in this region.
39093 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39094 * @return {Boolean}
39096 hasPanel : function(panel){
39097 if(typeof panel == "object"){ // must be panel obj
39098 panel = panel.getId();
39100 return this.getPanel(panel) ? true : false;
39104 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39105 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39106 * @param {Boolean} preservePanel Overrides the config preservePanel option
39107 * @return {Roo.ContentPanel} The panel that was removed
39109 remove : function(panel, preservePanel){
39110 panel = this.getPanel(panel);
39115 this.fireEvent("beforeremove", this, panel, e);
39116 if(e.cancel === true){
39119 var panelId = panel.getId();
39120 this.panels.removeKey(panelId);
39125 * Returns the panel specified or null if it's not in this region.
39126 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39127 * @return {Roo.ContentPanel}
39129 getPanel : function(id){
39130 if(typeof id == "object"){ // must be panel obj
39133 return this.panels.get(id);
39137 * Returns this regions position (north/south/east/west/center).
39140 getPosition: function(){
39141 return this.position;
39145 * Ext JS Library 1.1.1
39146 * Copyright(c) 2006-2007, Ext JS, LLC.
39148 * Originally Released Under LGPL - original licence link has changed is not relivant.
39151 * <script type="text/javascript">
39155 * @class Roo.bootstrap.layout.Region
39156 * @extends Roo.bootstrap.layout.Basic
39157 * This class represents a region in a layout manager.
39159 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39160 * @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})
39161 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39162 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39163 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39164 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39165 * @cfg {String} title The title for the region (overrides panel titles)
39166 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39167 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39168 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39169 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39170 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39171 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39172 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39173 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39174 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39175 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39177 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39178 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39179 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39180 * @cfg {Number} width For East/West panels
39181 * @cfg {Number} height For North/South panels
39182 * @cfg {Boolean} split To show the splitter
39183 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39185 * @cfg {string} cls Extra CSS classes to add to region
39187 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39188 * @cfg {string} region the region that it inhabits..
39191 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39192 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39194 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39195 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39196 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39198 Roo.bootstrap.layout.Region = function(config)
39200 this.applyConfig(config);
39202 var mgr = config.mgr;
39203 var pos = config.region;
39204 config.skipConfig = true;
39205 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39208 this.onRender(mgr.el);
39211 this.visible = true;
39212 this.collapsed = false;
39213 this.unrendered_panels = [];
39216 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39218 position: '', // set by wrapper (eg. north/south etc..)
39219 unrendered_panels : null, // unrendered panels.
39221 tabPosition : false,
39223 mgr: false, // points to 'Border'
39226 createBody : function(){
39227 /** This region's body element
39228 * @type Roo.Element */
39229 this.bodyEl = this.el.createChild({
39231 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39235 onRender: function(ctr, pos)
39237 var dh = Roo.DomHelper;
39238 /** This region's container element
39239 * @type Roo.Element */
39240 this.el = dh.append(ctr.dom, {
39242 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39244 /** This region's title element
39245 * @type Roo.Element */
39247 this.titleEl = dh.append(this.el.dom, {
39249 unselectable: "on",
39250 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39252 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39253 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39257 this.titleEl.enableDisplayMode();
39258 /** This region's title text element
39259 * @type HTMLElement */
39260 this.titleTextEl = this.titleEl.dom.firstChild;
39261 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39263 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39264 this.closeBtn.enableDisplayMode();
39265 this.closeBtn.on("click", this.closeClicked, this);
39266 this.closeBtn.hide();
39268 this.createBody(this.config);
39269 if(this.config.hideWhenEmpty){
39271 this.on("paneladded", this.validateVisibility, this);
39272 this.on("panelremoved", this.validateVisibility, this);
39274 if(this.autoScroll){
39275 this.bodyEl.setStyle("overflow", "auto");
39277 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39279 //if(c.titlebar !== false){
39280 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39281 this.titleEl.hide();
39283 this.titleEl.show();
39284 if(this.config.title){
39285 this.titleTextEl.innerHTML = this.config.title;
39289 if(this.config.collapsed){
39290 this.collapse(true);
39292 if(this.config.hidden){
39296 if (this.unrendered_panels && this.unrendered_panels.length) {
39297 for (var i =0;i< this.unrendered_panels.length; i++) {
39298 this.add(this.unrendered_panels[i]);
39300 this.unrendered_panels = null;
39306 applyConfig : function(c)
39309 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39310 var dh = Roo.DomHelper;
39311 if(c.titlebar !== false){
39312 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39313 this.collapseBtn.on("click", this.collapse, this);
39314 this.collapseBtn.enableDisplayMode();
39316 if(c.showPin === true || this.showPin){
39317 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39318 this.stickBtn.enableDisplayMode();
39319 this.stickBtn.on("click", this.expand, this);
39320 this.stickBtn.hide();
39325 /** This region's collapsed element
39326 * @type Roo.Element */
39329 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39330 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39333 if(c.floatable !== false){
39334 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39335 this.collapsedEl.on("click", this.collapseClick, this);
39338 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39339 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39340 id: "message", unselectable: "on", style:{"float":"left"}});
39341 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39343 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39344 this.expandBtn.on("click", this.expand, this);
39348 if(this.collapseBtn){
39349 this.collapseBtn.setVisible(c.collapsible == true);
39352 this.cmargins = c.cmargins || this.cmargins ||
39353 (this.position == "west" || this.position == "east" ?
39354 {top: 0, left: 2, right:2, bottom: 0} :
39355 {top: 2, left: 0, right:0, bottom: 2});
39357 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39360 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39362 this.autoScroll = c.autoScroll || false;
39367 this.duration = c.duration || .30;
39368 this.slideDuration = c.slideDuration || .45;
39373 * Returns true if this region is currently visible.
39374 * @return {Boolean}
39376 isVisible : function(){
39377 return this.visible;
39381 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39382 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39384 //setCollapsedTitle : function(title){
39385 // title = title || " ";
39386 // if(this.collapsedTitleTextEl){
39387 // this.collapsedTitleTextEl.innerHTML = title;
39391 getBox : function(){
39393 // if(!this.collapsed){
39394 b = this.el.getBox(false, true);
39396 // b = this.collapsedEl.getBox(false, true);
39401 getMargins : function(){
39402 return this.margins;
39403 //return this.collapsed ? this.cmargins : this.margins;
39406 highlight : function(){
39407 this.el.addClass("x-layout-panel-dragover");
39410 unhighlight : function(){
39411 this.el.removeClass("x-layout-panel-dragover");
39414 updateBox : function(box)
39416 if (!this.bodyEl) {
39417 return; // not rendered yet..
39421 if(!this.collapsed){
39422 this.el.dom.style.left = box.x + "px";
39423 this.el.dom.style.top = box.y + "px";
39424 this.updateBody(box.width, box.height);
39426 this.collapsedEl.dom.style.left = box.x + "px";
39427 this.collapsedEl.dom.style.top = box.y + "px";
39428 this.collapsedEl.setSize(box.width, box.height);
39431 this.tabs.autoSizeTabs();
39435 updateBody : function(w, h)
39438 this.el.setWidth(w);
39439 w -= this.el.getBorderWidth("rl");
39440 if(this.config.adjustments){
39441 w += this.config.adjustments[0];
39444 if(h !== null && h > 0){
39445 this.el.setHeight(h);
39446 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39447 h -= this.el.getBorderWidth("tb");
39448 if(this.config.adjustments){
39449 h += this.config.adjustments[1];
39451 this.bodyEl.setHeight(h);
39453 h = this.tabs.syncHeight(h);
39456 if(this.panelSize){
39457 w = w !== null ? w : this.panelSize.width;
39458 h = h !== null ? h : this.panelSize.height;
39460 if(this.activePanel){
39461 var el = this.activePanel.getEl();
39462 w = w !== null ? w : el.getWidth();
39463 h = h !== null ? h : el.getHeight();
39464 this.panelSize = {width: w, height: h};
39465 this.activePanel.setSize(w, h);
39467 if(Roo.isIE && this.tabs){
39468 this.tabs.el.repaint();
39473 * Returns the container element for this region.
39474 * @return {Roo.Element}
39476 getEl : function(){
39481 * Hides this region.
39484 //if(!this.collapsed){
39485 this.el.dom.style.left = "-2000px";
39488 // this.collapsedEl.dom.style.left = "-2000px";
39489 // this.collapsedEl.hide();
39491 this.visible = false;
39492 this.fireEvent("visibilitychange", this, false);
39496 * Shows this region if it was previously hidden.
39499 //if(!this.collapsed){
39502 // this.collapsedEl.show();
39504 this.visible = true;
39505 this.fireEvent("visibilitychange", this, true);
39508 closeClicked : function(){
39509 if(this.activePanel){
39510 this.remove(this.activePanel);
39514 collapseClick : function(e){
39516 e.stopPropagation();
39519 e.stopPropagation();
39525 * Collapses this region.
39526 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39529 collapse : function(skipAnim, skipCheck = false){
39530 if(this.collapsed) {
39534 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39536 this.collapsed = true;
39538 this.split.el.hide();
39540 if(this.config.animate && skipAnim !== true){
39541 this.fireEvent("invalidated", this);
39542 this.animateCollapse();
39544 this.el.setLocation(-20000,-20000);
39546 this.collapsedEl.show();
39547 this.fireEvent("collapsed", this);
39548 this.fireEvent("invalidated", this);
39554 animateCollapse : function(){
39559 * Expands this region if it was previously collapsed.
39560 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39561 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39564 expand : function(e, skipAnim){
39566 e.stopPropagation();
39568 if(!this.collapsed || this.el.hasActiveFx()) {
39572 this.afterSlideIn();
39575 this.collapsed = false;
39576 if(this.config.animate && skipAnim !== true){
39577 this.animateExpand();
39581 this.split.el.show();
39583 this.collapsedEl.setLocation(-2000,-2000);
39584 this.collapsedEl.hide();
39585 this.fireEvent("invalidated", this);
39586 this.fireEvent("expanded", this);
39590 animateExpand : function(){
39594 initTabs : function()
39596 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39598 var ts = new Roo.bootstrap.panel.Tabs({
39599 el: this.bodyEl.dom,
39601 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39602 disableTooltips: this.config.disableTabTips,
39603 toolbar : this.config.toolbar
39606 if(this.config.hideTabs){
39607 ts.stripWrap.setDisplayed(false);
39610 ts.resizeTabs = this.config.resizeTabs === true;
39611 ts.minTabWidth = this.config.minTabWidth || 40;
39612 ts.maxTabWidth = this.config.maxTabWidth || 250;
39613 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39614 ts.monitorResize = false;
39615 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39616 ts.bodyEl.addClass('roo-layout-tabs-body');
39617 this.panels.each(this.initPanelAsTab, this);
39620 initPanelAsTab : function(panel){
39621 var ti = this.tabs.addTab(
39625 this.config.closeOnTab && panel.isClosable(),
39628 if(panel.tabTip !== undefined){
39629 ti.setTooltip(panel.tabTip);
39631 ti.on("activate", function(){
39632 this.setActivePanel(panel);
39635 if(this.config.closeOnTab){
39636 ti.on("beforeclose", function(t, e){
39638 this.remove(panel);
39642 panel.tabItem = ti;
39647 updatePanelTitle : function(panel, title)
39649 if(this.activePanel == panel){
39650 this.updateTitle(title);
39653 var ti = this.tabs.getTab(panel.getEl().id);
39655 if(panel.tabTip !== undefined){
39656 ti.setTooltip(panel.tabTip);
39661 updateTitle : function(title){
39662 if(this.titleTextEl && !this.config.title){
39663 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39667 setActivePanel : function(panel)
39669 panel = this.getPanel(panel);
39670 if(this.activePanel && this.activePanel != panel){
39671 if(this.activePanel.setActiveState(false) === false){
39675 this.activePanel = panel;
39676 panel.setActiveState(true);
39677 if(this.panelSize){
39678 panel.setSize(this.panelSize.width, this.panelSize.height);
39681 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39683 this.updateTitle(panel.getTitle());
39685 this.fireEvent("invalidated", this);
39687 this.fireEvent("panelactivated", this, panel);
39691 * Shows the specified panel.
39692 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39693 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39695 showPanel : function(panel)
39697 panel = this.getPanel(panel);
39700 var tab = this.tabs.getTab(panel.getEl().id);
39701 if(tab.isHidden()){
39702 this.tabs.unhideTab(tab.id);
39706 this.setActivePanel(panel);
39713 * Get the active panel for this region.
39714 * @return {Roo.ContentPanel} The active panel or null
39716 getActivePanel : function(){
39717 return this.activePanel;
39720 validateVisibility : function(){
39721 if(this.panels.getCount() < 1){
39722 this.updateTitle(" ");
39723 this.closeBtn.hide();
39726 if(!this.isVisible()){
39733 * Adds the passed ContentPanel(s) to this region.
39734 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39735 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39737 add : function(panel)
39739 if(arguments.length > 1){
39740 for(var i = 0, len = arguments.length; i < len; i++) {
39741 this.add(arguments[i]);
39746 // if we have not been rendered yet, then we can not really do much of this..
39747 if (!this.bodyEl) {
39748 this.unrendered_panels.push(panel);
39755 if(this.hasPanel(panel)){
39756 this.showPanel(panel);
39759 panel.setRegion(this);
39760 this.panels.add(panel);
39761 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39762 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39763 // and hide them... ???
39764 this.bodyEl.dom.appendChild(panel.getEl().dom);
39765 if(panel.background !== true){
39766 this.setActivePanel(panel);
39768 this.fireEvent("paneladded", this, panel);
39775 this.initPanelAsTab(panel);
39779 if(panel.background !== true){
39780 this.tabs.activate(panel.getEl().id);
39782 this.fireEvent("paneladded", this, panel);
39787 * Hides the tab for the specified panel.
39788 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39790 hidePanel : function(panel){
39791 if(this.tabs && (panel = this.getPanel(panel))){
39792 this.tabs.hideTab(panel.getEl().id);
39797 * Unhides the tab for a previously hidden panel.
39798 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39800 unhidePanel : function(panel){
39801 if(this.tabs && (panel = this.getPanel(panel))){
39802 this.tabs.unhideTab(panel.getEl().id);
39806 clearPanels : function(){
39807 while(this.panels.getCount() > 0){
39808 this.remove(this.panels.first());
39813 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39814 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39815 * @param {Boolean} preservePanel Overrides the config preservePanel option
39816 * @return {Roo.ContentPanel} The panel that was removed
39818 remove : function(panel, preservePanel)
39820 panel = this.getPanel(panel);
39825 this.fireEvent("beforeremove", this, panel, e);
39826 if(e.cancel === true){
39829 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39830 var panelId = panel.getId();
39831 this.panels.removeKey(panelId);
39833 document.body.appendChild(panel.getEl().dom);
39836 this.tabs.removeTab(panel.getEl().id);
39837 }else if (!preservePanel){
39838 this.bodyEl.dom.removeChild(panel.getEl().dom);
39840 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39841 var p = this.panels.first();
39842 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39843 tempEl.appendChild(p.getEl().dom);
39844 this.bodyEl.update("");
39845 this.bodyEl.dom.appendChild(p.getEl().dom);
39847 this.updateTitle(p.getTitle());
39849 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39850 this.setActivePanel(p);
39852 panel.setRegion(null);
39853 if(this.activePanel == panel){
39854 this.activePanel = null;
39856 if(this.config.autoDestroy !== false && preservePanel !== true){
39857 try{panel.destroy();}catch(e){}
39859 this.fireEvent("panelremoved", this, panel);
39864 * Returns the TabPanel component used by this region
39865 * @return {Roo.TabPanel}
39867 getTabs : function(){
39871 createTool : function(parentEl, className){
39872 var btn = Roo.DomHelper.append(parentEl, {
39874 cls: "x-layout-tools-button",
39877 cls: "roo-layout-tools-button-inner " + className,
39881 btn.addClassOnOver("roo-layout-tools-button-over");
39886 * Ext JS Library 1.1.1
39887 * Copyright(c) 2006-2007, Ext JS, LLC.
39889 * Originally Released Under LGPL - original licence link has changed is not relivant.
39892 * <script type="text/javascript">
39898 * @class Roo.SplitLayoutRegion
39899 * @extends Roo.LayoutRegion
39900 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39902 Roo.bootstrap.layout.Split = function(config){
39903 this.cursor = config.cursor;
39904 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39907 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39909 splitTip : "Drag to resize.",
39910 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39911 useSplitTips : false,
39913 applyConfig : function(config){
39914 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39917 onRender : function(ctr,pos) {
39919 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39920 if(!this.config.split){
39925 var splitEl = Roo.DomHelper.append(ctr.dom, {
39927 id: this.el.id + "-split",
39928 cls: "roo-layout-split roo-layout-split-"+this.position,
39931 /** The SplitBar for this region
39932 * @type Roo.SplitBar */
39933 // does not exist yet...
39934 Roo.log([this.position, this.orientation]);
39936 this.split = new Roo.bootstrap.SplitBar({
39937 dragElement : splitEl,
39938 resizingElement: this.el,
39939 orientation : this.orientation
39942 this.split.on("moved", this.onSplitMove, this);
39943 this.split.useShim = this.config.useShim === true;
39944 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39945 if(this.useSplitTips){
39946 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39948 //if(config.collapsible){
39949 // this.split.el.on("dblclick", this.collapse, this);
39952 if(typeof this.config.minSize != "undefined"){
39953 this.split.minSize = this.config.minSize;
39955 if(typeof this.config.maxSize != "undefined"){
39956 this.split.maxSize = this.config.maxSize;
39958 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39959 this.hideSplitter();
39964 getHMaxSize : function(){
39965 var cmax = this.config.maxSize || 10000;
39966 var center = this.mgr.getRegion("center");
39967 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39970 getVMaxSize : function(){
39971 var cmax = this.config.maxSize || 10000;
39972 var center = this.mgr.getRegion("center");
39973 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39976 onSplitMove : function(split, newSize){
39977 this.fireEvent("resized", this, newSize);
39981 * Returns the {@link Roo.SplitBar} for this region.
39982 * @return {Roo.SplitBar}
39984 getSplitBar : function(){
39989 this.hideSplitter();
39990 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39993 hideSplitter : function(){
39995 this.split.el.setLocation(-2000,-2000);
39996 this.split.el.hide();
40002 this.split.el.show();
40004 Roo.bootstrap.layout.Split.superclass.show.call(this);
40007 beforeSlide: function(){
40008 if(Roo.isGecko){// firefox overflow auto bug workaround
40009 this.bodyEl.clip();
40011 this.tabs.bodyEl.clip();
40013 if(this.activePanel){
40014 this.activePanel.getEl().clip();
40016 if(this.activePanel.beforeSlide){
40017 this.activePanel.beforeSlide();
40023 afterSlide : function(){
40024 if(Roo.isGecko){// firefox overflow auto bug workaround
40025 this.bodyEl.unclip();
40027 this.tabs.bodyEl.unclip();
40029 if(this.activePanel){
40030 this.activePanel.getEl().unclip();
40031 if(this.activePanel.afterSlide){
40032 this.activePanel.afterSlide();
40038 initAutoHide : function(){
40039 if(this.autoHide !== false){
40040 if(!this.autoHideHd){
40041 var st = new Roo.util.DelayedTask(this.slideIn, this);
40042 this.autoHideHd = {
40043 "mouseout": function(e){
40044 if(!e.within(this.el, true)){
40048 "mouseover" : function(e){
40054 this.el.on(this.autoHideHd);
40058 clearAutoHide : function(){
40059 if(this.autoHide !== false){
40060 this.el.un("mouseout", this.autoHideHd.mouseout);
40061 this.el.un("mouseover", this.autoHideHd.mouseover);
40065 clearMonitor : function(){
40066 Roo.get(document).un("click", this.slideInIf, this);
40069 // these names are backwards but not changed for compat
40070 slideOut : function(){
40071 if(this.isSlid || this.el.hasActiveFx()){
40074 this.isSlid = true;
40075 if(this.collapseBtn){
40076 this.collapseBtn.hide();
40078 this.closeBtnState = this.closeBtn.getStyle('display');
40079 this.closeBtn.hide();
40081 this.stickBtn.show();
40084 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40085 this.beforeSlide();
40086 this.el.setStyle("z-index", 10001);
40087 this.el.slideIn(this.getSlideAnchor(), {
40088 callback: function(){
40090 this.initAutoHide();
40091 Roo.get(document).on("click", this.slideInIf, this);
40092 this.fireEvent("slideshow", this);
40099 afterSlideIn : function(){
40100 this.clearAutoHide();
40101 this.isSlid = false;
40102 this.clearMonitor();
40103 this.el.setStyle("z-index", "");
40104 if(this.collapseBtn){
40105 this.collapseBtn.show();
40107 this.closeBtn.setStyle('display', this.closeBtnState);
40109 this.stickBtn.hide();
40111 this.fireEvent("slidehide", this);
40114 slideIn : function(cb){
40115 if(!this.isSlid || this.el.hasActiveFx()){
40119 this.isSlid = false;
40120 this.beforeSlide();
40121 this.el.slideOut(this.getSlideAnchor(), {
40122 callback: function(){
40123 this.el.setLeftTop(-10000, -10000);
40125 this.afterSlideIn();
40133 slideInIf : function(e){
40134 if(!e.within(this.el)){
40139 animateCollapse : function(){
40140 this.beforeSlide();
40141 this.el.setStyle("z-index", 20000);
40142 var anchor = this.getSlideAnchor();
40143 this.el.slideOut(anchor, {
40144 callback : function(){
40145 this.el.setStyle("z-index", "");
40146 this.collapsedEl.slideIn(anchor, {duration:.3});
40148 this.el.setLocation(-10000,-10000);
40150 this.fireEvent("collapsed", this);
40157 animateExpand : function(){
40158 this.beforeSlide();
40159 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40160 this.el.setStyle("z-index", 20000);
40161 this.collapsedEl.hide({
40164 this.el.slideIn(this.getSlideAnchor(), {
40165 callback : function(){
40166 this.el.setStyle("z-index", "");
40169 this.split.el.show();
40171 this.fireEvent("invalidated", this);
40172 this.fireEvent("expanded", this);
40200 getAnchor : function(){
40201 return this.anchors[this.position];
40204 getCollapseAnchor : function(){
40205 return this.canchors[this.position];
40208 getSlideAnchor : function(){
40209 return this.sanchors[this.position];
40212 getAlignAdj : function(){
40213 var cm = this.cmargins;
40214 switch(this.position){
40230 getExpandAdj : function(){
40231 var c = this.collapsedEl, cm = this.cmargins;
40232 switch(this.position){
40234 return [-(cm.right+c.getWidth()+cm.left), 0];
40237 return [cm.right+c.getWidth()+cm.left, 0];
40240 return [0, -(cm.top+cm.bottom+c.getHeight())];
40243 return [0, cm.top+cm.bottom+c.getHeight()];
40249 * Ext JS Library 1.1.1
40250 * Copyright(c) 2006-2007, Ext JS, LLC.
40252 * Originally Released Under LGPL - original licence link has changed is not relivant.
40255 * <script type="text/javascript">
40258 * These classes are private internal classes
40260 Roo.bootstrap.layout.Center = function(config){
40261 config.region = "center";
40262 Roo.bootstrap.layout.Region.call(this, config);
40263 this.visible = true;
40264 this.minWidth = config.minWidth || 20;
40265 this.minHeight = config.minHeight || 20;
40268 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40270 // center panel can't be hidden
40274 // center panel can't be hidden
40277 getMinWidth: function(){
40278 return this.minWidth;
40281 getMinHeight: function(){
40282 return this.minHeight;
40296 Roo.bootstrap.layout.North = function(config)
40298 config.region = 'north';
40299 config.cursor = 'n-resize';
40301 Roo.bootstrap.layout.Split.call(this, config);
40305 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40306 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40307 this.split.el.addClass("roo-layout-split-v");
40309 //var size = config.initialSize || config.height;
40310 //if(this.el && typeof size != "undefined"){
40311 // this.el.setHeight(size);
40314 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40316 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40319 onRender : function(ctr, pos)
40321 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40322 var size = this.config.initialSize || this.config.height;
40323 if(this.el && typeof size != "undefined"){
40324 this.el.setHeight(size);
40329 getBox : function(){
40330 if(this.collapsed){
40331 return this.collapsedEl.getBox();
40333 var box = this.el.getBox();
40335 box.height += this.split.el.getHeight();
40340 updateBox : function(box){
40341 if(this.split && !this.collapsed){
40342 box.height -= this.split.el.getHeight();
40343 this.split.el.setLeft(box.x);
40344 this.split.el.setTop(box.y+box.height);
40345 this.split.el.setWidth(box.width);
40347 if(this.collapsed){
40348 this.updateBody(box.width, null);
40350 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40358 Roo.bootstrap.layout.South = function(config){
40359 config.region = 'south';
40360 config.cursor = 's-resize';
40361 Roo.bootstrap.layout.Split.call(this, config);
40363 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40364 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40365 this.split.el.addClass("roo-layout-split-v");
40370 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40371 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40373 onRender : function(ctr, pos)
40375 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40376 var size = this.config.initialSize || this.config.height;
40377 if(this.el && typeof size != "undefined"){
40378 this.el.setHeight(size);
40383 getBox : function(){
40384 if(this.collapsed){
40385 return this.collapsedEl.getBox();
40387 var box = this.el.getBox();
40389 var sh = this.split.el.getHeight();
40396 updateBox : function(box){
40397 if(this.split && !this.collapsed){
40398 var sh = this.split.el.getHeight();
40401 this.split.el.setLeft(box.x);
40402 this.split.el.setTop(box.y-sh);
40403 this.split.el.setWidth(box.width);
40405 if(this.collapsed){
40406 this.updateBody(box.width, null);
40408 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40412 Roo.bootstrap.layout.East = function(config){
40413 config.region = "east";
40414 config.cursor = "e-resize";
40415 Roo.bootstrap.layout.Split.call(this, config);
40417 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40418 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40419 this.split.el.addClass("roo-layout-split-h");
40423 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40424 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40426 onRender : function(ctr, pos)
40428 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40429 var size = this.config.initialSize || this.config.width;
40430 if(this.el && typeof size != "undefined"){
40431 this.el.setWidth(size);
40436 getBox : function(){
40437 if(this.collapsed){
40438 return this.collapsedEl.getBox();
40440 var box = this.el.getBox();
40442 var sw = this.split.el.getWidth();
40449 updateBox : function(box){
40450 if(this.split && !this.collapsed){
40451 var sw = this.split.el.getWidth();
40453 this.split.el.setLeft(box.x);
40454 this.split.el.setTop(box.y);
40455 this.split.el.setHeight(box.height);
40458 if(this.collapsed){
40459 this.updateBody(null, box.height);
40461 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40465 Roo.bootstrap.layout.West = function(config){
40466 config.region = "west";
40467 config.cursor = "w-resize";
40469 Roo.bootstrap.layout.Split.call(this, config);
40471 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40472 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40473 this.split.el.addClass("roo-layout-split-h");
40477 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40478 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40480 onRender: function(ctr, pos)
40482 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40483 var size = this.config.initialSize || this.config.width;
40484 if(typeof size != "undefined"){
40485 this.el.setWidth(size);
40489 getBox : function(){
40490 if(this.collapsed){
40491 return this.collapsedEl.getBox();
40493 var box = this.el.getBox();
40494 if (box.width == 0) {
40495 box.width = this.config.width; // kludge?
40498 box.width += this.split.el.getWidth();
40503 updateBox : function(box){
40504 if(this.split && !this.collapsed){
40505 var sw = this.split.el.getWidth();
40507 this.split.el.setLeft(box.x+box.width);
40508 this.split.el.setTop(box.y);
40509 this.split.el.setHeight(box.height);
40511 if(this.collapsed){
40512 this.updateBody(null, box.height);
40514 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40516 });Roo.namespace("Roo.bootstrap.panel");/*
40518 * Ext JS Library 1.1.1
40519 * Copyright(c) 2006-2007, Ext JS, LLC.
40521 * Originally Released Under LGPL - original licence link has changed is not relivant.
40524 * <script type="text/javascript">
40527 * @class Roo.ContentPanel
40528 * @extends Roo.util.Observable
40529 * A basic ContentPanel element.
40530 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40531 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40532 * @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
40533 * @cfg {Boolean} closable True if the panel can be closed/removed
40534 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40535 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40536 * @cfg {Toolbar} toolbar A toolbar for this panel
40537 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40538 * @cfg {String} title The title for this panel
40539 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40540 * @cfg {String} url Calls {@link #setUrl} with this value
40541 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40542 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40543 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40544 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40545 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40546 * @cfg {Boolean} badges render the badges
40547 * @cfg {String} cls extra classes to use
40548 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40551 * Create a new ContentPanel.
40552 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40553 * @param {String/Object} config A string to set only the title or a config object
40554 * @param {String} content (optional) Set the HTML content for this panel
40555 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40557 Roo.bootstrap.panel.Content = function( config){
40559 this.tpl = config.tpl || false;
40561 var el = config.el;
40562 var content = config.content;
40564 if(config.autoCreate){ // xtype is available if this is called from factory
40567 this.el = Roo.get(el);
40568 if(!this.el && config && config.autoCreate){
40569 if(typeof config.autoCreate == "object"){
40570 if(!config.autoCreate.id){
40571 config.autoCreate.id = config.id||el;
40573 this.el = Roo.DomHelper.append(document.body,
40574 config.autoCreate, true);
40578 cls: (config.cls || '') +
40579 (config.background ? ' bg-' + config.background : '') +
40580 " roo-layout-inactive-content",
40583 if (config.iframe) {
40587 style : 'border: 0px',
40588 src : 'about:blank'
40594 elcfg.html = config.html;
40598 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40599 if (config.iframe) {
40600 this.iframeEl = this.el.select('iframe',true).first();
40605 this.closable = false;
40606 this.loaded = false;
40607 this.active = false;
40610 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40612 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40614 this.wrapEl = this.el; //this.el.wrap();
40616 if (config.toolbar.items) {
40617 ti = config.toolbar.items ;
40618 delete config.toolbar.items ;
40622 this.toolbar.render(this.wrapEl, 'before');
40623 for(var i =0;i < ti.length;i++) {
40624 // Roo.log(['add child', items[i]]);
40625 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40627 this.toolbar.items = nitems;
40628 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40629 delete config.toolbar;
40633 // xtype created footer. - not sure if will work as we normally have to render first..
40634 if (this.footer && !this.footer.el && this.footer.xtype) {
40635 if (!this.wrapEl) {
40636 this.wrapEl = this.el.wrap();
40639 this.footer.container = this.wrapEl.createChild();
40641 this.footer = Roo.factory(this.footer, Roo);
40646 if(typeof config == "string"){
40647 this.title = config;
40649 Roo.apply(this, config);
40653 this.resizeEl = Roo.get(this.resizeEl, true);
40655 this.resizeEl = this.el;
40657 // handle view.xtype
40665 * Fires when this panel is activated.
40666 * @param {Roo.ContentPanel} this
40670 * @event deactivate
40671 * Fires when this panel is activated.
40672 * @param {Roo.ContentPanel} this
40674 "deactivate" : true,
40678 * Fires when this panel is resized if fitToFrame is true.
40679 * @param {Roo.ContentPanel} this
40680 * @param {Number} width The width after any component adjustments
40681 * @param {Number} height The height after any component adjustments
40687 * Fires when this tab is created
40688 * @param {Roo.ContentPanel} this
40694 * Fires when this content is scrolled
40695 * @param {Roo.ContentPanel} this
40696 * @param {Event} scrollEvent
40707 if(this.autoScroll && !this.iframe){
40708 this.resizeEl.setStyle("overflow", "auto");
40709 this.resizeEl.on('scroll', this.onScroll, this);
40711 // fix randome scrolling
40712 //this.el.on('scroll', function() {
40713 // Roo.log('fix random scolling');
40714 // this.scrollTo('top',0);
40717 content = content || this.content;
40719 this.setContent(content);
40721 if(config && config.url){
40722 this.setUrl(this.url, this.params, this.loadOnce);
40727 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40729 if (this.view && typeof(this.view.xtype) != 'undefined') {
40730 this.view.el = this.el.appendChild(document.createElement("div"));
40731 this.view = Roo.factory(this.view);
40732 this.view.render && this.view.render(false, '');
40736 this.fireEvent('render', this);
40739 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40749 /* Resize Element - use this to work out scroll etc. */
40752 setRegion : function(region){
40753 this.region = region;
40754 this.setActiveClass(region && !this.background);
40758 setActiveClass: function(state)
40761 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40762 this.el.setStyle('position','relative');
40764 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40765 this.el.setStyle('position', 'absolute');
40770 * Returns the toolbar for this Panel if one was configured.
40771 * @return {Roo.Toolbar}
40773 getToolbar : function(){
40774 return this.toolbar;
40777 setActiveState : function(active)
40779 this.active = active;
40780 this.setActiveClass(active);
40782 if(this.fireEvent("deactivate", this) === false){
40787 this.fireEvent("activate", this);
40791 * Updates this panel's element (not for iframe)
40792 * @param {String} content The new content
40793 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40795 setContent : function(content, loadScripts){
40800 this.el.update(content, loadScripts);
40803 ignoreResize : function(w, h){
40804 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40807 this.lastSize = {width: w, height: h};
40812 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40813 * @return {Roo.UpdateManager} The UpdateManager
40815 getUpdateManager : function(){
40819 return this.el.getUpdateManager();
40822 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40823 * Does not work with IFRAME contents
40824 * @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:
40827 url: "your-url.php",
40828 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40829 callback: yourFunction,
40830 scope: yourObject, //(optional scope)
40833 text: "Loading...",
40839 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40840 * 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.
40841 * @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}
40842 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40843 * @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.
40844 * @return {Roo.ContentPanel} this
40852 var um = this.el.getUpdateManager();
40853 um.update.apply(um, arguments);
40859 * 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.
40860 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40861 * @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)
40862 * @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)
40863 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40865 setUrl : function(url, params, loadOnce){
40867 this.iframeEl.dom.src = url;
40871 if(this.refreshDelegate){
40872 this.removeListener("activate", this.refreshDelegate);
40874 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40875 this.on("activate", this.refreshDelegate);
40876 return this.el.getUpdateManager();
40879 _handleRefresh : function(url, params, loadOnce){
40880 if(!loadOnce || !this.loaded){
40881 var updater = this.el.getUpdateManager();
40882 updater.update(url, params, this._setLoaded.createDelegate(this));
40886 _setLoaded : function(){
40887 this.loaded = true;
40891 * Returns this panel's id
40894 getId : function(){
40899 * Returns this panel's element - used by regiosn to add.
40900 * @return {Roo.Element}
40902 getEl : function(){
40903 return this.wrapEl || this.el;
40908 adjustForComponents : function(width, height)
40910 //Roo.log('adjustForComponents ');
40911 if(this.resizeEl != this.el){
40912 width -= this.el.getFrameWidth('lr');
40913 height -= this.el.getFrameWidth('tb');
40916 var te = this.toolbar.getEl();
40917 te.setWidth(width);
40918 height -= te.getHeight();
40921 var te = this.footer.getEl();
40922 te.setWidth(width);
40923 height -= te.getHeight();
40927 if(this.adjustments){
40928 width += this.adjustments[0];
40929 height += this.adjustments[1];
40931 return {"width": width, "height": height};
40934 setSize : function(width, height){
40935 if(this.fitToFrame && !this.ignoreResize(width, height)){
40936 if(this.fitContainer && this.resizeEl != this.el){
40937 this.el.setSize(width, height);
40939 var size = this.adjustForComponents(width, height);
40941 this.iframeEl.setSize(width,height);
40944 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40945 this.fireEvent('resize', this, size.width, size.height);
40952 * Returns this panel's title
40955 getTitle : function(){
40957 if (typeof(this.title) != 'object') {
40962 for (var k in this.title) {
40963 if (!this.title.hasOwnProperty(k)) {
40967 if (k.indexOf('-') >= 0) {
40968 var s = k.split('-');
40969 for (var i = 0; i<s.length; i++) {
40970 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40973 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40980 * Set this panel's title
40981 * @param {String} title
40983 setTitle : function(title){
40984 this.title = title;
40986 this.region.updatePanelTitle(this, title);
40991 * Returns true is this panel was configured to be closable
40992 * @return {Boolean}
40994 isClosable : function(){
40995 return this.closable;
40998 beforeSlide : function(){
41000 this.resizeEl.clip();
41003 afterSlide : function(){
41005 this.resizeEl.unclip();
41009 * Force a content refresh from the URL specified in the {@link #setUrl} method.
41010 * Will fail silently if the {@link #setUrl} method has not been called.
41011 * This does not activate the panel, just updates its content.
41013 refresh : function(){
41014 if(this.refreshDelegate){
41015 this.loaded = false;
41016 this.refreshDelegate();
41021 * Destroys this panel
41023 destroy : function(){
41024 this.el.removeAllListeners();
41025 var tempEl = document.createElement("span");
41026 tempEl.appendChild(this.el.dom);
41027 tempEl.innerHTML = "";
41033 * form - if the content panel contains a form - this is a reference to it.
41034 * @type {Roo.form.Form}
41038 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41039 * This contains a reference to it.
41045 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41055 * @param {Object} cfg Xtype definition of item to add.
41059 getChildContainer: function () {
41060 return this.getEl();
41064 onScroll : function(e)
41066 this.fireEvent('scroll', this, e);
41071 var ret = new Roo.factory(cfg);
41076 if (cfg.xtype.match(/^Form$/)) {
41079 //if (this.footer) {
41080 // el = this.footer.container.insertSibling(false, 'before');
41082 el = this.el.createChild();
41085 this.form = new Roo.form.Form(cfg);
41088 if ( this.form.allItems.length) {
41089 this.form.render(el.dom);
41093 // should only have one of theses..
41094 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41095 // views.. should not be just added - used named prop 'view''
41097 cfg.el = this.el.appendChild(document.createElement("div"));
41100 var ret = new Roo.factory(cfg);
41102 ret.render && ret.render(false, ''); // render blank..
41112 * @class Roo.bootstrap.panel.Grid
41113 * @extends Roo.bootstrap.panel.Content
41115 * Create a new GridPanel.
41116 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41117 * @param {Object} config A the config object
41123 Roo.bootstrap.panel.Grid = function(config)
41127 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41128 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41130 config.el = this.wrapper;
41131 //this.el = this.wrapper;
41133 if (config.container) {
41134 // ctor'ed from a Border/panel.grid
41137 this.wrapper.setStyle("overflow", "hidden");
41138 this.wrapper.addClass('roo-grid-container');
41143 if(config.toolbar){
41144 var tool_el = this.wrapper.createChild();
41145 this.toolbar = Roo.factory(config.toolbar);
41147 if (config.toolbar.items) {
41148 ti = config.toolbar.items ;
41149 delete config.toolbar.items ;
41153 this.toolbar.render(tool_el);
41154 for(var i =0;i < ti.length;i++) {
41155 // Roo.log(['add child', items[i]]);
41156 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41158 this.toolbar.items = nitems;
41160 delete config.toolbar;
41163 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41164 config.grid.scrollBody = true;;
41165 config.grid.monitorWindowResize = false; // turn off autosizing
41166 config.grid.autoHeight = false;
41167 config.grid.autoWidth = false;
41169 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41171 if (config.background) {
41172 // render grid on panel activation (if panel background)
41173 this.on('activate', function(gp) {
41174 if (!gp.grid.rendered) {
41175 gp.grid.render(this.wrapper);
41176 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41181 this.grid.render(this.wrapper);
41182 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41185 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41186 // ??? needed ??? config.el = this.wrapper;
41191 // xtype created footer. - not sure if will work as we normally have to render first..
41192 if (this.footer && !this.footer.el && this.footer.xtype) {
41194 var ctr = this.grid.getView().getFooterPanel(true);
41195 this.footer.dataSource = this.grid.dataSource;
41196 this.footer = Roo.factory(this.footer, Roo);
41197 this.footer.render(ctr);
41207 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41208 getId : function(){
41209 return this.grid.id;
41213 * Returns the grid for this panel
41214 * @return {Roo.bootstrap.Table}
41216 getGrid : function(){
41220 setSize : function(width, height){
41221 if(!this.ignoreResize(width, height)){
41222 var grid = this.grid;
41223 var size = this.adjustForComponents(width, height);
41224 // tfoot is not a footer?
41227 var gridel = grid.getGridEl();
41228 gridel.setSize(size.width, size.height);
41230 var tbd = grid.getGridEl().select('tbody', true).first();
41231 var thd = grid.getGridEl().select('thead',true).first();
41232 var tbf= grid.getGridEl().select('tfoot', true).first();
41235 size.height -= tbf.getHeight();
41238 size.height -= thd.getHeight();
41241 tbd.setSize(size.width, size.height );
41242 // this is for the account management tab -seems to work there.
41243 var thd = grid.getGridEl().select('thead',true).first();
41245 // tbd.setSize(size.width, size.height - thd.getHeight());
41254 beforeSlide : function(){
41255 this.grid.getView().scroller.clip();
41258 afterSlide : function(){
41259 this.grid.getView().scroller.unclip();
41262 destroy : function(){
41263 this.grid.destroy();
41265 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41270 * @class Roo.bootstrap.panel.Nest
41271 * @extends Roo.bootstrap.panel.Content
41273 * Create a new Panel, that can contain a layout.Border.
41276 * @param {Roo.BorderLayout} layout The layout for this panel
41277 * @param {String/Object} config A string to set only the title or a config object
41279 Roo.bootstrap.panel.Nest = function(config)
41281 // construct with only one argument..
41282 /* FIXME - implement nicer consturctors
41283 if (layout.layout) {
41285 layout = config.layout;
41286 delete config.layout;
41288 if (layout.xtype && !layout.getEl) {
41289 // then layout needs constructing..
41290 layout = Roo.factory(layout, Roo);
41294 config.el = config.layout.getEl();
41296 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41298 config.layout.monitorWindowResize = false; // turn off autosizing
41299 this.layout = config.layout;
41300 this.layout.getEl().addClass("roo-layout-nested-layout");
41301 this.layout.parent = this;
41308 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41310 setSize : function(width, height){
41311 if(!this.ignoreResize(width, height)){
41312 var size = this.adjustForComponents(width, height);
41313 var el = this.layout.getEl();
41314 if (size.height < 1) {
41315 el.setWidth(size.width);
41317 el.setSize(size.width, size.height);
41319 var touch = el.dom.offsetWidth;
41320 this.layout.layout();
41321 // ie requires a double layout on the first pass
41322 if(Roo.isIE && !this.initialized){
41323 this.initialized = true;
41324 this.layout.layout();
41329 // activate all subpanels if not currently active..
41331 setActiveState : function(active){
41332 this.active = active;
41333 this.setActiveClass(active);
41336 this.fireEvent("deactivate", this);
41340 this.fireEvent("activate", this);
41341 // not sure if this should happen before or after..
41342 if (!this.layout) {
41343 return; // should not happen..
41346 for (var r in this.layout.regions) {
41347 reg = this.layout.getRegion(r);
41348 if (reg.getActivePanel()) {
41349 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41350 reg.setActivePanel(reg.getActivePanel());
41353 if (!reg.panels.length) {
41356 reg.showPanel(reg.getPanel(0));
41365 * Returns the nested BorderLayout for this panel
41366 * @return {Roo.BorderLayout}
41368 getLayout : function(){
41369 return this.layout;
41373 * Adds a xtype elements to the layout of the nested panel
41377 xtype : 'ContentPanel',
41384 xtype : 'NestedLayoutPanel',
41390 items : [ ... list of content panels or nested layout panels.. ]
41394 * @param {Object} cfg Xtype definition of item to add.
41396 addxtype : function(cfg) {
41397 return this.layout.addxtype(cfg);
41402 * Ext JS Library 1.1.1
41403 * Copyright(c) 2006-2007, Ext JS, LLC.
41405 * Originally Released Under LGPL - original licence link has changed is not relivant.
41408 * <script type="text/javascript">
41411 * @class Roo.TabPanel
41412 * @extends Roo.util.Observable
41413 * A lightweight tab container.
41417 // basic tabs 1, built from existing content
41418 var tabs = new Roo.TabPanel("tabs1");
41419 tabs.addTab("script", "View Script");
41420 tabs.addTab("markup", "View Markup");
41421 tabs.activate("script");
41423 // more advanced tabs, built from javascript
41424 var jtabs = new Roo.TabPanel("jtabs");
41425 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41427 // set up the UpdateManager
41428 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41429 var updater = tab2.getUpdateManager();
41430 updater.setDefaultUrl("ajax1.htm");
41431 tab2.on('activate', updater.refresh, updater, true);
41433 // Use setUrl for Ajax loading
41434 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41435 tab3.setUrl("ajax2.htm", null, true);
41438 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41441 jtabs.activate("jtabs-1");
41444 * Create a new TabPanel.
41445 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41446 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41448 Roo.bootstrap.panel.Tabs = function(config){
41450 * The container element for this TabPanel.
41451 * @type Roo.Element
41453 this.el = Roo.get(config.el);
41456 if(typeof config == "boolean"){
41457 this.tabPosition = config ? "bottom" : "top";
41459 Roo.apply(this, config);
41463 if(this.tabPosition == "bottom"){
41464 // if tabs are at the bottom = create the body first.
41465 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41466 this.el.addClass("roo-tabs-bottom");
41468 // next create the tabs holders
41470 if (this.tabPosition == "west"){
41472 var reg = this.region; // fake it..
41474 if (!reg.mgr.parent) {
41477 reg = reg.mgr.parent.region;
41479 Roo.log("got nest?");
41481 if (reg.mgr.getRegion('west')) {
41482 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41483 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41484 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41485 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41486 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41494 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41495 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41496 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41497 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41502 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41505 // finally - if tabs are at the top, then create the body last..
41506 if(this.tabPosition != "bottom"){
41507 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41508 * @type Roo.Element
41510 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41511 this.el.addClass("roo-tabs-top");
41515 this.bodyEl.setStyle("position", "relative");
41517 this.active = null;
41518 this.activateDelegate = this.activate.createDelegate(this);
41523 * Fires when the active tab changes
41524 * @param {Roo.TabPanel} this
41525 * @param {Roo.TabPanelItem} activePanel The new active tab
41529 * @event beforetabchange
41530 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41531 * @param {Roo.TabPanel} this
41532 * @param {Object} e Set cancel to true on this object to cancel the tab change
41533 * @param {Roo.TabPanelItem} tab The tab being changed to
41535 "beforetabchange" : true
41538 Roo.EventManager.onWindowResize(this.onResize, this);
41539 this.cpad = this.el.getPadding("lr");
41540 this.hiddenCount = 0;
41543 // toolbar on the tabbar support...
41544 if (this.toolbar) {
41545 alert("no toolbar support yet");
41546 this.toolbar = false;
41548 var tcfg = this.toolbar;
41549 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41550 this.toolbar = new Roo.Toolbar(tcfg);
41551 if (Roo.isSafari) {
41552 var tbl = tcfg.container.child('table', true);
41553 tbl.setAttribute('width', '100%');
41561 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41564 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41566 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41568 tabPosition : "top",
41570 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41572 currentTabWidth : 0,
41574 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41578 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41582 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41584 preferredTabWidth : 175,
41586 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41588 resizeTabs : false,
41590 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41592 monitorResize : true,
41594 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41596 toolbar : false, // set by caller..
41598 region : false, /// set by caller
41600 disableTooltips : true, // not used yet...
41603 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41604 * @param {String} id The id of the div to use <b>or create</b>
41605 * @param {String} text The text for the tab
41606 * @param {String} content (optional) Content to put in the TabPanelItem body
41607 * @param {Boolean} closable (optional) True to create a close icon on the tab
41608 * @return {Roo.TabPanelItem} The created TabPanelItem
41610 addTab : function(id, text, content, closable, tpl)
41612 var item = new Roo.bootstrap.panel.TabItem({
41616 closable : closable,
41619 this.addTabItem(item);
41621 item.setContent(content);
41627 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41628 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41629 * @return {Roo.TabPanelItem}
41631 getTab : function(id){
41632 return this.items[id];
41636 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41637 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41639 hideTab : function(id){
41640 var t = this.items[id];
41643 this.hiddenCount++;
41644 this.autoSizeTabs();
41649 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41650 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41652 unhideTab : function(id){
41653 var t = this.items[id];
41655 t.setHidden(false);
41656 this.hiddenCount--;
41657 this.autoSizeTabs();
41662 * Adds an existing {@link Roo.TabPanelItem}.
41663 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41665 addTabItem : function(item)
41667 this.items[item.id] = item;
41668 this.items.push(item);
41669 this.autoSizeTabs();
41670 // if(this.resizeTabs){
41671 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41672 // this.autoSizeTabs();
41674 // item.autoSize();
41679 * Removes a {@link Roo.TabPanelItem}.
41680 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41682 removeTab : function(id){
41683 var items = this.items;
41684 var tab = items[id];
41685 if(!tab) { return; }
41686 var index = items.indexOf(tab);
41687 if(this.active == tab && items.length > 1){
41688 var newTab = this.getNextAvailable(index);
41693 this.stripEl.dom.removeChild(tab.pnode.dom);
41694 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41695 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41697 items.splice(index, 1);
41698 delete this.items[tab.id];
41699 tab.fireEvent("close", tab);
41700 tab.purgeListeners();
41701 this.autoSizeTabs();
41704 getNextAvailable : function(start){
41705 var items = this.items;
41707 // look for a next tab that will slide over to
41708 // replace the one being removed
41709 while(index < items.length){
41710 var item = items[++index];
41711 if(item && !item.isHidden()){
41715 // if one isn't found select the previous tab (on the left)
41718 var item = items[--index];
41719 if(item && !item.isHidden()){
41727 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41728 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41730 disableTab : function(id){
41731 var tab = this.items[id];
41732 if(tab && this.active != tab){
41738 * Enables a {@link Roo.TabPanelItem} that is disabled.
41739 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41741 enableTab : function(id){
41742 var tab = this.items[id];
41747 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41748 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41749 * @return {Roo.TabPanelItem} The TabPanelItem.
41751 activate : function(id)
41753 //Roo.log('activite:' + id);
41755 var tab = this.items[id];
41759 if(tab == this.active || tab.disabled){
41763 this.fireEvent("beforetabchange", this, e, tab);
41764 if(e.cancel !== true && !tab.disabled){
41766 this.active.hide();
41768 this.active = this.items[id];
41769 this.active.show();
41770 this.fireEvent("tabchange", this, this.active);
41776 * Gets the active {@link Roo.TabPanelItem}.
41777 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41779 getActiveTab : function(){
41780 return this.active;
41784 * Updates the tab body element to fit the height of the container element
41785 * for overflow scrolling
41786 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41788 syncHeight : function(targetHeight){
41789 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41790 var bm = this.bodyEl.getMargins();
41791 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41792 this.bodyEl.setHeight(newHeight);
41796 onResize : function(){
41797 if(this.monitorResize){
41798 this.autoSizeTabs();
41803 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41805 beginUpdate : function(){
41806 this.updating = true;
41810 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41812 endUpdate : function(){
41813 this.updating = false;
41814 this.autoSizeTabs();
41818 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41820 autoSizeTabs : function()
41822 var count = this.items.length;
41823 var vcount = count - this.hiddenCount;
41826 this.stripEl.hide();
41828 this.stripEl.show();
41831 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41836 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41837 var availWidth = Math.floor(w / vcount);
41838 var b = this.stripBody;
41839 if(b.getWidth() > w){
41840 var tabs = this.items;
41841 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41842 if(availWidth < this.minTabWidth){
41843 /*if(!this.sleft){ // incomplete scrolling code
41844 this.createScrollButtons();
41847 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41850 if(this.currentTabWidth < this.preferredTabWidth){
41851 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41857 * Returns the number of tabs in this TabPanel.
41860 getCount : function(){
41861 return this.items.length;
41865 * Resizes all the tabs to the passed width
41866 * @param {Number} The new width
41868 setTabWidth : function(width){
41869 this.currentTabWidth = width;
41870 for(var i = 0, len = this.items.length; i < len; i++) {
41871 if(!this.items[i].isHidden()) {
41872 this.items[i].setWidth(width);
41878 * Destroys this TabPanel
41879 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41881 destroy : function(removeEl){
41882 Roo.EventManager.removeResizeListener(this.onResize, this);
41883 for(var i = 0, len = this.items.length; i < len; i++){
41884 this.items[i].purgeListeners();
41886 if(removeEl === true){
41887 this.el.update("");
41892 createStrip : function(container)
41894 var strip = document.createElement("nav");
41895 strip.className = Roo.bootstrap.version == 4 ?
41896 "navbar-light bg-light" :
41897 "navbar navbar-default"; //"x-tabs-wrap";
41898 container.appendChild(strip);
41902 createStripList : function(strip)
41904 // div wrapper for retard IE
41905 // returns the "tr" element.
41906 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41907 //'<div class="x-tabs-strip-wrap">'+
41908 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41909 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41910 return strip.firstChild; //.firstChild.firstChild.firstChild;
41912 createBody : function(container)
41914 var body = document.createElement("div");
41915 Roo.id(body, "tab-body");
41916 //Roo.fly(body).addClass("x-tabs-body");
41917 Roo.fly(body).addClass("tab-content");
41918 container.appendChild(body);
41921 createItemBody :function(bodyEl, id){
41922 var body = Roo.getDom(id);
41924 body = document.createElement("div");
41927 //Roo.fly(body).addClass("x-tabs-item-body");
41928 Roo.fly(body).addClass("tab-pane");
41929 bodyEl.insertBefore(body, bodyEl.firstChild);
41933 createStripElements : function(stripEl, text, closable, tpl)
41935 var td = document.createElement("li"); // was td..
41936 td.className = 'nav-item';
41938 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41941 stripEl.appendChild(td);
41943 td.className = "x-tabs-closable";
41944 if(!this.closeTpl){
41945 this.closeTpl = new Roo.Template(
41946 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41947 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41948 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41951 var el = this.closeTpl.overwrite(td, {"text": text});
41952 var close = el.getElementsByTagName("div")[0];
41953 var inner = el.getElementsByTagName("em")[0];
41954 return {"el": el, "close": close, "inner": inner};
41957 // not sure what this is..
41958 // if(!this.tabTpl){
41959 //this.tabTpl = new Roo.Template(
41960 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41961 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41963 // this.tabTpl = new Roo.Template(
41964 // '<a href="#">' +
41965 // '<span unselectable="on"' +
41966 // (this.disableTooltips ? '' : ' title="{text}"') +
41967 // ' >{text}</span></a>'
41973 var template = tpl || this.tabTpl || false;
41976 template = new Roo.Template(
41977 Roo.bootstrap.version == 4 ?
41979 '<a class="nav-link" href="#" unselectable="on"' +
41980 (this.disableTooltips ? '' : ' title="{text}"') +
41983 '<a class="nav-link" href="#">' +
41984 '<span unselectable="on"' +
41985 (this.disableTooltips ? '' : ' title="{text}"') +
41986 ' >{text}</span></a>'
41991 switch (typeof(template)) {
41995 template = new Roo.Template(template);
42001 var el = template.overwrite(td, {"text": text});
42003 var inner = el.getElementsByTagName("span")[0];
42005 return {"el": el, "inner": inner};
42013 * @class Roo.TabPanelItem
42014 * @extends Roo.util.Observable
42015 * Represents an individual item (tab plus body) in a TabPanel.
42016 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42017 * @param {String} id The id of this TabPanelItem
42018 * @param {String} text The text for the tab of this TabPanelItem
42019 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42021 Roo.bootstrap.panel.TabItem = function(config){
42023 * The {@link Roo.TabPanel} this TabPanelItem belongs to
42024 * @type Roo.TabPanel
42026 this.tabPanel = config.panel;
42028 * The id for this TabPanelItem
42031 this.id = config.id;
42033 this.disabled = false;
42035 this.text = config.text;
42037 this.loaded = false;
42038 this.closable = config.closable;
42041 * The body element for this TabPanelItem.
42042 * @type Roo.Element
42044 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42045 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42046 this.bodyEl.setStyle("display", "block");
42047 this.bodyEl.setStyle("zoom", "1");
42048 //this.hideAction();
42050 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42052 this.el = Roo.get(els.el);
42053 this.inner = Roo.get(els.inner, true);
42054 this.textEl = Roo.bootstrap.version == 4 ?
42055 this.el : Roo.get(this.el.dom.firstChild, true);
42057 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42058 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42061 // this.el.on("mousedown", this.onTabMouseDown, this);
42062 this.el.on("click", this.onTabClick, this);
42064 if(config.closable){
42065 var c = Roo.get(els.close, true);
42066 c.dom.title = this.closeText;
42067 c.addClassOnOver("close-over");
42068 c.on("click", this.closeClick, this);
42074 * Fires when this tab becomes the active tab.
42075 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42076 * @param {Roo.TabPanelItem} this
42080 * @event beforeclose
42081 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42082 * @param {Roo.TabPanelItem} this
42083 * @param {Object} e Set cancel to true on this object to cancel the close.
42085 "beforeclose": true,
42088 * Fires when this tab is closed.
42089 * @param {Roo.TabPanelItem} this
42093 * @event deactivate
42094 * Fires when this tab is no longer the active tab.
42095 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42096 * @param {Roo.TabPanelItem} this
42098 "deactivate" : true
42100 this.hidden = false;
42102 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42105 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42107 purgeListeners : function(){
42108 Roo.util.Observable.prototype.purgeListeners.call(this);
42109 this.el.removeAllListeners();
42112 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42115 this.status_node.addClass("active");
42118 this.tabPanel.stripWrap.repaint();
42120 this.fireEvent("activate", this.tabPanel, this);
42124 * Returns true if this tab is the active tab.
42125 * @return {Boolean}
42127 isActive : function(){
42128 return this.tabPanel.getActiveTab() == this;
42132 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42135 this.status_node.removeClass("active");
42137 this.fireEvent("deactivate", this.tabPanel, this);
42140 hideAction : function(){
42141 this.bodyEl.hide();
42142 this.bodyEl.setStyle("position", "absolute");
42143 this.bodyEl.setLeft("-20000px");
42144 this.bodyEl.setTop("-20000px");
42147 showAction : function(){
42148 this.bodyEl.setStyle("position", "relative");
42149 this.bodyEl.setTop("");
42150 this.bodyEl.setLeft("");
42151 this.bodyEl.show();
42155 * Set the tooltip for the tab.
42156 * @param {String} tooltip The tab's tooltip
42158 setTooltip : function(text){
42159 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42160 this.textEl.dom.qtip = text;
42161 this.textEl.dom.removeAttribute('title');
42163 this.textEl.dom.title = text;
42167 onTabClick : function(e){
42168 e.preventDefault();
42169 this.tabPanel.activate(this.id);
42172 onTabMouseDown : function(e){
42173 e.preventDefault();
42174 this.tabPanel.activate(this.id);
42177 getWidth : function(){
42178 return this.inner.getWidth();
42181 setWidth : function(width){
42182 var iwidth = width - this.linode.getPadding("lr");
42183 this.inner.setWidth(iwidth);
42184 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42185 this.linode.setWidth(width);
42189 * Show or hide the tab
42190 * @param {Boolean} hidden True to hide or false to show.
42192 setHidden : function(hidden){
42193 this.hidden = hidden;
42194 this.linode.setStyle("display", hidden ? "none" : "");
42198 * Returns true if this tab is "hidden"
42199 * @return {Boolean}
42201 isHidden : function(){
42202 return this.hidden;
42206 * Returns the text for this tab
42209 getText : function(){
42213 autoSize : function(){
42214 //this.el.beginMeasure();
42215 this.textEl.setWidth(1);
42217 * #2804 [new] Tabs in Roojs
42218 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42220 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42221 //this.el.endMeasure();
42225 * Sets the text for the tab (Note: this also sets the tooltip text)
42226 * @param {String} text The tab's text and tooltip
42228 setText : function(text){
42230 this.textEl.update(text);
42231 this.setTooltip(text);
42232 //if(!this.tabPanel.resizeTabs){
42233 // this.autoSize();
42237 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42239 activate : function(){
42240 this.tabPanel.activate(this.id);
42244 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42246 disable : function(){
42247 if(this.tabPanel.active != this){
42248 this.disabled = true;
42249 this.status_node.addClass("disabled");
42254 * Enables this TabPanelItem if it was previously disabled.
42256 enable : function(){
42257 this.disabled = false;
42258 this.status_node.removeClass("disabled");
42262 * Sets the content for this TabPanelItem.
42263 * @param {String} content The content
42264 * @param {Boolean} loadScripts true to look for and load scripts
42266 setContent : function(content, loadScripts){
42267 this.bodyEl.update(content, loadScripts);
42271 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42272 * @return {Roo.UpdateManager} The UpdateManager
42274 getUpdateManager : function(){
42275 return this.bodyEl.getUpdateManager();
42279 * Set a URL to be used to load the content for this TabPanelItem.
42280 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42281 * @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)
42282 * @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)
42283 * @return {Roo.UpdateManager} The UpdateManager
42285 setUrl : function(url, params, loadOnce){
42286 if(this.refreshDelegate){
42287 this.un('activate', this.refreshDelegate);
42289 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42290 this.on("activate", this.refreshDelegate);
42291 return this.bodyEl.getUpdateManager();
42295 _handleRefresh : function(url, params, loadOnce){
42296 if(!loadOnce || !this.loaded){
42297 var updater = this.bodyEl.getUpdateManager();
42298 updater.update(url, params, this._setLoaded.createDelegate(this));
42303 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42304 * Will fail silently if the setUrl method has not been called.
42305 * This does not activate the panel, just updates its content.
42307 refresh : function(){
42308 if(this.refreshDelegate){
42309 this.loaded = false;
42310 this.refreshDelegate();
42315 _setLoaded : function(){
42316 this.loaded = true;
42320 closeClick : function(e){
42323 this.fireEvent("beforeclose", this, o);
42324 if(o.cancel !== true){
42325 this.tabPanel.removeTab(this.id);
42329 * The text displayed in the tooltip for the close icon.
42332 closeText : "Close this tab"
42335 * This script refer to:
42336 * Title: International Telephone Input
42337 * Author: Jack O'Connor
42338 * Code version: v12.1.12
42339 * Availability: https://github.com/jackocnr/intl-tel-input.git
42342 Roo.bootstrap.PhoneInputData = function() {
42345 "Afghanistan (افغانستان)",
42350 "Albania (Shqipëri)",
42355 "Algeria (الجزائر)",
42380 "Antigua and Barbuda",
42390 "Armenia (Հայաստան)",
42406 "Austria (Österreich)",
42411 "Azerbaijan (Azərbaycan)",
42421 "Bahrain (البحرين)",
42426 "Bangladesh (বাংলাদেশ)",
42436 "Belarus (Беларусь)",
42441 "Belgium (België)",
42471 "Bosnia and Herzegovina (Босна и Херцеговина)",
42486 "British Indian Ocean Territory",
42491 "British Virgin Islands",
42501 "Bulgaria (България)",
42511 "Burundi (Uburundi)",
42516 "Cambodia (កម្ពុជា)",
42521 "Cameroon (Cameroun)",
42530 ["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"]
42533 "Cape Verde (Kabu Verdi)",
42538 "Caribbean Netherlands",
42549 "Central African Republic (République centrafricaine)",
42569 "Christmas Island",
42575 "Cocos (Keeling) Islands",
42586 "Comoros (جزر القمر)",
42591 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42596 "Congo (Republic) (Congo-Brazzaville)",
42616 "Croatia (Hrvatska)",
42637 "Czech Republic (Česká republika)",
42642 "Denmark (Danmark)",
42657 "Dominican Republic (República Dominicana)",
42661 ["809", "829", "849"]
42679 "Equatorial Guinea (Guinea Ecuatorial)",
42699 "Falkland Islands (Islas Malvinas)",
42704 "Faroe Islands (Føroyar)",
42725 "French Guiana (Guyane française)",
42730 "French Polynesia (Polynésie française)",
42745 "Georgia (საქართველო)",
42750 "Germany (Deutschland)",
42770 "Greenland (Kalaallit Nunaat)",
42807 "Guinea-Bissau (Guiné Bissau)",
42832 "Hungary (Magyarország)",
42837 "Iceland (Ísland)",
42857 "Iraq (العراق)",
42873 "Israel (ישראל)",
42900 "Jordan (الأردن)",
42905 "Kazakhstan (Казахстан)",
42926 "Kuwait (الكويت)",
42931 "Kyrgyzstan (Кыргызстан)",
42941 "Latvia (Latvija)",
42946 "Lebanon (لبنان)",
42961 "Libya (ليبيا)",
42971 "Lithuania (Lietuva)",
42986 "Macedonia (FYROM) (Македонија)",
42991 "Madagascar (Madagasikara)",
43021 "Marshall Islands",
43031 "Mauritania (موريتانيا)",
43036 "Mauritius (Moris)",
43057 "Moldova (Republica Moldova)",
43067 "Mongolia (Монгол)",
43072 "Montenegro (Crna Gora)",
43082 "Morocco (المغرب)",
43088 "Mozambique (Moçambique)",
43093 "Myanmar (Burma) (မြန်မာ)",
43098 "Namibia (Namibië)",
43113 "Netherlands (Nederland)",
43118 "New Caledonia (Nouvelle-Calédonie)",
43153 "North Korea (조선 민주주의 인민 공화국)",
43158 "Northern Mariana Islands",
43174 "Pakistan (پاکستان)",
43184 "Palestine (فلسطين)",
43194 "Papua New Guinea",
43236 "Réunion (La Réunion)",
43242 "Romania (România)",
43258 "Saint Barthélemy",
43269 "Saint Kitts and Nevis",
43279 "Saint Martin (Saint-Martin (partie française))",
43285 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43290 "Saint Vincent and the Grenadines",
43305 "São Tomé and Príncipe (São Tomé e Príncipe)",
43310 "Saudi Arabia (المملكة العربية السعودية)",
43315 "Senegal (Sénégal)",
43345 "Slovakia (Slovensko)",
43350 "Slovenia (Slovenija)",
43360 "Somalia (Soomaaliya)",
43370 "South Korea (대한민국)",
43375 "South Sudan (جنوب السودان)",
43385 "Sri Lanka (ශ්රී ලංකාව)",
43390 "Sudan (السودان)",
43400 "Svalbard and Jan Mayen",
43411 "Sweden (Sverige)",
43416 "Switzerland (Schweiz)",
43421 "Syria (سوريا)",
43466 "Trinidad and Tobago",
43471 "Tunisia (تونس)",
43476 "Turkey (Türkiye)",
43486 "Turks and Caicos Islands",
43496 "U.S. Virgin Islands",
43506 "Ukraine (Україна)",
43511 "United Arab Emirates (الإمارات العربية المتحدة)",
43533 "Uzbekistan (Oʻzbekiston)",
43543 "Vatican City (Città del Vaticano)",
43554 "Vietnam (Việt Nam)",
43559 "Wallis and Futuna (Wallis-et-Futuna)",
43564 "Western Sahara (الصحراء الغربية)",
43570 "Yemen (اليمن)",
43594 * This script refer to:
43595 * Title: International Telephone Input
43596 * Author: Jack O'Connor
43597 * Code version: v12.1.12
43598 * Availability: https://github.com/jackocnr/intl-tel-input.git
43602 * @class Roo.bootstrap.PhoneInput
43603 * @extends Roo.bootstrap.TriggerField
43604 * An input with International dial-code selection
43606 * @cfg {String} defaultDialCode default '+852'
43607 * @cfg {Array} preferedCountries default []
43610 * Create a new PhoneInput.
43611 * @param {Object} config Configuration options
43614 Roo.bootstrap.PhoneInput = function(config) {
43615 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43618 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43620 listWidth: undefined,
43622 selectedClass: 'active',
43624 invalidClass : "has-warning",
43626 validClass: 'has-success',
43628 allowed: '0123456789',
43633 * @cfg {String} defaultDialCode The default dial code when initializing the input
43635 defaultDialCode: '+852',
43638 * @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
43640 preferedCountries: false,
43642 getAutoCreate : function()
43644 var data = Roo.bootstrap.PhoneInputData();
43645 var align = this.labelAlign || this.parentLabelAlign();
43648 this.allCountries = [];
43649 this.dialCodeMapping = [];
43651 for (var i = 0; i < data.length; i++) {
43653 this.allCountries[i] = {
43657 priority: c[3] || 0,
43658 areaCodes: c[4] || null
43660 this.dialCodeMapping[c[2]] = {
43663 priority: c[3] || 0,
43664 areaCodes: c[4] || null
43676 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43677 maxlength: this.max_length,
43678 cls : 'form-control tel-input',
43679 autocomplete: 'new-password'
43682 var hiddenInput = {
43685 cls: 'hidden-tel-input'
43689 hiddenInput.name = this.name;
43692 if (this.disabled) {
43693 input.disabled = true;
43696 var flag_container = {
43713 cls: this.hasFeedback ? 'has-feedback' : '',
43719 cls: 'dial-code-holder',
43726 cls: 'roo-select2-container input-group',
43733 if (this.fieldLabel.length) {
43736 tooltip: 'This field is required'
43742 cls: 'control-label',
43748 html: this.fieldLabel
43751 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43757 if(this.indicatorpos == 'right') {
43758 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43765 if(align == 'left') {
43773 if(this.labelWidth > 12){
43774 label.style = "width: " + this.labelWidth + 'px';
43776 if(this.labelWidth < 13 && this.labelmd == 0){
43777 this.labelmd = this.labelWidth;
43779 if(this.labellg > 0){
43780 label.cls += ' col-lg-' + this.labellg;
43781 input.cls += ' col-lg-' + (12 - this.labellg);
43783 if(this.labelmd > 0){
43784 label.cls += ' col-md-' + this.labelmd;
43785 container.cls += ' col-md-' + (12 - this.labelmd);
43787 if(this.labelsm > 0){
43788 label.cls += ' col-sm-' + this.labelsm;
43789 container.cls += ' col-sm-' + (12 - this.labelsm);
43791 if(this.labelxs > 0){
43792 label.cls += ' col-xs-' + this.labelxs;
43793 container.cls += ' col-xs-' + (12 - this.labelxs);
43803 var settings = this;
43805 ['xs','sm','md','lg'].map(function(size){
43806 if (settings[size]) {
43807 cfg.cls += ' col-' + size + '-' + settings[size];
43811 this.store = new Roo.data.Store({
43812 proxy : new Roo.data.MemoryProxy({}),
43813 reader : new Roo.data.JsonReader({
43824 'name' : 'dialCode',
43828 'name' : 'priority',
43832 'name' : 'areaCodes',
43839 if(!this.preferedCountries) {
43840 this.preferedCountries = [
43847 var p = this.preferedCountries.reverse();
43850 for (var i = 0; i < p.length; i++) {
43851 for (var j = 0; j < this.allCountries.length; j++) {
43852 if(this.allCountries[j].iso2 == p[i]) {
43853 var t = this.allCountries[j];
43854 this.allCountries.splice(j,1);
43855 this.allCountries.unshift(t);
43861 this.store.proxy.data = {
43863 data: this.allCountries
43869 initEvents : function()
43872 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43874 this.indicator = this.indicatorEl();
43875 this.flag = this.flagEl();
43876 this.dialCodeHolder = this.dialCodeHolderEl();
43878 this.trigger = this.el.select('div.flag-box',true).first();
43879 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43884 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43885 _this.list.setWidth(lw);
43888 this.list.on('mouseover', this.onViewOver, this);
43889 this.list.on('mousemove', this.onViewMove, this);
43890 this.inputEl().on("keyup", this.onKeyUp, this);
43891 this.inputEl().on("keypress", this.onKeyPress, this);
43893 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43895 this.view = new Roo.View(this.list, this.tpl, {
43896 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43899 this.view.on('click', this.onViewClick, this);
43900 this.setValue(this.defaultDialCode);
43903 onTriggerClick : function(e)
43905 Roo.log('trigger click');
43910 if(this.isExpanded()){
43912 this.hasFocus = false;
43914 this.store.load({});
43915 this.hasFocus = true;
43920 isExpanded : function()
43922 return this.list.isVisible();
43925 collapse : function()
43927 if(!this.isExpanded()){
43931 Roo.get(document).un('mousedown', this.collapseIf, this);
43932 Roo.get(document).un('mousewheel', this.collapseIf, this);
43933 this.fireEvent('collapse', this);
43937 expand : function()
43941 if(this.isExpanded() || !this.hasFocus){
43945 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43946 this.list.setWidth(lw);
43949 this.restrictHeight();
43951 Roo.get(document).on('mousedown', this.collapseIf, this);
43952 Roo.get(document).on('mousewheel', this.collapseIf, this);
43954 this.fireEvent('expand', this);
43957 restrictHeight : function()
43959 this.list.alignTo(this.inputEl(), this.listAlign);
43960 this.list.alignTo(this.inputEl(), this.listAlign);
43963 onViewOver : function(e, t)
43965 if(this.inKeyMode){
43968 var item = this.view.findItemFromChild(t);
43971 var index = this.view.indexOf(item);
43972 this.select(index, false);
43977 onViewClick : function(view, doFocus, el, e)
43979 var index = this.view.getSelectedIndexes()[0];
43981 var r = this.store.getAt(index);
43984 this.onSelect(r, index);
43986 if(doFocus !== false && !this.blockFocus){
43987 this.inputEl().focus();
43991 onViewMove : function(e, t)
43993 this.inKeyMode = false;
43996 select : function(index, scrollIntoView)
43998 this.selectedIndex = index;
43999 this.view.select(index);
44000 if(scrollIntoView !== false){
44001 var el = this.view.getNode(index);
44003 this.list.scrollChildIntoView(el, false);
44008 createList : function()
44010 this.list = Roo.get(document.body).createChild({
44012 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44013 style: 'display:none'
44016 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44019 collapseIf : function(e)
44021 var in_combo = e.within(this.el);
44022 var in_list = e.within(this.list);
44023 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44025 if (in_combo || in_list || is_list) {
44031 onSelect : function(record, index)
44033 if(this.fireEvent('beforeselect', this, record, index) !== false){
44035 this.setFlagClass(record.data.iso2);
44036 this.setDialCode(record.data.dialCode);
44037 this.hasFocus = false;
44039 this.fireEvent('select', this, record, index);
44043 flagEl : function()
44045 var flag = this.el.select('div.flag',true).first();
44052 dialCodeHolderEl : function()
44054 var d = this.el.select('input.dial-code-holder',true).first();
44061 setDialCode : function(v)
44063 this.dialCodeHolder.dom.value = '+'+v;
44066 setFlagClass : function(n)
44068 this.flag.dom.className = 'flag '+n;
44071 getValue : function()
44073 var v = this.inputEl().getValue();
44074 if(this.dialCodeHolder) {
44075 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44080 setValue : function(v)
44082 var d = this.getDialCode(v);
44084 //invalid dial code
44085 if(v.length == 0 || !d || d.length == 0) {
44087 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44088 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44094 this.setFlagClass(this.dialCodeMapping[d].iso2);
44095 this.setDialCode(d);
44096 this.inputEl().dom.value = v.replace('+'+d,'');
44097 this.hiddenEl().dom.value = this.getValue();
44102 getDialCode : function(v)
44106 if (v.length == 0) {
44107 return this.dialCodeHolder.dom.value;
44111 if (v.charAt(0) != "+") {
44114 var numericChars = "";
44115 for (var i = 1; i < v.length; i++) {
44116 var c = v.charAt(i);
44119 if (this.dialCodeMapping[numericChars]) {
44120 dialCode = v.substr(1, i);
44122 if (numericChars.length == 4) {
44132 this.setValue(this.defaultDialCode);
44136 hiddenEl : function()
44138 return this.el.select('input.hidden-tel-input',true).first();
44141 // after setting val
44142 onKeyUp : function(e){
44143 this.setValue(this.getValue());
44146 onKeyPress : function(e){
44147 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44154 * @class Roo.bootstrap.MoneyField
44155 * @extends Roo.bootstrap.ComboBox
44156 * Bootstrap MoneyField class
44159 * Create a new MoneyField.
44160 * @param {Object} config Configuration options
44163 Roo.bootstrap.MoneyField = function(config) {
44165 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44169 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44172 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44174 allowDecimals : true,
44176 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44178 decimalSeparator : ".",
44180 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44182 decimalPrecision : 0,
44184 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44186 allowNegative : true,
44188 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44192 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44194 minValue : Number.NEGATIVE_INFINITY,
44196 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44198 maxValue : Number.MAX_VALUE,
44200 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44202 minText : "The minimum value for this field is {0}",
44204 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44206 maxText : "The maximum value for this field is {0}",
44208 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44209 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44211 nanText : "{0} is not a valid number",
44213 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44217 * @cfg {String} defaults currency of the MoneyField
44218 * value should be in lkey
44220 defaultCurrency : false,
44222 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44224 thousandsDelimiter : false,
44226 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44237 getAutoCreate : function()
44239 var align = this.labelAlign || this.parentLabelAlign();
44251 cls : 'form-control roo-money-amount-input',
44252 autocomplete: 'new-password'
44255 var hiddenInput = {
44259 cls: 'hidden-number-input'
44262 if(this.max_length) {
44263 input.maxlength = this.max_length;
44267 hiddenInput.name = this.name;
44270 if (this.disabled) {
44271 input.disabled = true;
44274 var clg = 12 - this.inputlg;
44275 var cmd = 12 - this.inputmd;
44276 var csm = 12 - this.inputsm;
44277 var cxs = 12 - this.inputxs;
44281 cls : 'row roo-money-field',
44285 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44289 cls: 'roo-select2-container input-group',
44293 cls : 'form-control roo-money-currency-input',
44294 autocomplete: 'new-password',
44296 name : this.currencyName
44300 cls : 'input-group-addon',
44314 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44318 cls: this.hasFeedback ? 'has-feedback' : '',
44329 if (this.fieldLabel.length) {
44332 tooltip: 'This field is required'
44338 cls: 'control-label',
44344 html: this.fieldLabel
44347 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44353 if(this.indicatorpos == 'right') {
44354 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44361 if(align == 'left') {
44369 if(this.labelWidth > 12){
44370 label.style = "width: " + this.labelWidth + 'px';
44372 if(this.labelWidth < 13 && this.labelmd == 0){
44373 this.labelmd = this.labelWidth;
44375 if(this.labellg > 0){
44376 label.cls += ' col-lg-' + this.labellg;
44377 input.cls += ' col-lg-' + (12 - this.labellg);
44379 if(this.labelmd > 0){
44380 label.cls += ' col-md-' + this.labelmd;
44381 container.cls += ' col-md-' + (12 - this.labelmd);
44383 if(this.labelsm > 0){
44384 label.cls += ' col-sm-' + this.labelsm;
44385 container.cls += ' col-sm-' + (12 - this.labelsm);
44387 if(this.labelxs > 0){
44388 label.cls += ' col-xs-' + this.labelxs;
44389 container.cls += ' col-xs-' + (12 - this.labelxs);
44400 var settings = this;
44402 ['xs','sm','md','lg'].map(function(size){
44403 if (settings[size]) {
44404 cfg.cls += ' col-' + size + '-' + settings[size];
44411 initEvents : function()
44413 this.indicator = this.indicatorEl();
44415 this.initCurrencyEvent();
44417 this.initNumberEvent();
44420 initCurrencyEvent : function()
44423 throw "can not find store for combo";
44426 this.store = Roo.factory(this.store, Roo.data);
44427 this.store.parent = this;
44431 this.triggerEl = this.el.select('.input-group-addon', true).first();
44433 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44438 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44439 _this.list.setWidth(lw);
44442 this.list.on('mouseover', this.onViewOver, this);
44443 this.list.on('mousemove', this.onViewMove, this);
44444 this.list.on('scroll', this.onViewScroll, this);
44447 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44450 this.view = new Roo.View(this.list, this.tpl, {
44451 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44454 this.view.on('click', this.onViewClick, this);
44456 this.store.on('beforeload', this.onBeforeLoad, this);
44457 this.store.on('load', this.onLoad, this);
44458 this.store.on('loadexception', this.onLoadException, this);
44460 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44461 "up" : function(e){
44462 this.inKeyMode = true;
44466 "down" : function(e){
44467 if(!this.isExpanded()){
44468 this.onTriggerClick();
44470 this.inKeyMode = true;
44475 "enter" : function(e){
44478 if(this.fireEvent("specialkey", this, e)){
44479 this.onViewClick(false);
44485 "esc" : function(e){
44489 "tab" : function(e){
44492 if(this.fireEvent("specialkey", this, e)){
44493 this.onViewClick(false);
44501 doRelay : function(foo, bar, hname){
44502 if(hname == 'down' || this.scope.isExpanded()){
44503 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44511 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44515 initNumberEvent : function(e)
44517 this.inputEl().on("keydown" , this.fireKey, this);
44518 this.inputEl().on("focus", this.onFocus, this);
44519 this.inputEl().on("blur", this.onBlur, this);
44521 this.inputEl().relayEvent('keyup', this);
44523 if(this.indicator){
44524 this.indicator.addClass('invisible');
44527 this.originalValue = this.getValue();
44529 if(this.validationEvent == 'keyup'){
44530 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44531 this.inputEl().on('keyup', this.filterValidation, this);
44533 else if(this.validationEvent !== false){
44534 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44537 if(this.selectOnFocus){
44538 this.on("focus", this.preFocus, this);
44541 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44542 this.inputEl().on("keypress", this.filterKeys, this);
44544 this.inputEl().relayEvent('keypress', this);
44547 var allowed = "0123456789";
44549 if(this.allowDecimals){
44550 allowed += this.decimalSeparator;
44553 if(this.allowNegative){
44557 if(this.thousandsDelimiter) {
44561 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44563 var keyPress = function(e){
44565 var k = e.getKey();
44567 var c = e.getCharCode();
44570 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44571 allowed.indexOf(String.fromCharCode(c)) === -1
44577 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44581 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44586 this.inputEl().on("keypress", keyPress, this);
44590 onTriggerClick : function(e)
44597 this.loadNext = false;
44599 if(this.isExpanded()){
44604 this.hasFocus = true;
44606 if(this.triggerAction == 'all') {
44607 this.doQuery(this.allQuery, true);
44611 this.doQuery(this.getRawValue());
44614 getCurrency : function()
44616 var v = this.currencyEl().getValue();
44621 restrictHeight : function()
44623 this.list.alignTo(this.currencyEl(), this.listAlign);
44624 this.list.alignTo(this.currencyEl(), this.listAlign);
44627 onViewClick : function(view, doFocus, el, e)
44629 var index = this.view.getSelectedIndexes()[0];
44631 var r = this.store.getAt(index);
44634 this.onSelect(r, index);
44638 onSelect : function(record, index){
44640 if(this.fireEvent('beforeselect', this, record, index) !== false){
44642 this.setFromCurrencyData(index > -1 ? record.data : false);
44646 this.fireEvent('select', this, record, index);
44650 setFromCurrencyData : function(o)
44654 this.lastCurrency = o;
44656 if (this.currencyField) {
44657 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44659 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44662 this.lastSelectionText = currency;
44664 //setting default currency
44665 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44666 this.setCurrency(this.defaultCurrency);
44670 this.setCurrency(currency);
44673 setFromData : function(o)
44677 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44679 this.setFromCurrencyData(c);
44684 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44686 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44689 this.setValue(value);
44693 setCurrency : function(v)
44695 this.currencyValue = v;
44698 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44703 setValue : function(v)
44705 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44711 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44713 this.inputEl().dom.value = (v == '') ? '' :
44714 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44716 if(!this.allowZero && v === '0') {
44717 this.hiddenEl().dom.value = '';
44718 this.inputEl().dom.value = '';
44725 getRawValue : function()
44727 var v = this.inputEl().getValue();
44732 getValue : function()
44734 return this.fixPrecision(this.parseValue(this.getRawValue()));
44737 parseValue : function(value)
44739 if(this.thousandsDelimiter) {
44741 r = new RegExp(",", "g");
44742 value = value.replace(r, "");
44745 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44746 return isNaN(value) ? '' : value;
44750 fixPrecision : function(value)
44752 if(this.thousandsDelimiter) {
44754 r = new RegExp(",", "g");
44755 value = value.replace(r, "");
44758 var nan = isNaN(value);
44760 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44761 return nan ? '' : value;
44763 return parseFloat(value).toFixed(this.decimalPrecision);
44766 decimalPrecisionFcn : function(v)
44768 return Math.floor(v);
44771 validateValue : function(value)
44773 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44777 var num = this.parseValue(value);
44780 this.markInvalid(String.format(this.nanText, value));
44784 if(num < this.minValue){
44785 this.markInvalid(String.format(this.minText, this.minValue));
44789 if(num > this.maxValue){
44790 this.markInvalid(String.format(this.maxText, this.maxValue));
44797 validate : function()
44799 if(this.disabled || this.allowBlank){
44804 var currency = this.getCurrency();
44806 if(this.validateValue(this.getRawValue()) && currency.length){
44811 this.markInvalid();
44815 getName: function()
44820 beforeBlur : function()
44826 var v = this.parseValue(this.getRawValue());
44833 onBlur : function()
44837 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44838 //this.el.removeClass(this.focusClass);
44841 this.hasFocus = false;
44843 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44847 var v = this.getValue();
44849 if(String(v) !== String(this.startValue)){
44850 this.fireEvent('change', this, v, this.startValue);
44853 this.fireEvent("blur", this);
44856 inputEl : function()
44858 return this.el.select('.roo-money-amount-input', true).first();
44861 currencyEl : function()
44863 return this.el.select('.roo-money-currency-input', true).first();
44866 hiddenEl : function()
44868 return this.el.select('input.hidden-number-input',true).first();
44872 * @class Roo.bootstrap.BezierSignature
44873 * @extends Roo.bootstrap.Component
44874 * Bootstrap BezierSignature class
44875 * This script refer to:
44876 * Title: Signature Pad
44878 * Availability: https://github.com/szimek/signature_pad
44881 * Create a new BezierSignature
44882 * @param {Object} config The config object
44885 Roo.bootstrap.BezierSignature = function(config){
44886 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44892 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44899 mouse_btn_down: true,
44902 * @cfg {int} canvas height
44904 canvas_height: '200px',
44907 * @cfg {float|function} Radius of a single dot.
44912 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44917 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44922 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44927 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44932 * @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.
44934 bg_color: 'rgba(0, 0, 0, 0)',
44937 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44939 dot_color: 'black',
44942 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44944 velocity_filter_weight: 0.7,
44947 * @cfg {function} Callback when stroke begin.
44952 * @cfg {function} Callback when stroke end.
44956 getAutoCreate : function()
44958 var cls = 'roo-signature column';
44961 cls += ' ' + this.cls;
44971 for(var i = 0; i < col_sizes.length; i++) {
44972 if(this[col_sizes[i]]) {
44973 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44983 cls: 'roo-signature-body',
44987 cls: 'roo-signature-body-canvas',
44988 height: this.canvas_height,
44989 width: this.canvas_width
44996 style: 'display: none'
45004 initEvents: function()
45006 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45008 var canvas = this.canvasEl();
45010 // mouse && touch event swapping...
45011 canvas.dom.style.touchAction = 'none';
45012 canvas.dom.style.msTouchAction = 'none';
45014 this.mouse_btn_down = false;
45015 canvas.on('mousedown', this._handleMouseDown, this);
45016 canvas.on('mousemove', this._handleMouseMove, this);
45017 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45019 if (window.PointerEvent) {
45020 canvas.on('pointerdown', this._handleMouseDown, this);
45021 canvas.on('pointermove', this._handleMouseMove, this);
45022 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45025 if ('ontouchstart' in window) {
45026 canvas.on('touchstart', this._handleTouchStart, this);
45027 canvas.on('touchmove', this._handleTouchMove, this);
45028 canvas.on('touchend', this._handleTouchEnd, this);
45031 Roo.EventManager.onWindowResize(this.resize, this, true);
45033 // file input event
45034 this.fileEl().on('change', this.uploadImage, this);
45041 resize: function(){
45043 var canvas = this.canvasEl().dom;
45044 var ctx = this.canvasElCtx();
45045 var img_data = false;
45047 if(canvas.width > 0) {
45048 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45050 // setting canvas width will clean img data
45053 var style = window.getComputedStyle ?
45054 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45056 var padding_left = parseInt(style.paddingLeft) || 0;
45057 var padding_right = parseInt(style.paddingRight) || 0;
45059 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45062 ctx.putImageData(img_data, 0, 0);
45066 _handleMouseDown: function(e)
45068 if (e.browserEvent.which === 1) {
45069 this.mouse_btn_down = true;
45070 this.strokeBegin(e);
45074 _handleMouseMove: function (e)
45076 if (this.mouse_btn_down) {
45077 this.strokeMoveUpdate(e);
45081 _handleMouseUp: function (e)
45083 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45084 this.mouse_btn_down = false;
45089 _handleTouchStart: function (e) {
45091 e.preventDefault();
45092 if (e.browserEvent.targetTouches.length === 1) {
45093 // var touch = e.browserEvent.changedTouches[0];
45094 // this.strokeBegin(touch);
45096 this.strokeBegin(e); // assume e catching the correct xy...
45100 _handleTouchMove: function (e) {
45101 e.preventDefault();
45102 // var touch = event.targetTouches[0];
45103 // _this._strokeMoveUpdate(touch);
45104 this.strokeMoveUpdate(e);
45107 _handleTouchEnd: function (e) {
45108 var wasCanvasTouched = e.target === this.canvasEl().dom;
45109 if (wasCanvasTouched) {
45110 e.preventDefault();
45111 // var touch = event.changedTouches[0];
45112 // _this._strokeEnd(touch);
45117 reset: function () {
45118 this._lastPoints = [];
45119 this._lastVelocity = 0;
45120 this._lastWidth = (this.min_width + this.max_width) / 2;
45121 this.canvasElCtx().fillStyle = this.dot_color;
45124 strokeMoveUpdate: function(e)
45126 this.strokeUpdate(e);
45128 if (this.throttle) {
45129 this.throttleStroke(this.strokeUpdate, this.throttle);
45132 this.strokeUpdate(e);
45136 strokeBegin: function(e)
45138 var newPointGroup = {
45139 color: this.dot_color,
45143 if (typeof this.onBegin === 'function') {
45147 this.curve_data.push(newPointGroup);
45149 this.strokeUpdate(e);
45152 strokeUpdate: function(e)
45154 var rect = this.canvasEl().dom.getBoundingClientRect();
45155 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45156 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45157 var lastPoints = lastPointGroup.points;
45158 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45159 var isLastPointTooClose = lastPoint
45160 ? point.distanceTo(lastPoint) <= this.min_distance
45162 var color = lastPointGroup.color;
45163 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45164 var curve = this.addPoint(point);
45166 this.drawDot({color: color, point: point});
45169 this.drawCurve({color: color, curve: curve});
45179 strokeEnd: function(e)
45181 this.strokeUpdate(e);
45182 if (typeof this.onEnd === 'function') {
45187 addPoint: function (point) {
45188 var _lastPoints = this._lastPoints;
45189 _lastPoints.push(point);
45190 if (_lastPoints.length > 2) {
45191 if (_lastPoints.length === 3) {
45192 _lastPoints.unshift(_lastPoints[0]);
45194 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45195 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45196 _lastPoints.shift();
45202 calculateCurveWidths: function (startPoint, endPoint) {
45203 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45204 (1 - this.velocity_filter_weight) * this._lastVelocity;
45206 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45209 start: this._lastWidth
45212 this._lastVelocity = velocity;
45213 this._lastWidth = newWidth;
45217 drawDot: function (_a) {
45218 var color = _a.color, point = _a.point;
45219 var ctx = this.canvasElCtx();
45220 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45222 this.drawCurveSegment(point.x, point.y, width);
45224 ctx.fillStyle = color;
45228 drawCurve: function (_a) {
45229 var color = _a.color, curve = _a.curve;
45230 var ctx = this.canvasElCtx();
45231 var widthDelta = curve.endWidth - curve.startWidth;
45232 var drawSteps = Math.floor(curve.length()) * 2;
45234 ctx.fillStyle = color;
45235 for (var i = 0; i < drawSteps; i += 1) {
45236 var t = i / drawSteps;
45242 var x = uuu * curve.startPoint.x;
45243 x += 3 * uu * t * curve.control1.x;
45244 x += 3 * u * tt * curve.control2.x;
45245 x += ttt * curve.endPoint.x;
45246 var y = uuu * curve.startPoint.y;
45247 y += 3 * uu * t * curve.control1.y;
45248 y += 3 * u * tt * curve.control2.y;
45249 y += ttt * curve.endPoint.y;
45250 var width = curve.startWidth + ttt * widthDelta;
45251 this.drawCurveSegment(x, y, width);
45257 drawCurveSegment: function (x, y, width) {
45258 var ctx = this.canvasElCtx();
45260 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45261 this.is_empty = false;
45266 var ctx = this.canvasElCtx();
45267 var canvas = this.canvasEl().dom;
45268 ctx.fillStyle = this.bg_color;
45269 ctx.clearRect(0, 0, canvas.width, canvas.height);
45270 ctx.fillRect(0, 0, canvas.width, canvas.height);
45271 this.curve_data = [];
45273 this.is_empty = true;
45278 return this.el.select('input',true).first();
45281 canvasEl: function()
45283 return this.el.select('canvas',true).first();
45286 canvasElCtx: function()
45288 return this.el.select('canvas',true).first().dom.getContext('2d');
45291 getImage: function(type)
45293 if(this.is_empty) {
45298 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45301 drawFromImage: function(img_src)
45303 var img = new Image();
45305 img.onload = function(){
45306 this.canvasElCtx().drawImage(img, 0, 0);
45311 this.is_empty = false;
45314 selectImage: function()
45316 this.fileEl().dom.click();
45319 uploadImage: function(e)
45321 var reader = new FileReader();
45323 reader.onload = function(e){
45324 var img = new Image();
45325 img.onload = function(){
45327 this.canvasElCtx().drawImage(img, 0, 0);
45329 img.src = e.target.result;
45332 reader.readAsDataURL(e.target.files[0]);
45335 // Bezier Point Constructor
45336 Point: (function () {
45337 function Point(x, y, time) {
45340 this.time = time || Date.now();
45342 Point.prototype.distanceTo = function (start) {
45343 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45345 Point.prototype.equals = function (other) {
45346 return this.x === other.x && this.y === other.y && this.time === other.time;
45348 Point.prototype.velocityFrom = function (start) {
45349 return this.time !== start.time
45350 ? this.distanceTo(start) / (this.time - start.time)
45357 // Bezier Constructor
45358 Bezier: (function () {
45359 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45360 this.startPoint = startPoint;
45361 this.control2 = control2;
45362 this.control1 = control1;
45363 this.endPoint = endPoint;
45364 this.startWidth = startWidth;
45365 this.endWidth = endWidth;
45367 Bezier.fromPoints = function (points, widths, scope) {
45368 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45369 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45370 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45372 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45373 var dx1 = s1.x - s2.x;
45374 var dy1 = s1.y - s2.y;
45375 var dx2 = s2.x - s3.x;
45376 var dy2 = s2.y - s3.y;
45377 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45378 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45379 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45380 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45381 var dxm = m1.x - m2.x;
45382 var dym = m1.y - m2.y;
45383 var k = l2 / (l1 + l2);
45384 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45385 var tx = s2.x - cm.x;
45386 var ty = s2.y - cm.y;
45388 c1: new scope.Point(m1.x + tx, m1.y + ty),
45389 c2: new scope.Point(m2.x + tx, m2.y + ty)
45392 Bezier.prototype.length = function () {
45397 for (var i = 0; i <= steps; i += 1) {
45399 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45400 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45402 var xdiff = cx - px;
45403 var ydiff = cy - py;
45404 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45411 Bezier.prototype.point = function (t, start, c1, c2, end) {
45412 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45413 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45414 + (3.0 * c2 * (1.0 - t) * t * t)
45415 + (end * t * t * t);
45420 throttleStroke: function(fn, wait) {
45421 if (wait === void 0) { wait = 250; }
45423 var timeout = null;
45427 var later = function () {
45428 previous = Date.now();
45430 result = fn.apply(storedContext, storedArgs);
45432 storedContext = null;
45436 return function wrapper() {
45438 for (var _i = 0; _i < arguments.length; _i++) {
45439 args[_i] = arguments[_i];
45441 var now = Date.now();
45442 var remaining = wait - (now - previous);
45443 storedContext = this;
45445 if (remaining <= 0 || remaining > wait) {
45447 clearTimeout(timeout);
45451 result = fn.apply(storedContext, storedArgs);
45453 storedContext = null;
45457 else if (!timeout) {
45458 timeout = window.setTimeout(later, remaining);