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 splithide = 'display: none';
9136 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9139 splithide = 'display:none;';
9142 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9143 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9148 //Roo.log(styles.join(''));
9149 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9155 onContextMenu : function(e, t)
9157 this.processEvent("contextmenu", e);
9160 processEvent : function(name, e)
9162 if (name != 'touchstart' ) {
9163 this.fireEvent(name, e);
9166 var t = e.getTarget();
9168 var cell = Roo.get(t);
9174 if(cell.findParent('tfoot', false, true)){
9178 if(cell.findParent('thead', false, true)){
9180 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9181 cell = Roo.get(t).findParent('th', false, true);
9183 Roo.log("failed to find th in thead?");
9184 Roo.log(e.getTarget());
9189 var cellIndex = cell.dom.cellIndex;
9191 var ename = name == 'touchstart' ? 'click' : name;
9192 this.fireEvent("header" + ename, this, cellIndex, e);
9197 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9198 cell = Roo.get(t).findParent('td', false, true);
9200 Roo.log("failed to find th in tbody?");
9201 Roo.log(e.getTarget());
9206 var row = cell.findParent('tr', false, true);
9207 var cellIndex = cell.dom.cellIndex;
9208 var rowIndex = row.dom.rowIndex - 1;
9212 this.fireEvent("row" + name, this, rowIndex, e);
9216 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9222 onMouseover : function(e, el)
9224 var cell = Roo.get(el);
9230 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9231 cell = cell.findParent('td', false, true);
9234 var row = cell.findParent('tr', false, true);
9235 var cellIndex = cell.dom.cellIndex;
9236 var rowIndex = row.dom.rowIndex - 1; // start from 0
9238 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9242 onMouseout : function(e, el)
9244 var cell = Roo.get(el);
9250 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9251 cell = cell.findParent('td', false, true);
9254 var row = cell.findParent('tr', false, true);
9255 var cellIndex = cell.dom.cellIndex;
9256 var rowIndex = row.dom.rowIndex - 1; // start from 0
9258 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9262 onClick : function(e, el)
9264 var cell = Roo.get(el);
9266 if(!cell || (!this.cellSelection && !this.rowSelection)){
9270 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9271 cell = cell.findParent('td', false, true);
9274 if(!cell || typeof(cell) == 'undefined'){
9278 var row = cell.findParent('tr', false, true);
9280 if(!row || typeof(row) == 'undefined'){
9284 var cellIndex = cell.dom.cellIndex;
9285 var rowIndex = this.getRowIndex(row);
9287 // why??? - should these not be based on SelectionModel?
9288 //if(this.cellSelection){
9289 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9292 //if(this.rowSelection){
9293 this.fireEvent('rowclick', this, row, rowIndex, e);
9298 onDblClick : function(e,el)
9300 var cell = Roo.get(el);
9302 if(!cell || (!this.cellSelection && !this.rowSelection)){
9306 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9307 cell = cell.findParent('td', false, true);
9310 if(!cell || typeof(cell) == 'undefined'){
9314 var row = cell.findParent('tr', false, true);
9316 if(!row || typeof(row) == 'undefined'){
9320 var cellIndex = cell.dom.cellIndex;
9321 var rowIndex = this.getRowIndex(row);
9323 if(this.cellSelection){
9324 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9327 if(this.rowSelection){
9328 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9331 findRowIndex : function(el)
9333 var cell = Roo.get(el);
9337 var row = cell.findParent('tr', false, true);
9339 if(!row || typeof(row) == 'undefined'){
9342 return this.getRowIndex(row);
9344 sort : function(e,el)
9346 var col = Roo.get(el);
9348 if(!col.hasClass('sortable')){
9352 var sort = col.attr('sort');
9355 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9359 this.store.sortInfo = {field : sort, direction : dir};
9362 Roo.log("calling footer first");
9363 this.footer.onClick('first');
9366 this.store.load({ params : { start : 0 } });
9370 renderHeader : function()
9378 this.totalWidth = 0;
9380 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9382 var config = cm.config[i];
9386 cls : 'x-hcol-' + i,
9389 html: cm.getColumnHeader(i)
9392 var tooltip = cm.getColumnTooltip(i);
9394 c.tooltip = tooltip;
9400 if(typeof(config.sortable) != 'undefined' && config.sortable){
9401 c.cls += ' sortable';
9402 c.html = '<i class="fa"></i>' + c.html;
9405 // could use BS4 hidden-..-down
9407 if(typeof(config.lgHeader) != 'undefined'){
9408 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9411 if(typeof(config.mdHeader) != 'undefined'){
9412 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9415 if(typeof(config.smHeader) != 'undefined'){
9416 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9419 if(typeof(config.xsHeader) != 'undefined'){
9420 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9427 if(typeof(config.tooltip) != 'undefined'){
9428 c.tooltip = config.tooltip;
9431 if(typeof(config.colspan) != 'undefined'){
9432 c.colspan = config.colspan;
9435 // hidden is handled by CSS now
9437 if(typeof(config.dataIndex) != 'undefined'){
9438 c.sort = config.dataIndex;
9443 if(typeof(config.align) != 'undefined' && config.align.length){
9444 c.style += ' text-align:' + config.align + ';';
9447 /* width is done in CSS
9448 *if(typeof(config.width) != 'undefined'){
9449 c.style += ' width:' + config.width + 'px;';
9450 this.totalWidth += config.width;
9452 this.totalWidth += 100; // assume minimum of 100 per column?
9456 if(typeof(config.cls) != 'undefined'){
9457 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9459 // this is the bit that doesnt reall work at all...
9461 if (this.responsive) {
9464 ['xs','sm','md','lg'].map(function(size){
9466 if(typeof(config[size]) == 'undefined'){
9470 if (!config[size]) { // 0 = hidden
9471 // BS 4 '0' is treated as hide that column and below.
9472 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9476 c.cls += ' col-' + size + '-' + config[size] + (
9477 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9485 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9496 renderBody : function()
9506 colspan : this.cm.getColumnCount()
9516 renderFooter : function()
9526 colspan : this.cm.getColumnCount()
9540 // Roo.log('ds onload');
9545 var ds = this.store;
9547 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9548 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9549 if (_this.store.sortInfo) {
9551 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9552 e.select('i', true).addClass(['fa-arrow-up']);
9555 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9556 e.select('i', true).addClass(['fa-arrow-down']);
9561 var tbody = this.bodyEl;
9563 if(ds.getCount() > 0){
9564 ds.data.each(function(d,rowIndex){
9565 var row = this.renderRow(cm, ds, rowIndex);
9567 tbody.createChild(row);
9571 if(row.cellObjects.length){
9572 Roo.each(row.cellObjects, function(r){
9573 _this.renderCellObject(r);
9580 var tfoot = this.el.select('tfoot', true).first();
9582 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9584 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9586 var total = this.ds.getTotalCount();
9588 if(this.footer.pageSize < total){
9589 this.mainFoot.show();
9593 Roo.each(this.el.select('tbody td', true).elements, function(e){
9594 e.on('mouseover', _this.onMouseover, _this);
9597 Roo.each(this.el.select('tbody td', true).elements, function(e){
9598 e.on('mouseout', _this.onMouseout, _this);
9600 this.fireEvent('rowsrendered', this);
9604 this.initCSS(); /// resize cols
9610 onUpdate : function(ds,record)
9612 this.refreshRow(record);
9616 onRemove : function(ds, record, index, isUpdate){
9617 if(isUpdate !== true){
9618 this.fireEvent("beforerowremoved", this, index, record);
9620 var bt = this.bodyEl.dom;
9622 var rows = this.el.select('tbody > tr', true).elements;
9624 if(typeof(rows[index]) != 'undefined'){
9625 bt.removeChild(rows[index].dom);
9628 // if(bt.rows[index]){
9629 // bt.removeChild(bt.rows[index]);
9632 if(isUpdate !== true){
9633 //this.stripeRows(index);
9634 //this.syncRowHeights(index, index);
9636 this.fireEvent("rowremoved", this, index, record);
9640 onAdd : function(ds, records, rowIndex)
9642 //Roo.log('on Add called');
9643 // - note this does not handle multiple adding very well..
9644 var bt = this.bodyEl.dom;
9645 for (var i =0 ; i < records.length;i++) {
9646 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9647 //Roo.log(records[i]);
9648 //Roo.log(this.store.getAt(rowIndex+i));
9649 this.insertRow(this.store, rowIndex + i, false);
9656 refreshRow : function(record){
9657 var ds = this.store, index;
9658 if(typeof record == 'number'){
9660 record = ds.getAt(index);
9662 index = ds.indexOf(record);
9664 return; // should not happen - but seems to
9667 this.insertRow(ds, index, true);
9669 this.onRemove(ds, record, index+1, true);
9671 //this.syncRowHeights(index, index);
9673 this.fireEvent("rowupdated", this, index, record);
9675 // private - called by RowSelection
9676 onRowSelect : function(rowIndex){
9677 var row = this.getRowDom(rowIndex);
9678 row.addClass(['bg-info','info']);
9680 // private - called by RowSelection
9681 onRowDeselect : function(rowIndex)
9686 var row = this.getRowDom(rowIndex);
9687 row.removeClass(['bg-info','info']);
9690 * Focuses the specified row.
9691 * @param {Number} row The row index
9693 focusRow : function(row)
9695 //Roo.log('GridView.focusRow');
9696 var x = this.bodyEl.dom.scrollLeft;
9697 this.focusCell(row, 0, false);
9698 this.bodyEl.dom.scrollLeft = x;
9702 * Focuses the specified cell.
9703 * @param {Number} row The row index
9704 * @param {Number} col The column index
9705 * @param {Boolean} hscroll false to disable horizontal scrolling
9707 focusCell : function(row, col, hscroll)
9709 //Roo.log('GridView.focusCell');
9710 var el = this.ensureVisible(row, col, hscroll);
9711 // not sure what focusEL achives = it's a <a> pos relative
9712 //this.focusEl.alignTo(el, "tl-tl");
9714 // this.focusEl.focus();
9716 // this.focusEl.focus.defer(1, this.focusEl);
9721 * Scrolls the specified cell into view
9722 * @param {Number} row The row index
9723 * @param {Number} col The column index
9724 * @param {Boolean} hscroll false to disable horizontal scrolling
9726 ensureVisible : function(row, col, hscroll)
9728 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9729 //return null; //disable for testing.
9730 if(typeof row != "number"){
9733 if(row < 0 && row >= this.ds.getCount()){
9736 col = (col !== undefined ? col : 0);
9738 while(cm.isHidden(col)){
9742 var el = this.getCellDom(row, col);
9746 var c = this.bodyEl.dom;
9748 var ctop = parseInt(el.offsetTop, 10);
9749 var cleft = parseInt(el.offsetLeft, 10);
9750 var cbot = ctop + el.offsetHeight;
9751 var cright = cleft + el.offsetWidth;
9753 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9754 var ch = 0; //?? header is not withing the area?
9755 var stop = parseInt(c.scrollTop, 10);
9756 var sleft = parseInt(c.scrollLeft, 10);
9757 var sbot = stop + ch;
9758 var sright = sleft + c.clientWidth;
9760 Roo.log('GridView.ensureVisible:' +
9762 ' c.clientHeight:' + c.clientHeight +
9763 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9772 //Roo.log("set scrolltop to ctop DISABLE?");
9773 }else if(cbot > sbot){
9774 //Roo.log("set scrolltop to cbot-ch");
9775 c.scrollTop = cbot-ch;
9778 if(hscroll !== false){
9780 c.scrollLeft = cleft;
9781 }else if(cright > sright){
9782 c.scrollLeft = cright-c.clientWidth;
9790 insertRow : function(dm, rowIndex, isUpdate){
9793 this.fireEvent("beforerowsinserted", this, rowIndex);
9795 //var s = this.getScrollState();
9796 var row = this.renderRow(this.cm, this.store, rowIndex);
9797 // insert before rowIndex..
9798 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9802 if(row.cellObjects.length){
9803 Roo.each(row.cellObjects, function(r){
9804 _this.renderCellObject(r);
9809 this.fireEvent("rowsinserted", this, rowIndex);
9810 //this.syncRowHeights(firstRow, lastRow);
9811 //this.stripeRows(firstRow);
9818 getRowDom : function(rowIndex)
9820 var rows = this.el.select('tbody > tr', true).elements;
9822 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9825 getCellDom : function(rowIndex, colIndex)
9827 var row = this.getRowDom(rowIndex);
9828 if (row === false) {
9831 var cols = row.select('td', true).elements;
9832 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9836 // returns the object tree for a tr..
9839 renderRow : function(cm, ds, rowIndex)
9841 var d = ds.getAt(rowIndex);
9845 cls : 'x-row-' + rowIndex,
9849 var cellObjects = [];
9851 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9852 var config = cm.config[i];
9854 var renderer = cm.getRenderer(i);
9858 if(typeof(renderer) !== 'undefined'){
9859 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9861 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9862 // and are rendered into the cells after the row is rendered - using the id for the element.
9864 if(typeof(value) === 'object'){
9874 rowIndex : rowIndex,
9879 this.fireEvent('rowclass', this, rowcfg);
9883 // this might end up displaying HTML?
9884 // this is too messy... - better to only do it on columsn you know are going to be too long
9885 //tooltip : (typeof(value) === 'object') ? '' : value,
9886 cls : rowcfg.rowClass + ' x-col-' + i,
9888 html: (typeof(value) === 'object') ? '' : value
9895 if(typeof(config.colspan) != 'undefined'){
9896 td.colspan = config.colspan;
9901 if(typeof(config.align) != 'undefined' && config.align.length){
9902 td.style += ' text-align:' + config.align + ';';
9904 if(typeof(config.valign) != 'undefined' && config.valign.length){
9905 td.style += ' vertical-align:' + config.valign + ';';
9908 if(typeof(config.width) != 'undefined'){
9909 td.style += ' width:' + config.width + 'px;';
9913 if(typeof(config.cursor) != 'undefined'){
9914 td.style += ' cursor:' + config.cursor + ';';
9917 if(typeof(config.cls) != 'undefined'){
9918 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9920 if (this.responsive) {
9921 ['xs','sm','md','lg'].map(function(size){
9923 if(typeof(config[size]) == 'undefined'){
9929 if (!config[size]) { // 0 = hidden
9930 // BS 4 '0' is treated as hide that column and below.
9931 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9935 td.cls += ' col-' + size + '-' + config[size] + (
9936 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9946 row.cellObjects = cellObjects;
9954 onBeforeLoad : function()
9963 this.el.select('tbody', true).first().dom.innerHTML = '';
9966 * Show or hide a row.
9967 * @param {Number} rowIndex to show or hide
9968 * @param {Boolean} state hide
9970 setRowVisibility : function(rowIndex, state)
9972 var bt = this.bodyEl.dom;
9974 var rows = this.el.select('tbody > tr', true).elements;
9976 if(typeof(rows[rowIndex]) == 'undefined'){
9979 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9984 getSelectionModel : function(){
9986 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9988 return this.selModel;
9991 * Render the Roo.bootstrap object from renderder
9993 renderCellObject : function(r)
9997 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9999 var t = r.cfg.render(r.container);
10002 Roo.each(r.cfg.cn, function(c){
10004 container: t.getChildContainer(),
10007 _this.renderCellObject(child);
10012 * get the Row Index from a dom element.
10013 * @param {Roo.Element} row The row to look for
10014 * @returns {Number} the row
10016 getRowIndex : function(row)
10020 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10031 * get the header TH element for columnIndex
10032 * @param {Number} columnIndex
10033 * @returns {Roo.Element}
10035 getHeaderIndex: function(colIndex)
10037 var cols = this.headEl.select('th', true).elements;
10038 return cols[colIndex];
10041 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10042 * @param {domElement} cell to look for
10043 * @returns {Number} the column
10045 getCellIndex : function(cell)
10047 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10049 return parseInt(id[1], 10);
10054 * Returns the grid's underlying element = used by panel.Grid
10055 * @return {Element} The element
10057 getGridEl : function(){
10061 * Forces a resize - used by panel.Grid
10062 * @return {Element} The element
10064 autoSize : function()
10066 //var ctr = Roo.get(this.container.dom.parentElement);
10067 var ctr = Roo.get(this.el.dom);
10069 var thd = this.getGridEl().select('thead',true).first();
10070 var tbd = this.getGridEl().select('tbody', true).first();
10071 var tfd = this.getGridEl().select('tfoot', true).first();
10073 var cw = ctr.getWidth();
10074 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10078 tbd.setWidth(ctr.getWidth());
10079 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10080 // this needs fixing for various usage - currently only hydra job advers I think..
10082 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10084 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10087 cw = Math.max(cw, this.totalWidth);
10088 this.getGridEl().select('tbody tr',true).setWidth(cw);
10091 // resize 'expandable coloumn?
10093 return; // we doe not have a view in this design..
10096 onBodyScroll: function()
10098 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10100 this.headEl.setStyle({
10101 'position' : 'relative',
10102 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10108 var scrollHeight = this.bodyEl.dom.scrollHeight;
10110 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10112 var height = this.bodyEl.getHeight();
10114 if(scrollHeight - height == scrollTop) {
10116 var total = this.ds.getTotalCount();
10118 if(this.footer.cursor + this.footer.pageSize < total){
10120 this.footer.ds.load({
10122 start : this.footer.cursor + this.footer.pageSize,
10123 limit : this.footer.pageSize
10132 onColumnSplitterMoved : function(i, diff)
10134 this.userResized = true;
10136 var cm = this.colModel;
10138 var w = this.getHeaderIndex(i).getWidth() + diff;
10141 cm.setColumnWidth(i, w, true);
10143 //var cid = cm.getColumnId(i); << not used in this version?
10144 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10146 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10147 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10148 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10150 //this.updateSplitters();
10151 //this.layout(); << ??
10152 this.fireEvent("columnresize", i, w);
10154 onHeaderChange : function()
10156 var header = this.renderHeader();
10157 var table = this.el.select('table', true).first();
10159 this.headEl.remove();
10160 this.headEl = table.createChild(header, this.bodyEl, false);
10162 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10163 e.on('click', this.sort, this);
10166 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10167 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10172 onHiddenChange : function(colModel, colIndex, hidden)
10174 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10175 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10177 this.CSS.updateRule(thSelector, "display", "");
10178 this.CSS.updateRule(tdSelector, "display", "");
10181 this.CSS.updateRule(thSelector, "display", "none");
10182 this.CSS.updateRule(tdSelector, "display", "none");
10185 this.onHeaderChange();
10189 setColumnWidth: function(col_index, width)
10191 // width = "md-2 xs-2..."
10192 if(!this.colModel.config[col_index]) {
10196 var w = width.split(" ");
10198 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10200 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10203 for(var j = 0; j < w.length; j++) {
10209 var size_cls = w[j].split("-");
10211 if(!Number.isInteger(size_cls[1] * 1)) {
10215 if(!this.colModel.config[col_index][size_cls[0]]) {
10219 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10223 h_row[0].classList.replace(
10224 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10225 "col-"+size_cls[0]+"-"+size_cls[1]
10228 for(var i = 0; i < rows.length; i++) {
10230 var size_cls = w[j].split("-");
10232 if(!Number.isInteger(size_cls[1] * 1)) {
10236 if(!this.colModel.config[col_index][size_cls[0]]) {
10240 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10244 rows[i].classList.replace(
10245 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10246 "col-"+size_cls[0]+"-"+size_cls[1]
10250 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10255 // currently only used to find the split on drag..
10256 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10261 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10262 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10271 * @class Roo.bootstrap.TableCell
10272 * @extends Roo.bootstrap.Component
10273 * Bootstrap TableCell class
10274 * @cfg {String} html cell contain text
10275 * @cfg {String} cls cell class
10276 * @cfg {String} tag cell tag (td|th) default td
10277 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10278 * @cfg {String} align Aligns the content in a cell
10279 * @cfg {String} axis Categorizes cells
10280 * @cfg {String} bgcolor Specifies the background color of a cell
10281 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10282 * @cfg {Number} colspan Specifies the number of columns a cell should span
10283 * @cfg {String} headers Specifies one or more header cells a cell is related to
10284 * @cfg {Number} height Sets the height of a cell
10285 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10286 * @cfg {Number} rowspan Sets the number of rows a cell should span
10287 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10288 * @cfg {String} valign Vertical aligns the content in a cell
10289 * @cfg {Number} width Specifies the width of a cell
10292 * Create a new TableCell
10293 * @param {Object} config The config object
10296 Roo.bootstrap.TableCell = function(config){
10297 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10300 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10320 getAutoCreate : function(){
10321 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10328 cfg.tag = this.tag;
10341 cfg.align=this.align
10346 if (this.bgcolor) {
10347 cfg.bgcolor=this.bgcolor
10349 if (this.charoff) {
10350 cfg.charoff=this.charoff
10352 if (this.colspan) {
10353 cfg.colspan=this.colspan
10355 if (this.headers) {
10356 cfg.headers=this.headers
10359 cfg.height=this.height
10362 cfg.nowrap=this.nowrap
10364 if (this.rowspan) {
10365 cfg.rowspan=this.rowspan
10368 cfg.scope=this.scope
10371 cfg.valign=this.valign
10374 cfg.width=this.width
10393 * @class Roo.bootstrap.TableRow
10394 * @extends Roo.bootstrap.Component
10395 * Bootstrap TableRow class
10396 * @cfg {String} cls row class
10397 * @cfg {String} align Aligns the content in a table row
10398 * @cfg {String} bgcolor Specifies a background color for a table row
10399 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10400 * @cfg {String} valign Vertical aligns the content in a table row
10403 * Create a new TableRow
10404 * @param {Object} config The config object
10407 Roo.bootstrap.TableRow = function(config){
10408 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10411 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10419 getAutoCreate : function(){
10420 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10427 cfg.cls = this.cls;
10430 cfg.align = this.align;
10433 cfg.bgcolor = this.bgcolor;
10436 cfg.charoff = this.charoff;
10439 cfg.valign = this.valign;
10457 * @class Roo.bootstrap.TableBody
10458 * @extends Roo.bootstrap.Component
10459 * Bootstrap TableBody class
10460 * @cfg {String} cls element class
10461 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10462 * @cfg {String} align Aligns the content inside the element
10463 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10464 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10467 * Create a new TableBody
10468 * @param {Object} config The config object
10471 Roo.bootstrap.TableBody = function(config){
10472 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10475 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10483 getAutoCreate : function(){
10484 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10494 cfg.tag = this.tag;
10498 cfg.align = this.align;
10501 cfg.charoff = this.charoff;
10504 cfg.valign = this.valign;
10511 // initEvents : function()
10514 // if(!this.store){
10518 // this.store = Roo.factory(this.store, Roo.data);
10519 // this.store.on('load', this.onLoad, this);
10521 // this.store.load();
10525 // onLoad: function ()
10527 // this.fireEvent('load', this);
10537 * Ext JS Library 1.1.1
10538 * Copyright(c) 2006-2007, Ext JS, LLC.
10540 * Originally Released Under LGPL - original licence link has changed is not relivant.
10543 * <script type="text/javascript">
10546 // as we use this in bootstrap.
10547 Roo.namespace('Roo.form');
10549 * @class Roo.form.Action
10550 * Internal Class used to handle form actions
10552 * @param {Roo.form.BasicForm} el The form element or its id
10553 * @param {Object} config Configuration options
10558 // define the action interface
10559 Roo.form.Action = function(form, options){
10561 this.options = options || {};
10564 * Client Validation Failed
10567 Roo.form.Action.CLIENT_INVALID = 'client';
10569 * Server Validation Failed
10572 Roo.form.Action.SERVER_INVALID = 'server';
10574 * Connect to Server Failed
10577 Roo.form.Action.CONNECT_FAILURE = 'connect';
10579 * Reading Data from Server Failed
10582 Roo.form.Action.LOAD_FAILURE = 'load';
10584 Roo.form.Action.prototype = {
10586 failureType : undefined,
10587 response : undefined,
10588 result : undefined,
10590 // interface method
10591 run : function(options){
10595 // interface method
10596 success : function(response){
10600 // interface method
10601 handleResponse : function(response){
10605 // default connection failure
10606 failure : function(response){
10608 this.response = response;
10609 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10610 this.form.afterAction(this, false);
10613 processResponse : function(response){
10614 this.response = response;
10615 if(!response.responseText){
10618 this.result = this.handleResponse(response);
10619 return this.result;
10622 // utility functions used internally
10623 getUrl : function(appendParams){
10624 var url = this.options.url || this.form.url || this.form.el.dom.action;
10626 var p = this.getParams();
10628 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10634 getMethod : function(){
10635 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10638 getParams : function(){
10639 var bp = this.form.baseParams;
10640 var p = this.options.params;
10642 if(typeof p == "object"){
10643 p = Roo.urlEncode(Roo.applyIf(p, bp));
10644 }else if(typeof p == 'string' && bp){
10645 p += '&' + Roo.urlEncode(bp);
10648 p = Roo.urlEncode(bp);
10653 createCallback : function(){
10655 success: this.success,
10656 failure: this.failure,
10658 timeout: (this.form.timeout*1000),
10659 upload: this.form.fileUpload ? this.success : undefined
10664 Roo.form.Action.Submit = function(form, options){
10665 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10668 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10671 haveProgress : false,
10672 uploadComplete : false,
10674 // uploadProgress indicator.
10675 uploadProgress : function()
10677 if (!this.form.progressUrl) {
10681 if (!this.haveProgress) {
10682 Roo.MessageBox.progress("Uploading", "Uploading");
10684 if (this.uploadComplete) {
10685 Roo.MessageBox.hide();
10689 this.haveProgress = true;
10691 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10693 var c = new Roo.data.Connection();
10695 url : this.form.progressUrl,
10700 success : function(req){
10701 //console.log(data);
10705 rdata = Roo.decode(req.responseText)
10707 Roo.log("Invalid data from server..");
10711 if (!rdata || !rdata.success) {
10713 Roo.MessageBox.alert(Roo.encode(rdata));
10716 var data = rdata.data;
10718 if (this.uploadComplete) {
10719 Roo.MessageBox.hide();
10724 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10725 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10728 this.uploadProgress.defer(2000,this);
10731 failure: function(data) {
10732 Roo.log('progress url failed ');
10743 // run get Values on the form, so it syncs any secondary forms.
10744 this.form.getValues();
10746 var o = this.options;
10747 var method = this.getMethod();
10748 var isPost = method == 'POST';
10749 if(o.clientValidation === false || this.form.isValid()){
10751 if (this.form.progressUrl) {
10752 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10753 (new Date() * 1) + '' + Math.random());
10758 Roo.Ajax.request(Roo.apply(this.createCallback(), {
10759 form:this.form.el.dom,
10760 url:this.getUrl(!isPost),
10762 params:isPost ? this.getParams() : null,
10763 isUpload: this.form.fileUpload,
10764 formData : this.form.formData
10767 this.uploadProgress();
10769 }else if (o.clientValidation !== false){ // client validation failed
10770 this.failureType = Roo.form.Action.CLIENT_INVALID;
10771 this.form.afterAction(this, false);
10775 success : function(response)
10777 this.uploadComplete= true;
10778 if (this.haveProgress) {
10779 Roo.MessageBox.hide();
10783 var result = this.processResponse(response);
10784 if(result === true || result.success){
10785 this.form.afterAction(this, true);
10789 this.form.markInvalid(result.errors);
10790 this.failureType = Roo.form.Action.SERVER_INVALID;
10792 this.form.afterAction(this, false);
10794 failure : function(response)
10796 this.uploadComplete= true;
10797 if (this.haveProgress) {
10798 Roo.MessageBox.hide();
10801 this.response = response;
10802 this.failureType = Roo.form.Action.CONNECT_FAILURE;
10803 this.form.afterAction(this, false);
10806 handleResponse : function(response){
10807 if(this.form.errorReader){
10808 var rs = this.form.errorReader.read(response);
10811 for(var i = 0, len = rs.records.length; i < len; i++) {
10812 var r = rs.records[i];
10813 errors[i] = r.data;
10816 if(errors.length < 1){
10820 success : rs.success,
10826 ret = Roo.decode(response.responseText);
10830 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10840 Roo.form.Action.Load = function(form, options){
10841 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10842 this.reader = this.form.reader;
10845 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10850 Roo.Ajax.request(Roo.apply(
10851 this.createCallback(), {
10852 method:this.getMethod(),
10853 url:this.getUrl(false),
10854 params:this.getParams()
10858 success : function(response){
10860 var result = this.processResponse(response);
10861 if(result === true || !result.success || !result.data){
10862 this.failureType = Roo.form.Action.LOAD_FAILURE;
10863 this.form.afterAction(this, false);
10866 this.form.clearInvalid();
10867 this.form.setValues(result.data);
10868 this.form.afterAction(this, true);
10871 handleResponse : function(response){
10872 if(this.form.reader){
10873 var rs = this.form.reader.read(response);
10874 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10876 success : rs.success,
10880 return Roo.decode(response.responseText);
10884 Roo.form.Action.ACTION_TYPES = {
10885 'load' : Roo.form.Action.Load,
10886 'submit' : Roo.form.Action.Submit
10895 * @class Roo.bootstrap.Form
10896 * @extends Roo.bootstrap.Component
10897 * Bootstrap Form class
10898 * @cfg {String} method GET | POST (default POST)
10899 * @cfg {String} labelAlign top | left (default top)
10900 * @cfg {String} align left | right - for navbars
10901 * @cfg {Boolean} loadMask load mask when submit (default true)
10905 * Create a new Form
10906 * @param {Object} config The config object
10910 Roo.bootstrap.Form = function(config){
10912 Roo.bootstrap.Form.superclass.constructor.call(this, config);
10914 Roo.bootstrap.Form.popover.apply();
10918 * @event clientvalidation
10919 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10920 * @param {Form} this
10921 * @param {Boolean} valid true if the form has passed client-side validation
10923 clientvalidation: true,
10925 * @event beforeaction
10926 * Fires before any action is performed. Return false to cancel the action.
10927 * @param {Form} this
10928 * @param {Action} action The action to be performed
10930 beforeaction: true,
10932 * @event actionfailed
10933 * Fires when an action fails.
10934 * @param {Form} this
10935 * @param {Action} action The action that failed
10937 actionfailed : true,
10939 * @event actioncomplete
10940 * Fires when an action is completed.
10941 * @param {Form} this
10942 * @param {Action} action The action that completed
10944 actioncomplete : true
10948 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
10951 * @cfg {String} method
10952 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10956 * @cfg {String} url
10957 * The URL to use for form actions if one isn't supplied in the action options.
10960 * @cfg {Boolean} fileUpload
10961 * Set to true if this form is a file upload.
10965 * @cfg {Object} baseParams
10966 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10970 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10974 * @cfg {Sting} align (left|right) for navbar forms
10979 activeAction : null,
10982 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10983 * element by passing it or its id or mask the form itself by passing in true.
10986 waitMsgTarget : false,
10991 * @cfg {Boolean} errorMask (true|false) default false
10996 * @cfg {Number} maskOffset Default 100
11001 * @cfg {Boolean} maskBody
11005 getAutoCreate : function(){
11009 method : this.method || 'POST',
11010 id : this.id || Roo.id(),
11013 if (this.parent().xtype.match(/^Nav/)) {
11014 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11018 if (this.labelAlign == 'left' ) {
11019 cfg.cls += ' form-horizontal';
11025 initEvents : function()
11027 this.el.on('submit', this.onSubmit, this);
11028 // this was added as random key presses on the form where triggering form submit.
11029 this.el.on('keypress', function(e) {
11030 if (e.getCharCode() != 13) {
11033 // we might need to allow it for textareas.. and some other items.
11034 // check e.getTarget().
11036 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11040 Roo.log("keypress blocked");
11042 e.preventDefault();
11048 onSubmit : function(e){
11053 * Returns true if client-side validation on the form is successful.
11056 isValid : function(){
11057 var items = this.getItems();
11059 var target = false;
11061 items.each(function(f){
11067 Roo.log('invalid field: ' + f.name);
11071 if(!target && f.el.isVisible(true)){
11077 if(this.errorMask && !valid){
11078 Roo.bootstrap.Form.popover.mask(this, target);
11085 * Returns true if any fields in this form have changed since their original load.
11088 isDirty : function(){
11090 var items = this.getItems();
11091 items.each(function(f){
11101 * Performs a predefined action (submit or load) or custom actions you define on this form.
11102 * @param {String} actionName The name of the action type
11103 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11104 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11105 * accept other config options):
11107 Property Type Description
11108 ---------------- --------------- ----------------------------------------------------------------------------------
11109 url String The url for the action (defaults to the form's url)
11110 method String The form method to use (defaults to the form's method, or POST if not defined)
11111 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11112 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11113 validate the form on the client (defaults to false)
11115 * @return {BasicForm} this
11117 doAction : function(action, options){
11118 if(typeof action == 'string'){
11119 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11121 if(this.fireEvent('beforeaction', this, action) !== false){
11122 this.beforeAction(action);
11123 action.run.defer(100, action);
11129 beforeAction : function(action){
11130 var o = action.options;
11135 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11137 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11140 // not really supported yet.. ??
11142 //if(this.waitMsgTarget === true){
11143 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11144 //}else if(this.waitMsgTarget){
11145 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11146 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11148 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11154 afterAction : function(action, success){
11155 this.activeAction = null;
11156 var o = action.options;
11161 Roo.get(document.body).unmask();
11167 //if(this.waitMsgTarget === true){
11168 // this.el.unmask();
11169 //}else if(this.waitMsgTarget){
11170 // this.waitMsgTarget.unmask();
11172 // Roo.MessageBox.updateProgress(1);
11173 // Roo.MessageBox.hide();
11180 Roo.callback(o.success, o.scope, [this, action]);
11181 this.fireEvent('actioncomplete', this, action);
11185 // failure condition..
11186 // we have a scenario where updates need confirming.
11187 // eg. if a locking scenario exists..
11188 // we look for { errors : { needs_confirm : true }} in the response.
11190 (typeof(action.result) != 'undefined') &&
11191 (typeof(action.result.errors) != 'undefined') &&
11192 (typeof(action.result.errors.needs_confirm) != 'undefined')
11195 Roo.log("not supported yet");
11198 Roo.MessageBox.confirm(
11199 "Change requires confirmation",
11200 action.result.errorMsg,
11205 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11215 Roo.callback(o.failure, o.scope, [this, action]);
11216 // show an error message if no failed handler is set..
11217 if (!this.hasListener('actionfailed')) {
11218 Roo.log("need to add dialog support");
11220 Roo.MessageBox.alert("Error",
11221 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11222 action.result.errorMsg :
11223 "Saving Failed, please check your entries or try again"
11228 this.fireEvent('actionfailed', this, action);
11233 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11234 * @param {String} id The value to search for
11237 findField : function(id){
11238 var items = this.getItems();
11239 var field = items.get(id);
11241 items.each(function(f){
11242 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11249 return field || null;
11252 * Mark fields in this form invalid in bulk.
11253 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11254 * @return {BasicForm} this
11256 markInvalid : function(errors){
11257 if(errors instanceof Array){
11258 for(var i = 0, len = errors.length; i < len; i++){
11259 var fieldError = errors[i];
11260 var f = this.findField(fieldError.id);
11262 f.markInvalid(fieldError.msg);
11268 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11269 field.markInvalid(errors[id]);
11273 //Roo.each(this.childForms || [], function (f) {
11274 // f.markInvalid(errors);
11281 * Set values for fields in this form in bulk.
11282 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11283 * @return {BasicForm} this
11285 setValues : function(values){
11286 if(values instanceof Array){ // array of objects
11287 for(var i = 0, len = values.length; i < len; i++){
11289 var f = this.findField(v.id);
11291 f.setValue(v.value);
11292 if(this.trackResetOnLoad){
11293 f.originalValue = f.getValue();
11297 }else{ // object hash
11300 if(typeof values[id] != 'function' && (field = this.findField(id))){
11302 if (field.setFromData &&
11303 field.valueField &&
11304 field.displayField &&
11305 // combos' with local stores can
11306 // be queried via setValue()
11307 // to set their value..
11308 (field.store && !field.store.isLocal)
11312 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11313 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11314 field.setFromData(sd);
11316 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11318 field.setFromData(values);
11321 field.setValue(values[id]);
11325 if(this.trackResetOnLoad){
11326 field.originalValue = field.getValue();
11332 //Roo.each(this.childForms || [], function (f) {
11333 // f.setValues(values);
11340 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11341 * they are returned as an array.
11342 * @param {Boolean} asString
11345 getValues : function(asString){
11346 //if (this.childForms) {
11347 // copy values from the child forms
11348 // Roo.each(this.childForms, function (f) {
11349 // this.setValues(f.getValues());
11355 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11356 if(asString === true){
11359 return Roo.urlDecode(fs);
11363 * Returns the fields in this form as an object with key/value pairs.
11364 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11367 getFieldValues : function(with_hidden)
11369 var items = this.getItems();
11371 items.each(function(f){
11373 if (!f.getName()) {
11377 var v = f.getValue();
11379 if (f.inputType =='radio') {
11380 if (typeof(ret[f.getName()]) == 'undefined') {
11381 ret[f.getName()] = ''; // empty..
11384 if (!f.el.dom.checked) {
11388 v = f.el.dom.value;
11392 if(f.xtype == 'MoneyField'){
11393 ret[f.currencyName] = f.getCurrency();
11396 // not sure if this supported any more..
11397 if ((typeof(v) == 'object') && f.getRawValue) {
11398 v = f.getRawValue() ; // dates..
11400 // combo boxes where name != hiddenName...
11401 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11402 ret[f.name] = f.getRawValue();
11404 ret[f.getName()] = v;
11411 * Clears all invalid messages in this form.
11412 * @return {BasicForm} this
11414 clearInvalid : function(){
11415 var items = this.getItems();
11417 items.each(function(f){
11425 * Resets this form.
11426 * @return {BasicForm} this
11428 reset : function(){
11429 var items = this.getItems();
11430 items.each(function(f){
11434 Roo.each(this.childForms || [], function (f) {
11442 getItems : function()
11444 var r=new Roo.util.MixedCollection(false, function(o){
11445 return o.id || (o.id = Roo.id());
11447 var iter = function(el) {
11454 Roo.each(el.items,function(e) {
11463 hideFields : function(items)
11465 Roo.each(items, function(i){
11467 var f = this.findField(i);
11478 showFields : function(items)
11480 Roo.each(items, function(i){
11482 var f = this.findField(i);
11495 Roo.apply(Roo.bootstrap.Form, {
11511 intervalID : false,
11517 if(this.isApplied){
11522 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11523 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11524 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11525 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11528 this.maskEl.top.enableDisplayMode("block");
11529 this.maskEl.left.enableDisplayMode("block");
11530 this.maskEl.bottom.enableDisplayMode("block");
11531 this.maskEl.right.enableDisplayMode("block");
11533 this.toolTip = new Roo.bootstrap.Tooltip({
11534 cls : 'roo-form-error-popover',
11536 'left' : ['r-l', [-2,0], 'right'],
11537 'right' : ['l-r', [2,0], 'left'],
11538 'bottom' : ['tl-bl', [0,2], 'top'],
11539 'top' : [ 'bl-tl', [0,-2], 'bottom']
11543 this.toolTip.render(Roo.get(document.body));
11545 this.toolTip.el.enableDisplayMode("block");
11547 Roo.get(document.body).on('click', function(){
11551 Roo.get(document.body).on('touchstart', function(){
11555 this.isApplied = true
11558 mask : function(form, target)
11562 this.target = target;
11564 if(!this.form.errorMask || !target.el){
11568 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11570 Roo.log(scrollable);
11572 var ot = this.target.el.calcOffsetsTo(scrollable);
11574 var scrollTo = ot[1] - this.form.maskOffset;
11576 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11578 scrollable.scrollTo('top', scrollTo);
11580 var box = this.target.el.getBox();
11582 var zIndex = Roo.bootstrap.Modal.zIndex++;
11585 this.maskEl.top.setStyle('position', 'absolute');
11586 this.maskEl.top.setStyle('z-index', zIndex);
11587 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11588 this.maskEl.top.setLeft(0);
11589 this.maskEl.top.setTop(0);
11590 this.maskEl.top.show();
11592 this.maskEl.left.setStyle('position', 'absolute');
11593 this.maskEl.left.setStyle('z-index', zIndex);
11594 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11595 this.maskEl.left.setLeft(0);
11596 this.maskEl.left.setTop(box.y - this.padding);
11597 this.maskEl.left.show();
11599 this.maskEl.bottom.setStyle('position', 'absolute');
11600 this.maskEl.bottom.setStyle('z-index', zIndex);
11601 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11602 this.maskEl.bottom.setLeft(0);
11603 this.maskEl.bottom.setTop(box.bottom + this.padding);
11604 this.maskEl.bottom.show();
11606 this.maskEl.right.setStyle('position', 'absolute');
11607 this.maskEl.right.setStyle('z-index', zIndex);
11608 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11609 this.maskEl.right.setLeft(box.right + this.padding);
11610 this.maskEl.right.setTop(box.y - this.padding);
11611 this.maskEl.right.show();
11613 this.toolTip.bindEl = this.target.el;
11615 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11617 var tip = this.target.blankText;
11619 if(this.target.getValue() !== '' ) {
11621 if (this.target.invalidText.length) {
11622 tip = this.target.invalidText;
11623 } else if (this.target.regexText.length){
11624 tip = this.target.regexText;
11628 this.toolTip.show(tip);
11630 this.intervalID = window.setInterval(function() {
11631 Roo.bootstrap.Form.popover.unmask();
11634 window.onwheel = function(){ return false;};
11636 (function(){ this.isMasked = true; }).defer(500, this);
11640 unmask : function()
11642 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11646 this.maskEl.top.setStyle('position', 'absolute');
11647 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11648 this.maskEl.top.hide();
11650 this.maskEl.left.setStyle('position', 'absolute');
11651 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11652 this.maskEl.left.hide();
11654 this.maskEl.bottom.setStyle('position', 'absolute');
11655 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11656 this.maskEl.bottom.hide();
11658 this.maskEl.right.setStyle('position', 'absolute');
11659 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11660 this.maskEl.right.hide();
11662 this.toolTip.hide();
11664 this.toolTip.el.hide();
11666 window.onwheel = function(){ return true;};
11668 if(this.intervalID){
11669 window.clearInterval(this.intervalID);
11670 this.intervalID = false;
11673 this.isMasked = false;
11683 * Ext JS Library 1.1.1
11684 * Copyright(c) 2006-2007, Ext JS, LLC.
11686 * Originally Released Under LGPL - original licence link has changed is not relivant.
11689 * <script type="text/javascript">
11692 * @class Roo.form.VTypes
11693 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11696 Roo.form.VTypes = function(){
11697 // closure these in so they are only created once.
11698 var alpha = /^[a-zA-Z_]+$/;
11699 var alphanum = /^[a-zA-Z0-9_]+$/;
11700 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11701 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11703 // All these messages and functions are configurable
11706 * The function used to validate email addresses
11707 * @param {String} value The email address
11709 'email' : function(v){
11710 return email.test(v);
11713 * The error text to display when the email validation function returns false
11716 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11718 * The keystroke filter mask to be applied on email input
11721 'emailMask' : /[a-z0-9_\.\-@]/i,
11724 * The function used to validate URLs
11725 * @param {String} value The URL
11727 'url' : function(v){
11728 return url.test(v);
11731 * The error text to display when the url validation function returns false
11734 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11737 * The function used to validate alpha values
11738 * @param {String} value The value
11740 'alpha' : function(v){
11741 return alpha.test(v);
11744 * The error text to display when the alpha validation function returns false
11747 'alphaText' : 'This field should only contain letters and _',
11749 * The keystroke filter mask to be applied on alpha input
11752 'alphaMask' : /[a-z_]/i,
11755 * The function used to validate alphanumeric values
11756 * @param {String} value The value
11758 'alphanum' : function(v){
11759 return alphanum.test(v);
11762 * The error text to display when the alphanumeric validation function returns false
11765 'alphanumText' : 'This field should only contain letters, numbers and _',
11767 * The keystroke filter mask to be applied on alphanumeric input
11770 'alphanumMask' : /[a-z0-9_]/i
11780 * @class Roo.bootstrap.Input
11781 * @extends Roo.bootstrap.Component
11782 * Bootstrap Input class
11783 * @cfg {Boolean} disabled is it disabled
11784 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
11785 * @cfg {String} name name of the input
11786 * @cfg {string} fieldLabel - the label associated
11787 * @cfg {string} placeholder - placeholder to put in text.
11788 * @cfg {string} before - input group add on before
11789 * @cfg {string} after - input group add on after
11790 * @cfg {string} size - (lg|sm) or leave empty..
11791 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11792 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11793 * @cfg {Number} md colspan out of 12 for computer-sized screens
11794 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11795 * @cfg {string} value default value of the input
11796 * @cfg {Number} labelWidth set the width of label
11797 * @cfg {Number} labellg set the width of label (1-12)
11798 * @cfg {Number} labelmd set the width of label (1-12)
11799 * @cfg {Number} labelsm set the width of label (1-12)
11800 * @cfg {Number} labelxs set the width of label (1-12)
11801 * @cfg {String} labelAlign (top|left)
11802 * @cfg {Boolean} readOnly Specifies that the field should be read-only
11803 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11804 * @cfg {String} indicatorpos (left|right) default left
11805 * @cfg {String} capture (user|camera) use for file input only. (default empty)
11806 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11807 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11809 * @cfg {String} align (left|center|right) Default left
11810 * @cfg {Boolean} forceFeedback (true|false) Default false
11813 * Create a new Input
11814 * @param {Object} config The config object
11817 Roo.bootstrap.Input = function(config){
11819 Roo.bootstrap.Input.superclass.constructor.call(this, config);
11824 * Fires when this field receives input focus.
11825 * @param {Roo.form.Field} this
11830 * Fires when this field loses input focus.
11831 * @param {Roo.form.Field} this
11835 * @event specialkey
11836 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
11837 * {@link Roo.EventObject#getKey} to determine which key was pressed.
11838 * @param {Roo.form.Field} this
11839 * @param {Roo.EventObject} e The event object
11844 * Fires just before the field blurs if the field value has changed.
11845 * @param {Roo.form.Field} this
11846 * @param {Mixed} newValue The new value
11847 * @param {Mixed} oldValue The original value
11852 * Fires after the field has been marked as invalid.
11853 * @param {Roo.form.Field} this
11854 * @param {String} msg The validation message
11859 * Fires after the field has been validated with no errors.
11860 * @param {Roo.form.Field} this
11865 * Fires after the key up
11866 * @param {Roo.form.Field} this
11867 * @param {Roo.EventObject} e The event Object
11872 * Fires after the user pastes into input
11873 * @param {Roo.form.Field} this
11874 * @param {Roo.EventObject} e The event Object
11880 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
11882 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11883 automatic validation (defaults to "keyup").
11885 validationEvent : "keyup",
11887 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11889 validateOnBlur : true,
11891 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11893 validationDelay : 250,
11895 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11897 focusClass : "x-form-focus", // not needed???
11901 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11903 invalidClass : "has-warning",
11906 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11908 validClass : "has-success",
11911 * @cfg {Boolean} hasFeedback (true|false) default true
11913 hasFeedback : true,
11916 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11918 invalidFeedbackClass : "glyphicon-warning-sign",
11921 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11923 validFeedbackClass : "glyphicon-ok",
11926 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11928 selectOnFocus : false,
11931 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11935 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11940 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11942 disableKeyFilter : false,
11945 * @cfg {Boolean} disabled True to disable the field (defaults to false).
11949 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11953 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11955 blankText : "Please complete this mandatory field",
11958 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11962 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11964 maxLength : Number.MAX_VALUE,
11966 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11968 minLengthText : "The minimum length for this field is {0}",
11970 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11972 maxLengthText : "The maximum length for this field is {0}",
11976 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11977 * If available, this function will be called only after the basic validators all return true, and will be passed the
11978 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11982 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11983 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11984 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
11988 * @cfg {String} regexText -- Depricated - use Invalid Text
11993 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11999 autocomplete: false,
12003 inputType : 'text',
12006 placeholder: false,
12011 preventMark: false,
12012 isFormField : true,
12015 labelAlign : false,
12018 formatedValue : false,
12019 forceFeedback : false,
12021 indicatorpos : 'left',
12031 parentLabelAlign : function()
12034 while (parent.parent()) {
12035 parent = parent.parent();
12036 if (typeof(parent.labelAlign) !='undefined') {
12037 return parent.labelAlign;
12044 getAutoCreate : function()
12046 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12052 if(this.inputType != 'hidden'){
12053 cfg.cls = 'form-group' //input-group
12059 type : this.inputType,
12060 value : this.value,
12061 cls : 'form-control',
12062 placeholder : this.placeholder || '',
12063 autocomplete : this.autocomplete || 'new-password'
12065 if (this.inputType == 'file') {
12066 input.style = 'overflow:hidden'; // why not in CSS?
12069 if(this.capture.length){
12070 input.capture = this.capture;
12073 if(this.accept.length){
12074 input.accept = this.accept + "/*";
12078 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12081 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12082 input.maxLength = this.maxLength;
12085 if (this.disabled) {
12086 input.disabled=true;
12089 if (this.readOnly) {
12090 input.readonly=true;
12094 input.name = this.name;
12098 input.cls += ' input-' + this.size;
12102 ['xs','sm','md','lg'].map(function(size){
12103 if (settings[size]) {
12104 cfg.cls += ' col-' + size + '-' + settings[size];
12108 var inputblock = input;
12112 cls: 'glyphicon form-control-feedback'
12115 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12118 cls : 'has-feedback',
12126 if (this.before || this.after) {
12129 cls : 'input-group',
12133 if (this.before && typeof(this.before) == 'string') {
12135 inputblock.cn.push({
12137 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12141 if (this.before && typeof(this.before) == 'object') {
12142 this.before = Roo.factory(this.before);
12144 inputblock.cn.push({
12146 cls : 'roo-input-before input-group-prepend input-group-' +
12147 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12151 inputblock.cn.push(input);
12153 if (this.after && typeof(this.after) == 'string') {
12154 inputblock.cn.push({
12156 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12160 if (this.after && typeof(this.after) == 'object') {
12161 this.after = Roo.factory(this.after);
12163 inputblock.cn.push({
12165 cls : 'roo-input-after input-group-append input-group-' +
12166 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12170 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12171 inputblock.cls += ' has-feedback';
12172 inputblock.cn.push(feedback);
12177 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12178 tooltip : 'This field is required'
12180 if (this.allowBlank ) {
12181 indicator.style = this.allowBlank ? ' display:none' : '';
12183 if (align ==='left' && this.fieldLabel.length) {
12185 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12192 cls : 'control-label col-form-label',
12193 html : this.fieldLabel
12204 var labelCfg = cfg.cn[1];
12205 var contentCfg = cfg.cn[2];
12207 if(this.indicatorpos == 'right'){
12212 cls : 'control-label col-form-label',
12216 html : this.fieldLabel
12230 labelCfg = cfg.cn[0];
12231 contentCfg = cfg.cn[1];
12235 if(this.labelWidth > 12){
12236 labelCfg.style = "width: " + this.labelWidth + 'px';
12239 if(this.labelWidth < 13 && this.labelmd == 0){
12240 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12243 if(this.labellg > 0){
12244 labelCfg.cls += ' col-lg-' + this.labellg;
12245 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12248 if(this.labelmd > 0){
12249 labelCfg.cls += ' col-md-' + this.labelmd;
12250 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12253 if(this.labelsm > 0){
12254 labelCfg.cls += ' col-sm-' + this.labelsm;
12255 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12258 if(this.labelxs > 0){
12259 labelCfg.cls += ' col-xs-' + this.labelxs;
12260 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12264 } else if ( this.fieldLabel.length) {
12271 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12272 tooltip : 'This field is required',
12273 style : this.allowBlank ? ' display:none' : ''
12277 //cls : 'input-group-addon',
12278 html : this.fieldLabel
12286 if(this.indicatorpos == 'right'){
12291 //cls : 'input-group-addon',
12292 html : this.fieldLabel
12297 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12298 tooltip : 'This field is required',
12299 style : this.allowBlank ? ' display:none' : ''
12319 if (this.parentType === 'Navbar' && this.parent().bar) {
12320 cfg.cls += ' navbar-form';
12323 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12324 // on BS4 we do this only if not form
12325 cfg.cls += ' navbar-form';
12333 * return the real input element.
12335 inputEl: function ()
12337 return this.el.select('input.form-control',true).first();
12340 tooltipEl : function()
12342 return this.inputEl();
12345 indicatorEl : function()
12347 if (Roo.bootstrap.version == 4) {
12348 return false; // not enabled in v4 yet.
12351 var indicator = this.el.select('i.roo-required-indicator',true).first();
12361 setDisabled : function(v)
12363 var i = this.inputEl().dom;
12365 i.removeAttribute('disabled');
12369 i.setAttribute('disabled','true');
12371 initEvents : function()
12374 this.inputEl().on("keydown" , this.fireKey, this);
12375 this.inputEl().on("focus", this.onFocus, this);
12376 this.inputEl().on("blur", this.onBlur, this);
12378 this.inputEl().relayEvent('keyup', this);
12379 this.inputEl().relayEvent('paste', this);
12381 this.indicator = this.indicatorEl();
12383 if(this.indicator){
12384 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12387 // reference to original value for reset
12388 this.originalValue = this.getValue();
12389 //Roo.form.TextField.superclass.initEvents.call(this);
12390 if(this.validationEvent == 'keyup'){
12391 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12392 this.inputEl().on('keyup', this.filterValidation, this);
12394 else if(this.validationEvent !== false){
12395 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12398 if(this.selectOnFocus){
12399 this.on("focus", this.preFocus, this);
12402 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12403 this.inputEl().on("keypress", this.filterKeys, this);
12405 this.inputEl().relayEvent('keypress', this);
12408 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12409 this.el.on("click", this.autoSize, this);
12412 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12413 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12416 if (typeof(this.before) == 'object') {
12417 this.before.render(this.el.select('.roo-input-before',true).first());
12419 if (typeof(this.after) == 'object') {
12420 this.after.render(this.el.select('.roo-input-after',true).first());
12423 this.inputEl().on('change', this.onChange, this);
12426 filterValidation : function(e){
12427 if(!e.isNavKeyPress()){
12428 this.validationTask.delay(this.validationDelay);
12432 * Validates the field value
12433 * @return {Boolean} True if the value is valid, else false
12435 validate : function(){
12436 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12437 if(this.disabled || this.validateValue(this.getRawValue())){
12442 this.markInvalid();
12448 * Validates a value according to the field's validation rules and marks the field as invalid
12449 * if the validation fails
12450 * @param {Mixed} value The value to validate
12451 * @return {Boolean} True if the value is valid, else false
12453 validateValue : function(value)
12455 if(this.getVisibilityEl().hasClass('hidden')){
12459 if(value.length < 1) { // if it's blank
12460 if(this.allowBlank){
12466 if(value.length < this.minLength){
12469 if(value.length > this.maxLength){
12473 var vt = Roo.form.VTypes;
12474 if(!vt[this.vtype](value, this)){
12478 if(typeof this.validator == "function"){
12479 var msg = this.validator(value);
12483 if (typeof(msg) == 'string') {
12484 this.invalidText = msg;
12488 if(this.regex && !this.regex.test(value)){
12496 fireKey : function(e){
12497 //Roo.log('field ' + e.getKey());
12498 if(e.isNavKeyPress()){
12499 this.fireEvent("specialkey", this, e);
12502 focus : function (selectText){
12504 this.inputEl().focus();
12505 if(selectText === true){
12506 this.inputEl().dom.select();
12512 onFocus : function(){
12513 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12514 // this.el.addClass(this.focusClass);
12516 if(!this.hasFocus){
12517 this.hasFocus = true;
12518 this.startValue = this.getValue();
12519 this.fireEvent("focus", this);
12523 beforeBlur : Roo.emptyFn,
12527 onBlur : function(){
12529 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12530 //this.el.removeClass(this.focusClass);
12532 this.hasFocus = false;
12533 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12536 var v = this.getValue();
12537 if(String(v) !== String(this.startValue)){
12538 this.fireEvent('change', this, v, this.startValue);
12540 this.fireEvent("blur", this);
12543 onChange : function(e)
12545 var v = this.getValue();
12546 if(String(v) !== String(this.startValue)){
12547 this.fireEvent('change', this, v, this.startValue);
12553 * Resets the current field value to the originally loaded value and clears any validation messages
12555 reset : function(){
12556 this.setValue(this.originalValue);
12560 * Returns the name of the field
12561 * @return {Mixed} name The name field
12563 getName: function(){
12567 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12568 * @return {Mixed} value The field value
12570 getValue : function(){
12572 var v = this.inputEl().getValue();
12577 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
12578 * @return {Mixed} value The field value
12580 getRawValue : function(){
12581 var v = this.inputEl().getValue();
12587 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
12588 * @param {Mixed} value The value to set
12590 setRawValue : function(v){
12591 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12594 selectText : function(start, end){
12595 var v = this.getRawValue();
12597 start = start === undefined ? 0 : start;
12598 end = end === undefined ? v.length : end;
12599 var d = this.inputEl().dom;
12600 if(d.setSelectionRange){
12601 d.setSelectionRange(start, end);
12602 }else if(d.createTextRange){
12603 var range = d.createTextRange();
12604 range.moveStart("character", start);
12605 range.moveEnd("character", v.length-end);
12612 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
12613 * @param {Mixed} value The value to set
12615 setValue : function(v){
12618 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12624 processValue : function(value){
12625 if(this.stripCharsRe){
12626 var newValue = value.replace(this.stripCharsRe, '');
12627 if(newValue !== value){
12628 this.setRawValue(newValue);
12635 preFocus : function(){
12637 if(this.selectOnFocus){
12638 this.inputEl().dom.select();
12641 filterKeys : function(e){
12642 var k = e.getKey();
12643 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12646 var c = e.getCharCode(), cc = String.fromCharCode(c);
12647 if(Roo.isIE && (e.isSpecialKey() || !cc)){
12650 if(!this.maskRe.test(cc)){
12655 * Clear any invalid styles/messages for this field
12657 clearInvalid : function(){
12659 if(!this.el || this.preventMark){ // not rendered
12664 this.el.removeClass([this.invalidClass, 'is-invalid']);
12666 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12668 var feedback = this.el.select('.form-control-feedback', true).first();
12671 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12676 if(this.indicator){
12677 this.indicator.removeClass('visible');
12678 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12681 this.fireEvent('valid', this);
12685 * Mark this field as valid
12687 markValid : function()
12689 if(!this.el || this.preventMark){ // not rendered...
12693 this.el.removeClass([this.invalidClass, this.validClass]);
12694 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12696 var feedback = this.el.select('.form-control-feedback', true).first();
12699 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12702 if(this.indicator){
12703 this.indicator.removeClass('visible');
12704 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12712 if(this.allowBlank && !this.getRawValue().length){
12715 if (Roo.bootstrap.version == 3) {
12716 this.el.addClass(this.validClass);
12718 this.inputEl().addClass('is-valid');
12721 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12723 var feedback = this.el.select('.form-control-feedback', true).first();
12726 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12727 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12732 this.fireEvent('valid', this);
12736 * Mark this field as invalid
12737 * @param {String} msg The validation message
12739 markInvalid : function(msg)
12741 if(!this.el || this.preventMark){ // not rendered
12745 this.el.removeClass([this.invalidClass, this.validClass]);
12746 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12748 var feedback = this.el.select('.form-control-feedback', true).first();
12751 this.el.select('.form-control-feedback', true).first().removeClass(
12752 [this.invalidFeedbackClass, this.validFeedbackClass]);
12759 if(this.allowBlank && !this.getRawValue().length){
12763 if(this.indicator){
12764 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12765 this.indicator.addClass('visible');
12767 if (Roo.bootstrap.version == 3) {
12768 this.el.addClass(this.invalidClass);
12770 this.inputEl().addClass('is-invalid');
12775 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12777 var feedback = this.el.select('.form-control-feedback', true).first();
12780 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12782 if(this.getValue().length || this.forceFeedback){
12783 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12790 this.fireEvent('invalid', this, msg);
12793 SafariOnKeyDown : function(event)
12795 // this is a workaround for a password hang bug on chrome/ webkit.
12796 if (this.inputEl().dom.type != 'password') {
12800 var isSelectAll = false;
12802 if(this.inputEl().dom.selectionEnd > 0){
12803 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12805 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12806 event.preventDefault();
12811 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12813 event.preventDefault();
12814 // this is very hacky as keydown always get's upper case.
12816 var cc = String.fromCharCode(event.getCharCode());
12817 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
12821 adjustWidth : function(tag, w){
12822 tag = tag.toLowerCase();
12823 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12824 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12825 if(tag == 'input'){
12828 if(tag == 'textarea'){
12831 }else if(Roo.isOpera){
12832 if(tag == 'input'){
12835 if(tag == 'textarea'){
12843 setFieldLabel : function(v)
12845 if(!this.rendered){
12849 if(this.indicatorEl()){
12850 var ar = this.el.select('label > span',true);
12852 if (ar.elements.length) {
12853 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12854 this.fieldLabel = v;
12858 var br = this.el.select('label',true);
12860 if(br.elements.length) {
12861 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12862 this.fieldLabel = v;
12866 Roo.log('Cannot Found any of label > span || label in input');
12870 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12871 this.fieldLabel = v;
12886 * @class Roo.bootstrap.TextArea
12887 * @extends Roo.bootstrap.Input
12888 * Bootstrap TextArea class
12889 * @cfg {Number} cols Specifies the visible width of a text area
12890 * @cfg {Number} rows Specifies the visible number of lines in a text area
12891 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12892 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12893 * @cfg {string} html text
12896 * Create a new TextArea
12897 * @param {Object} config The config object
12900 Roo.bootstrap.TextArea = function(config){
12901 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12905 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
12915 getAutoCreate : function(){
12917 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12923 if(this.inputType != 'hidden'){
12924 cfg.cls = 'form-group' //input-group
12932 value : this.value || '',
12933 html: this.html || '',
12934 cls : 'form-control',
12935 placeholder : this.placeholder || ''
12939 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12940 input.maxLength = this.maxLength;
12944 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12948 input.cols = this.cols;
12951 if (this.readOnly) {
12952 input.readonly = true;
12956 input.name = this.name;
12960 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12964 ['xs','sm','md','lg'].map(function(size){
12965 if (settings[size]) {
12966 cfg.cls += ' col-' + size + '-' + settings[size];
12970 var inputblock = input;
12972 if(this.hasFeedback && !this.allowBlank){
12976 cls: 'glyphicon form-control-feedback'
12980 cls : 'has-feedback',
12989 if (this.before || this.after) {
12992 cls : 'input-group',
12996 inputblock.cn.push({
12998 cls : 'input-group-addon',
13003 inputblock.cn.push(input);
13005 if(this.hasFeedback && !this.allowBlank){
13006 inputblock.cls += ' has-feedback';
13007 inputblock.cn.push(feedback);
13011 inputblock.cn.push({
13013 cls : 'input-group-addon',
13020 if (align ==='left' && this.fieldLabel.length) {
13025 cls : 'control-label',
13026 html : this.fieldLabel
13037 if(this.labelWidth > 12){
13038 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13041 if(this.labelWidth < 13 && this.labelmd == 0){
13042 this.labelmd = this.labelWidth;
13045 if(this.labellg > 0){
13046 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13047 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13050 if(this.labelmd > 0){
13051 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13052 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13055 if(this.labelsm > 0){
13056 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13057 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13060 if(this.labelxs > 0){
13061 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13062 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13065 } else if ( this.fieldLabel.length) {
13070 //cls : 'input-group-addon',
13071 html : this.fieldLabel
13089 if (this.disabled) {
13090 input.disabled=true;
13097 * return the real textarea element.
13099 inputEl: function ()
13101 return this.el.select('textarea.form-control',true).first();
13105 * Clear any invalid styles/messages for this field
13107 clearInvalid : function()
13110 if(!this.el || this.preventMark){ // not rendered
13114 var label = this.el.select('label', true).first();
13115 var icon = this.el.select('i.fa-star', true).first();
13120 this.el.removeClass( this.validClass);
13121 this.inputEl().removeClass('is-invalid');
13123 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13125 var feedback = this.el.select('.form-control-feedback', true).first();
13128 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13133 this.fireEvent('valid', this);
13137 * Mark this field as valid
13139 markValid : function()
13141 if(!this.el || this.preventMark){ // not rendered
13145 this.el.removeClass([this.invalidClass, this.validClass]);
13146 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13148 var feedback = this.el.select('.form-control-feedback', true).first();
13151 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13154 if(this.disabled || this.allowBlank){
13158 var label = this.el.select('label', true).first();
13159 var icon = this.el.select('i.fa-star', true).first();
13164 if (Roo.bootstrap.version == 3) {
13165 this.el.addClass(this.validClass);
13167 this.inputEl().addClass('is-valid');
13171 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13173 var feedback = this.el.select('.form-control-feedback', true).first();
13176 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13177 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13182 this.fireEvent('valid', this);
13186 * Mark this field as invalid
13187 * @param {String} msg The validation message
13189 markInvalid : function(msg)
13191 if(!this.el || this.preventMark){ // not rendered
13195 this.el.removeClass([this.invalidClass, this.validClass]);
13196 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13198 var feedback = this.el.select('.form-control-feedback', true).first();
13201 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13204 if(this.disabled || this.allowBlank){
13208 var label = this.el.select('label', true).first();
13209 var icon = this.el.select('i.fa-star', true).first();
13211 if(!this.getValue().length && label && !icon){
13212 this.el.createChild({
13214 cls : 'text-danger fa fa-lg fa-star',
13215 tooltip : 'This field is required',
13216 style : 'margin-right:5px;'
13220 if (Roo.bootstrap.version == 3) {
13221 this.el.addClass(this.invalidClass);
13223 this.inputEl().addClass('is-invalid');
13226 // fixme ... this may be depricated need to test..
13227 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13229 var feedback = this.el.select('.form-control-feedback', true).first();
13232 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13234 if(this.getValue().length || this.forceFeedback){
13235 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13242 this.fireEvent('invalid', this, msg);
13250 * trigger field - base class for combo..
13255 * @class Roo.bootstrap.TriggerField
13256 * @extends Roo.bootstrap.Input
13257 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13258 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13259 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13260 * for which you can provide a custom implementation. For example:
13262 var trigger = new Roo.bootstrap.TriggerField();
13263 trigger.onTriggerClick = myTriggerFn;
13264 trigger.applyTo('my-field');
13267 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13268 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13269 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13270 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13271 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13274 * Create a new TriggerField.
13275 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13276 * to the base TextField)
13278 Roo.bootstrap.TriggerField = function(config){
13279 this.mimicing = false;
13280 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13283 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
13285 * @cfg {String} triggerClass A CSS class to apply to the trigger
13288 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13293 * @cfg {Boolean} removable (true|false) special filter default false
13297 /** @cfg {Boolean} grow @hide */
13298 /** @cfg {Number} growMin @hide */
13299 /** @cfg {Number} growMax @hide */
13305 autoSize: Roo.emptyFn,
13309 deferHeight : true,
13312 actionMode : 'wrap',
13317 getAutoCreate : function(){
13319 var align = this.labelAlign || this.parentLabelAlign();
13324 cls: 'form-group' //input-group
13331 type : this.inputType,
13332 cls : 'form-control',
13333 autocomplete: 'new-password',
13334 placeholder : this.placeholder || ''
13338 input.name = this.name;
13341 input.cls += ' input-' + this.size;
13344 if (this.disabled) {
13345 input.disabled=true;
13348 var inputblock = input;
13350 if(this.hasFeedback && !this.allowBlank){
13354 cls: 'glyphicon form-control-feedback'
13357 if(this.removable && !this.editable ){
13359 cls : 'has-feedback',
13365 cls : 'roo-combo-removable-btn close'
13372 cls : 'has-feedback',
13381 if(this.removable && !this.editable ){
13383 cls : 'roo-removable',
13389 cls : 'roo-combo-removable-btn close'
13396 if (this.before || this.after) {
13399 cls : 'input-group',
13403 inputblock.cn.push({
13405 cls : 'input-group-addon input-group-prepend input-group-text',
13410 inputblock.cn.push(input);
13412 if(this.hasFeedback && !this.allowBlank){
13413 inputblock.cls += ' has-feedback';
13414 inputblock.cn.push(feedback);
13418 inputblock.cn.push({
13420 cls : 'input-group-addon input-group-append input-group-text',
13429 var ibwrap = inputblock;
13434 cls: 'roo-select2-choices',
13438 cls: 'roo-select2-search-field',
13450 cls: 'roo-select2-container input-group',
13455 cls: 'form-hidden-field'
13461 if(!this.multiple && this.showToggleBtn){
13467 if (this.caret != false) {
13470 cls: 'fa fa-' + this.caret
13477 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13479 Roo.bootstrap.version == 3 ? caret : '',
13482 cls: 'combobox-clear',
13496 combobox.cls += ' roo-select2-container-multi';
13500 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13501 tooltip : 'This field is required'
13503 if (Roo.bootstrap.version == 4) {
13506 style : 'display:none'
13511 if (align ==='left' && this.fieldLabel.length) {
13513 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13520 cls : 'control-label',
13521 html : this.fieldLabel
13533 var labelCfg = cfg.cn[1];
13534 var contentCfg = cfg.cn[2];
13536 if(this.indicatorpos == 'right'){
13541 cls : 'control-label',
13545 html : this.fieldLabel
13559 labelCfg = cfg.cn[0];
13560 contentCfg = cfg.cn[1];
13563 if(this.labelWidth > 12){
13564 labelCfg.style = "width: " + this.labelWidth + 'px';
13567 if(this.labelWidth < 13 && this.labelmd == 0){
13568 this.labelmd = this.labelWidth;
13571 if(this.labellg > 0){
13572 labelCfg.cls += ' col-lg-' + this.labellg;
13573 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13576 if(this.labelmd > 0){
13577 labelCfg.cls += ' col-md-' + this.labelmd;
13578 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13581 if(this.labelsm > 0){
13582 labelCfg.cls += ' col-sm-' + this.labelsm;
13583 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13586 if(this.labelxs > 0){
13587 labelCfg.cls += ' col-xs-' + this.labelxs;
13588 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13591 } else if ( this.fieldLabel.length) {
13592 // Roo.log(" label");
13597 //cls : 'input-group-addon',
13598 html : this.fieldLabel
13606 if(this.indicatorpos == 'right'){
13614 html : this.fieldLabel
13628 // Roo.log(" no label && no align");
13635 ['xs','sm','md','lg'].map(function(size){
13636 if (settings[size]) {
13637 cfg.cls += ' col-' + size + '-' + settings[size];
13648 onResize : function(w, h){
13649 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13650 // if(typeof w == 'number'){
13651 // var x = w - this.trigger.getWidth();
13652 // this.inputEl().setWidth(this.adjustWidth('input', x));
13653 // this.trigger.setStyle('left', x+'px');
13658 adjustSize : Roo.BoxComponent.prototype.adjustSize,
13661 getResizeEl : function(){
13662 return this.inputEl();
13666 getPositionEl : function(){
13667 return this.inputEl();
13671 alignErrorIcon : function(){
13672 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13676 initEvents : function(){
13680 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13681 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13682 if(!this.multiple && this.showToggleBtn){
13683 this.trigger = this.el.select('span.dropdown-toggle',true).first();
13684 if(this.hideTrigger){
13685 this.trigger.setDisplayed(false);
13687 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13691 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13694 if(this.removable && !this.editable && !this.tickable){
13695 var close = this.closeTriggerEl();
13698 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13699 close.on('click', this.removeBtnClick, this, close);
13703 //this.trigger.addClassOnOver('x-form-trigger-over');
13704 //this.trigger.addClassOnClick('x-form-trigger-click');
13707 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13711 closeTriggerEl : function()
13713 var close = this.el.select('.roo-combo-removable-btn', true).first();
13714 return close ? close : false;
13717 removeBtnClick : function(e, h, el)
13719 e.preventDefault();
13721 if(this.fireEvent("remove", this) !== false){
13723 this.fireEvent("afterremove", this)
13727 createList : function()
13729 this.list = Roo.get(document.body).createChild({
13730 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13731 cls: 'typeahead typeahead-long dropdown-menu shadow',
13732 style: 'display:none'
13735 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13740 initTrigger : function(){
13745 onDestroy : function(){
13747 this.trigger.removeAllListeners();
13748 // this.trigger.remove();
13751 // this.wrap.remove();
13753 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13757 onFocus : function(){
13758 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13760 if(!this.mimicing){
13761 this.wrap.addClass('x-trigger-wrap-focus');
13762 this.mimicing = true;
13763 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13764 if(this.monitorTab){
13765 this.el.on("keydown", this.checkTab, this);
13772 checkTab : function(e){
13773 if(e.getKey() == e.TAB){
13774 this.triggerBlur();
13779 onBlur : function(){
13784 mimicBlur : function(e, t){
13786 if(!this.wrap.contains(t) && this.validateBlur()){
13787 this.triggerBlur();
13793 triggerBlur : function(){
13794 this.mimicing = false;
13795 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13796 if(this.monitorTab){
13797 this.el.un("keydown", this.checkTab, this);
13799 //this.wrap.removeClass('x-trigger-wrap-focus');
13800 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13804 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13805 validateBlur : function(e, t){
13810 onDisable : function(){
13811 this.inputEl().dom.disabled = true;
13812 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13814 // this.wrap.addClass('x-item-disabled');
13819 onEnable : function(){
13820 this.inputEl().dom.disabled = false;
13821 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13823 // this.el.removeClass('x-item-disabled');
13828 onShow : function(){
13829 var ae = this.getActionEl();
13832 ae.dom.style.display = '';
13833 ae.dom.style.visibility = 'visible';
13839 onHide : function(){
13840 var ae = this.getActionEl();
13841 ae.dom.style.display = 'none';
13845 * The function that should handle the trigger's click event. This method does nothing by default until overridden
13846 * by an implementing function.
13848 * @param {EventObject} e
13850 onTriggerClick : Roo.emptyFn
13858 * @class Roo.bootstrap.CardUploader
13859 * @extends Roo.bootstrap.Button
13860 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13861 * @cfg {Number} errorTimeout default 3000
13862 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
13863 * @cfg {Array} html The button text.
13867 * Create a new CardUploader
13868 * @param {Object} config The config object
13871 Roo.bootstrap.CardUploader = function(config){
13875 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13878 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
13886 * When a image is clicked on - and needs to display a slideshow or similar..
13887 * @param {Roo.bootstrap.Card} this
13888 * @param {Object} The image information data
13894 * When a the download link is clicked
13895 * @param {Roo.bootstrap.Card} this
13896 * @param {Object} The image information data contains
13903 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
13906 errorTimeout : 3000,
13910 fileCollection : false,
13913 getAutoCreate : function()
13917 cls :'form-group' ,
13922 //cls : 'input-group-addon',
13923 html : this.fieldLabel
13931 value : this.value,
13932 cls : 'd-none form-control'
13937 multiple : 'multiple',
13939 cls : 'd-none roo-card-upload-selector'
13943 cls : 'roo-card-uploader-button-container w-100 mb-2'
13946 cls : 'card-columns roo-card-uploader-container'
13956 getChildContainer : function() /// what children are added to.
13958 return this.containerEl;
13961 getButtonContainer : function() /// what children are added to.
13963 return this.el.select(".roo-card-uploader-button-container").first();
13966 initEvents : function()
13969 Roo.bootstrap.Input.prototype.initEvents.call(this);
13973 xns: Roo.bootstrap,
13976 container_method : 'getButtonContainer' ,
13977 html : this.html, // fix changable?
13980 'click' : function(btn, e) {
13989 this.urlAPI = (window.createObjectURL && window) ||
13990 (window.URL && URL.revokeObjectURL && URL) ||
13991 (window.webkitURL && webkitURL);
13996 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
13998 this.selectorEl.on('change', this.onFileSelected, this);
14001 this.images.forEach(function(img) {
14004 this.images = false;
14006 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14012 onClick : function(e)
14014 e.preventDefault();
14016 this.selectorEl.dom.click();
14020 onFileSelected : function(e)
14022 e.preventDefault();
14024 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14028 Roo.each(this.selectorEl.dom.files, function(file){
14029 this.addFile(file);
14038 addFile : function(file)
14041 if(typeof(file) === 'string'){
14042 throw "Add file by name?"; // should not happen
14046 if(!file || !this.urlAPI){
14056 var url = _this.urlAPI.createObjectURL( file);
14059 id : Roo.bootstrap.CardUploader.ID--,
14060 is_uploaded : false,
14064 mimetype : file.type,
14072 * addCard - add an Attachment to the uploader
14073 * @param data - the data about the image to upload
14077 title : "Title of file",
14078 is_uploaded : false,
14079 src : "http://.....",
14080 srcfile : { the File upload object },
14081 mimetype : file.type,
14084 .. any other data...
14090 addCard : function (data)
14092 // hidden input element?
14093 // if the file is not an image...
14094 //then we need to use something other that and header_image
14099 xns : Roo.bootstrap,
14100 xtype : 'CardFooter',
14103 xns : Roo.bootstrap,
14109 xns : Roo.bootstrap,
14111 html : String.format("<small>{0}</small>", data.title),
14112 cls : 'col-10 text-left',
14117 click : function() {
14119 t.fireEvent( "download", t, data );
14125 xns : Roo.bootstrap,
14127 style: 'max-height: 28px; ',
14133 click : function() {
14134 t.removeCard(data.id)
14146 var cn = this.addxtype(
14149 xns : Roo.bootstrap,
14152 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14153 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14154 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14159 initEvents : function() {
14160 Roo.bootstrap.Card.prototype.initEvents.call(this);
14162 this.imgEl = this.el.select('.card-img-top').first();
14164 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14165 this.imgEl.set({ 'pointer' : 'cursor' });
14168 this.getCardFooter().addClass('p-1');
14175 // dont' really need ot update items.
14176 // this.items.push(cn);
14177 this.fileCollection.add(cn);
14179 if (!data.srcfile) {
14180 this.updateInput();
14185 var reader = new FileReader();
14186 reader.addEventListener("load", function() {
14187 data.srcdata = reader.result;
14190 reader.readAsDataURL(data.srcfile);
14195 removeCard : function(id)
14198 var card = this.fileCollection.get(id);
14199 card.data.is_deleted = 1;
14200 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14201 //this.fileCollection.remove(card);
14202 //this.items = this.items.filter(function(e) { return e != card });
14203 // dont' really need ot update items.
14204 card.el.dom.parentNode.removeChild(card.el.dom);
14205 this.updateInput();
14211 this.fileCollection.each(function(card) {
14212 if (card.el.dom && card.el.dom.parentNode) {
14213 card.el.dom.parentNode.removeChild(card.el.dom);
14216 this.fileCollection.clear();
14217 this.updateInput();
14220 updateInput : function()
14223 this.fileCollection.each(function(e) {
14227 this.inputEl().dom.value = JSON.stringify(data);
14237 Roo.bootstrap.CardUploader.ID = -1;/*
14239 * Ext JS Library 1.1.1
14240 * Copyright(c) 2006-2007, Ext JS, LLC.
14242 * Originally Released Under LGPL - original licence link has changed is not relivant.
14245 * <script type="text/javascript">
14250 * @class Roo.data.SortTypes
14252 * Defines the default sorting (casting?) comparison functions used when sorting data.
14254 Roo.data.SortTypes = {
14256 * Default sort that does nothing
14257 * @param {Mixed} s The value being converted
14258 * @return {Mixed} The comparison value
14260 none : function(s){
14265 * The regular expression used to strip tags
14269 stripTagsRE : /<\/?[^>]+>/gi,
14272 * Strips all HTML tags to sort on text only
14273 * @param {Mixed} s The value being converted
14274 * @return {String} The comparison value
14276 asText : function(s){
14277 return String(s).replace(this.stripTagsRE, "");
14281 * Strips all HTML tags to sort on text only - Case insensitive
14282 * @param {Mixed} s The value being converted
14283 * @return {String} The comparison value
14285 asUCText : function(s){
14286 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14290 * Case insensitive string
14291 * @param {Mixed} s The value being converted
14292 * @return {String} The comparison value
14294 asUCString : function(s) {
14295 return String(s).toUpperCase();
14300 * @param {Mixed} s The value being converted
14301 * @return {Number} The comparison value
14303 asDate : function(s) {
14307 if(s instanceof Date){
14308 return s.getTime();
14310 return Date.parse(String(s));
14315 * @param {Mixed} s The value being converted
14316 * @return {Float} The comparison value
14318 asFloat : function(s) {
14319 var val = parseFloat(String(s).replace(/,/g, ""));
14328 * @param {Mixed} s The value being converted
14329 * @return {Number} The comparison value
14331 asInt : function(s) {
14332 var val = parseInt(String(s).replace(/,/g, ""));
14340 * Ext JS Library 1.1.1
14341 * Copyright(c) 2006-2007, Ext JS, LLC.
14343 * Originally Released Under LGPL - original licence link has changed is not relivant.
14346 * <script type="text/javascript">
14350 * @class Roo.data.Record
14351 * Instances of this class encapsulate both record <em>definition</em> information, and record
14352 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14353 * to access Records cached in an {@link Roo.data.Store} object.<br>
14355 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14356 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14359 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14361 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14362 * {@link #create}. The parameters are the same.
14363 * @param {Array} data An associative Array of data values keyed by the field name.
14364 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14365 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14366 * not specified an integer id is generated.
14368 Roo.data.Record = function(data, id){
14369 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14374 * Generate a constructor for a specific record layout.
14375 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14376 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14377 * Each field definition object may contain the following properties: <ul>
14378 * <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,
14379 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14380 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14381 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14382 * is being used, then this is a string containing the javascript expression to reference the data relative to
14383 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14384 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14385 * this may be omitted.</p></li>
14386 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14387 * <ul><li>auto (Default, implies no conversion)</li>
14392 * <li>date</li></ul></p></li>
14393 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14394 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14395 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14396 * by the Reader into an object that will be stored in the Record. It is passed the
14397 * following parameters:<ul>
14398 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14400 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14402 * <br>usage:<br><pre><code>
14403 var TopicRecord = Roo.data.Record.create(
14404 {name: 'title', mapping: 'topic_title'},
14405 {name: 'author', mapping: 'username'},
14406 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14407 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14408 {name: 'lastPoster', mapping: 'user2'},
14409 {name: 'excerpt', mapping: 'post_text'}
14412 var myNewRecord = new TopicRecord({
14413 title: 'Do my job please',
14416 lastPost: new Date(),
14417 lastPoster: 'Animal',
14418 excerpt: 'No way dude!'
14420 myStore.add(myNewRecord);
14425 Roo.data.Record.create = function(o){
14426 var f = function(){
14427 f.superclass.constructor.apply(this, arguments);
14429 Roo.extend(f, Roo.data.Record);
14430 var p = f.prototype;
14431 p.fields = new Roo.util.MixedCollection(false, function(field){
14434 for(var i = 0, len = o.length; i < len; i++){
14435 p.fields.add(new Roo.data.Field(o[i]));
14437 f.getField = function(name){
14438 return p.fields.get(name);
14443 Roo.data.Record.AUTO_ID = 1000;
14444 Roo.data.Record.EDIT = 'edit';
14445 Roo.data.Record.REJECT = 'reject';
14446 Roo.data.Record.COMMIT = 'commit';
14448 Roo.data.Record.prototype = {
14450 * Readonly flag - true if this record has been modified.
14459 join : function(store){
14460 this.store = store;
14464 * Set the named field to the specified value.
14465 * @param {String} name The name of the field to set.
14466 * @param {Object} value The value to set the field to.
14468 set : function(name, value){
14469 if(this.data[name] == value){
14473 if(!this.modified){
14474 this.modified = {};
14476 if(typeof this.modified[name] == 'undefined'){
14477 this.modified[name] = this.data[name];
14479 this.data[name] = value;
14480 if(!this.editing && this.store){
14481 this.store.afterEdit(this);
14486 * Get the value of the named field.
14487 * @param {String} name The name of the field to get the value of.
14488 * @return {Object} The value of the field.
14490 get : function(name){
14491 return this.data[name];
14495 beginEdit : function(){
14496 this.editing = true;
14497 this.modified = {};
14501 cancelEdit : function(){
14502 this.editing = false;
14503 delete this.modified;
14507 endEdit : function(){
14508 this.editing = false;
14509 if(this.dirty && this.store){
14510 this.store.afterEdit(this);
14515 * Usually called by the {@link Roo.data.Store} which owns the Record.
14516 * Rejects all changes made to the Record since either creation, or the last commit operation.
14517 * Modified fields are reverted to their original values.
14519 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14520 * of reject operations.
14522 reject : function(){
14523 var m = this.modified;
14525 if(typeof m[n] != "function"){
14526 this.data[n] = m[n];
14529 this.dirty = false;
14530 delete this.modified;
14531 this.editing = false;
14533 this.store.afterReject(this);
14538 * Usually called by the {@link Roo.data.Store} which owns the Record.
14539 * Commits all changes made to the Record since either creation, or the last commit operation.
14541 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14542 * of commit operations.
14544 commit : function(){
14545 this.dirty = false;
14546 delete this.modified;
14547 this.editing = false;
14549 this.store.afterCommit(this);
14554 hasError : function(){
14555 return this.error != null;
14559 clearError : function(){
14564 * Creates a copy of this record.
14565 * @param {String} id (optional) A new record id if you don't want to use this record's id
14568 copy : function(newId) {
14569 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14573 * Ext JS Library 1.1.1
14574 * Copyright(c) 2006-2007, Ext JS, LLC.
14576 * Originally Released Under LGPL - original licence link has changed is not relivant.
14579 * <script type="text/javascript">
14585 * @class Roo.data.Store
14586 * @extends Roo.util.Observable
14587 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14588 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14590 * 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
14591 * has no knowledge of the format of the data returned by the Proxy.<br>
14593 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14594 * instances from the data object. These records are cached and made available through accessor functions.
14596 * Creates a new Store.
14597 * @param {Object} config A config object containing the objects needed for the Store to access data,
14598 * and read the data into Records.
14600 Roo.data.Store = function(config){
14601 this.data = new Roo.util.MixedCollection(false);
14602 this.data.getKey = function(o){
14605 this.baseParams = {};
14607 this.paramNames = {
14612 "multisort" : "_multisort"
14615 if(config && config.data){
14616 this.inlineData = config.data;
14617 delete config.data;
14620 Roo.apply(this, config);
14622 if(this.reader){ // reader passed
14623 this.reader = Roo.factory(this.reader, Roo.data);
14624 this.reader.xmodule = this.xmodule || false;
14625 if(!this.recordType){
14626 this.recordType = this.reader.recordType;
14628 if(this.reader.onMetaChange){
14629 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14633 if(this.recordType){
14634 this.fields = this.recordType.prototype.fields;
14636 this.modified = [];
14640 * @event datachanged
14641 * Fires when the data cache has changed, and a widget which is using this Store
14642 * as a Record cache should refresh its view.
14643 * @param {Store} this
14645 datachanged : true,
14647 * @event metachange
14648 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14649 * @param {Store} this
14650 * @param {Object} meta The JSON metadata
14655 * Fires when Records have been added to the Store
14656 * @param {Store} this
14657 * @param {Roo.data.Record[]} records The array of Records added
14658 * @param {Number} index The index at which the record(s) were added
14663 * Fires when a Record has been removed from the Store
14664 * @param {Store} this
14665 * @param {Roo.data.Record} record The Record that was removed
14666 * @param {Number} index The index at which the record was removed
14671 * Fires when a Record has been updated
14672 * @param {Store} this
14673 * @param {Roo.data.Record} record The Record that was updated
14674 * @param {String} operation The update operation being performed. Value may be one of:
14676 Roo.data.Record.EDIT
14677 Roo.data.Record.REJECT
14678 Roo.data.Record.COMMIT
14684 * Fires when the data cache has been cleared.
14685 * @param {Store} this
14689 * @event beforeload
14690 * Fires before a request is made for a new data object. If the beforeload handler returns false
14691 * the load action will be canceled.
14692 * @param {Store} this
14693 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14697 * @event beforeloadadd
14698 * Fires after a new set of Records has been loaded.
14699 * @param {Store} this
14700 * @param {Roo.data.Record[]} records The Records that were loaded
14701 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14703 beforeloadadd : true,
14706 * Fires after a new set of Records has been loaded, before they are added to the store.
14707 * @param {Store} this
14708 * @param {Roo.data.Record[]} records The Records that were loaded
14709 * @param {Object} options The loading options that were specified (see {@link #load} for details)
14710 * @params {Object} return from reader
14714 * @event loadexception
14715 * Fires if an exception occurs in the Proxy during loading.
14716 * Called with the signature of the Proxy's "loadexception" event.
14717 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14720 * @param {Object} return from JsonData.reader() - success, totalRecords, records
14721 * @param {Object} load options
14722 * @param {Object} jsonData from your request (normally this contains the Exception)
14724 loadexception : true
14728 this.proxy = Roo.factory(this.proxy, Roo.data);
14729 this.proxy.xmodule = this.xmodule || false;
14730 this.relayEvents(this.proxy, ["loadexception"]);
14732 this.sortToggle = {};
14733 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14735 Roo.data.Store.superclass.constructor.call(this);
14737 if(this.inlineData){
14738 this.loadData(this.inlineData);
14739 delete this.inlineData;
14743 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14745 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
14746 * without a remote query - used by combo/forms at present.
14750 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14753 * @cfg {Array} data Inline data to be loaded when the store is initialized.
14756 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14757 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14760 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14761 * on any HTTP request
14764 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14767 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14771 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14772 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14774 remoteSort : false,
14777 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14778 * loaded or when a record is removed. (defaults to false).
14780 pruneModifiedRecords : false,
14783 lastOptions : null,
14786 * Add Records to the Store and fires the add event.
14787 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14789 add : function(records){
14790 records = [].concat(records);
14791 for(var i = 0, len = records.length; i < len; i++){
14792 records[i].join(this);
14794 var index = this.data.length;
14795 this.data.addAll(records);
14796 this.fireEvent("add", this, records, index);
14800 * Remove a Record from the Store and fires the remove event.
14801 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14803 remove : function(record){
14804 var index = this.data.indexOf(record);
14805 this.data.removeAt(index);
14807 if(this.pruneModifiedRecords){
14808 this.modified.remove(record);
14810 this.fireEvent("remove", this, record, index);
14814 * Remove all Records from the Store and fires the clear event.
14816 removeAll : function(){
14818 if(this.pruneModifiedRecords){
14819 this.modified = [];
14821 this.fireEvent("clear", this);
14825 * Inserts Records to the Store at the given index and fires the add event.
14826 * @param {Number} index The start index at which to insert the passed Records.
14827 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14829 insert : function(index, records){
14830 records = [].concat(records);
14831 for(var i = 0, len = records.length; i < len; i++){
14832 this.data.insert(index, records[i]);
14833 records[i].join(this);
14835 this.fireEvent("add", this, records, index);
14839 * Get the index within the cache of the passed Record.
14840 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14841 * @return {Number} The index of the passed Record. Returns -1 if not found.
14843 indexOf : function(record){
14844 return this.data.indexOf(record);
14848 * Get the index within the cache of the Record with the passed id.
14849 * @param {String} id The id of the Record to find.
14850 * @return {Number} The index of the Record. Returns -1 if not found.
14852 indexOfId : function(id){
14853 return this.data.indexOfKey(id);
14857 * Get the Record with the specified id.
14858 * @param {String} id The id of the Record to find.
14859 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14861 getById : function(id){
14862 return this.data.key(id);
14866 * Get the Record at the specified index.
14867 * @param {Number} index The index of the Record to find.
14868 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14870 getAt : function(index){
14871 return this.data.itemAt(index);
14875 * Returns a range of Records between specified indices.
14876 * @param {Number} startIndex (optional) The starting index (defaults to 0)
14877 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14878 * @return {Roo.data.Record[]} An array of Records
14880 getRange : function(start, end){
14881 return this.data.getRange(start, end);
14885 storeOptions : function(o){
14886 o = Roo.apply({}, o);
14889 this.lastOptions = o;
14893 * Loads the Record cache from the configured Proxy using the configured Reader.
14895 * If using remote paging, then the first load call must specify the <em>start</em>
14896 * and <em>limit</em> properties in the options.params property to establish the initial
14897 * position within the dataset, and the number of Records to cache on each read from the Proxy.
14899 * <strong>It is important to note that for remote data sources, loading is asynchronous,
14900 * and this call will return before the new data has been loaded. Perform any post-processing
14901 * in a callback function, or in a "load" event handler.</strong>
14903 * @param {Object} options An object containing properties which control loading options:<ul>
14904 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14905 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14906 * passed the following arguments:<ul>
14907 * <li>r : Roo.data.Record[]</li>
14908 * <li>options: Options object from the load call</li>
14909 * <li>success: Boolean success indicator</li></ul></li>
14910 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14911 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14914 load : function(options){
14915 options = options || {};
14916 if(this.fireEvent("beforeload", this, options) !== false){
14917 this.storeOptions(options);
14918 var p = Roo.apply(options.params || {}, this.baseParams);
14919 // if meta was not loaded from remote source.. try requesting it.
14920 if (!this.reader.metaFromRemote) {
14921 p._requestMeta = 1;
14923 if(this.sortInfo && this.remoteSort){
14924 var pn = this.paramNames;
14925 p[pn["sort"]] = this.sortInfo.field;
14926 p[pn["dir"]] = this.sortInfo.direction;
14928 if (this.multiSort) {
14929 var pn = this.paramNames;
14930 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14933 this.proxy.load(p, this.reader, this.loadRecords, this, options);
14938 * Reloads the Record cache from the configured Proxy using the configured Reader and
14939 * the options from the last load operation performed.
14940 * @param {Object} options (optional) An object containing properties which may override the options
14941 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14942 * the most recently used options are reused).
14944 reload : function(options){
14945 this.load(Roo.applyIf(options||{}, this.lastOptions));
14949 // Called as a callback by the Reader during a load operation.
14950 loadRecords : function(o, options, success){
14951 if(!o || success === false){
14952 if(success !== false){
14953 this.fireEvent("load", this, [], options, o);
14955 if(options.callback){
14956 options.callback.call(options.scope || this, [], options, false);
14960 // if data returned failure - throw an exception.
14961 if (o.success === false) {
14962 // show a message if no listener is registered.
14963 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14964 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14966 // loadmask wil be hooked into this..
14967 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14970 var r = o.records, t = o.totalRecords || r.length;
14972 this.fireEvent("beforeloadadd", this, r, options, o);
14974 if(!options || options.add !== true){
14975 if(this.pruneModifiedRecords){
14976 this.modified = [];
14978 for(var i = 0, len = r.length; i < len; i++){
14982 this.data = this.snapshot;
14983 delete this.snapshot;
14986 this.data.addAll(r);
14987 this.totalLength = t;
14989 this.fireEvent("datachanged", this);
14991 this.totalLength = Math.max(t, this.data.length+r.length);
14995 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
14997 var e = new Roo.data.Record({});
14999 e.set(this.parent.displayField, this.parent.emptyTitle);
15000 e.set(this.parent.valueField, '');
15005 this.fireEvent("load", this, r, options, o);
15006 if(options.callback){
15007 options.callback.call(options.scope || this, r, options, true);
15013 * Loads data from a passed data block. A Reader which understands the format of the data
15014 * must have been configured in the constructor.
15015 * @param {Object} data The data block from which to read the Records. The format of the data expected
15016 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15017 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15019 loadData : function(o, append){
15020 var r = this.reader.readRecords(o);
15021 this.loadRecords(r, {add: append}, true);
15025 * using 'cn' the nested child reader read the child array into it's child stores.
15026 * @param {Object} rec The record with a 'children array
15028 loadDataFromChildren : function(rec)
15030 this.loadData(this.reader.toLoadData(rec));
15035 * Gets the number of cached records.
15037 * <em>If using paging, this may not be the total size of the dataset. If the data object
15038 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15039 * the data set size</em>
15041 getCount : function(){
15042 return this.data.length || 0;
15046 * Gets the total number of records in the dataset as returned by the server.
15048 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15049 * the dataset size</em>
15051 getTotalCount : function(){
15052 return this.totalLength || 0;
15056 * Returns the sort state of the Store as an object with two properties:
15058 field {String} The name of the field by which the Records are sorted
15059 direction {String} The sort order, "ASC" or "DESC"
15062 getSortState : function(){
15063 return this.sortInfo;
15067 applySort : function(){
15068 if(this.sortInfo && !this.remoteSort){
15069 var s = this.sortInfo, f = s.field;
15070 var st = this.fields.get(f).sortType;
15071 var fn = function(r1, r2){
15072 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15073 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15075 this.data.sort(s.direction, fn);
15076 if(this.snapshot && this.snapshot != this.data){
15077 this.snapshot.sort(s.direction, fn);
15083 * Sets the default sort column and order to be used by the next load operation.
15084 * @param {String} fieldName The name of the field to sort by.
15085 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15087 setDefaultSort : function(field, dir){
15088 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15092 * Sort the Records.
15093 * If remote sorting is used, the sort is performed on the server, and the cache is
15094 * reloaded. If local sorting is used, the cache is sorted internally.
15095 * @param {String} fieldName The name of the field to sort by.
15096 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15098 sort : function(fieldName, dir){
15099 var f = this.fields.get(fieldName);
15101 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15103 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15104 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15109 this.sortToggle[f.name] = dir;
15110 this.sortInfo = {field: f.name, direction: dir};
15111 if(!this.remoteSort){
15113 this.fireEvent("datachanged", this);
15115 this.load(this.lastOptions);
15120 * Calls the specified function for each of the Records in the cache.
15121 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15122 * Returning <em>false</em> aborts and exits the iteration.
15123 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15125 each : function(fn, scope){
15126 this.data.each(fn, scope);
15130 * Gets all records modified since the last commit. Modified records are persisted across load operations
15131 * (e.g., during paging).
15132 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15134 getModifiedRecords : function(){
15135 return this.modified;
15139 createFilterFn : function(property, value, anyMatch){
15140 if(!value.exec){ // not a regex
15141 value = String(value);
15142 if(value.length == 0){
15145 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15147 return function(r){
15148 return value.test(r.data[property]);
15153 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15154 * @param {String} property A field on your records
15155 * @param {Number} start The record index to start at (defaults to 0)
15156 * @param {Number} end The last record index to include (defaults to length - 1)
15157 * @return {Number} The sum
15159 sum : function(property, start, end){
15160 var rs = this.data.items, v = 0;
15161 start = start || 0;
15162 end = (end || end === 0) ? end : rs.length-1;
15164 for(var i = start; i <= end; i++){
15165 v += (rs[i].data[property] || 0);
15171 * Filter the records by a specified property.
15172 * @param {String} field A field on your records
15173 * @param {String/RegExp} value Either a string that the field
15174 * should start with or a RegExp to test against the field
15175 * @param {Boolean} anyMatch True to match any part not just the beginning
15177 filter : function(property, value, anyMatch){
15178 var fn = this.createFilterFn(property, value, anyMatch);
15179 return fn ? this.filterBy(fn) : this.clearFilter();
15183 * Filter by a function. The specified function will be called with each
15184 * record in this data source. If the function returns true the record is included,
15185 * otherwise it is filtered.
15186 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15187 * @param {Object} scope (optional) The scope of the function (defaults to this)
15189 filterBy : function(fn, scope){
15190 this.snapshot = this.snapshot || this.data;
15191 this.data = this.queryBy(fn, scope||this);
15192 this.fireEvent("datachanged", this);
15196 * Query the records by a specified property.
15197 * @param {String} field A field on your records
15198 * @param {String/RegExp} value Either a string that the field
15199 * should start with or a RegExp to test against the field
15200 * @param {Boolean} anyMatch True to match any part not just the beginning
15201 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15203 query : function(property, value, anyMatch){
15204 var fn = this.createFilterFn(property, value, anyMatch);
15205 return fn ? this.queryBy(fn) : this.data.clone();
15209 * Query by a function. The specified function will be called with each
15210 * record in this data source. If the function returns true the record is included
15212 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15213 * @param {Object} scope (optional) The scope of the function (defaults to this)
15214 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15216 queryBy : function(fn, scope){
15217 var data = this.snapshot || this.data;
15218 return data.filterBy(fn, scope||this);
15222 * Collects unique values for a particular dataIndex from this store.
15223 * @param {String} dataIndex The property to collect
15224 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15225 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15226 * @return {Array} An array of the unique values
15228 collect : function(dataIndex, allowNull, bypassFilter){
15229 var d = (bypassFilter === true && this.snapshot) ?
15230 this.snapshot.items : this.data.items;
15231 var v, sv, r = [], l = {};
15232 for(var i = 0, len = d.length; i < len; i++){
15233 v = d[i].data[dataIndex];
15235 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15244 * Revert to a view of the Record cache with no filtering applied.
15245 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15247 clearFilter : function(suppressEvent){
15248 if(this.snapshot && this.snapshot != this.data){
15249 this.data = this.snapshot;
15250 delete this.snapshot;
15251 if(suppressEvent !== true){
15252 this.fireEvent("datachanged", this);
15258 afterEdit : function(record){
15259 if(this.modified.indexOf(record) == -1){
15260 this.modified.push(record);
15262 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15266 afterReject : function(record){
15267 this.modified.remove(record);
15268 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15272 afterCommit : function(record){
15273 this.modified.remove(record);
15274 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15278 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15279 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15281 commitChanges : function(){
15282 var m = this.modified.slice(0);
15283 this.modified = [];
15284 for(var i = 0, len = m.length; i < len; i++){
15290 * Cancel outstanding changes on all changed records.
15292 rejectChanges : function(){
15293 var m = this.modified.slice(0);
15294 this.modified = [];
15295 for(var i = 0, len = m.length; i < len; i++){
15300 onMetaChange : function(meta, rtype, o){
15301 this.recordType = rtype;
15302 this.fields = rtype.prototype.fields;
15303 delete this.snapshot;
15304 this.sortInfo = meta.sortInfo || this.sortInfo;
15305 this.modified = [];
15306 this.fireEvent('metachange', this, this.reader.meta);
15309 moveIndex : function(data, type)
15311 var index = this.indexOf(data);
15313 var newIndex = index + type;
15317 this.insert(newIndex, data);
15322 * Ext JS Library 1.1.1
15323 * Copyright(c) 2006-2007, Ext JS, LLC.
15325 * Originally Released Under LGPL - original licence link has changed is not relivant.
15328 * <script type="text/javascript">
15332 * @class Roo.data.SimpleStore
15333 * @extends Roo.data.Store
15334 * Small helper class to make creating Stores from Array data easier.
15335 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15336 * @cfg {Array} fields An array of field definition objects, or field name strings.
15337 * @cfg {Object} an existing reader (eg. copied from another store)
15338 * @cfg {Array} data The multi-dimensional array of data
15340 * @param {Object} config
15342 Roo.data.SimpleStore = function(config)
15344 Roo.data.SimpleStore.superclass.constructor.call(this, {
15346 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15349 Roo.data.Record.create(config.fields)
15351 proxy : new Roo.data.MemoryProxy(config.data)
15355 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15357 * Ext JS Library 1.1.1
15358 * Copyright(c) 2006-2007, Ext JS, LLC.
15360 * Originally Released Under LGPL - original licence link has changed is not relivant.
15363 * <script type="text/javascript">
15368 * @extends Roo.data.Store
15369 * @class Roo.data.JsonStore
15370 * Small helper class to make creating Stores for JSON data easier. <br/>
15372 var store = new Roo.data.JsonStore({
15373 url: 'get-images.php',
15375 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15378 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15379 * JsonReader and HttpProxy (unless inline data is provided).</b>
15380 * @cfg {Array} fields An array of field definition objects, or field name strings.
15382 * @param {Object} config
15384 Roo.data.JsonStore = function(c){
15385 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15386 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15387 reader: new Roo.data.JsonReader(c, c.fields)
15390 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15392 * Ext JS Library 1.1.1
15393 * Copyright(c) 2006-2007, Ext JS, LLC.
15395 * Originally Released Under LGPL - original licence link has changed is not relivant.
15398 * <script type="text/javascript">
15402 Roo.data.Field = function(config){
15403 if(typeof config == "string"){
15404 config = {name: config};
15406 Roo.apply(this, config);
15409 this.type = "auto";
15412 var st = Roo.data.SortTypes;
15413 // named sortTypes are supported, here we look them up
15414 if(typeof this.sortType == "string"){
15415 this.sortType = st[this.sortType];
15418 // set default sortType for strings and dates
15419 if(!this.sortType){
15422 this.sortType = st.asUCString;
15425 this.sortType = st.asDate;
15428 this.sortType = st.none;
15433 var stripRe = /[\$,%]/g;
15435 // prebuilt conversion function for this field, instead of
15436 // switching every time we're reading a value
15438 var cv, dateFormat = this.dateFormat;
15443 cv = function(v){ return v; };
15446 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15450 return v !== undefined && v !== null && v !== '' ?
15451 parseInt(String(v).replace(stripRe, ""), 10) : '';
15456 return v !== undefined && v !== null && v !== '' ?
15457 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15462 cv = function(v){ return v === true || v === "true" || v == 1; };
15469 if(v instanceof Date){
15473 if(dateFormat == "timestamp"){
15474 return new Date(v*1000);
15476 return Date.parseDate(v, dateFormat);
15478 var parsed = Date.parse(v);
15479 return parsed ? new Date(parsed) : null;
15488 Roo.data.Field.prototype = {
15496 * Ext JS Library 1.1.1
15497 * Copyright(c) 2006-2007, Ext JS, LLC.
15499 * Originally Released Under LGPL - original licence link has changed is not relivant.
15502 * <script type="text/javascript">
15505 // Base class for reading structured data from a data source. This class is intended to be
15506 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15509 * @class Roo.data.DataReader
15510 * Base class for reading structured data from a data source. This class is intended to be
15511 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15514 Roo.data.DataReader = function(meta, recordType){
15518 this.recordType = recordType instanceof Array ?
15519 Roo.data.Record.create(recordType) : recordType;
15522 Roo.data.DataReader.prototype = {
15525 readerType : 'Data',
15527 * Create an empty record
15528 * @param {Object} data (optional) - overlay some values
15529 * @return {Roo.data.Record} record created.
15531 newRow : function(d) {
15533 this.recordType.prototype.fields.each(function(c) {
15535 case 'int' : da[c.name] = 0; break;
15536 case 'date' : da[c.name] = new Date(); break;
15537 case 'float' : da[c.name] = 0.0; break;
15538 case 'boolean' : da[c.name] = false; break;
15539 default : da[c.name] = ""; break;
15543 return new this.recordType(Roo.apply(da, d));
15549 * Ext JS Library 1.1.1
15550 * Copyright(c) 2006-2007, Ext JS, LLC.
15552 * Originally Released Under LGPL - original licence link has changed is not relivant.
15555 * <script type="text/javascript">
15559 * @class Roo.data.DataProxy
15560 * @extends Roo.data.Observable
15561 * This class is an abstract base class for implementations which provide retrieval of
15562 * unformatted data objects.<br>
15564 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15565 * (of the appropriate type which knows how to parse the data object) to provide a block of
15566 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15568 * Custom implementations must implement the load method as described in
15569 * {@link Roo.data.HttpProxy#load}.
15571 Roo.data.DataProxy = function(){
15574 * @event beforeload
15575 * Fires before a network request is made to retrieve a data object.
15576 * @param {Object} This DataProxy object.
15577 * @param {Object} params The params parameter to the load function.
15582 * Fires before the load method's callback is called.
15583 * @param {Object} This DataProxy object.
15584 * @param {Object} o The data object.
15585 * @param {Object} arg The callback argument object passed to the load function.
15589 * @event loadexception
15590 * Fires if an Exception occurs during data retrieval.
15591 * @param {Object} This DataProxy object.
15592 * @param {Object} o The data object.
15593 * @param {Object} arg The callback argument object passed to the load function.
15594 * @param {Object} e The Exception.
15596 loadexception : true
15598 Roo.data.DataProxy.superclass.constructor.call(this);
15601 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15604 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15608 * Ext JS Library 1.1.1
15609 * Copyright(c) 2006-2007, Ext JS, LLC.
15611 * Originally Released Under LGPL - original licence link has changed is not relivant.
15614 * <script type="text/javascript">
15617 * @class Roo.data.MemoryProxy
15618 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15619 * to the Reader when its load method is called.
15621 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15623 Roo.data.MemoryProxy = function(data){
15627 Roo.data.MemoryProxy.superclass.constructor.call(this);
15631 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15634 * Load data from the requested source (in this case an in-memory
15635 * data object passed to the constructor), read the data object into
15636 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15637 * process that block using the passed callback.
15638 * @param {Object} params This parameter is not used by the MemoryProxy class.
15639 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15640 * object into a block of Roo.data.Records.
15641 * @param {Function} callback The function into which to pass the block of Roo.data.records.
15642 * The function must be passed <ul>
15643 * <li>The Record block object</li>
15644 * <li>The "arg" argument from the load function</li>
15645 * <li>A boolean success indicator</li>
15647 * @param {Object} scope The scope in which to call the callback
15648 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15650 load : function(params, reader, callback, scope, arg){
15651 params = params || {};
15654 result = reader.readRecords(params.data ? params.data :this.data);
15656 this.fireEvent("loadexception", this, arg, null, e);
15657 callback.call(scope, null, arg, false);
15660 callback.call(scope, result, arg, true);
15664 update : function(params, records){
15669 * Ext JS Library 1.1.1
15670 * Copyright(c) 2006-2007, Ext JS, LLC.
15672 * Originally Released Under LGPL - original licence link has changed is not relivant.
15675 * <script type="text/javascript">
15678 * @class Roo.data.HttpProxy
15679 * @extends Roo.data.DataProxy
15680 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15681 * configured to reference a certain URL.<br><br>
15683 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15684 * from which the running page was served.<br><br>
15686 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15688 * Be aware that to enable the browser to parse an XML document, the server must set
15689 * the Content-Type header in the HTTP response to "text/xml".
15691 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15692 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
15693 * will be used to make the request.
15695 Roo.data.HttpProxy = function(conn){
15696 Roo.data.HttpProxy.superclass.constructor.call(this);
15697 // is conn a conn config or a real conn?
15699 this.useAjax = !conn || !conn.events;
15703 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15704 // thse are take from connection...
15707 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15710 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15711 * extra parameters to each request made by this object. (defaults to undefined)
15714 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15715 * to each request made by this object. (defaults to undefined)
15718 * @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)
15721 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15724 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15730 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15734 * Return the {@link Roo.data.Connection} object being used by this Proxy.
15735 * @return {Connection} The Connection object. This object may be used to subscribe to events on
15736 * a finer-grained basis than the DataProxy events.
15738 getConnection : function(){
15739 return this.useAjax ? Roo.Ajax : this.conn;
15743 * Load data from the configured {@link Roo.data.Connection}, read the data object into
15744 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15745 * process that block using the passed callback.
15746 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15747 * for the request to the remote server.
15748 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15749 * object into a block of Roo.data.Records.
15750 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15751 * The function must be passed <ul>
15752 * <li>The Record block object</li>
15753 * <li>The "arg" argument from the load function</li>
15754 * <li>A boolean success indicator</li>
15756 * @param {Object} scope The scope in which to call the callback
15757 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15759 load : function(params, reader, callback, scope, arg){
15760 if(this.fireEvent("beforeload", this, params) !== false){
15762 params : params || {},
15764 callback : callback,
15769 callback : this.loadResponse,
15773 Roo.applyIf(o, this.conn);
15774 if(this.activeRequest){
15775 Roo.Ajax.abort(this.activeRequest);
15777 this.activeRequest = Roo.Ajax.request(o);
15779 this.conn.request(o);
15782 callback.call(scope||this, null, arg, false);
15787 loadResponse : function(o, success, response){
15788 delete this.activeRequest;
15790 this.fireEvent("loadexception", this, o, response);
15791 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15796 result = o.reader.read(response);
15798 this.fireEvent("loadexception", this, o, response, e);
15799 o.request.callback.call(o.request.scope, null, o.request.arg, false);
15803 this.fireEvent("load", this, o, o.request.arg);
15804 o.request.callback.call(o.request.scope, result, o.request.arg, true);
15808 update : function(dataSet){
15813 updateResponse : function(dataSet){
15818 * Ext JS Library 1.1.1
15819 * Copyright(c) 2006-2007, Ext JS, LLC.
15821 * Originally Released Under LGPL - original licence link has changed is not relivant.
15824 * <script type="text/javascript">
15828 * @class Roo.data.ScriptTagProxy
15829 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15830 * other than the originating domain of the running page.<br><br>
15832 * <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
15833 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15835 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15836 * source code that is used as the source inside a <script> tag.<br><br>
15838 * In order for the browser to process the returned data, the server must wrap the data object
15839 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15840 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15841 * depending on whether the callback name was passed:
15844 boolean scriptTag = false;
15845 String cb = request.getParameter("callback");
15848 response.setContentType("text/javascript");
15850 response.setContentType("application/x-json");
15852 Writer out = response.getWriter();
15854 out.write(cb + "(");
15856 out.print(dataBlock.toJsonString());
15863 * @param {Object} config A configuration object.
15865 Roo.data.ScriptTagProxy = function(config){
15866 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15867 Roo.apply(this, config);
15868 this.head = document.getElementsByTagName("head")[0];
15871 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15873 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15875 * @cfg {String} url The URL from which to request the data object.
15878 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15882 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15883 * the server the name of the callback function set up by the load call to process the returned data object.
15884 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15885 * javascript output which calls this named function passing the data object as its only parameter.
15887 callbackParam : "callback",
15889 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15890 * name to the request.
15895 * Load data from the configured URL, read the data object into
15896 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15897 * process that block using the passed callback.
15898 * @param {Object} params An object containing properties which are to be used as HTTP parameters
15899 * for the request to the remote server.
15900 * @param {Roo.data.DataReader} reader The Reader object which converts the data
15901 * object into a block of Roo.data.Records.
15902 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15903 * The function must be passed <ul>
15904 * <li>The Record block object</li>
15905 * <li>The "arg" argument from the load function</li>
15906 * <li>A boolean success indicator</li>
15908 * @param {Object} scope The scope in which to call the callback
15909 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15911 load : function(params, reader, callback, scope, arg){
15912 if(this.fireEvent("beforeload", this, params) !== false){
15914 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15916 var url = this.url;
15917 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15919 url += "&_dc=" + (new Date().getTime());
15921 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15924 cb : "stcCallback"+transId,
15925 scriptId : "stcScript"+transId,
15929 callback : callback,
15935 window[trans.cb] = function(o){
15936 conn.handleResponse(o, trans);
15939 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15941 if(this.autoAbort !== false){
15945 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15947 var script = document.createElement("script");
15948 script.setAttribute("src", url);
15949 script.setAttribute("type", "text/javascript");
15950 script.setAttribute("id", trans.scriptId);
15951 this.head.appendChild(script);
15953 this.trans = trans;
15955 callback.call(scope||this, null, arg, false);
15960 isLoading : function(){
15961 return this.trans ? true : false;
15965 * Abort the current server request.
15967 abort : function(){
15968 if(this.isLoading()){
15969 this.destroyTrans(this.trans);
15974 destroyTrans : function(trans, isLoaded){
15975 this.head.removeChild(document.getElementById(trans.scriptId));
15976 clearTimeout(trans.timeoutId);
15978 window[trans.cb] = undefined;
15980 delete window[trans.cb];
15983 // if hasn't been loaded, wait for load to remove it to prevent script error
15984 window[trans.cb] = function(){
15985 window[trans.cb] = undefined;
15987 delete window[trans.cb];
15994 handleResponse : function(o, trans){
15995 this.trans = false;
15996 this.destroyTrans(trans, true);
15999 result = trans.reader.readRecords(o);
16001 this.fireEvent("loadexception", this, o, trans.arg, e);
16002 trans.callback.call(trans.scope||window, null, trans.arg, false);
16005 this.fireEvent("load", this, o, trans.arg);
16006 trans.callback.call(trans.scope||window, result, trans.arg, true);
16010 handleFailure : function(trans){
16011 this.trans = false;
16012 this.destroyTrans(trans, false);
16013 this.fireEvent("loadexception", this, null, trans.arg);
16014 trans.callback.call(trans.scope||window, null, trans.arg, false);
16018 * Ext JS Library 1.1.1
16019 * Copyright(c) 2006-2007, Ext JS, LLC.
16021 * Originally Released Under LGPL - original licence link has changed is not relivant.
16024 * <script type="text/javascript">
16028 * @class Roo.data.JsonReader
16029 * @extends Roo.data.DataReader
16030 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16031 * based on mappings in a provided Roo.data.Record constructor.
16033 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16034 * in the reply previously.
16039 var RecordDef = Roo.data.Record.create([
16040 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16041 {name: 'occupation'} // This field will use "occupation" as the mapping.
16043 var myReader = new Roo.data.JsonReader({
16044 totalProperty: "results", // The property which contains the total dataset size (optional)
16045 root: "rows", // The property which contains an Array of row objects
16046 id: "id" // The property within each row object that provides an ID for the record (optional)
16050 * This would consume a JSON file like this:
16052 { 'results': 2, 'rows': [
16053 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16054 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16057 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16058 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16059 * paged from the remote server.
16060 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16061 * @cfg {String} root name of the property which contains the Array of row objects.
16062 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16063 * @cfg {Array} fields Array of field definition objects
16065 * Create a new JsonReader
16066 * @param {Object} meta Metadata configuration options
16067 * @param {Object} recordType Either an Array of field definition objects,
16068 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16070 Roo.data.JsonReader = function(meta, recordType){
16073 // set some defaults:
16074 Roo.applyIf(meta, {
16075 totalProperty: 'total',
16076 successProperty : 'success',
16081 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16083 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16085 readerType : 'Json',
16088 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16089 * Used by Store query builder to append _requestMeta to params.
16092 metaFromRemote : false,
16094 * This method is only used by a DataProxy which has retrieved data from a remote server.
16095 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16096 * @return {Object} data A data block which is used by an Roo.data.Store object as
16097 * a cache of Roo.data.Records.
16099 read : function(response){
16100 var json = response.responseText;
16102 var o = /* eval:var:o */ eval("("+json+")");
16104 throw {message: "JsonReader.read: Json object not found"};
16110 this.metaFromRemote = true;
16111 this.meta = o.metaData;
16112 this.recordType = Roo.data.Record.create(o.metaData.fields);
16113 this.onMetaChange(this.meta, this.recordType, o);
16115 return this.readRecords(o);
16118 // private function a store will implement
16119 onMetaChange : function(meta, recordType, o){
16126 simpleAccess: function(obj, subsc) {
16133 getJsonAccessor: function(){
16135 return function(expr) {
16137 return(re.test(expr))
16138 ? new Function("obj", "return obj." + expr)
16143 return Roo.emptyFn;
16148 * Create a data block containing Roo.data.Records from an XML document.
16149 * @param {Object} o An object which contains an Array of row objects in the property specified
16150 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16151 * which contains the total size of the dataset.
16152 * @return {Object} data A data block which is used by an Roo.data.Store object as
16153 * a cache of Roo.data.Records.
16155 readRecords : function(o){
16157 * After any data loads, the raw JSON data is available for further custom processing.
16161 var s = this.meta, Record = this.recordType,
16162 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16164 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16166 if(s.totalProperty) {
16167 this.getTotal = this.getJsonAccessor(s.totalProperty);
16169 if(s.successProperty) {
16170 this.getSuccess = this.getJsonAccessor(s.successProperty);
16172 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16174 var g = this.getJsonAccessor(s.id);
16175 this.getId = function(rec) {
16177 return (r === undefined || r === "") ? null : r;
16180 this.getId = function(){return null;};
16183 for(var jj = 0; jj < fl; jj++){
16185 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16186 this.ef[jj] = this.getJsonAccessor(map);
16190 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16191 if(s.totalProperty){
16192 var vt = parseInt(this.getTotal(o), 10);
16197 if(s.successProperty){
16198 var vs = this.getSuccess(o);
16199 if(vs === false || vs === 'false'){
16204 for(var i = 0; i < c; i++){
16207 var id = this.getId(n);
16208 for(var j = 0; j < fl; j++){
16210 var v = this.ef[j](n);
16212 Roo.log('missing convert for ' + f.name);
16216 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16218 var record = new Record(values, id);
16220 records[i] = record;
16226 totalRecords : totalRecords
16229 // used when loading children.. @see loadDataFromChildren
16230 toLoadData: function(rec)
16232 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16233 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16234 return { data : data, total : data.length };
16239 * Ext JS Library 1.1.1
16240 * Copyright(c) 2006-2007, Ext JS, LLC.
16242 * Originally Released Under LGPL - original licence link has changed is not relivant.
16245 * <script type="text/javascript">
16249 * @class Roo.data.ArrayReader
16250 * @extends Roo.data.DataReader
16251 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16252 * Each element of that Array represents a row of data fields. The
16253 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16254 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16258 var RecordDef = Roo.data.Record.create([
16259 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16260 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16262 var myReader = new Roo.data.ArrayReader({
16263 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16267 * This would consume an Array like this:
16269 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16273 * Create a new JsonReader
16274 * @param {Object} meta Metadata configuration options.
16275 * @param {Object|Array} recordType Either an Array of field definition objects
16277 * @cfg {Array} fields Array of field definition objects
16278 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16279 * as specified to {@link Roo.data.Record#create},
16280 * or an {@link Roo.data.Record} object
16283 * created using {@link Roo.data.Record#create}.
16285 Roo.data.ArrayReader = function(meta, recordType)
16287 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16290 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16293 * Create a data block containing Roo.data.Records from an XML document.
16294 * @param {Object} o An Array of row objects which represents the dataset.
16295 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16296 * a cache of Roo.data.Records.
16298 readRecords : function(o)
16300 var sid = this.meta ? this.meta.id : null;
16301 var recordType = this.recordType, fields = recordType.prototype.fields;
16304 for(var i = 0; i < root.length; i++){
16307 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16308 for(var j = 0, jlen = fields.length; j < jlen; j++){
16309 var f = fields.items[j];
16310 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16311 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16313 values[f.name] = v;
16315 var record = new recordType(values, id);
16317 records[records.length] = record;
16321 totalRecords : records.length
16324 // used when loading children.. @see loadDataFromChildren
16325 toLoadData: function(rec)
16327 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16328 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16339 * @class Roo.bootstrap.ComboBox
16340 * @extends Roo.bootstrap.TriggerField
16341 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16342 * @cfg {Boolean} append (true|false) default false
16343 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16344 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16345 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16346 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16347 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16348 * @cfg {Boolean} animate default true
16349 * @cfg {Boolean} emptyResultText only for touch device
16350 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16351 * @cfg {String} emptyTitle default ''
16352 * @cfg {Number} width fixed with? experimental
16354 * Create a new ComboBox.
16355 * @param {Object} config Configuration options
16357 Roo.bootstrap.ComboBox = function(config){
16358 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16362 * Fires when the dropdown list is expanded
16363 * @param {Roo.bootstrap.ComboBox} combo This combo box
16368 * Fires when the dropdown list is collapsed
16369 * @param {Roo.bootstrap.ComboBox} combo This combo box
16373 * @event beforeselect
16374 * Fires before a list item is selected. Return false to cancel the selection.
16375 * @param {Roo.bootstrap.ComboBox} combo This combo box
16376 * @param {Roo.data.Record} record The data record returned from the underlying store
16377 * @param {Number} index The index of the selected item in the dropdown list
16379 'beforeselect' : true,
16382 * Fires when a list item is selected
16383 * @param {Roo.bootstrap.ComboBox} combo This combo box
16384 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16385 * @param {Number} index The index of the selected item in the dropdown list
16389 * @event beforequery
16390 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16391 * The event object passed has these properties:
16392 * @param {Roo.bootstrap.ComboBox} combo This combo box
16393 * @param {String} query The query
16394 * @param {Boolean} forceAll true to force "all" query
16395 * @param {Boolean} cancel true to cancel the query
16396 * @param {Object} e The query event object
16398 'beforequery': true,
16401 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16402 * @param {Roo.bootstrap.ComboBox} combo This combo box
16407 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16408 * @param {Roo.bootstrap.ComboBox} combo This combo box
16409 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16414 * Fires when the remove value from the combobox array
16415 * @param {Roo.bootstrap.ComboBox} combo This combo box
16419 * @event afterremove
16420 * Fires when the remove value from the combobox array
16421 * @param {Roo.bootstrap.ComboBox} combo This combo box
16423 'afterremove' : true,
16425 * @event specialfilter
16426 * Fires when specialfilter
16427 * @param {Roo.bootstrap.ComboBox} combo This combo box
16429 'specialfilter' : true,
16432 * Fires when tick the element
16433 * @param {Roo.bootstrap.ComboBox} combo This combo box
16437 * @event touchviewdisplay
16438 * Fires when touch view require special display (default is using displayField)
16439 * @param {Roo.bootstrap.ComboBox} combo This combo box
16440 * @param {Object} cfg set html .
16442 'touchviewdisplay' : true
16447 this.tickItems = [];
16449 this.selectedIndex = -1;
16450 if(this.mode == 'local'){
16451 if(config.queryDelay === undefined){
16452 this.queryDelay = 10;
16454 if(config.minChars === undefined){
16460 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16463 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16464 * rendering into an Roo.Editor, defaults to false)
16467 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16468 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16471 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16474 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16475 * the dropdown list (defaults to undefined, with no header element)
16479 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16483 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16485 listWidth: undefined,
16487 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16488 * mode = 'remote' or 'text' if mode = 'local')
16490 displayField: undefined,
16493 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16494 * mode = 'remote' or 'value' if mode = 'local').
16495 * Note: use of a valueField requires the user make a selection
16496 * in order for a value to be mapped.
16498 valueField: undefined,
16500 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16505 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16506 * field's data value (defaults to the underlying DOM element's name)
16508 hiddenName: undefined,
16510 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16514 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16516 selectedClass: 'active',
16519 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16523 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16524 * anchor positions (defaults to 'tl-bl')
16526 listAlign: 'tl-bl?',
16528 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16532 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16533 * query specified by the allQuery config option (defaults to 'query')
16535 triggerAction: 'query',
16537 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16538 * (defaults to 4, does not apply if editable = false)
16542 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16543 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16547 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16548 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16552 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16553 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16557 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16558 * when editable = true (defaults to false)
16560 selectOnFocus:false,
16562 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16564 queryParam: 'query',
16566 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
16567 * when mode = 'remote' (defaults to 'Loading...')
16569 loadingText: 'Loading...',
16571 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16575 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16579 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16580 * traditional select (defaults to true)
16584 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16588 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16592 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16593 * listWidth has a higher value)
16597 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16598 * allow the user to set arbitrary text into the field (defaults to false)
16600 forceSelection:false,
16602 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16603 * if typeAhead = true (defaults to 250)
16605 typeAheadDelay : 250,
16607 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16608 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16610 valueNotFoundText : undefined,
16612 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16614 blockFocus : false,
16617 * @cfg {Boolean} disableClear Disable showing of clear button.
16619 disableClear : false,
16621 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
16623 alwaysQuery : false,
16626 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
16631 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16633 invalidClass : "has-warning",
16636 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16638 validClass : "has-success",
16641 * @cfg {Boolean} specialFilter (true|false) special filter default false
16643 specialFilter : false,
16646 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16648 mobileTouchView : true,
16651 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16653 useNativeIOS : false,
16656 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16658 mobile_restrict_height : false,
16660 ios_options : false,
16672 btnPosition : 'right',
16673 triggerList : true,
16674 showToggleBtn : true,
16676 emptyResultText: 'Empty',
16677 triggerText : 'Select',
16681 // element that contains real text value.. (when hidden is used..)
16683 getAutoCreate : function()
16688 * Render classic select for iso
16691 if(Roo.isIOS && this.useNativeIOS){
16692 cfg = this.getAutoCreateNativeIOS();
16700 if(Roo.isTouch && this.mobileTouchView){
16701 cfg = this.getAutoCreateTouchView();
16708 if(!this.tickable){
16709 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16714 * ComboBox with tickable selections
16717 var align = this.labelAlign || this.parentLabelAlign();
16720 cls : 'form-group roo-combobox-tickable' //input-group
16723 var btn_text_select = '';
16724 var btn_text_done = '';
16725 var btn_text_cancel = '';
16727 if (this.btn_text_show) {
16728 btn_text_select = 'Select';
16729 btn_text_done = 'Done';
16730 btn_text_cancel = 'Cancel';
16735 cls : 'tickable-buttons',
16740 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16741 //html : this.triggerText
16742 html: btn_text_select
16748 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16750 html: btn_text_done
16756 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16758 html: btn_text_cancel
16764 buttons.cn.unshift({
16766 cls: 'roo-select2-search-field-input'
16772 Roo.each(buttons.cn, function(c){
16774 c.cls += ' btn-' + _this.size;
16777 if (_this.disabled) {
16784 style : 'display: contents',
16789 cls: 'form-hidden-field'
16793 cls: 'roo-select2-choices',
16797 cls: 'roo-select2-search-field',
16808 cls: 'roo-select2-container input-group roo-select2-container-multi',
16814 // cls: 'typeahead typeahead-long dropdown-menu',
16815 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
16820 if(this.hasFeedback && !this.allowBlank){
16824 cls: 'glyphicon form-control-feedback'
16827 combobox.cn.push(feedback);
16834 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16835 tooltip : 'This field is required'
16837 if (Roo.bootstrap.version == 4) {
16840 style : 'display:none'
16843 if (align ==='left' && this.fieldLabel.length) {
16845 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
16852 cls : 'control-label col-form-label',
16853 html : this.fieldLabel
16865 var labelCfg = cfg.cn[1];
16866 var contentCfg = cfg.cn[2];
16869 if(this.indicatorpos == 'right'){
16875 cls : 'control-label col-form-label',
16879 html : this.fieldLabel
16895 labelCfg = cfg.cn[0];
16896 contentCfg = cfg.cn[1];
16900 if(this.labelWidth > 12){
16901 labelCfg.style = "width: " + this.labelWidth + 'px';
16903 if(this.width * 1 > 0){
16904 contentCfg.style = "width: " + this.width + 'px';
16906 if(this.labelWidth < 13 && this.labelmd == 0){
16907 this.labelmd = this.labelWidth;
16910 if(this.labellg > 0){
16911 labelCfg.cls += ' col-lg-' + this.labellg;
16912 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16915 if(this.labelmd > 0){
16916 labelCfg.cls += ' col-md-' + this.labelmd;
16917 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16920 if(this.labelsm > 0){
16921 labelCfg.cls += ' col-sm-' + this.labelsm;
16922 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16925 if(this.labelxs > 0){
16926 labelCfg.cls += ' col-xs-' + this.labelxs;
16927 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16931 } else if ( this.fieldLabel.length) {
16932 // Roo.log(" label");
16937 //cls : 'input-group-addon',
16938 html : this.fieldLabel
16943 if(this.indicatorpos == 'right'){
16947 //cls : 'input-group-addon',
16948 html : this.fieldLabel
16958 // Roo.log(" no label && no align");
16965 ['xs','sm','md','lg'].map(function(size){
16966 if (settings[size]) {
16967 cfg.cls += ' col-' + size + '-' + settings[size];
16975 _initEventsCalled : false,
16978 initEvents: function()
16980 if (this._initEventsCalled) { // as we call render... prevent looping...
16983 this._initEventsCalled = true;
16986 throw "can not find store for combo";
16989 this.indicator = this.indicatorEl();
16991 this.store = Roo.factory(this.store, Roo.data);
16992 this.store.parent = this;
16994 // if we are building from html. then this element is so complex, that we can not really
16995 // use the rendered HTML.
16996 // so we have to trash and replace the previous code.
16997 if (Roo.XComponent.build_from_html) {
16998 // remove this element....
16999 var e = this.el.dom, k=0;
17000 while (e ) { e = e.previousSibling; ++k;}
17005 this.rendered = false;
17007 this.render(this.parent().getChildContainer(true), k);
17010 if(Roo.isIOS && this.useNativeIOS){
17011 this.initIOSView();
17019 if(Roo.isTouch && this.mobileTouchView){
17020 this.initTouchView();
17025 this.initTickableEvents();
17029 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17031 if(this.hiddenName){
17033 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17035 this.hiddenField.dom.value =
17036 this.hiddenValue !== undefined ? this.hiddenValue :
17037 this.value !== undefined ? this.value : '';
17039 // prevent input submission
17040 this.el.dom.removeAttribute('name');
17041 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17046 // this.el.dom.setAttribute('autocomplete', 'off');
17049 var cls = 'x-combo-list';
17051 //this.list = new Roo.Layer({
17052 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17058 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17059 _this.list.setWidth(lw);
17062 this.list.on('mouseover', this.onViewOver, this);
17063 this.list.on('mousemove', this.onViewMove, this);
17064 this.list.on('scroll', this.onViewScroll, this);
17067 this.list.swallowEvent('mousewheel');
17068 this.assetHeight = 0;
17071 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17072 this.assetHeight += this.header.getHeight();
17075 this.innerList = this.list.createChild({cls:cls+'-inner'});
17076 this.innerList.on('mouseover', this.onViewOver, this);
17077 this.innerList.on('mousemove', this.onViewMove, this);
17078 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17080 if(this.allowBlank && !this.pageSize && !this.disableClear){
17081 this.footer = this.list.createChild({cls:cls+'-ft'});
17082 this.pageTb = new Roo.Toolbar(this.footer);
17086 this.footer = this.list.createChild({cls:cls+'-ft'});
17087 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17088 {pageSize: this.pageSize});
17092 if (this.pageTb && this.allowBlank && !this.disableClear) {
17094 this.pageTb.add(new Roo.Toolbar.Fill(), {
17095 cls: 'x-btn-icon x-btn-clear',
17097 handler: function()
17100 _this.clearValue();
17101 _this.onSelect(false, -1);
17106 this.assetHeight += this.footer.getHeight();
17111 this.tpl = Roo.bootstrap.version == 4 ?
17112 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17113 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17116 this.view = new Roo.View(this.list, this.tpl, {
17117 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17119 //this.view.wrapEl.setDisplayed(false);
17120 this.view.on('click', this.onViewClick, this);
17123 this.store.on('beforeload', this.onBeforeLoad, this);
17124 this.store.on('load', this.onLoad, this);
17125 this.store.on('loadexception', this.onLoadException, this);
17127 if(this.resizable){
17128 this.resizer = new Roo.Resizable(this.list, {
17129 pinned:true, handles:'se'
17131 this.resizer.on('resize', function(r, w, h){
17132 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17133 this.listWidth = w;
17134 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17135 this.restrictHeight();
17137 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17140 if(!this.editable){
17141 this.editable = true;
17142 this.setEditable(false);
17147 if (typeof(this.events.add.listeners) != 'undefined') {
17149 this.addicon = this.wrap.createChild(
17150 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17152 this.addicon.on('click', function(e) {
17153 this.fireEvent('add', this);
17156 if (typeof(this.events.edit.listeners) != 'undefined') {
17158 this.editicon = this.wrap.createChild(
17159 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17160 if (this.addicon) {
17161 this.editicon.setStyle('margin-left', '40px');
17163 this.editicon.on('click', function(e) {
17165 // we fire even if inothing is selected..
17166 this.fireEvent('edit', this, this.lastData );
17172 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17173 "up" : function(e){
17174 this.inKeyMode = true;
17178 "down" : function(e){
17179 if(!this.isExpanded()){
17180 this.onTriggerClick();
17182 this.inKeyMode = true;
17187 "enter" : function(e){
17188 // this.onViewClick();
17192 if(this.fireEvent("specialkey", this, e)){
17193 this.onViewClick(false);
17199 "esc" : function(e){
17203 "tab" : function(e){
17206 if(this.fireEvent("specialkey", this, e)){
17207 this.onViewClick(false);
17215 doRelay : function(foo, bar, hname){
17216 if(hname == 'down' || this.scope.isExpanded()){
17217 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17226 this.queryDelay = Math.max(this.queryDelay || 10,
17227 this.mode == 'local' ? 10 : 250);
17230 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17232 if(this.typeAhead){
17233 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17235 if(this.editable !== false){
17236 this.inputEl().on("keyup", this.onKeyUp, this);
17238 if(this.forceSelection){
17239 this.inputEl().on('blur', this.doForce, this);
17243 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17244 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17248 initTickableEvents: function()
17252 if(this.hiddenName){
17254 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17256 this.hiddenField.dom.value =
17257 this.hiddenValue !== undefined ? this.hiddenValue :
17258 this.value !== undefined ? this.value : '';
17260 // prevent input submission
17261 this.el.dom.removeAttribute('name');
17262 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17267 // this.list = this.el.select('ul.dropdown-menu',true).first();
17269 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17270 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17271 if(this.triggerList){
17272 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17275 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17276 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17278 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17279 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17281 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17282 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17284 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17285 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17286 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17289 this.cancelBtn.hide();
17294 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17295 _this.list.setWidth(lw);
17298 this.list.on('mouseover', this.onViewOver, this);
17299 this.list.on('mousemove', this.onViewMove, this);
17301 this.list.on('scroll', this.onViewScroll, this);
17304 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17305 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17308 this.view = new Roo.View(this.list, this.tpl, {
17313 selectedClass: this.selectedClass
17316 //this.view.wrapEl.setDisplayed(false);
17317 this.view.on('click', this.onViewClick, this);
17321 this.store.on('beforeload', this.onBeforeLoad, this);
17322 this.store.on('load', this.onLoad, this);
17323 this.store.on('loadexception', this.onLoadException, this);
17326 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17327 "up" : function(e){
17328 this.inKeyMode = true;
17332 "down" : function(e){
17333 this.inKeyMode = true;
17337 "enter" : function(e){
17338 if(this.fireEvent("specialkey", this, e)){
17339 this.onViewClick(false);
17345 "esc" : function(e){
17346 this.onTickableFooterButtonClick(e, false, false);
17349 "tab" : function(e){
17350 this.fireEvent("specialkey", this, e);
17352 this.onTickableFooterButtonClick(e, false, false);
17359 doRelay : function(e, fn, key){
17360 if(this.scope.isExpanded()){
17361 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17370 this.queryDelay = Math.max(this.queryDelay || 10,
17371 this.mode == 'local' ? 10 : 250);
17374 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17376 if(this.typeAhead){
17377 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17380 if(this.editable !== false){
17381 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17384 this.indicator = this.indicatorEl();
17386 if(this.indicator){
17387 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17388 this.indicator.hide();
17393 onDestroy : function(){
17395 this.view.setStore(null);
17396 this.view.el.removeAllListeners();
17397 this.view.el.remove();
17398 this.view.purgeListeners();
17401 this.list.dom.innerHTML = '';
17405 this.store.un('beforeload', this.onBeforeLoad, this);
17406 this.store.un('load', this.onLoad, this);
17407 this.store.un('loadexception', this.onLoadException, this);
17409 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17413 fireKey : function(e){
17414 if(e.isNavKeyPress() && !this.list.isVisible()){
17415 this.fireEvent("specialkey", this, e);
17420 onResize: function(w, h)
17424 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17426 // if(typeof w != 'number'){
17427 // // we do not handle it!?!?
17430 // var tw = this.trigger.getWidth();
17431 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17432 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17434 // this.inputEl().setWidth( this.adjustWidth('input', x));
17436 // //this.trigger.setStyle('left', x+'px');
17438 // if(this.list && this.listWidth === undefined){
17439 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17440 // this.list.setWidth(lw);
17441 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17449 * Allow or prevent the user from directly editing the field text. If false is passed,
17450 * the user will only be able to select from the items defined in the dropdown list. This method
17451 * is the runtime equivalent of setting the 'editable' config option at config time.
17452 * @param {Boolean} value True to allow the user to directly edit the field text
17454 setEditable : function(value){
17455 if(value == this.editable){
17458 this.editable = value;
17460 this.inputEl().dom.setAttribute('readOnly', true);
17461 this.inputEl().on('mousedown', this.onTriggerClick, this);
17462 this.inputEl().addClass('x-combo-noedit');
17464 this.inputEl().dom.removeAttribute('readOnly');
17465 this.inputEl().un('mousedown', this.onTriggerClick, this);
17466 this.inputEl().removeClass('x-combo-noedit');
17472 onBeforeLoad : function(combo,opts){
17473 if(!this.hasFocus){
17477 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17479 this.restrictHeight();
17480 this.selectedIndex = -1;
17484 onLoad : function(){
17486 this.hasQuery = false;
17488 if(!this.hasFocus){
17492 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17493 this.loading.hide();
17496 if(this.store.getCount() > 0){
17499 this.restrictHeight();
17500 if(this.lastQuery == this.allQuery){
17501 if(this.editable && !this.tickable){
17502 this.inputEl().dom.select();
17506 !this.selectByValue(this.value, true) &&
17509 !this.store.lastOptions ||
17510 typeof(this.store.lastOptions.add) == 'undefined' ||
17511 this.store.lastOptions.add != true
17514 this.select(0, true);
17517 if(this.autoFocus){
17520 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17521 this.taTask.delay(this.typeAheadDelay);
17525 this.onEmptyResults();
17531 onLoadException : function()
17533 this.hasQuery = false;
17535 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17536 this.loading.hide();
17539 if(this.tickable && this.editable){
17544 // only causes errors at present
17545 //Roo.log(this.store.reader.jsonData);
17546 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17548 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17554 onTypeAhead : function(){
17555 if(this.store.getCount() > 0){
17556 var r = this.store.getAt(0);
17557 var newValue = r.data[this.displayField];
17558 var len = newValue.length;
17559 var selStart = this.getRawValue().length;
17561 if(selStart != len){
17562 this.setRawValue(newValue);
17563 this.selectText(selStart, newValue.length);
17569 onSelect : function(record, index){
17571 if(this.fireEvent('beforeselect', this, record, index) !== false){
17573 this.setFromData(index > -1 ? record.data : false);
17576 this.fireEvent('select', this, record, index);
17581 * Returns the currently selected field value or empty string if no value is set.
17582 * @return {String} value The selected value
17584 getValue : function()
17586 if(Roo.isIOS && this.useNativeIOS){
17587 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17591 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17594 if(this.valueField){
17595 return typeof this.value != 'undefined' ? this.value : '';
17597 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17601 getRawValue : function()
17603 if(Roo.isIOS && this.useNativeIOS){
17604 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17607 var v = this.inputEl().getValue();
17613 * Clears any text/value currently set in the field
17615 clearValue : function(){
17617 if(this.hiddenField){
17618 this.hiddenField.dom.value = '';
17621 this.setRawValue('');
17622 this.lastSelectionText = '';
17623 this.lastData = false;
17625 var close = this.closeTriggerEl();
17636 * Sets the specified value into the field. If the value finds a match, the corresponding record text
17637 * will be displayed in the field. If the value does not match the data value of an existing item,
17638 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17639 * Otherwise the field will be blank (although the value will still be set).
17640 * @param {String} value The value to match
17642 setValue : function(v)
17644 if(Roo.isIOS && this.useNativeIOS){
17645 this.setIOSValue(v);
17655 if(this.valueField){
17656 var r = this.findRecord(this.valueField, v);
17658 text = r.data[this.displayField];
17659 }else if(this.valueNotFoundText !== undefined){
17660 text = this.valueNotFoundText;
17663 this.lastSelectionText = text;
17664 if(this.hiddenField){
17665 this.hiddenField.dom.value = v;
17667 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17670 var close = this.closeTriggerEl();
17673 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17679 * @property {Object} the last set data for the element
17684 * Sets the value of the field based on a object which is related to the record format for the store.
17685 * @param {Object} value the value to set as. or false on reset?
17687 setFromData : function(o){
17694 var dv = ''; // display value
17695 var vv = ''; // value value..
17697 if (this.displayField) {
17698 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17700 // this is an error condition!!!
17701 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17704 if(this.valueField){
17705 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17708 var close = this.closeTriggerEl();
17711 if(dv.length || vv * 1 > 0){
17713 this.blockFocus=true;
17719 if(this.hiddenField){
17720 this.hiddenField.dom.value = vv;
17722 this.lastSelectionText = dv;
17723 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17727 // no hidden field.. - we store the value in 'value', but still display
17728 // display field!!!!
17729 this.lastSelectionText = dv;
17730 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17737 reset : function(){
17738 // overridden so that last data is reset..
17745 this.setValue(this.originalValue);
17746 //this.clearInvalid();
17747 this.lastData = false;
17749 this.view.clearSelections();
17755 findRecord : function(prop, value){
17757 if(this.store.getCount() > 0){
17758 this.store.each(function(r){
17759 if(r.data[prop] == value){
17769 getName: function()
17771 // returns hidden if it's set..
17772 if (!this.rendered) {return ''};
17773 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
17777 onViewMove : function(e, t){
17778 this.inKeyMode = false;
17782 onViewOver : function(e, t){
17783 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17786 var item = this.view.findItemFromChild(t);
17789 var index = this.view.indexOf(item);
17790 this.select(index, false);
17795 onViewClick : function(view, doFocus, el, e)
17797 var index = this.view.getSelectedIndexes()[0];
17799 var r = this.store.getAt(index);
17803 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17810 Roo.each(this.tickItems, function(v,k){
17812 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17814 _this.tickItems.splice(k, 1);
17816 if(typeof(e) == 'undefined' && view == false){
17817 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17829 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17830 this.tickItems.push(r.data);
17833 if(typeof(e) == 'undefined' && view == false){
17834 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17841 this.onSelect(r, index);
17843 if(doFocus !== false && !this.blockFocus){
17844 this.inputEl().focus();
17849 restrictHeight : function(){
17850 //this.innerList.dom.style.height = '';
17851 //var inner = this.innerList.dom;
17852 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17853 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17854 //this.list.beginUpdate();
17855 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17856 this.list.alignTo(this.inputEl(), this.listAlign);
17857 this.list.alignTo(this.inputEl(), this.listAlign);
17858 //this.list.endUpdate();
17862 onEmptyResults : function(){
17864 if(this.tickable && this.editable){
17865 this.hasFocus = false;
17866 this.restrictHeight();
17874 * Returns true if the dropdown list is expanded, else false.
17876 isExpanded : function(){
17877 return this.list.isVisible();
17881 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17882 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17883 * @param {String} value The data value of the item to select
17884 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17885 * selected item if it is not currently in view (defaults to true)
17886 * @return {Boolean} True if the value matched an item in the list, else false
17888 selectByValue : function(v, scrollIntoView){
17889 if(v !== undefined && v !== null){
17890 var r = this.findRecord(this.valueField || this.displayField, v);
17892 this.select(this.store.indexOf(r), scrollIntoView);
17900 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17901 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17902 * @param {Number} index The zero-based index of the list item to select
17903 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17904 * selected item if it is not currently in view (defaults to true)
17906 select : function(index, scrollIntoView){
17907 this.selectedIndex = index;
17908 this.view.select(index);
17909 if(scrollIntoView !== false){
17910 var el = this.view.getNode(index);
17912 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17915 this.list.scrollChildIntoView(el, false);
17921 selectNext : function(){
17922 var ct = this.store.getCount();
17924 if(this.selectedIndex == -1){
17926 }else if(this.selectedIndex < ct-1){
17927 this.select(this.selectedIndex+1);
17933 selectPrev : function(){
17934 var ct = this.store.getCount();
17936 if(this.selectedIndex == -1){
17938 }else if(this.selectedIndex != 0){
17939 this.select(this.selectedIndex-1);
17945 onKeyUp : function(e){
17946 if(this.editable !== false && !e.isSpecialKey()){
17947 this.lastKey = e.getKey();
17948 this.dqTask.delay(this.queryDelay);
17953 validateBlur : function(){
17954 return !this.list || !this.list.isVisible();
17958 initQuery : function(){
17960 var v = this.getRawValue();
17962 if(this.tickable && this.editable){
17963 v = this.tickableInputEl().getValue();
17970 doForce : function(){
17971 if(this.inputEl().dom.value.length > 0){
17972 this.inputEl().dom.value =
17973 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17979 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
17980 * query allowing the query action to be canceled if needed.
17981 * @param {String} query The SQL query to execute
17982 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17983 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
17984 * saved in the current store (defaults to false)
17986 doQuery : function(q, forceAll){
17988 if(q === undefined || q === null){
17993 forceAll: forceAll,
17997 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18002 forceAll = qe.forceAll;
18003 if(forceAll === true || (q.length >= this.minChars)){
18005 this.hasQuery = true;
18007 if(this.lastQuery != q || this.alwaysQuery){
18008 this.lastQuery = q;
18009 if(this.mode == 'local'){
18010 this.selectedIndex = -1;
18012 this.store.clearFilter();
18015 if(this.specialFilter){
18016 this.fireEvent('specialfilter', this);
18021 this.store.filter(this.displayField, q);
18024 this.store.fireEvent("datachanged", this.store);
18031 this.store.baseParams[this.queryParam] = q;
18033 var options = {params : this.getParams(q)};
18036 options.add = true;
18037 options.params.start = this.page * this.pageSize;
18040 this.store.load(options);
18043 * this code will make the page width larger, at the beginning, the list not align correctly,
18044 * we should expand the list on onLoad
18045 * so command out it
18050 this.selectedIndex = -1;
18055 this.loadNext = false;
18059 getParams : function(q){
18061 //p[this.queryParam] = q;
18065 p.limit = this.pageSize;
18071 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18073 collapse : function(){
18074 if(!this.isExpanded()){
18080 this.hasFocus = false;
18084 this.cancelBtn.hide();
18085 this.trigger.show();
18088 this.tickableInputEl().dom.value = '';
18089 this.tickableInputEl().blur();
18094 Roo.get(document).un('mousedown', this.collapseIf, this);
18095 Roo.get(document).un('mousewheel', this.collapseIf, this);
18096 if (!this.editable) {
18097 Roo.get(document).un('keydown', this.listKeyPress, this);
18099 this.fireEvent('collapse', this);
18105 collapseIf : function(e){
18106 var in_combo = e.within(this.el);
18107 var in_list = e.within(this.list);
18108 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18110 if (in_combo || in_list || is_list) {
18111 //e.stopPropagation();
18116 this.onTickableFooterButtonClick(e, false, false);
18124 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18126 expand : function(){
18128 if(this.isExpanded() || !this.hasFocus){
18132 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18133 this.list.setWidth(lw);
18139 this.restrictHeight();
18143 this.tickItems = Roo.apply([], this.item);
18146 this.cancelBtn.show();
18147 this.trigger.hide();
18150 this.tickableInputEl().focus();
18155 Roo.get(document).on('mousedown', this.collapseIf, this);
18156 Roo.get(document).on('mousewheel', this.collapseIf, this);
18157 if (!this.editable) {
18158 Roo.get(document).on('keydown', this.listKeyPress, this);
18161 this.fireEvent('expand', this);
18165 // Implements the default empty TriggerField.onTriggerClick function
18166 onTriggerClick : function(e)
18168 Roo.log('trigger click');
18170 if(this.disabled || !this.triggerList){
18175 this.loadNext = false;
18177 if(this.isExpanded()){
18179 if (!this.blockFocus) {
18180 this.inputEl().focus();
18184 this.hasFocus = true;
18185 if(this.triggerAction == 'all') {
18186 this.doQuery(this.allQuery, true);
18188 this.doQuery(this.getRawValue());
18190 if (!this.blockFocus) {
18191 this.inputEl().focus();
18196 onTickableTriggerClick : function(e)
18203 this.loadNext = false;
18204 this.hasFocus = true;
18206 if(this.triggerAction == 'all') {
18207 this.doQuery(this.allQuery, true);
18209 this.doQuery(this.getRawValue());
18213 onSearchFieldClick : function(e)
18215 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18216 this.onTickableFooterButtonClick(e, false, false);
18220 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18225 this.loadNext = false;
18226 this.hasFocus = true;
18228 if(this.triggerAction == 'all') {
18229 this.doQuery(this.allQuery, true);
18231 this.doQuery(this.getRawValue());
18235 listKeyPress : function(e)
18237 //Roo.log('listkeypress');
18238 // scroll to first matching element based on key pres..
18239 if (e.isSpecialKey()) {
18242 var k = String.fromCharCode(e.getKey()).toUpperCase();
18245 var csel = this.view.getSelectedNodes();
18246 var cselitem = false;
18248 var ix = this.view.indexOf(csel[0]);
18249 cselitem = this.store.getAt(ix);
18250 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18256 this.store.each(function(v) {
18258 // start at existing selection.
18259 if (cselitem.id == v.id) {
18265 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18266 match = this.store.indexOf(v);
18272 if (match === false) {
18273 return true; // no more action?
18276 this.view.select(match);
18277 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18278 sn.scrollIntoView(sn.dom.parentNode, false);
18281 onViewScroll : function(e, t){
18283 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){
18287 this.hasQuery = true;
18289 this.loading = this.list.select('.loading', true).first();
18291 if(this.loading === null){
18292 this.list.createChild({
18294 cls: 'loading roo-select2-more-results roo-select2-active',
18295 html: 'Loading more results...'
18298 this.loading = this.list.select('.loading', true).first();
18300 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18302 this.loading.hide();
18305 this.loading.show();
18310 this.loadNext = true;
18312 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18317 addItem : function(o)
18319 var dv = ''; // display value
18321 if (this.displayField) {
18322 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18324 // this is an error condition!!!
18325 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18332 var choice = this.choices.createChild({
18334 cls: 'roo-select2-search-choice',
18343 cls: 'roo-select2-search-choice-close fa fa-times',
18348 }, this.searchField);
18350 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18352 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18360 this.inputEl().dom.value = '';
18365 onRemoveItem : function(e, _self, o)
18367 e.preventDefault();
18369 this.lastItem = Roo.apply([], this.item);
18371 var index = this.item.indexOf(o.data) * 1;
18374 Roo.log('not this item?!');
18378 this.item.splice(index, 1);
18383 this.fireEvent('remove', this, e);
18389 syncValue : function()
18391 if(!this.item.length){
18398 Roo.each(this.item, function(i){
18399 if(_this.valueField){
18400 value.push(i[_this.valueField]);
18407 this.value = value.join(',');
18409 if(this.hiddenField){
18410 this.hiddenField.dom.value = this.value;
18413 this.store.fireEvent("datachanged", this.store);
18418 clearItem : function()
18420 if(!this.multiple){
18426 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18434 if(this.tickable && !Roo.isTouch){
18435 this.view.refresh();
18439 inputEl: function ()
18441 if(Roo.isIOS && this.useNativeIOS){
18442 return this.el.select('select.roo-ios-select', true).first();
18445 if(Roo.isTouch && this.mobileTouchView){
18446 return this.el.select('input.form-control',true).first();
18450 return this.searchField;
18453 return this.el.select('input.form-control',true).first();
18456 onTickableFooterButtonClick : function(e, btn, el)
18458 e.preventDefault();
18460 this.lastItem = Roo.apply([], this.item);
18462 if(btn && btn.name == 'cancel'){
18463 this.tickItems = Roo.apply([], this.item);
18472 Roo.each(this.tickItems, function(o){
18480 validate : function()
18482 if(this.getVisibilityEl().hasClass('hidden')){
18486 var v = this.getRawValue();
18489 v = this.getValue();
18492 if(this.disabled || this.allowBlank || v.length){
18497 this.markInvalid();
18501 tickableInputEl : function()
18503 if(!this.tickable || !this.editable){
18504 return this.inputEl();
18507 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18511 getAutoCreateTouchView : function()
18516 cls: 'form-group' //input-group
18522 type : this.inputType,
18523 cls : 'form-control x-combo-noedit',
18524 autocomplete: 'new-password',
18525 placeholder : this.placeholder || '',
18530 input.name = this.name;
18534 input.cls += ' input-' + this.size;
18537 if (this.disabled) {
18538 input.disabled = true;
18542 cls : 'roo-combobox-wrap',
18549 inputblock.cls += ' input-group';
18551 inputblock.cn.unshift({
18553 cls : 'input-group-addon input-group-prepend input-group-text',
18558 if(this.removable && !this.multiple){
18559 inputblock.cls += ' roo-removable';
18561 inputblock.cn.push({
18564 cls : 'roo-combo-removable-btn close'
18568 if(this.hasFeedback && !this.allowBlank){
18570 inputblock.cls += ' has-feedback';
18572 inputblock.cn.push({
18574 cls: 'glyphicon form-control-feedback'
18581 inputblock.cls += (this.before) ? '' : ' input-group';
18583 inputblock.cn.push({
18585 cls : 'input-group-addon input-group-append input-group-text',
18591 var ibwrap = inputblock;
18596 cls: 'roo-select2-choices',
18600 cls: 'roo-select2-search-field',
18613 cls: 'roo-select2-container input-group roo-touchview-combobox ',
18618 cls: 'form-hidden-field'
18624 if(!this.multiple && this.showToggleBtn){
18630 if (this.caret != false) {
18633 cls: 'fa fa-' + this.caret
18640 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18642 Roo.bootstrap.version == 3 ? caret : '',
18645 cls: 'combobox-clear',
18659 combobox.cls += ' roo-select2-container-multi';
18662 var required = this.allowBlank ? {
18664 style: 'display: none'
18667 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18668 tooltip : 'This field is required'
18671 var align = this.labelAlign || this.parentLabelAlign();
18673 if (align ==='left' && this.fieldLabel.length) {
18679 cls : 'control-label col-form-label',
18680 html : this.fieldLabel
18684 cls : 'roo-combobox-wrap ',
18691 var labelCfg = cfg.cn[1];
18692 var contentCfg = cfg.cn[2];
18695 if(this.indicatorpos == 'right'){
18700 cls : 'control-label col-form-label',
18704 html : this.fieldLabel
18710 cls : "roo-combobox-wrap ",
18718 labelCfg = cfg.cn[0];
18719 contentCfg = cfg.cn[1];
18724 if(this.labelWidth > 12){
18725 labelCfg.style = "width: " + this.labelWidth + 'px';
18728 if(this.labelWidth < 13 && this.labelmd == 0){
18729 this.labelmd = this.labelWidth;
18732 if(this.labellg > 0){
18733 labelCfg.cls += ' col-lg-' + this.labellg;
18734 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18737 if(this.labelmd > 0){
18738 labelCfg.cls += ' col-md-' + this.labelmd;
18739 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18742 if(this.labelsm > 0){
18743 labelCfg.cls += ' col-sm-' + this.labelsm;
18744 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18747 if(this.labelxs > 0){
18748 labelCfg.cls += ' col-xs-' + this.labelxs;
18749 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18753 } else if ( this.fieldLabel.length) {
18758 cls : 'control-label',
18759 html : this.fieldLabel
18770 if(this.indicatorpos == 'right'){
18774 cls : 'control-label',
18775 html : this.fieldLabel,
18793 var settings = this;
18795 ['xs','sm','md','lg'].map(function(size){
18796 if (settings[size]) {
18797 cfg.cls += ' col-' + size + '-' + settings[size];
18804 initTouchView : function()
18806 this.renderTouchView();
18808 this.touchViewEl.on('scroll', function(){
18809 this.el.dom.scrollTop = 0;
18812 this.originalValue = this.getValue();
18814 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18816 this.inputEl().on("click", this.showTouchView, this);
18817 if (this.triggerEl) {
18818 this.triggerEl.on("click", this.showTouchView, this);
18822 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18823 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18825 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18827 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18828 this.store.on('load', this.onTouchViewLoad, this);
18829 this.store.on('loadexception', this.onTouchViewLoadException, this);
18831 if(this.hiddenName){
18833 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18835 this.hiddenField.dom.value =
18836 this.hiddenValue !== undefined ? this.hiddenValue :
18837 this.value !== undefined ? this.value : '';
18839 this.el.dom.removeAttribute('name');
18840 this.hiddenField.dom.setAttribute('name', this.hiddenName);
18844 this.choices = this.el.select('ul.roo-select2-choices', true).first();
18845 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18848 if(this.removable && !this.multiple){
18849 var close = this.closeTriggerEl();
18851 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18852 close.on('click', this.removeBtnClick, this, close);
18856 * fix the bug in Safari iOS8
18858 this.inputEl().on("focus", function(e){
18859 document.activeElement.blur();
18862 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18869 renderTouchView : function()
18871 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18872 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18874 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18875 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18877 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18878 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18879 this.touchViewBodyEl.setStyle('overflow', 'auto');
18881 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18882 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18884 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18885 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18889 showTouchView : function()
18895 this.touchViewHeaderEl.hide();
18897 if(this.modalTitle.length){
18898 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18899 this.touchViewHeaderEl.show();
18902 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18903 this.touchViewEl.show();
18905 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18907 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18908 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18910 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18912 if(this.modalTitle.length){
18913 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18916 this.touchViewBodyEl.setHeight(bodyHeight);
18920 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18922 this.touchViewEl.addClass(['in','show']);
18925 if(this._touchViewMask){
18926 Roo.get(document.body).addClass("x-body-masked");
18927 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18928 this._touchViewMask.setStyle('z-index', 10000);
18929 this._touchViewMask.addClass('show');
18932 this.doTouchViewQuery();
18936 hideTouchView : function()
18938 this.touchViewEl.removeClass(['in','show']);
18942 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18944 this.touchViewEl.setStyle('display', 'none');
18947 if(this._touchViewMask){
18948 this._touchViewMask.removeClass('show');
18949 Roo.get(document.body).removeClass("x-body-masked");
18953 setTouchViewValue : function()
18960 Roo.each(this.tickItems, function(o){
18965 this.hideTouchView();
18968 doTouchViewQuery : function()
18977 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18981 if(!this.alwaysQuery || this.mode == 'local'){
18982 this.onTouchViewLoad();
18989 onTouchViewBeforeLoad : function(combo,opts)
18995 onTouchViewLoad : function()
18997 if(this.store.getCount() < 1){
18998 this.onTouchViewEmptyResults();
19002 this.clearTouchView();
19004 var rawValue = this.getRawValue();
19006 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19008 this.tickItems = [];
19010 this.store.data.each(function(d, rowIndex){
19011 var row = this.touchViewListGroup.createChild(template);
19013 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19014 row.addClass(d.data.cls);
19017 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19020 html : d.data[this.displayField]
19023 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19024 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19027 row.removeClass('selected');
19028 if(!this.multiple && this.valueField &&
19029 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19032 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19033 row.addClass('selected');
19036 if(this.multiple && this.valueField &&
19037 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19041 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19042 this.tickItems.push(d.data);
19045 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19049 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19051 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19053 if(this.modalTitle.length){
19054 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19057 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19059 if(this.mobile_restrict_height && listHeight < bodyHeight){
19060 this.touchViewBodyEl.setHeight(listHeight);
19065 if(firstChecked && listHeight > bodyHeight){
19066 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19071 onTouchViewLoadException : function()
19073 this.hideTouchView();
19076 onTouchViewEmptyResults : function()
19078 this.clearTouchView();
19080 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19082 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19086 clearTouchView : function()
19088 this.touchViewListGroup.dom.innerHTML = '';
19091 onTouchViewClick : function(e, el, o)
19093 e.preventDefault();
19096 var rowIndex = o.rowIndex;
19098 var r = this.store.getAt(rowIndex);
19100 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19102 if(!this.multiple){
19103 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19104 c.dom.removeAttribute('checked');
19107 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19109 this.setFromData(r.data);
19111 var close = this.closeTriggerEl();
19117 this.hideTouchView();
19119 this.fireEvent('select', this, r, rowIndex);
19124 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19125 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19126 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19130 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19131 this.addItem(r.data);
19132 this.tickItems.push(r.data);
19136 getAutoCreateNativeIOS : function()
19139 cls: 'form-group' //input-group,
19144 cls : 'roo-ios-select'
19148 combobox.name = this.name;
19151 if (this.disabled) {
19152 combobox.disabled = true;
19155 var settings = this;
19157 ['xs','sm','md','lg'].map(function(size){
19158 if (settings[size]) {
19159 cfg.cls += ' col-' + size + '-' + settings[size];
19169 initIOSView : function()
19171 this.store.on('load', this.onIOSViewLoad, this);
19176 onIOSViewLoad : function()
19178 if(this.store.getCount() < 1){
19182 this.clearIOSView();
19184 if(this.allowBlank) {
19186 var default_text = '-- SELECT --';
19188 if(this.placeholder.length){
19189 default_text = this.placeholder;
19192 if(this.emptyTitle.length){
19193 default_text += ' - ' + this.emptyTitle + ' -';
19196 var opt = this.inputEl().createChild({
19199 html : default_text
19203 o[this.valueField] = 0;
19204 o[this.displayField] = default_text;
19206 this.ios_options.push({
19213 this.store.data.each(function(d, rowIndex){
19217 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19218 html = d.data[this.displayField];
19223 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19224 value = d.data[this.valueField];
19233 if(this.value == d.data[this.valueField]){
19234 option['selected'] = true;
19237 var opt = this.inputEl().createChild(option);
19239 this.ios_options.push({
19246 this.inputEl().on('change', function(){
19247 this.fireEvent('select', this);
19252 clearIOSView: function()
19254 this.inputEl().dom.innerHTML = '';
19256 this.ios_options = [];
19259 setIOSValue: function(v)
19263 if(!this.ios_options){
19267 Roo.each(this.ios_options, function(opts){
19269 opts.el.dom.removeAttribute('selected');
19271 if(opts.data[this.valueField] != v){
19275 opts.el.dom.setAttribute('selected', true);
19281 * @cfg {Boolean} grow
19285 * @cfg {Number} growMin
19289 * @cfg {Number} growMax
19298 Roo.apply(Roo.bootstrap.ComboBox, {
19302 cls: 'modal-header',
19324 cls: 'list-group-item',
19328 cls: 'roo-combobox-list-group-item-value'
19332 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19346 listItemCheckbox : {
19348 cls: 'list-group-item',
19352 cls: 'roo-combobox-list-group-item-value'
19356 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19372 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19377 cls: 'modal-footer',
19385 cls: 'col-xs-6 text-left',
19388 cls: 'btn btn-danger roo-touch-view-cancel',
19394 cls: 'col-xs-6 text-right',
19397 cls: 'btn btn-success roo-touch-view-ok',
19408 Roo.apply(Roo.bootstrap.ComboBox, {
19410 touchViewTemplate : {
19412 cls: 'modal fade roo-combobox-touch-view',
19416 cls: 'modal-dialog',
19417 style : 'position:fixed', // we have to fix position....
19421 cls: 'modal-content',
19423 Roo.bootstrap.ComboBox.header,
19424 Roo.bootstrap.ComboBox.body,
19425 Roo.bootstrap.ComboBox.footer
19434 * Ext JS Library 1.1.1
19435 * Copyright(c) 2006-2007, Ext JS, LLC.
19437 * Originally Released Under LGPL - original licence link has changed is not relivant.
19440 * <script type="text/javascript">
19445 * @extends Roo.util.Observable
19446 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19447 * This class also supports single and multi selection modes. <br>
19448 * Create a data model bound view:
19450 var store = new Roo.data.Store(...);
19452 var view = new Roo.View({
19454 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19456 singleSelect: true,
19457 selectedClass: "ydataview-selected",
19461 // listen for node click?
19462 view.on("click", function(vw, index, node, e){
19463 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19467 dataModel.load("foobar.xml");
19469 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19471 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19472 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19474 * Note: old style constructor is still suported (container, template, config)
19477 * Create a new View
19478 * @param {Object} config The config object
19481 Roo.View = function(config, depreciated_tpl, depreciated_config){
19483 this.parent = false;
19485 if (typeof(depreciated_tpl) == 'undefined') {
19486 // new way.. - universal constructor.
19487 Roo.apply(this, config);
19488 this.el = Roo.get(this.el);
19491 this.el = Roo.get(config);
19492 this.tpl = depreciated_tpl;
19493 Roo.apply(this, depreciated_config);
19495 this.wrapEl = this.el.wrap().wrap();
19496 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19499 if(typeof(this.tpl) == "string"){
19500 this.tpl = new Roo.Template(this.tpl);
19502 // support xtype ctors..
19503 this.tpl = new Roo.factory(this.tpl, Roo);
19507 this.tpl.compile();
19512 * @event beforeclick
19513 * Fires before a click is processed. Returns false to cancel the default action.
19514 * @param {Roo.View} this
19515 * @param {Number} index The index of the target node
19516 * @param {HTMLElement} node The target node
19517 * @param {Roo.EventObject} e The raw event object
19519 "beforeclick" : true,
19522 * Fires when a template node is clicked.
19523 * @param {Roo.View} this
19524 * @param {Number} index The index of the target node
19525 * @param {HTMLElement} node The target node
19526 * @param {Roo.EventObject} e The raw event object
19531 * Fires when a template node is double clicked.
19532 * @param {Roo.View} this
19533 * @param {Number} index The index of the target node
19534 * @param {HTMLElement} node The target node
19535 * @param {Roo.EventObject} e The raw event object
19539 * @event contextmenu
19540 * Fires when a template node is right clicked.
19541 * @param {Roo.View} this
19542 * @param {Number} index The index of the target node
19543 * @param {HTMLElement} node The target node
19544 * @param {Roo.EventObject} e The raw event object
19546 "contextmenu" : true,
19548 * @event selectionchange
19549 * Fires when the selected nodes change.
19550 * @param {Roo.View} this
19551 * @param {Array} selections Array of the selected nodes
19553 "selectionchange" : true,
19556 * @event beforeselect
19557 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19558 * @param {Roo.View} this
19559 * @param {HTMLElement} node The node to be selected
19560 * @param {Array} selections Array of currently selected nodes
19562 "beforeselect" : true,
19564 * @event preparedata
19565 * Fires on every row to render, to allow you to change the data.
19566 * @param {Roo.View} this
19567 * @param {Object} data to be rendered (change this)
19569 "preparedata" : true
19577 "click": this.onClick,
19578 "dblclick": this.onDblClick,
19579 "contextmenu": this.onContextMenu,
19583 this.selections = [];
19585 this.cmp = new Roo.CompositeElementLite([]);
19587 this.store = Roo.factory(this.store, Roo.data);
19588 this.setStore(this.store, true);
19591 if ( this.footer && this.footer.xtype) {
19593 var fctr = this.wrapEl.appendChild(document.createElement("div"));
19595 this.footer.dataSource = this.store;
19596 this.footer.container = fctr;
19597 this.footer = Roo.factory(this.footer, Roo);
19598 fctr.insertFirst(this.el);
19600 // this is a bit insane - as the paging toolbar seems to detach the el..
19601 // dom.parentNode.parentNode.parentNode
19602 // they get detached?
19606 Roo.View.superclass.constructor.call(this);
19611 Roo.extend(Roo.View, Roo.util.Observable, {
19614 * @cfg {Roo.data.Store} store Data store to load data from.
19619 * @cfg {String|Roo.Element} el The container element.
19624 * @cfg {String|Roo.Template} tpl The template used by this View
19628 * @cfg {String} dataName the named area of the template to use as the data area
19629 * Works with domtemplates roo-name="name"
19633 * @cfg {String} selectedClass The css class to add to selected nodes
19635 selectedClass : "x-view-selected",
19637 * @cfg {String} emptyText The empty text to show when nothing is loaded.
19642 * @cfg {String} text to display on mask (default Loading)
19646 * @cfg {Boolean} multiSelect Allow multiple selection
19648 multiSelect : false,
19650 * @cfg {Boolean} singleSelect Allow single selection
19652 singleSelect: false,
19655 * @cfg {Boolean} toggleSelect - selecting
19657 toggleSelect : false,
19660 * @cfg {Boolean} tickable - selecting
19665 * Returns the element this view is bound to.
19666 * @return {Roo.Element}
19668 getEl : function(){
19669 return this.wrapEl;
19675 * Refreshes the view. - called by datachanged on the store. - do not call directly.
19677 refresh : function(){
19678 //Roo.log('refresh');
19681 // if we are using something like 'domtemplate', then
19682 // the what gets used is:
19683 // t.applySubtemplate(NAME, data, wrapping data..)
19684 // the outer template then get' applied with
19685 // the store 'extra data'
19686 // and the body get's added to the
19687 // roo-name="data" node?
19688 // <span class='roo-tpl-{name}'></span> ?????
19692 this.clearSelections();
19693 this.el.update("");
19695 var records = this.store.getRange();
19696 if(records.length < 1) {
19698 // is this valid?? = should it render a template??
19700 this.el.update(this.emptyText);
19704 if (this.dataName) {
19705 this.el.update(t.apply(this.store.meta)); //????
19706 el = this.el.child('.roo-tpl-' + this.dataName);
19709 for(var i = 0, len = records.length; i < len; i++){
19710 var data = this.prepareData(records[i].data, i, records[i]);
19711 this.fireEvent("preparedata", this, data, i, records[i]);
19713 var d = Roo.apply({}, data);
19716 Roo.apply(d, {'roo-id' : Roo.id()});
19720 Roo.each(this.parent.item, function(item){
19721 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19724 Roo.apply(d, {'roo-data-checked' : 'checked'});
19728 html[html.length] = Roo.util.Format.trim(
19730 t.applySubtemplate(this.dataName, d, this.store.meta) :
19737 el.update(html.join(""));
19738 this.nodes = el.dom.childNodes;
19739 this.updateIndexes(0);
19744 * Function to override to reformat the data that is sent to
19745 * the template for each node.
19746 * DEPRICATED - use the preparedata event handler.
19747 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19748 * a JSON object for an UpdateManager bound view).
19750 prepareData : function(data, index, record)
19752 this.fireEvent("preparedata", this, data, index, record);
19756 onUpdate : function(ds, record){
19757 // Roo.log('on update');
19758 this.clearSelections();
19759 var index = this.store.indexOf(record);
19760 var n = this.nodes[index];
19761 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19762 n.parentNode.removeChild(n);
19763 this.updateIndexes(index, index);
19769 onAdd : function(ds, records, index)
19771 //Roo.log(['on Add', ds, records, index] );
19772 this.clearSelections();
19773 if(this.nodes.length == 0){
19777 var n = this.nodes[index];
19778 for(var i = 0, len = records.length; i < len; i++){
19779 var d = this.prepareData(records[i].data, i, records[i]);
19781 this.tpl.insertBefore(n, d);
19784 this.tpl.append(this.el, d);
19787 this.updateIndexes(index);
19790 onRemove : function(ds, record, index){
19791 // Roo.log('onRemove');
19792 this.clearSelections();
19793 var el = this.dataName ?
19794 this.el.child('.roo-tpl-' + this.dataName) :
19797 el.dom.removeChild(this.nodes[index]);
19798 this.updateIndexes(index);
19802 * Refresh an individual node.
19803 * @param {Number} index
19805 refreshNode : function(index){
19806 this.onUpdate(this.store, this.store.getAt(index));
19809 updateIndexes : function(startIndex, endIndex){
19810 var ns = this.nodes;
19811 startIndex = startIndex || 0;
19812 endIndex = endIndex || ns.length - 1;
19813 for(var i = startIndex; i <= endIndex; i++){
19814 ns[i].nodeIndex = i;
19819 * Changes the data store this view uses and refresh the view.
19820 * @param {Store} store
19822 setStore : function(store, initial){
19823 if(!initial && this.store){
19824 this.store.un("datachanged", this.refresh);
19825 this.store.un("add", this.onAdd);
19826 this.store.un("remove", this.onRemove);
19827 this.store.un("update", this.onUpdate);
19828 this.store.un("clear", this.refresh);
19829 this.store.un("beforeload", this.onBeforeLoad);
19830 this.store.un("load", this.onLoad);
19831 this.store.un("loadexception", this.onLoad);
19835 store.on("datachanged", this.refresh, this);
19836 store.on("add", this.onAdd, this);
19837 store.on("remove", this.onRemove, this);
19838 store.on("update", this.onUpdate, this);
19839 store.on("clear", this.refresh, this);
19840 store.on("beforeload", this.onBeforeLoad, this);
19841 store.on("load", this.onLoad, this);
19842 store.on("loadexception", this.onLoad, this);
19850 * onbeforeLoad - masks the loading area.
19853 onBeforeLoad : function(store,opts)
19855 //Roo.log('onBeforeLoad');
19857 this.el.update("");
19859 this.el.mask(this.mask ? this.mask : "Loading" );
19861 onLoad : function ()
19868 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19869 * @param {HTMLElement} node
19870 * @return {HTMLElement} The template node
19872 findItemFromChild : function(node){
19873 var el = this.dataName ?
19874 this.el.child('.roo-tpl-' + this.dataName,true) :
19877 if(!node || node.parentNode == el){
19880 var p = node.parentNode;
19881 while(p && p != el){
19882 if(p.parentNode == el){
19891 onClick : function(e){
19892 var item = this.findItemFromChild(e.getTarget());
19894 var index = this.indexOf(item);
19895 if(this.onItemClick(item, index, e) !== false){
19896 this.fireEvent("click", this, index, item, e);
19899 this.clearSelections();
19904 onContextMenu : function(e){
19905 var item = this.findItemFromChild(e.getTarget());
19907 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19912 onDblClick : function(e){
19913 var item = this.findItemFromChild(e.getTarget());
19915 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19919 onItemClick : function(item, index, e)
19921 if(this.fireEvent("beforeclick", this, index, item, e) === false){
19924 if (this.toggleSelect) {
19925 var m = this.isSelected(item) ? 'unselect' : 'select';
19928 _t[m](item, true, false);
19931 if(this.multiSelect || this.singleSelect){
19932 if(this.multiSelect && e.shiftKey && this.lastSelection){
19933 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19935 this.select(item, this.multiSelect && e.ctrlKey);
19936 this.lastSelection = item;
19939 if(!this.tickable){
19940 e.preventDefault();
19948 * Get the number of selected nodes.
19951 getSelectionCount : function(){
19952 return this.selections.length;
19956 * Get the currently selected nodes.
19957 * @return {Array} An array of HTMLElements
19959 getSelectedNodes : function(){
19960 return this.selections;
19964 * Get the indexes of the selected nodes.
19967 getSelectedIndexes : function(){
19968 var indexes = [], s = this.selections;
19969 for(var i = 0, len = s.length; i < len; i++){
19970 indexes.push(s[i].nodeIndex);
19976 * Clear all selections
19977 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19979 clearSelections : function(suppressEvent){
19980 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19981 this.cmp.elements = this.selections;
19982 this.cmp.removeClass(this.selectedClass);
19983 this.selections = [];
19984 if(!suppressEvent){
19985 this.fireEvent("selectionchange", this, this.selections);
19991 * Returns true if the passed node is selected
19992 * @param {HTMLElement/Number} node The node or node index
19993 * @return {Boolean}
19995 isSelected : function(node){
19996 var s = this.selections;
20000 node = this.getNode(node);
20001 return s.indexOf(node) !== -1;
20006 * @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
20007 * @param {Boolean} keepExisting (optional) true to keep existing selections
20008 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20010 select : function(nodeInfo, keepExisting, suppressEvent){
20011 if(nodeInfo instanceof Array){
20013 this.clearSelections(true);
20015 for(var i = 0, len = nodeInfo.length; i < len; i++){
20016 this.select(nodeInfo[i], true, true);
20020 var node = this.getNode(nodeInfo);
20021 if(!node || this.isSelected(node)){
20022 return; // already selected.
20025 this.clearSelections(true);
20028 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20029 Roo.fly(node).addClass(this.selectedClass);
20030 this.selections.push(node);
20031 if(!suppressEvent){
20032 this.fireEvent("selectionchange", this, this.selections);
20040 * @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
20041 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20042 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20044 unselect : function(nodeInfo, keepExisting, suppressEvent)
20046 if(nodeInfo instanceof Array){
20047 Roo.each(this.selections, function(s) {
20048 this.unselect(s, nodeInfo);
20052 var node = this.getNode(nodeInfo);
20053 if(!node || !this.isSelected(node)){
20054 //Roo.log("not selected");
20055 return; // not selected.
20059 Roo.each(this.selections, function(s) {
20061 Roo.fly(node).removeClass(this.selectedClass);
20068 this.selections= ns;
20069 this.fireEvent("selectionchange", this, this.selections);
20073 * Gets a template node.
20074 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20075 * @return {HTMLElement} The node or null if it wasn't found
20077 getNode : function(nodeInfo){
20078 if(typeof nodeInfo == "string"){
20079 return document.getElementById(nodeInfo);
20080 }else if(typeof nodeInfo == "number"){
20081 return this.nodes[nodeInfo];
20087 * Gets a range template nodes.
20088 * @param {Number} startIndex
20089 * @param {Number} endIndex
20090 * @return {Array} An array of nodes
20092 getNodes : function(start, end){
20093 var ns = this.nodes;
20094 start = start || 0;
20095 end = typeof end == "undefined" ? ns.length - 1 : end;
20098 for(var i = start; i <= end; i++){
20102 for(var i = start; i >= end; i--){
20110 * Finds the index of the passed node
20111 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20112 * @return {Number} The index of the node or -1
20114 indexOf : function(node){
20115 node = this.getNode(node);
20116 if(typeof node.nodeIndex == "number"){
20117 return node.nodeIndex;
20119 var ns = this.nodes;
20120 for(var i = 0, len = ns.length; i < len; i++){
20131 * based on jquery fullcalendar
20135 Roo.bootstrap = Roo.bootstrap || {};
20137 * @class Roo.bootstrap.Calendar
20138 * @extends Roo.bootstrap.Component
20139 * Bootstrap Calendar class
20140 * @cfg {Boolean} loadMask (true|false) default false
20141 * @cfg {Object} header generate the user specific header of the calendar, default false
20144 * Create a new Container
20145 * @param {Object} config The config object
20150 Roo.bootstrap.Calendar = function(config){
20151 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20155 * Fires when a date is selected
20156 * @param {DatePicker} this
20157 * @param {Date} date The selected date
20161 * @event monthchange
20162 * Fires when the displayed month changes
20163 * @param {DatePicker} this
20164 * @param {Date} date The selected month
20166 'monthchange': true,
20168 * @event evententer
20169 * Fires when mouse over an event
20170 * @param {Calendar} this
20171 * @param {event} Event
20173 'evententer': true,
20175 * @event eventleave
20176 * Fires when the mouse leaves an
20177 * @param {Calendar} this
20180 'eventleave': true,
20182 * @event eventclick
20183 * Fires when the mouse click an
20184 * @param {Calendar} this
20193 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20196 * @cfg {Number} startDay
20197 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20205 getAutoCreate : function(){
20208 var fc_button = function(name, corner, style, content ) {
20209 return Roo.apply({},{
20211 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20213 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20216 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20227 style : 'width:100%',
20234 cls : 'fc-header-left',
20236 fc_button('prev', 'left', 'arrow', '‹' ),
20237 fc_button('next', 'right', 'arrow', '›' ),
20238 { tag: 'span', cls: 'fc-header-space' },
20239 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20247 cls : 'fc-header-center',
20251 cls: 'fc-header-title',
20254 html : 'month / year'
20262 cls : 'fc-header-right',
20264 /* fc_button('month', 'left', '', 'month' ),
20265 fc_button('week', '', '', 'week' ),
20266 fc_button('day', 'right', '', 'day' )
20278 header = this.header;
20281 var cal_heads = function() {
20283 // fixme - handle this.
20285 for (var i =0; i < Date.dayNames.length; i++) {
20286 var d = Date.dayNames[i];
20289 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20290 html : d.substring(0,3)
20294 ret[0].cls += ' fc-first';
20295 ret[6].cls += ' fc-last';
20298 var cal_cell = function(n) {
20301 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20306 cls: 'fc-day-number',
20310 cls: 'fc-day-content',
20314 style: 'position: relative;' // height: 17px;
20326 var cal_rows = function() {
20329 for (var r = 0; r < 6; r++) {
20336 for (var i =0; i < Date.dayNames.length; i++) {
20337 var d = Date.dayNames[i];
20338 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20341 row.cn[0].cls+=' fc-first';
20342 row.cn[0].cn[0].style = 'min-height:90px';
20343 row.cn[6].cls+=' fc-last';
20347 ret[0].cls += ' fc-first';
20348 ret[4].cls += ' fc-prev-last';
20349 ret[5].cls += ' fc-last';
20356 cls: 'fc-border-separate',
20357 style : 'width:100%',
20365 cls : 'fc-first fc-last',
20383 cls : 'fc-content',
20384 style : "position: relative;",
20387 cls : 'fc-view fc-view-month fc-grid',
20388 style : 'position: relative',
20389 unselectable : 'on',
20392 cls : 'fc-event-container',
20393 style : 'position:absolute;z-index:8;top:0;left:0;'
20411 initEvents : function()
20414 throw "can not find store for calendar";
20420 style: "text-align:center",
20424 style: "background-color:white;width:50%;margin:250 auto",
20428 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20439 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20441 var size = this.el.select('.fc-content', true).first().getSize();
20442 this.maskEl.setSize(size.width, size.height);
20443 this.maskEl.enableDisplayMode("block");
20444 if(!this.loadMask){
20445 this.maskEl.hide();
20448 this.store = Roo.factory(this.store, Roo.data);
20449 this.store.on('load', this.onLoad, this);
20450 this.store.on('beforeload', this.onBeforeLoad, this);
20454 this.cells = this.el.select('.fc-day',true);
20455 //Roo.log(this.cells);
20456 this.textNodes = this.el.query('.fc-day-number');
20457 this.cells.addClassOnOver('fc-state-hover');
20459 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20460 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20461 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20462 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20464 this.on('monthchange', this.onMonthChange, this);
20466 this.update(new Date().clearTime());
20469 resize : function() {
20470 var sz = this.el.getSize();
20472 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20473 this.el.select('.fc-day-content div',true).setHeight(34);
20478 showPrevMonth : function(e){
20479 this.update(this.activeDate.add("mo", -1));
20481 showToday : function(e){
20482 this.update(new Date().clearTime());
20485 showNextMonth : function(e){
20486 this.update(this.activeDate.add("mo", 1));
20490 showPrevYear : function(){
20491 this.update(this.activeDate.add("y", -1));
20495 showNextYear : function(){
20496 this.update(this.activeDate.add("y", 1));
20501 update : function(date)
20503 var vd = this.activeDate;
20504 this.activeDate = date;
20505 // if(vd && this.el){
20506 // var t = date.getTime();
20507 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20508 // Roo.log('using add remove');
20510 // this.fireEvent('monthchange', this, date);
20512 // this.cells.removeClass("fc-state-highlight");
20513 // this.cells.each(function(c){
20514 // if(c.dateValue == t){
20515 // c.addClass("fc-state-highlight");
20516 // setTimeout(function(){
20517 // try{c.dom.firstChild.focus();}catch(e){}
20527 var days = date.getDaysInMonth();
20529 var firstOfMonth = date.getFirstDateOfMonth();
20530 var startingPos = firstOfMonth.getDay()-this.startDay;
20532 if(startingPos < this.startDay){
20536 var pm = date.add(Date.MONTH, -1);
20537 var prevStart = pm.getDaysInMonth()-startingPos;
20539 this.cells = this.el.select('.fc-day',true);
20540 this.textNodes = this.el.query('.fc-day-number');
20541 this.cells.addClassOnOver('fc-state-hover');
20543 var cells = this.cells.elements;
20544 var textEls = this.textNodes;
20546 Roo.each(cells, function(cell){
20547 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20550 days += startingPos;
20552 // convert everything to numbers so it's fast
20553 var day = 86400000;
20554 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20557 //Roo.log(prevStart);
20559 var today = new Date().clearTime().getTime();
20560 var sel = date.clearTime().getTime();
20561 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20562 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20563 var ddMatch = this.disabledDatesRE;
20564 var ddText = this.disabledDatesText;
20565 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20566 var ddaysText = this.disabledDaysText;
20567 var format = this.format;
20569 var setCellClass = function(cal, cell){
20573 //Roo.log('set Cell Class');
20575 var t = d.getTime();
20579 cell.dateValue = t;
20581 cell.className += " fc-today";
20582 cell.className += " fc-state-highlight";
20583 cell.title = cal.todayText;
20586 // disable highlight in other month..
20587 //cell.className += " fc-state-highlight";
20592 cell.className = " fc-state-disabled";
20593 cell.title = cal.minText;
20597 cell.className = " fc-state-disabled";
20598 cell.title = cal.maxText;
20602 if(ddays.indexOf(d.getDay()) != -1){
20603 cell.title = ddaysText;
20604 cell.className = " fc-state-disabled";
20607 if(ddMatch && format){
20608 var fvalue = d.dateFormat(format);
20609 if(ddMatch.test(fvalue)){
20610 cell.title = ddText.replace("%0", fvalue);
20611 cell.className = " fc-state-disabled";
20615 if (!cell.initialClassName) {
20616 cell.initialClassName = cell.dom.className;
20619 cell.dom.className = cell.initialClassName + ' ' + cell.className;
20624 for(; i < startingPos; i++) {
20625 textEls[i].innerHTML = (++prevStart);
20626 d.setDate(d.getDate()+1);
20628 cells[i].className = "fc-past fc-other-month";
20629 setCellClass(this, cells[i]);
20634 for(; i < days; i++){
20635 intDay = i - startingPos + 1;
20636 textEls[i].innerHTML = (intDay);
20637 d.setDate(d.getDate()+1);
20639 cells[i].className = ''; // "x-date-active";
20640 setCellClass(this, cells[i]);
20644 for(; i < 42; i++) {
20645 textEls[i].innerHTML = (++extraDays);
20646 d.setDate(d.getDate()+1);
20648 cells[i].className = "fc-future fc-other-month";
20649 setCellClass(this, cells[i]);
20652 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20654 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20656 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20657 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20659 if(totalRows != 6){
20660 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20661 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20664 this.fireEvent('monthchange', this, date);
20668 if(!this.internalRender){
20669 var main = this.el.dom.firstChild;
20670 var w = main.offsetWidth;
20671 this.el.setWidth(w + this.el.getBorderWidth("lr"));
20672 Roo.fly(main).setWidth(w);
20673 this.internalRender = true;
20674 // opera does not respect the auto grow header center column
20675 // then, after it gets a width opera refuses to recalculate
20676 // without a second pass
20677 if(Roo.isOpera && !this.secondPass){
20678 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20679 this.secondPass = true;
20680 this.update.defer(10, this, [date]);
20687 findCell : function(dt) {
20688 dt = dt.clearTime().getTime();
20690 this.cells.each(function(c){
20691 //Roo.log("check " +c.dateValue + '?=' + dt);
20692 if(c.dateValue == dt){
20702 findCells : function(ev) {
20703 var s = ev.start.clone().clearTime().getTime();
20705 var e= ev.end.clone().clearTime().getTime();
20708 this.cells.each(function(c){
20709 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20711 if(c.dateValue > e){
20714 if(c.dateValue < s){
20723 // findBestRow: function(cells)
20727 // for (var i =0 ; i < cells.length;i++) {
20728 // ret = Math.max(cells[i].rows || 0,ret);
20735 addItem : function(ev)
20737 // look for vertical location slot in
20738 var cells = this.findCells(ev);
20740 // ev.row = this.findBestRow(cells);
20742 // work out the location.
20746 for(var i =0; i < cells.length; i++) {
20748 cells[i].row = cells[0].row;
20751 cells[i].row = cells[i].row + 1;
20761 if (crow.start.getY() == cells[i].getY()) {
20763 crow.end = cells[i];
20780 cells[0].events.push(ev);
20782 this.calevents.push(ev);
20785 clearEvents: function() {
20787 if(!this.calevents){
20791 Roo.each(this.cells.elements, function(c){
20797 Roo.each(this.calevents, function(e) {
20798 Roo.each(e.els, function(el) {
20799 el.un('mouseenter' ,this.onEventEnter, this);
20800 el.un('mouseleave' ,this.onEventLeave, this);
20805 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20811 renderEvents: function()
20815 this.cells.each(function(c) {
20824 if(c.row != c.events.length){
20825 r = 4 - (4 - (c.row - c.events.length));
20828 c.events = ev.slice(0, r);
20829 c.more = ev.slice(r);
20831 if(c.more.length && c.more.length == 1){
20832 c.events.push(c.more.pop());
20835 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20839 this.cells.each(function(c) {
20841 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20844 for (var e = 0; e < c.events.length; e++){
20845 var ev = c.events[e];
20846 var rows = ev.rows;
20848 for(var i = 0; i < rows.length; i++) {
20850 // how many rows should it span..
20853 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20854 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20856 unselectable : "on",
20859 cls: 'fc-event-inner',
20863 // cls: 'fc-event-time',
20864 // html : cells.length > 1 ? '' : ev.time
20868 cls: 'fc-event-title',
20869 html : String.format('{0}', ev.title)
20876 cls: 'ui-resizable-handle ui-resizable-e',
20877 html : '  '
20884 cfg.cls += ' fc-event-start';
20886 if ((i+1) == rows.length) {
20887 cfg.cls += ' fc-event-end';
20890 var ctr = _this.el.select('.fc-event-container',true).first();
20891 var cg = ctr.createChild(cfg);
20893 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20894 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20896 var r = (c.more.length) ? 1 : 0;
20897 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
20898 cg.setWidth(ebox.right - sbox.x -2);
20900 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20901 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20902 cg.on('click', _this.onEventClick, _this, ev);
20913 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20914 style : 'position: absolute',
20915 unselectable : "on",
20918 cls: 'fc-event-inner',
20922 cls: 'fc-event-title',
20930 cls: 'ui-resizable-handle ui-resizable-e',
20931 html : '  '
20937 var ctr = _this.el.select('.fc-event-container',true).first();
20938 var cg = ctr.createChild(cfg);
20940 var sbox = c.select('.fc-day-content',true).first().getBox();
20941 var ebox = c.select('.fc-day-content',true).first().getBox();
20943 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
20944 cg.setWidth(ebox.right - sbox.x -2);
20946 cg.on('click', _this.onMoreEventClick, _this, c.more);
20956 onEventEnter: function (e, el,event,d) {
20957 this.fireEvent('evententer', this, el, event);
20960 onEventLeave: function (e, el,event,d) {
20961 this.fireEvent('eventleave', this, el, event);
20964 onEventClick: function (e, el,event,d) {
20965 this.fireEvent('eventclick', this, el, event);
20968 onMonthChange: function () {
20972 onMoreEventClick: function(e, el, more)
20976 this.calpopover.placement = 'right';
20977 this.calpopover.setTitle('More');
20979 this.calpopover.setContent('');
20981 var ctr = this.calpopover.el.select('.popover-content', true).first();
20983 Roo.each(more, function(m){
20985 cls : 'fc-event-hori fc-event-draggable',
20988 var cg = ctr.createChild(cfg);
20990 cg.on('click', _this.onEventClick, _this, m);
20993 this.calpopover.show(el);
20998 onLoad: function ()
21000 this.calevents = [];
21003 if(this.store.getCount() > 0){
21004 this.store.data.each(function(d){
21007 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21008 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21009 time : d.data.start_time,
21010 title : d.data.title,
21011 description : d.data.description,
21012 venue : d.data.venue
21017 this.renderEvents();
21019 if(this.calevents.length && this.loadMask){
21020 this.maskEl.hide();
21024 onBeforeLoad: function()
21026 this.clearEvents();
21028 this.maskEl.show();
21042 * @class Roo.bootstrap.Popover
21043 * @extends Roo.bootstrap.Component
21044 * Bootstrap Popover class
21045 * @cfg {String} html contents of the popover (or false to use children..)
21046 * @cfg {String} title of popover (or false to hide)
21047 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21048 * @cfg {String} trigger click || hover (or false to trigger manually)
21049 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21050 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21051 * - if false and it has a 'parent' then it will be automatically added to that element
21052 * - if string - Roo.get will be called
21053 * @cfg {Number} delay - delay before showing
21056 * Create a new Popover
21057 * @param {Object} config The config object
21060 Roo.bootstrap.Popover = function(config){
21061 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21067 * After the popover show
21069 * @param {Roo.bootstrap.Popover} this
21074 * After the popover hide
21076 * @param {Roo.bootstrap.Popover} this
21082 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21087 placement : 'right',
21088 trigger : 'hover', // hover
21094 can_build_overlaid : false,
21096 maskEl : false, // the mask element
21099 alignEl : false, // when show is called with an element - this get's stored.
21101 getChildContainer : function()
21103 return this.contentEl;
21106 getPopoverHeader : function()
21108 this.title = true; // flag not to hide it..
21109 this.headerEl.addClass('p-0');
21110 return this.headerEl
21114 getAutoCreate : function(){
21117 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21118 style: 'display:block',
21124 cls : 'popover-inner ',
21128 cls: 'popover-title popover-header',
21129 html : this.title === false ? '' : this.title
21132 cls : 'popover-content popover-body ' + (this.cls || ''),
21133 html : this.html || ''
21144 * @param {string} the title
21146 setTitle: function(str)
21150 this.headerEl.dom.innerHTML = str;
21155 * @param {string} the body content
21157 setContent: function(str)
21160 if (this.contentEl) {
21161 this.contentEl.dom.innerHTML = str;
21165 // as it get's added to the bottom of the page.
21166 onRender : function(ct, position)
21168 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21173 var cfg = Roo.apply({}, this.getAutoCreate());
21177 cfg.cls += ' ' + this.cls;
21180 cfg.style = this.style;
21182 //Roo.log("adding to ");
21183 this.el = Roo.get(document.body).createChild(cfg, position);
21184 // Roo.log(this.el);
21187 this.contentEl = this.el.select('.popover-content',true).first();
21188 this.headerEl = this.el.select('.popover-title',true).first();
21191 if(typeof(this.items) != 'undefined'){
21192 var items = this.items;
21195 for(var i =0;i < items.length;i++) {
21196 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21200 this.items = nitems;
21202 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21203 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21210 resizeMask : function()
21212 this.maskEl.setSize(
21213 Roo.lib.Dom.getViewWidth(true),
21214 Roo.lib.Dom.getViewHeight(true)
21218 initEvents : function()
21222 Roo.bootstrap.Popover.register(this);
21225 this.arrowEl = this.el.select('.arrow',true).first();
21226 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21227 this.el.enableDisplayMode('block');
21231 if (this.over === false && !this.parent()) {
21234 if (this.triggers === false) {
21239 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21240 var triggers = this.trigger ? this.trigger.split(' ') : [];
21241 Roo.each(triggers, function(trigger) {
21243 if (trigger == 'click') {
21244 on_el.on('click', this.toggle, this);
21245 } else if (trigger != 'manual') {
21246 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21247 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21249 on_el.on(eventIn ,this.enter, this);
21250 on_el.on(eventOut, this.leave, this);
21260 toggle : function () {
21261 this.hoverState == 'in' ? this.leave() : this.enter();
21264 enter : function () {
21266 clearTimeout(this.timeout);
21268 this.hoverState = 'in';
21270 if (!this.delay || !this.delay.show) {
21275 this.timeout = setTimeout(function () {
21276 if (_t.hoverState == 'in') {
21279 }, this.delay.show)
21282 leave : function() {
21283 clearTimeout(this.timeout);
21285 this.hoverState = 'out';
21287 if (!this.delay || !this.delay.hide) {
21292 this.timeout = setTimeout(function () {
21293 if (_t.hoverState == 'out') {
21296 }, this.delay.hide)
21300 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21301 * @param {string} (left|right|top|bottom) position
21303 show : function (on_el, placement)
21305 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21306 on_el = on_el || false; // default to false
21309 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21310 on_el = this.parent().el;
21311 } else if (this.over) {
21312 on_el = Roo.get(this.over);
21317 this.alignEl = Roo.get( on_el );
21320 this.render(document.body);
21326 if (this.title === false) {
21327 this.headerEl.hide();
21332 this.el.dom.style.display = 'block';
21335 if (this.alignEl) {
21336 this.updatePosition(this.placement, true);
21339 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21340 var es = this.el.getSize();
21341 var x = Roo.lib.Dom.getViewWidth()/2;
21342 var y = Roo.lib.Dom.getViewHeight()/2;
21343 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21348 //var arrow = this.el.select('.arrow',true).first();
21349 //arrow.set(align[2],
21351 this.el.addClass('in');
21355 this.hoverState = 'in';
21358 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21359 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21360 this.maskEl.dom.style.display = 'block';
21361 this.maskEl.addClass('show');
21363 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21365 this.fireEvent('show', this);
21369 * fire this manually after loading a grid in the table for example
21370 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21371 * @param {Boolean} try and move it if we cant get right position.
21373 updatePosition : function(placement, try_move)
21375 // allow for calling with no parameters
21376 placement = placement ? placement : this.placement;
21377 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21379 this.el.removeClass([
21380 'fade','top','bottom', 'left', 'right','in',
21381 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21383 this.el.addClass(placement + ' bs-popover-' + placement);
21385 if (!this.alignEl ) {
21389 switch (placement) {
21391 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21392 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21393 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21394 //normal display... or moved up/down.
21395 this.el.setXY(offset);
21396 var xy = this.alignEl.getAnchorXY('tr', false);
21398 this.arrowEl.setXY(xy);
21401 // continue through...
21402 return this.updatePosition('left', false);
21406 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21407 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21408 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21409 //normal display... or moved up/down.
21410 this.el.setXY(offset);
21411 var xy = this.alignEl.getAnchorXY('tl', false);
21412 xy[0]-=10;xy[1]+=5; // << fix me
21413 this.arrowEl.setXY(xy);
21417 return this.updatePosition('right', false);
21420 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21421 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21422 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21423 //normal display... or moved up/down.
21424 this.el.setXY(offset);
21425 var xy = this.alignEl.getAnchorXY('t', false);
21426 xy[1]-=10; // << fix me
21427 this.arrowEl.setXY(xy);
21431 return this.updatePosition('bottom', false);
21434 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21435 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21436 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21437 //normal display... or moved up/down.
21438 this.el.setXY(offset);
21439 var xy = this.alignEl.getAnchorXY('b', false);
21440 xy[1]+=2; // << fix me
21441 this.arrowEl.setXY(xy);
21445 return this.updatePosition('top', false);
21456 this.el.setXY([0,0]);
21457 this.el.removeClass('in');
21459 this.hoverState = null;
21460 this.maskEl.hide(); // always..
21461 this.fireEvent('hide', this);
21467 Roo.apply(Roo.bootstrap.Popover, {
21470 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21471 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21472 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21473 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21478 clickHander : false,
21482 onMouseDown : function(e)
21484 if (this.popups.length && !e.getTarget(".roo-popover")) {
21485 /// what is nothing is showing..
21494 register : function(popup)
21496 if (!Roo.bootstrap.Popover.clickHandler) {
21497 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21499 // hide other popups.
21500 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21501 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21502 this.hideAll(); //<< why?
21503 //this.popups.push(popup);
21505 hideAll : function()
21507 this.popups.forEach(function(p) {
21511 onShow : function() {
21512 Roo.bootstrap.Popover.popups.push(this);
21514 onHide : function() {
21515 Roo.bootstrap.Popover.popups.remove(this);
21521 * Card header - holder for the card header elements.
21526 * @class Roo.bootstrap.PopoverNav
21527 * @extends Roo.bootstrap.NavGroup
21528 * Bootstrap Popover header navigation class
21530 * Create a new Popover Header Navigation
21531 * @param {Object} config The config object
21534 Roo.bootstrap.PopoverNav = function(config){
21535 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21538 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
21541 container_method : 'getPopoverHeader'
21559 * @class Roo.bootstrap.Progress
21560 * @extends Roo.bootstrap.Component
21561 * Bootstrap Progress class
21562 * @cfg {Boolean} striped striped of the progress bar
21563 * @cfg {Boolean} active animated of the progress bar
21567 * Create a new Progress
21568 * @param {Object} config The config object
21571 Roo.bootstrap.Progress = function(config){
21572 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21575 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
21580 getAutoCreate : function(){
21588 cfg.cls += ' progress-striped';
21592 cfg.cls += ' active';
21611 * @class Roo.bootstrap.ProgressBar
21612 * @extends Roo.bootstrap.Component
21613 * Bootstrap ProgressBar class
21614 * @cfg {Number} aria_valuenow aria-value now
21615 * @cfg {Number} aria_valuemin aria-value min
21616 * @cfg {Number} aria_valuemax aria-value max
21617 * @cfg {String} label label for the progress bar
21618 * @cfg {String} panel (success | info | warning | danger )
21619 * @cfg {String} role role of the progress bar
21620 * @cfg {String} sr_only text
21624 * Create a new ProgressBar
21625 * @param {Object} config The config object
21628 Roo.bootstrap.ProgressBar = function(config){
21629 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21632 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
21636 aria_valuemax : 100,
21642 getAutoCreate : function()
21647 cls: 'progress-bar',
21648 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21660 cfg.role = this.role;
21663 if(this.aria_valuenow){
21664 cfg['aria-valuenow'] = this.aria_valuenow;
21667 if(this.aria_valuemin){
21668 cfg['aria-valuemin'] = this.aria_valuemin;
21671 if(this.aria_valuemax){
21672 cfg['aria-valuemax'] = this.aria_valuemax;
21675 if(this.label && !this.sr_only){
21676 cfg.html = this.label;
21680 cfg.cls += ' progress-bar-' + this.panel;
21686 update : function(aria_valuenow)
21688 this.aria_valuenow = aria_valuenow;
21690 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21705 * @class Roo.bootstrap.TabGroup
21706 * @extends Roo.bootstrap.Column
21707 * Bootstrap Column class
21708 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21709 * @cfg {Boolean} carousel true to make the group behave like a carousel
21710 * @cfg {Boolean} bullets show bullets for the panels
21711 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21712 * @cfg {Number} timer auto slide timer .. default 0 millisecond
21713 * @cfg {Boolean} showarrow (true|false) show arrow default true
21716 * Create a new TabGroup
21717 * @param {Object} config The config object
21720 Roo.bootstrap.TabGroup = function(config){
21721 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21723 this.navId = Roo.id();
21726 Roo.bootstrap.TabGroup.register(this);
21730 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
21733 transition : false,
21738 slideOnTouch : false,
21741 getAutoCreate : function()
21743 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21745 cfg.cls += ' tab-content';
21747 if (this.carousel) {
21748 cfg.cls += ' carousel slide';
21751 cls : 'carousel-inner',
21755 if(this.bullets && !Roo.isTouch){
21758 cls : 'carousel-bullets',
21762 if(this.bullets_cls){
21763 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21770 cfg.cn[0].cn.push(bullets);
21773 if(this.showarrow){
21774 cfg.cn[0].cn.push({
21776 class : 'carousel-arrow',
21780 class : 'carousel-prev',
21784 class : 'fa fa-chevron-left'
21790 class : 'carousel-next',
21794 class : 'fa fa-chevron-right'
21807 initEvents: function()
21809 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21810 // this.el.on("touchstart", this.onTouchStart, this);
21813 if(this.autoslide){
21816 this.slideFn = window.setInterval(function() {
21817 _this.showPanelNext();
21821 if(this.showarrow){
21822 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21823 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21829 // onTouchStart : function(e, el, o)
21831 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21835 // this.showPanelNext();
21839 getChildContainer : function()
21841 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21845 * register a Navigation item
21846 * @param {Roo.bootstrap.NavItem} the navitem to add
21848 register : function(item)
21850 this.tabs.push( item);
21851 item.navId = this.navId; // not really needed..
21856 getActivePanel : function()
21859 Roo.each(this.tabs, function(t) {
21869 getPanelByName : function(n)
21872 Roo.each(this.tabs, function(t) {
21873 if (t.tabId == n) {
21881 indexOfPanel : function(p)
21884 Roo.each(this.tabs, function(t,i) {
21885 if (t.tabId == p.tabId) {
21894 * show a specific panel
21895 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21896 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21898 showPanel : function (pan)
21900 if(this.transition || typeof(pan) == 'undefined'){
21901 Roo.log("waiting for the transitionend");
21905 if (typeof(pan) == 'number') {
21906 pan = this.tabs[pan];
21909 if (typeof(pan) == 'string') {
21910 pan = this.getPanelByName(pan);
21913 var cur = this.getActivePanel();
21916 Roo.log('pan or acitve pan is undefined');
21920 if (pan.tabId == this.getActivePanel().tabId) {
21924 if (false === cur.fireEvent('beforedeactivate')) {
21928 if(this.bullets > 0 && !Roo.isTouch){
21929 this.setActiveBullet(this.indexOfPanel(pan));
21932 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21934 //class="carousel-item carousel-item-next carousel-item-left"
21936 this.transition = true;
21937 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
21938 var lr = dir == 'next' ? 'left' : 'right';
21939 pan.el.addClass(dir); // or prev
21940 pan.el.addClass('carousel-item-' + dir); // or prev
21941 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21942 cur.el.addClass(lr); // or right
21943 pan.el.addClass(lr);
21944 cur.el.addClass('carousel-item-' +lr); // or right
21945 pan.el.addClass('carousel-item-' +lr);
21949 cur.el.on('transitionend', function() {
21950 Roo.log("trans end?");
21952 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21953 pan.setActive(true);
21955 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21956 cur.setActive(false);
21958 _this.transition = false;
21960 }, this, { single: true } );
21965 cur.setActive(false);
21966 pan.setActive(true);
21971 showPanelNext : function()
21973 var i = this.indexOfPanel(this.getActivePanel());
21975 if (i >= this.tabs.length - 1 && !this.autoslide) {
21979 if (i >= this.tabs.length - 1 && this.autoslide) {
21983 this.showPanel(this.tabs[i+1]);
21986 showPanelPrev : function()
21988 var i = this.indexOfPanel(this.getActivePanel());
21990 if (i < 1 && !this.autoslide) {
21994 if (i < 1 && this.autoslide) {
21995 i = this.tabs.length;
21998 this.showPanel(this.tabs[i-1]);
22002 addBullet: function()
22004 if(!this.bullets || Roo.isTouch){
22007 var ctr = this.el.select('.carousel-bullets',true).first();
22008 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22009 var bullet = ctr.createChild({
22010 cls : 'bullet bullet-' + i
22011 },ctr.dom.lastChild);
22016 bullet.on('click', (function(e, el, o, ii, t){
22018 e.preventDefault();
22020 this.showPanel(ii);
22022 if(this.autoslide && this.slideFn){
22023 clearInterval(this.slideFn);
22024 this.slideFn = window.setInterval(function() {
22025 _this.showPanelNext();
22029 }).createDelegate(this, [i, bullet], true));
22034 setActiveBullet : function(i)
22040 Roo.each(this.el.select('.bullet', true).elements, function(el){
22041 el.removeClass('selected');
22044 var bullet = this.el.select('.bullet-' + i, true).first();
22050 bullet.addClass('selected');
22061 Roo.apply(Roo.bootstrap.TabGroup, {
22065 * register a Navigation Group
22066 * @param {Roo.bootstrap.NavGroup} the navgroup to add
22068 register : function(navgrp)
22070 this.groups[navgrp.navId] = navgrp;
22074 * fetch a Navigation Group based on the navigation ID
22075 * if one does not exist , it will get created.
22076 * @param {string} the navgroup to add
22077 * @returns {Roo.bootstrap.NavGroup} the navgroup
22079 get: function(navId) {
22080 if (typeof(this.groups[navId]) == 'undefined') {
22081 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22083 return this.groups[navId] ;
22098 * @class Roo.bootstrap.TabPanel
22099 * @extends Roo.bootstrap.Component
22100 * Bootstrap TabPanel class
22101 * @cfg {Boolean} active panel active
22102 * @cfg {String} html panel content
22103 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22104 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22105 * @cfg {String} href click to link..
22106 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22110 * Create a new TabPanel
22111 * @param {Object} config The config object
22114 Roo.bootstrap.TabPanel = function(config){
22115 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22119 * Fires when the active status changes
22120 * @param {Roo.bootstrap.TabPanel} this
22121 * @param {Boolean} state the new state
22126 * @event beforedeactivate
22127 * Fires before a tab is de-activated - can be used to do validation on a form.
22128 * @param {Roo.bootstrap.TabPanel} this
22129 * @return {Boolean} false if there is an error
22132 'beforedeactivate': true
22135 this.tabId = this.tabId || Roo.id();
22139 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22146 touchSlide : false,
22147 getAutoCreate : function(){
22152 // item is needed for carousel - not sure if it has any effect otherwise
22153 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22154 html: this.html || ''
22158 cfg.cls += ' active';
22162 cfg.tabId = this.tabId;
22170 initEvents: function()
22172 var p = this.parent();
22174 this.navId = this.navId || p.navId;
22176 if (typeof(this.navId) != 'undefined') {
22177 // not really needed.. but just in case.. parent should be a NavGroup.
22178 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22182 var i = tg.tabs.length - 1;
22184 if(this.active && tg.bullets > 0 && i < tg.bullets){
22185 tg.setActiveBullet(i);
22189 this.el.on('click', this.onClick, this);
22191 if(Roo.isTouch && this.touchSlide){
22192 this.el.on("touchstart", this.onTouchStart, this);
22193 this.el.on("touchmove", this.onTouchMove, this);
22194 this.el.on("touchend", this.onTouchEnd, this);
22199 onRender : function(ct, position)
22201 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22204 setActive : function(state)
22206 Roo.log("panel - set active " + this.tabId + "=" + state);
22208 this.active = state;
22210 this.el.removeClass('active');
22212 } else if (!this.el.hasClass('active')) {
22213 this.el.addClass('active');
22216 this.fireEvent('changed', this, state);
22219 onClick : function(e)
22221 e.preventDefault();
22223 if(!this.href.length){
22227 window.location.href = this.href;
22236 onTouchStart : function(e)
22238 this.swiping = false;
22240 this.startX = e.browserEvent.touches[0].clientX;
22241 this.startY = e.browserEvent.touches[0].clientY;
22244 onTouchMove : function(e)
22246 this.swiping = true;
22248 this.endX = e.browserEvent.touches[0].clientX;
22249 this.endY = e.browserEvent.touches[0].clientY;
22252 onTouchEnd : function(e)
22259 var tabGroup = this.parent();
22261 if(this.endX > this.startX){ // swiping right
22262 tabGroup.showPanelPrev();
22266 if(this.startX > this.endX){ // swiping left
22267 tabGroup.showPanelNext();
22286 * @class Roo.bootstrap.DateField
22287 * @extends Roo.bootstrap.Input
22288 * Bootstrap DateField class
22289 * @cfg {Number} weekStart default 0
22290 * @cfg {String} viewMode default empty, (months|years)
22291 * @cfg {String} minViewMode default empty, (months|years)
22292 * @cfg {Number} startDate default -Infinity
22293 * @cfg {Number} endDate default Infinity
22294 * @cfg {Boolean} todayHighlight default false
22295 * @cfg {Boolean} todayBtn default false
22296 * @cfg {Boolean} calendarWeeks default false
22297 * @cfg {Object} daysOfWeekDisabled default empty
22298 * @cfg {Boolean} singleMode default false (true | false)
22300 * @cfg {Boolean} keyboardNavigation default true
22301 * @cfg {String} language default en
22304 * Create a new DateField
22305 * @param {Object} config The config object
22308 Roo.bootstrap.DateField = function(config){
22309 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22313 * Fires when this field show.
22314 * @param {Roo.bootstrap.DateField} this
22315 * @param {Mixed} date The date value
22320 * Fires when this field hide.
22321 * @param {Roo.bootstrap.DateField} this
22322 * @param {Mixed} date The date value
22327 * Fires when select a date.
22328 * @param {Roo.bootstrap.DateField} this
22329 * @param {Mixed} date The date value
22333 * @event beforeselect
22334 * Fires when before select a date.
22335 * @param {Roo.bootstrap.DateField} this
22336 * @param {Mixed} date The date value
22338 beforeselect : true
22342 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
22345 * @cfg {String} format
22346 * The default date format string which can be overriden for localization support. The format must be
22347 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22351 * @cfg {String} altFormats
22352 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22353 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22355 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22363 todayHighlight : false,
22369 keyboardNavigation: true,
22371 calendarWeeks: false,
22373 startDate: -Infinity,
22377 daysOfWeekDisabled: [],
22381 singleMode : false,
22383 UTCDate: function()
22385 return new Date(Date.UTC.apply(Date, arguments));
22388 UTCToday: function()
22390 var today = new Date();
22391 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22394 getDate: function() {
22395 var d = this.getUTCDate();
22396 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22399 getUTCDate: function() {
22403 setDate: function(d) {
22404 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22407 setUTCDate: function(d) {
22409 this.setValue(this.formatDate(this.date));
22412 onRender: function(ct, position)
22415 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22417 this.language = this.language || 'en';
22418 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22419 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22421 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22422 this.format = this.format || 'm/d/y';
22423 this.isInline = false;
22424 this.isInput = true;
22425 this.component = this.el.select('.add-on', true).first() || false;
22426 this.component = (this.component && this.component.length === 0) ? false : this.component;
22427 this.hasInput = this.component && this.inputEl().length;
22429 if (typeof(this.minViewMode === 'string')) {
22430 switch (this.minViewMode) {
22432 this.minViewMode = 1;
22435 this.minViewMode = 2;
22438 this.minViewMode = 0;
22443 if (typeof(this.viewMode === 'string')) {
22444 switch (this.viewMode) {
22457 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22459 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22461 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22463 this.picker().on('mousedown', this.onMousedown, this);
22464 this.picker().on('click', this.onClick, this);
22466 this.picker().addClass('datepicker-dropdown');
22468 this.startViewMode = this.viewMode;
22470 if(this.singleMode){
22471 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22472 v.setVisibilityMode(Roo.Element.DISPLAY);
22476 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22477 v.setStyle('width', '189px');
22481 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22482 if(!this.calendarWeeks){
22487 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22488 v.attr('colspan', function(i, val){
22489 return parseInt(val) + 1;
22494 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22496 this.setStartDate(this.startDate);
22497 this.setEndDate(this.endDate);
22499 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22506 if(this.isInline) {
22511 picker : function()
22513 return this.pickerEl;
22514 // return this.el.select('.datepicker', true).first();
22517 fillDow: function()
22519 var dowCnt = this.weekStart;
22528 if(this.calendarWeeks){
22536 while (dowCnt < this.weekStart + 7) {
22540 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22544 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22547 fillMonths: function()
22550 var months = this.picker().select('>.datepicker-months td', true).first();
22552 months.dom.innerHTML = '';
22558 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22561 months.createChild(month);
22568 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;
22570 if (this.date < this.startDate) {
22571 this.viewDate = new Date(this.startDate);
22572 } else if (this.date > this.endDate) {
22573 this.viewDate = new Date(this.endDate);
22575 this.viewDate = new Date(this.date);
22583 var d = new Date(this.viewDate),
22584 year = d.getUTCFullYear(),
22585 month = d.getUTCMonth(),
22586 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22587 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22588 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22589 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22590 currentDate = this.date && this.date.valueOf(),
22591 today = this.UTCToday();
22593 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22595 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22597 // this.picker.select('>tfoot th.today').
22598 // .text(dates[this.language].today)
22599 // .toggle(this.todayBtn !== false);
22601 this.updateNavArrows();
22604 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22606 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22608 prevMonth.setUTCDate(day);
22610 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22612 var nextMonth = new Date(prevMonth);
22614 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22616 nextMonth = nextMonth.valueOf();
22618 var fillMonths = false;
22620 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22622 while(prevMonth.valueOf() <= nextMonth) {
22625 if (prevMonth.getUTCDay() === this.weekStart) {
22627 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22635 if(this.calendarWeeks){
22636 // ISO 8601: First week contains first thursday.
22637 // ISO also states week starts on Monday, but we can be more abstract here.
22639 // Start of current week: based on weekstart/current date
22640 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22641 // Thursday of this week
22642 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22643 // First Thursday of year, year from thursday
22644 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22645 // Calendar week: ms between thursdays, div ms per day, div 7 days
22646 calWeek = (th - yth) / 864e5 / 7 + 1;
22648 fillMonths.cn.push({
22656 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22658 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22661 if (this.todayHighlight &&
22662 prevMonth.getUTCFullYear() == today.getFullYear() &&
22663 prevMonth.getUTCMonth() == today.getMonth() &&
22664 prevMonth.getUTCDate() == today.getDate()) {
22665 clsName += ' today';
22668 if (currentDate && prevMonth.valueOf() === currentDate) {
22669 clsName += ' active';
22672 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22673 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22674 clsName += ' disabled';
22677 fillMonths.cn.push({
22679 cls: 'day ' + clsName,
22680 html: prevMonth.getDate()
22683 prevMonth.setDate(prevMonth.getDate()+1);
22686 var currentYear = this.date && this.date.getUTCFullYear();
22687 var currentMonth = this.date && this.date.getUTCMonth();
22689 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22691 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22692 v.removeClass('active');
22694 if(currentYear === year && k === currentMonth){
22695 v.addClass('active');
22698 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22699 v.addClass('disabled');
22705 year = parseInt(year/10, 10) * 10;
22707 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22709 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22712 for (var i = -1; i < 11; i++) {
22713 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22715 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22723 showMode: function(dir)
22726 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22729 Roo.each(this.picker().select('>div',true).elements, function(v){
22730 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22733 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22738 if(this.isInline) {
22742 this.picker().removeClass(['bottom', 'top']);
22744 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22746 * place to the top of element!
22750 this.picker().addClass('top');
22751 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22756 this.picker().addClass('bottom');
22758 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22761 parseDate : function(value)
22763 if(!value || value instanceof Date){
22766 var v = Date.parseDate(value, this.format);
22767 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22768 v = Date.parseDate(value, 'Y-m-d');
22770 if(!v && this.altFormats){
22771 if(!this.altFormatsArray){
22772 this.altFormatsArray = this.altFormats.split("|");
22774 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22775 v = Date.parseDate(value, this.altFormatsArray[i]);
22781 formatDate : function(date, fmt)
22783 return (!date || !(date instanceof Date)) ?
22784 date : date.dateFormat(fmt || this.format);
22787 onFocus : function()
22789 Roo.bootstrap.DateField.superclass.onFocus.call(this);
22793 onBlur : function()
22795 Roo.bootstrap.DateField.superclass.onBlur.call(this);
22797 var d = this.inputEl().getValue();
22804 showPopup : function()
22806 this.picker().show();
22810 this.fireEvent('showpopup', this, this.date);
22813 hidePopup : function()
22815 if(this.isInline) {
22818 this.picker().hide();
22819 this.viewMode = this.startViewMode;
22822 this.fireEvent('hidepopup', this, this.date);
22826 onMousedown: function(e)
22828 e.stopPropagation();
22829 e.preventDefault();
22834 Roo.bootstrap.DateField.superclass.keyup.call(this);
22838 setValue: function(v)
22840 if(this.fireEvent('beforeselect', this, v) !== false){
22841 var d = new Date(this.parseDate(v) ).clearTime();
22843 if(isNaN(d.getTime())){
22844 this.date = this.viewDate = '';
22845 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22849 v = this.formatDate(d);
22851 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22853 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22857 this.fireEvent('select', this, this.date);
22861 getValue: function()
22863 return this.formatDate(this.date);
22866 fireKey: function(e)
22868 if (!this.picker().isVisible()){
22869 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22875 var dateChanged = false,
22877 newDate, newViewDate;
22882 e.preventDefault();
22886 if (!this.keyboardNavigation) {
22889 dir = e.keyCode == 37 ? -1 : 1;
22892 newDate = this.moveYear(this.date, dir);
22893 newViewDate = this.moveYear(this.viewDate, dir);
22894 } else if (e.shiftKey){
22895 newDate = this.moveMonth(this.date, dir);
22896 newViewDate = this.moveMonth(this.viewDate, dir);
22898 newDate = new Date(this.date);
22899 newDate.setUTCDate(this.date.getUTCDate() + dir);
22900 newViewDate = new Date(this.viewDate);
22901 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22903 if (this.dateWithinRange(newDate)){
22904 this.date = newDate;
22905 this.viewDate = newViewDate;
22906 this.setValue(this.formatDate(this.date));
22908 e.preventDefault();
22909 dateChanged = true;
22914 if (!this.keyboardNavigation) {
22917 dir = e.keyCode == 38 ? -1 : 1;
22919 newDate = this.moveYear(this.date, dir);
22920 newViewDate = this.moveYear(this.viewDate, dir);
22921 } else if (e.shiftKey){
22922 newDate = this.moveMonth(this.date, dir);
22923 newViewDate = this.moveMonth(this.viewDate, dir);
22925 newDate = new Date(this.date);
22926 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22927 newViewDate = new Date(this.viewDate);
22928 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22930 if (this.dateWithinRange(newDate)){
22931 this.date = newDate;
22932 this.viewDate = newViewDate;
22933 this.setValue(this.formatDate(this.date));
22935 e.preventDefault();
22936 dateChanged = true;
22940 this.setValue(this.formatDate(this.date));
22942 e.preventDefault();
22945 this.setValue(this.formatDate(this.date));
22959 onClick: function(e)
22961 e.stopPropagation();
22962 e.preventDefault();
22964 var target = e.getTarget();
22966 if(target.nodeName.toLowerCase() === 'i'){
22967 target = Roo.get(target).dom.parentNode;
22970 var nodeName = target.nodeName;
22971 var className = target.className;
22972 var html = target.innerHTML;
22973 //Roo.log(nodeName);
22975 switch(nodeName.toLowerCase()) {
22977 switch(className) {
22983 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22984 switch(this.viewMode){
22986 this.viewDate = this.moveMonth(this.viewDate, dir);
22990 this.viewDate = this.moveYear(this.viewDate, dir);
22996 var date = new Date();
22997 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
22999 this.setValue(this.formatDate(this.date));
23006 if (className.indexOf('disabled') < 0) {
23007 if (!this.viewDate) {
23008 this.viewDate = new Date();
23010 this.viewDate.setUTCDate(1);
23011 if (className.indexOf('month') > -1) {
23012 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23014 var year = parseInt(html, 10) || 0;
23015 this.viewDate.setUTCFullYear(year);
23019 if(this.singleMode){
23020 this.setValue(this.formatDate(this.viewDate));
23031 //Roo.log(className);
23032 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23033 var day = parseInt(html, 10) || 1;
23034 var year = (this.viewDate || new Date()).getUTCFullYear(),
23035 month = (this.viewDate || new Date()).getUTCMonth();
23037 if (className.indexOf('old') > -1) {
23044 } else if (className.indexOf('new') > -1) {
23052 //Roo.log([year,month,day]);
23053 this.date = this.UTCDate(year, month, day,0,0,0,0);
23054 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23056 //Roo.log(this.formatDate(this.date));
23057 this.setValue(this.formatDate(this.date));
23064 setStartDate: function(startDate)
23066 this.startDate = startDate || -Infinity;
23067 if (this.startDate !== -Infinity) {
23068 this.startDate = this.parseDate(this.startDate);
23071 this.updateNavArrows();
23074 setEndDate: function(endDate)
23076 this.endDate = endDate || Infinity;
23077 if (this.endDate !== Infinity) {
23078 this.endDate = this.parseDate(this.endDate);
23081 this.updateNavArrows();
23084 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23086 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23087 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23088 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23090 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23091 return parseInt(d, 10);
23094 this.updateNavArrows();
23097 updateNavArrows: function()
23099 if(this.singleMode){
23103 var d = new Date(this.viewDate),
23104 year = d.getUTCFullYear(),
23105 month = d.getUTCMonth();
23107 Roo.each(this.picker().select('.prev', true).elements, function(v){
23109 switch (this.viewMode) {
23112 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23118 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23125 Roo.each(this.picker().select('.next', true).elements, function(v){
23127 switch (this.viewMode) {
23130 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23136 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23144 moveMonth: function(date, dir)
23149 var new_date = new Date(date.valueOf()),
23150 day = new_date.getUTCDate(),
23151 month = new_date.getUTCMonth(),
23152 mag = Math.abs(dir),
23154 dir = dir > 0 ? 1 : -1;
23157 // If going back one month, make sure month is not current month
23158 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23160 return new_date.getUTCMonth() == month;
23162 // If going forward one month, make sure month is as expected
23163 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23165 return new_date.getUTCMonth() != new_month;
23167 new_month = month + dir;
23168 new_date.setUTCMonth(new_month);
23169 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23170 if (new_month < 0 || new_month > 11) {
23171 new_month = (new_month + 12) % 12;
23174 // For magnitudes >1, move one month at a time...
23175 for (var i=0; i<mag; i++) {
23176 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23177 new_date = this.moveMonth(new_date, dir);
23179 // ...then reset the day, keeping it in the new month
23180 new_month = new_date.getUTCMonth();
23181 new_date.setUTCDate(day);
23183 return new_month != new_date.getUTCMonth();
23186 // Common date-resetting loop -- if date is beyond end of month, make it
23189 new_date.setUTCDate(--day);
23190 new_date.setUTCMonth(new_month);
23195 moveYear: function(date, dir)
23197 return this.moveMonth(date, dir*12);
23200 dateWithinRange: function(date)
23202 return date >= this.startDate && date <= this.endDate;
23208 this.picker().remove();
23211 validateValue : function(value)
23213 if(this.getVisibilityEl().hasClass('hidden')){
23217 if(value.length < 1) {
23218 if(this.allowBlank){
23224 if(value.length < this.minLength){
23227 if(value.length > this.maxLength){
23231 var vt = Roo.form.VTypes;
23232 if(!vt[this.vtype](value, this)){
23236 if(typeof this.validator == "function"){
23237 var msg = this.validator(value);
23243 if(this.regex && !this.regex.test(value)){
23247 if(typeof(this.parseDate(value)) == 'undefined'){
23251 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23255 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23265 this.date = this.viewDate = '';
23267 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23272 Roo.apply(Roo.bootstrap.DateField, {
23283 html: '<i class="fa fa-arrow-left"/>'
23293 html: '<i class="fa fa-arrow-right"/>'
23335 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23336 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23337 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23338 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23339 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23352 navFnc: 'FullYear',
23357 navFnc: 'FullYear',
23362 Roo.apply(Roo.bootstrap.DateField, {
23366 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23370 cls: 'datepicker-days',
23374 cls: 'table-condensed',
23376 Roo.bootstrap.DateField.head,
23380 Roo.bootstrap.DateField.footer
23387 cls: 'datepicker-months',
23391 cls: 'table-condensed',
23393 Roo.bootstrap.DateField.head,
23394 Roo.bootstrap.DateField.content,
23395 Roo.bootstrap.DateField.footer
23402 cls: 'datepicker-years',
23406 cls: 'table-condensed',
23408 Roo.bootstrap.DateField.head,
23409 Roo.bootstrap.DateField.content,
23410 Roo.bootstrap.DateField.footer
23429 * @class Roo.bootstrap.TimeField
23430 * @extends Roo.bootstrap.Input
23431 * Bootstrap DateField class
23435 * Create a new TimeField
23436 * @param {Object} config The config object
23439 Roo.bootstrap.TimeField = function(config){
23440 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23444 * Fires when this field show.
23445 * @param {Roo.bootstrap.DateField} thisthis
23446 * @param {Mixed} date The date value
23451 * Fires when this field hide.
23452 * @param {Roo.bootstrap.DateField} this
23453 * @param {Mixed} date The date value
23458 * Fires when select a date.
23459 * @param {Roo.bootstrap.DateField} this
23460 * @param {Mixed} date The date value
23466 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
23469 * @cfg {String} format
23470 * The default time format string which can be overriden for localization support. The format must be
23471 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23475 getAutoCreate : function()
23477 this.after = '<i class="fa far fa-clock"></i>';
23478 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23482 onRender: function(ct, position)
23485 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23487 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23489 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23491 this.pop = this.picker().select('>.datepicker-time',true).first();
23492 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23494 this.picker().on('mousedown', this.onMousedown, this);
23495 this.picker().on('click', this.onClick, this);
23497 this.picker().addClass('datepicker-dropdown');
23502 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23503 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23504 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23505 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23506 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23507 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23511 fireKey: function(e){
23512 if (!this.picker().isVisible()){
23513 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23519 e.preventDefault();
23527 this.onTogglePeriod();
23530 this.onIncrementMinutes();
23533 this.onDecrementMinutes();
23542 onClick: function(e) {
23543 e.stopPropagation();
23544 e.preventDefault();
23547 picker : function()
23549 return this.pickerEl;
23552 fillTime: function()
23554 var time = this.pop.select('tbody', true).first();
23556 time.dom.innerHTML = '';
23571 cls: 'hours-up fa fas fa-chevron-up'
23591 cls: 'minutes-up fa fas fa-chevron-up'
23612 cls: 'timepicker-hour',
23627 cls: 'timepicker-minute',
23642 cls: 'btn btn-primary period',
23664 cls: 'hours-down fa fas fa-chevron-down'
23684 cls: 'minutes-down fa fas fa-chevron-down'
23702 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23709 var hours = this.time.getHours();
23710 var minutes = this.time.getMinutes();
23723 hours = hours - 12;
23727 hours = '0' + hours;
23731 minutes = '0' + minutes;
23734 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23735 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23736 this.pop.select('button', true).first().dom.innerHTML = period;
23742 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23744 var cls = ['bottom'];
23746 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23753 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23757 //this.picker().setXY(20000,20000);
23758 this.picker().addClass(cls.join('-'));
23762 Roo.each(cls, function(c){
23767 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
23768 //_this.picker().setTop(_this.inputEl().getHeight());
23772 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
23774 //_this.picker().setTop(0 - _this.picker().getHeight());
23779 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23783 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23791 onFocus : function()
23793 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23797 onBlur : function()
23799 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23805 this.picker().show();
23810 this.fireEvent('show', this, this.date);
23815 this.picker().hide();
23818 this.fireEvent('hide', this, this.date);
23821 setTime : function()
23824 this.setValue(this.time.format(this.format));
23826 this.fireEvent('select', this, this.date);
23831 onMousedown: function(e){
23832 e.stopPropagation();
23833 e.preventDefault();
23836 onIncrementHours: function()
23838 Roo.log('onIncrementHours');
23839 this.time = this.time.add(Date.HOUR, 1);
23844 onDecrementHours: function()
23846 Roo.log('onDecrementHours');
23847 this.time = this.time.add(Date.HOUR, -1);
23851 onIncrementMinutes: function()
23853 Roo.log('onIncrementMinutes');
23854 this.time = this.time.add(Date.MINUTE, 1);
23858 onDecrementMinutes: function()
23860 Roo.log('onDecrementMinutes');
23861 this.time = this.time.add(Date.MINUTE, -1);
23865 onTogglePeriod: function()
23867 Roo.log('onTogglePeriod');
23868 this.time = this.time.add(Date.HOUR, 12);
23876 Roo.apply(Roo.bootstrap.TimeField, {
23880 cls: 'datepicker dropdown-menu',
23884 cls: 'datepicker-time',
23888 cls: 'table-condensed',
23917 cls: 'btn btn-info ok',
23945 * @class Roo.bootstrap.MonthField
23946 * @extends Roo.bootstrap.Input
23947 * Bootstrap MonthField class
23949 * @cfg {String} language default en
23952 * Create a new MonthField
23953 * @param {Object} config The config object
23956 Roo.bootstrap.MonthField = function(config){
23957 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23962 * Fires when this field show.
23963 * @param {Roo.bootstrap.MonthField} this
23964 * @param {Mixed} date The date value
23969 * Fires when this field hide.
23970 * @param {Roo.bootstrap.MonthField} this
23971 * @param {Mixed} date The date value
23976 * Fires when select a date.
23977 * @param {Roo.bootstrap.MonthField} this
23978 * @param {String} oldvalue The old value
23979 * @param {String} newvalue The new value
23985 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
23987 onRender: function(ct, position)
23990 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23992 this.language = this.language || 'en';
23993 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23994 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23996 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
23997 this.isInline = false;
23998 this.isInput = true;
23999 this.component = this.el.select('.add-on', true).first() || false;
24000 this.component = (this.component && this.component.length === 0) ? false : this.component;
24001 this.hasInput = this.component && this.inputEL().length;
24003 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24005 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24007 this.picker().on('mousedown', this.onMousedown, this);
24008 this.picker().on('click', this.onClick, this);
24010 this.picker().addClass('datepicker-dropdown');
24012 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24013 v.setStyle('width', '189px');
24020 if(this.isInline) {
24026 setValue: function(v, suppressEvent)
24028 var o = this.getValue();
24030 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24034 if(suppressEvent !== true){
24035 this.fireEvent('select', this, o, v);
24040 getValue: function()
24045 onClick: function(e)
24047 e.stopPropagation();
24048 e.preventDefault();
24050 var target = e.getTarget();
24052 if(target.nodeName.toLowerCase() === 'i'){
24053 target = Roo.get(target).dom.parentNode;
24056 var nodeName = target.nodeName;
24057 var className = target.className;
24058 var html = target.innerHTML;
24060 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24064 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24066 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24072 picker : function()
24074 return this.pickerEl;
24077 fillMonths: function()
24080 var months = this.picker().select('>.datepicker-months td', true).first();
24082 months.dom.innerHTML = '';
24088 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24091 months.createChild(month);
24100 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24101 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24104 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24105 e.removeClass('active');
24107 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24108 e.addClass('active');
24115 if(this.isInline) {
24119 this.picker().removeClass(['bottom', 'top']);
24121 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24123 * place to the top of element!
24127 this.picker().addClass('top');
24128 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24133 this.picker().addClass('bottom');
24135 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24138 onFocus : function()
24140 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24144 onBlur : function()
24146 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24148 var d = this.inputEl().getValue();
24157 this.picker().show();
24158 this.picker().select('>.datepicker-months', true).first().show();
24162 this.fireEvent('show', this, this.date);
24167 if(this.isInline) {
24170 this.picker().hide();
24171 this.fireEvent('hide', this, this.date);
24175 onMousedown: function(e)
24177 e.stopPropagation();
24178 e.preventDefault();
24183 Roo.bootstrap.MonthField.superclass.keyup.call(this);
24187 fireKey: function(e)
24189 if (!this.picker().isVisible()){
24190 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24201 e.preventDefault();
24205 dir = e.keyCode == 37 ? -1 : 1;
24207 this.vIndex = this.vIndex + dir;
24209 if(this.vIndex < 0){
24213 if(this.vIndex > 11){
24217 if(isNaN(this.vIndex)){
24221 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24227 dir = e.keyCode == 38 ? -1 : 1;
24229 this.vIndex = this.vIndex + dir * 4;
24231 if(this.vIndex < 0){
24235 if(this.vIndex > 11){
24239 if(isNaN(this.vIndex)){
24243 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24248 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24249 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24253 e.preventDefault();
24256 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24257 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24273 this.picker().remove();
24278 Roo.apply(Roo.bootstrap.MonthField, {
24297 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24298 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24303 Roo.apply(Roo.bootstrap.MonthField, {
24307 cls: 'datepicker dropdown-menu roo-dynamic',
24311 cls: 'datepicker-months',
24315 cls: 'table-condensed',
24317 Roo.bootstrap.DateField.content
24337 * @class Roo.bootstrap.CheckBox
24338 * @extends Roo.bootstrap.Input
24339 * Bootstrap CheckBox class
24341 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24342 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24343 * @cfg {String} boxLabel The text that appears beside the checkbox
24344 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24345 * @cfg {Boolean} checked initnal the element
24346 * @cfg {Boolean} inline inline the element (default false)
24347 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24348 * @cfg {String} tooltip label tooltip
24351 * Create a new CheckBox
24352 * @param {Object} config The config object
24355 Roo.bootstrap.CheckBox = function(config){
24356 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24361 * Fires when the element is checked or unchecked.
24362 * @param {Roo.bootstrap.CheckBox} this This input
24363 * @param {Boolean} checked The new checked value
24368 * Fires when the element is click.
24369 * @param {Roo.bootstrap.CheckBox} this This input
24376 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
24378 inputType: 'checkbox',
24387 // checkbox success does not make any sense really..
24392 getAutoCreate : function()
24394 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24400 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24403 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24409 type : this.inputType,
24410 value : this.inputValue,
24411 cls : 'roo-' + this.inputType, //'form-box',
24412 placeholder : this.placeholder || ''
24416 if(this.inputType != 'radio'){
24420 cls : 'roo-hidden-value',
24421 value : this.checked ? this.inputValue : this.valueOff
24426 if (this.weight) { // Validity check?
24427 cfg.cls += " " + this.inputType + "-" + this.weight;
24430 if (this.disabled) {
24431 input.disabled=true;
24435 input.checked = this.checked;
24440 input.name = this.name;
24442 if(this.inputType != 'radio'){
24443 hidden.name = this.name;
24444 input.name = '_hidden_' + this.name;
24449 input.cls += ' input-' + this.size;
24454 ['xs','sm','md','lg'].map(function(size){
24455 if (settings[size]) {
24456 cfg.cls += ' col-' + size + '-' + settings[size];
24460 var inputblock = input;
24462 if (this.before || this.after) {
24465 cls : 'input-group',
24470 inputblock.cn.push({
24472 cls : 'input-group-addon',
24477 inputblock.cn.push(input);
24479 if(this.inputType != 'radio'){
24480 inputblock.cn.push(hidden);
24484 inputblock.cn.push({
24486 cls : 'input-group-addon',
24492 var boxLabelCfg = false;
24498 //'for': id, // box label is handled by onclick - so no for...
24500 html: this.boxLabel
24503 boxLabelCfg.tooltip = this.tooltip;
24509 if (align ==='left' && this.fieldLabel.length) {
24510 // Roo.log("left and has label");
24515 cls : 'control-label',
24516 html : this.fieldLabel
24527 cfg.cn[1].cn.push(boxLabelCfg);
24530 if(this.labelWidth > 12){
24531 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24534 if(this.labelWidth < 13 && this.labelmd == 0){
24535 this.labelmd = this.labelWidth;
24538 if(this.labellg > 0){
24539 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24540 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24543 if(this.labelmd > 0){
24544 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24545 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24548 if(this.labelsm > 0){
24549 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24550 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24553 if(this.labelxs > 0){
24554 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24555 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24558 } else if ( this.fieldLabel.length) {
24559 // Roo.log(" label");
24563 tag: this.boxLabel ? 'span' : 'label',
24565 cls: 'control-label box-input-label',
24566 //cls : 'input-group-addon',
24567 html : this.fieldLabel
24574 cfg.cn.push(boxLabelCfg);
24579 // Roo.log(" no label && no align");
24580 cfg.cn = [ inputblock ] ;
24582 cfg.cn.push(boxLabelCfg);
24590 if(this.inputType != 'radio'){
24591 cfg.cn.push(hidden);
24599 * return the real input element.
24601 inputEl: function ()
24603 return this.el.select('input.roo-' + this.inputType,true).first();
24605 hiddenEl: function ()
24607 return this.el.select('input.roo-hidden-value',true).first();
24610 labelEl: function()
24612 return this.el.select('label.control-label',true).first();
24614 /* depricated... */
24618 return this.labelEl();
24621 boxLabelEl: function()
24623 return this.el.select('label.box-label',true).first();
24626 initEvents : function()
24628 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24630 this.inputEl().on('click', this.onClick, this);
24632 if (this.boxLabel) {
24633 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
24636 this.startValue = this.getValue();
24639 Roo.bootstrap.CheckBox.register(this);
24643 onClick : function(e)
24645 if(this.fireEvent('click', this, e) !== false){
24646 this.setChecked(!this.checked);
24651 setChecked : function(state,suppressEvent)
24653 this.startValue = this.getValue();
24655 if(this.inputType == 'radio'){
24657 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24658 e.dom.checked = false;
24661 this.inputEl().dom.checked = true;
24663 this.inputEl().dom.value = this.inputValue;
24665 if(suppressEvent !== true){
24666 this.fireEvent('check', this, true);
24674 this.checked = state;
24676 this.inputEl().dom.checked = state;
24679 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24681 if(suppressEvent !== true){
24682 this.fireEvent('check', this, state);
24688 getValue : function()
24690 if(this.inputType == 'radio'){
24691 return this.getGroupValue();
24694 return this.hiddenEl().dom.value;
24698 getGroupValue : function()
24700 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24704 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24707 setValue : function(v,suppressEvent)
24709 if(this.inputType == 'radio'){
24710 this.setGroupValue(v, suppressEvent);
24714 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24719 setGroupValue : function(v, suppressEvent)
24721 this.startValue = this.getValue();
24723 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24724 e.dom.checked = false;
24726 if(e.dom.value == v){
24727 e.dom.checked = true;
24731 if(suppressEvent !== true){
24732 this.fireEvent('check', this, true);
24740 validate : function()
24742 if(this.getVisibilityEl().hasClass('hidden')){
24748 (this.inputType == 'radio' && this.validateRadio()) ||
24749 (this.inputType == 'checkbox' && this.validateCheckbox())
24755 this.markInvalid();
24759 validateRadio : function()
24761 if(this.getVisibilityEl().hasClass('hidden')){
24765 if(this.allowBlank){
24771 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24772 if(!e.dom.checked){
24784 validateCheckbox : function()
24787 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24788 //return (this.getValue() == this.inputValue) ? true : false;
24791 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24799 for(var i in group){
24800 if(group[i].el.isVisible(true)){
24808 for(var i in group){
24813 r = (group[i].getValue() == group[i].inputValue) ? true : false;
24820 * Mark this field as valid
24822 markValid : function()
24826 this.fireEvent('valid', this);
24828 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24831 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24838 if(this.inputType == 'radio'){
24839 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24840 var fg = e.findParent('.form-group', false, true);
24841 if (Roo.bootstrap.version == 3) {
24842 fg.removeClass([_this.invalidClass, _this.validClass]);
24843 fg.addClass(_this.validClass);
24845 fg.removeClass(['is-valid', 'is-invalid']);
24846 fg.addClass('is-valid');
24854 var fg = this.el.findParent('.form-group', false, true);
24855 if (Roo.bootstrap.version == 3) {
24856 fg.removeClass([this.invalidClass, this.validClass]);
24857 fg.addClass(this.validClass);
24859 fg.removeClass(['is-valid', 'is-invalid']);
24860 fg.addClass('is-valid');
24865 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24871 for(var i in group){
24872 var fg = group[i].el.findParent('.form-group', false, true);
24873 if (Roo.bootstrap.version == 3) {
24874 fg.removeClass([this.invalidClass, this.validClass]);
24875 fg.addClass(this.validClass);
24877 fg.removeClass(['is-valid', 'is-invalid']);
24878 fg.addClass('is-valid');
24884 * Mark this field as invalid
24885 * @param {String} msg The validation message
24887 markInvalid : function(msg)
24889 if(this.allowBlank){
24895 this.fireEvent('invalid', this, msg);
24897 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24900 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24904 label.markInvalid();
24907 if(this.inputType == 'radio'){
24909 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24910 var fg = e.findParent('.form-group', false, true);
24911 if (Roo.bootstrap.version == 3) {
24912 fg.removeClass([_this.invalidClass, _this.validClass]);
24913 fg.addClass(_this.invalidClass);
24915 fg.removeClass(['is-invalid', 'is-valid']);
24916 fg.addClass('is-invalid');
24924 var fg = this.el.findParent('.form-group', false, true);
24925 if (Roo.bootstrap.version == 3) {
24926 fg.removeClass([_this.invalidClass, _this.validClass]);
24927 fg.addClass(_this.invalidClass);
24929 fg.removeClass(['is-invalid', 'is-valid']);
24930 fg.addClass('is-invalid');
24935 var group = Roo.bootstrap.CheckBox.get(this.groupId);
24941 for(var i in group){
24942 var fg = group[i].el.findParent('.form-group', false, true);
24943 if (Roo.bootstrap.version == 3) {
24944 fg.removeClass([_this.invalidClass, _this.validClass]);
24945 fg.addClass(_this.invalidClass);
24947 fg.removeClass(['is-invalid', 'is-valid']);
24948 fg.addClass('is-invalid');
24954 clearInvalid : function()
24956 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24958 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24960 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24962 if (label && label.iconEl) {
24963 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24964 label.iconEl.removeClass(['is-invalid', 'is-valid']);
24968 disable : function()
24970 if(this.inputType != 'radio'){
24971 Roo.bootstrap.CheckBox.superclass.disable.call(this);
24978 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24979 _this.getActionEl().addClass(this.disabledClass);
24980 e.dom.disabled = true;
24984 this.disabled = true;
24985 this.fireEvent("disable", this);
24989 enable : function()
24991 if(this.inputType != 'radio'){
24992 Roo.bootstrap.CheckBox.superclass.enable.call(this);
24999 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25000 _this.getActionEl().removeClass(this.disabledClass);
25001 e.dom.disabled = false;
25005 this.disabled = false;
25006 this.fireEvent("enable", this);
25010 setBoxLabel : function(v)
25015 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25021 Roo.apply(Roo.bootstrap.CheckBox, {
25026 * register a CheckBox Group
25027 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25029 register : function(checkbox)
25031 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25032 this.groups[checkbox.groupId] = {};
25035 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25039 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25043 * fetch a CheckBox Group based on the group ID
25044 * @param {string} the group ID
25045 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25047 get: function(groupId) {
25048 if (typeof(this.groups[groupId]) == 'undefined') {
25052 return this.groups[groupId] ;
25065 * @class Roo.bootstrap.Radio
25066 * @extends Roo.bootstrap.Component
25067 * Bootstrap Radio class
25068 * @cfg {String} boxLabel - the label associated
25069 * @cfg {String} value - the value of radio
25072 * Create a new Radio
25073 * @param {Object} config The config object
25075 Roo.bootstrap.Radio = function(config){
25076 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25080 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25086 getAutoCreate : function()
25090 cls : 'form-group radio',
25095 html : this.boxLabel
25103 initEvents : function()
25105 this.parent().register(this);
25107 this.el.on('click', this.onClick, this);
25111 onClick : function(e)
25113 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25114 this.setChecked(true);
25118 setChecked : function(state, suppressEvent)
25120 this.parent().setValue(this.value, suppressEvent);
25124 setBoxLabel : function(v)
25129 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25144 * @class Roo.bootstrap.SecurePass
25145 * @extends Roo.bootstrap.Input
25146 * Bootstrap SecurePass class
25150 * Create a new SecurePass
25151 * @param {Object} config The config object
25154 Roo.bootstrap.SecurePass = function (config) {
25155 // these go here, so the translation tool can replace them..
25157 PwdEmpty: "Please type a password, and then retype it to confirm.",
25158 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25159 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25160 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25161 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25162 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25163 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25164 TooWeak: "Your password is Too Weak."
25166 this.meterLabel = "Password strength:";
25167 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25168 this.meterClass = [
25169 "roo-password-meter-tooweak",
25170 "roo-password-meter-weak",
25171 "roo-password-meter-medium",
25172 "roo-password-meter-strong",
25173 "roo-password-meter-grey"
25178 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25181 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25183 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25185 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25186 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25187 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25188 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25189 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25190 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25191 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25201 * @cfg {String/Object} Label for the strength meter (defaults to
25202 * 'Password strength:')
25207 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25208 * ['Weak', 'Medium', 'Strong'])
25211 pwdStrengths: false,
25224 initEvents: function ()
25226 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25228 if (this.el.is('input[type=password]') && Roo.isSafari) {
25229 this.el.on('keydown', this.SafariOnKeyDown, this);
25232 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25235 onRender: function (ct, position)
25237 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25238 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25239 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25241 this.trigger.createChild({
25246 cls: 'roo-password-meter-grey col-xs-12',
25249 //width: this.meterWidth + 'px'
25253 cls: 'roo-password-meter-text'
25259 if (this.hideTrigger) {
25260 this.trigger.setDisplayed(false);
25262 this.setSize(this.width || '', this.height || '');
25265 onDestroy: function ()
25267 if (this.trigger) {
25268 this.trigger.removeAllListeners();
25269 this.trigger.remove();
25272 this.wrap.remove();
25274 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25277 checkStrength: function ()
25279 var pwd = this.inputEl().getValue();
25280 if (pwd == this._lastPwd) {
25285 if (this.ClientSideStrongPassword(pwd)) {
25287 } else if (this.ClientSideMediumPassword(pwd)) {
25289 } else if (this.ClientSideWeakPassword(pwd)) {
25295 Roo.log('strength1: ' + strength);
25297 //var pm = this.trigger.child('div/div/div').dom;
25298 var pm = this.trigger.child('div/div');
25299 pm.removeClass(this.meterClass);
25300 pm.addClass(this.meterClass[strength]);
25303 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25305 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25307 this._lastPwd = pwd;
25311 Roo.bootstrap.SecurePass.superclass.reset.call(this);
25313 this._lastPwd = '';
25315 var pm = this.trigger.child('div/div');
25316 pm.removeClass(this.meterClass);
25317 pm.addClass('roo-password-meter-grey');
25320 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25323 this.inputEl().dom.type='password';
25326 validateValue: function (value)
25328 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25331 if (value.length == 0) {
25332 if (this.allowBlank) {
25333 this.clearInvalid();
25337 this.markInvalid(this.errors.PwdEmpty);
25338 this.errorMsg = this.errors.PwdEmpty;
25346 if (!value.match(/[\x21-\x7e]+/)) {
25347 this.markInvalid(this.errors.PwdBadChar);
25348 this.errorMsg = this.errors.PwdBadChar;
25351 if (value.length < 6) {
25352 this.markInvalid(this.errors.PwdShort);
25353 this.errorMsg = this.errors.PwdShort;
25356 if (value.length > 16) {
25357 this.markInvalid(this.errors.PwdLong);
25358 this.errorMsg = this.errors.PwdLong;
25362 if (this.ClientSideStrongPassword(value)) {
25364 } else if (this.ClientSideMediumPassword(value)) {
25366 } else if (this.ClientSideWeakPassword(value)) {
25373 if (strength < 2) {
25374 //this.markInvalid(this.errors.TooWeak);
25375 this.errorMsg = this.errors.TooWeak;
25380 console.log('strength2: ' + strength);
25382 //var pm = this.trigger.child('div/div/div').dom;
25384 var pm = this.trigger.child('div/div');
25385 pm.removeClass(this.meterClass);
25386 pm.addClass(this.meterClass[strength]);
25388 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25390 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25392 this.errorMsg = '';
25396 CharacterSetChecks: function (type)
25399 this.fResult = false;
25402 isctype: function (character, type)
25405 case this.kCapitalLetter:
25406 if (character >= 'A' && character <= 'Z') {
25411 case this.kSmallLetter:
25412 if (character >= 'a' && character <= 'z') {
25418 if (character >= '0' && character <= '9') {
25423 case this.kPunctuation:
25424 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25435 IsLongEnough: function (pwd, size)
25437 return !(pwd == null || isNaN(size) || pwd.length < size);
25440 SpansEnoughCharacterSets: function (word, nb)
25442 if (!this.IsLongEnough(word, nb))
25447 var characterSetChecks = new Array(
25448 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25449 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25452 for (var index = 0; index < word.length; ++index) {
25453 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25454 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25455 characterSetChecks[nCharSet].fResult = true;
25462 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25463 if (characterSetChecks[nCharSet].fResult) {
25468 if (nCharSets < nb) {
25474 ClientSideStrongPassword: function (pwd)
25476 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25479 ClientSideMediumPassword: function (pwd)
25481 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25484 ClientSideWeakPassword: function (pwd)
25486 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25489 })//<script type="text/javascript">
25492 * Based Ext JS Library 1.1.1
25493 * Copyright(c) 2006-2007, Ext JS, LLC.
25499 * @class Roo.HtmlEditorCore
25500 * @extends Roo.Component
25501 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25503 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25506 Roo.HtmlEditorCore = function(config){
25509 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25514 * @event initialize
25515 * Fires when the editor is fully initialized (including the iframe)
25516 * @param {Roo.HtmlEditorCore} this
25521 * Fires when the editor is first receives the focus. Any insertion must wait
25522 * until after this event.
25523 * @param {Roo.HtmlEditorCore} this
25527 * @event beforesync
25528 * Fires before the textarea is updated with content from the editor iframe. Return false
25529 * to cancel the sync.
25530 * @param {Roo.HtmlEditorCore} this
25531 * @param {String} html
25535 * @event beforepush
25536 * Fires before the iframe editor is updated with content from the textarea. Return false
25537 * to cancel the push.
25538 * @param {Roo.HtmlEditorCore} this
25539 * @param {String} html
25544 * Fires when the textarea is updated with content from the editor iframe.
25545 * @param {Roo.HtmlEditorCore} this
25546 * @param {String} html
25551 * Fires when the iframe editor is updated with content from the textarea.
25552 * @param {Roo.HtmlEditorCore} this
25553 * @param {String} html
25558 * @event editorevent
25559 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25560 * @param {Roo.HtmlEditorCore} this
25566 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25568 // defaults : white / black...
25569 this.applyBlacklists();
25576 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25580 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25586 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25591 * @cfg {Number} height (in pixels)
25595 * @cfg {Number} width (in pixels)
25600 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25603 stylesheets: false,
25608 // private properties
25609 validationEvent : false,
25611 initialized : false,
25613 sourceEditMode : false,
25614 onFocus : Roo.emptyFn,
25616 hideMode:'offsets',
25620 // blacklist + whitelisted elements..
25627 * Protected method that will not generally be called directly. It
25628 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25629 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25631 getDocMarkup : function(){
25635 // inherit styels from page...??
25636 if (this.stylesheets === false) {
25638 Roo.get(document.head).select('style').each(function(node) {
25639 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25642 Roo.get(document.head).select('link').each(function(node) {
25643 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25646 } else if (!this.stylesheets.length) {
25648 st = '<style type="text/css">' +
25649 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25652 for (var i in this.stylesheets) {
25653 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25658 st += '<style type="text/css">' +
25659 'IMG { cursor: pointer } ' +
25662 var cls = 'roo-htmleditor-body';
25664 if(this.bodyCls.length){
25665 cls += ' ' + this.bodyCls;
25668 return '<html><head>' + st +
25669 //<style type="text/css">' +
25670 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25672 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25676 onRender : function(ct, position)
25679 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25680 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25683 this.el.dom.style.border = '0 none';
25684 this.el.dom.setAttribute('tabIndex', -1);
25685 this.el.addClass('x-hidden hide');
25689 if(Roo.isIE){ // fix IE 1px bogus margin
25690 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25694 this.frameId = Roo.id();
25698 var iframe = this.owner.wrap.createChild({
25700 cls: 'form-control', // bootstrap..
25702 name: this.frameId,
25703 frameBorder : 'no',
25704 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25709 this.iframe = iframe.dom;
25711 this.assignDocWin();
25713 this.doc.designMode = 'on';
25716 this.doc.write(this.getDocMarkup());
25720 var task = { // must defer to wait for browser to be ready
25722 //console.log("run task?" + this.doc.readyState);
25723 this.assignDocWin();
25724 if(this.doc.body || this.doc.readyState == 'complete'){
25726 this.doc.designMode="on";
25730 Roo.TaskMgr.stop(task);
25731 this.initEditor.defer(10, this);
25738 Roo.TaskMgr.start(task);
25743 onResize : function(w, h)
25745 Roo.log('resize: ' +w + ',' + h );
25746 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25750 if(typeof w == 'number'){
25752 this.iframe.style.width = w + 'px';
25754 if(typeof h == 'number'){
25756 this.iframe.style.height = h + 'px';
25758 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25765 * Toggles the editor between standard and source edit mode.
25766 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25768 toggleSourceEdit : function(sourceEditMode){
25770 this.sourceEditMode = sourceEditMode === true;
25772 if(this.sourceEditMode){
25774 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
25777 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25778 //this.iframe.className = '';
25781 //this.setSize(this.owner.wrap.getSize());
25782 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25789 * Protected method that will not generally be called directly. If you need/want
25790 * custom HTML cleanup, this is the method you should override.
25791 * @param {String} html The HTML to be cleaned
25792 * return {String} The cleaned HTML
25794 cleanHtml : function(html){
25795 html = String(html);
25796 if(html.length > 5){
25797 if(Roo.isSafari){ // strip safari nonsense
25798 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25801 if(html == ' '){
25808 * HTML Editor -> Textarea
25809 * Protected method that will not generally be called directly. Syncs the contents
25810 * of the editor iframe with the textarea.
25812 syncValue : function(){
25813 if(this.initialized){
25814 var bd = (this.doc.body || this.doc.documentElement);
25815 //this.cleanUpPaste(); -- this is done else where and causes havoc..
25816 var html = bd.innerHTML;
25818 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25819 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25821 html = '<div style="'+m[0]+'">' + html + '</div>';
25824 html = this.cleanHtml(html);
25825 // fix up the special chars.. normaly like back quotes in word...
25826 // however we do not want to do this with chinese..
25827 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25829 var cc = match.charCodeAt();
25831 // Get the character value, handling surrogate pairs
25832 if (match.length == 2) {
25833 // It's a surrogate pair, calculate the Unicode code point
25834 var high = match.charCodeAt(0) - 0xD800;
25835 var low = match.charCodeAt(1) - 0xDC00;
25836 cc = (high * 0x400) + low + 0x10000;
25838 (cc >= 0x4E00 && cc < 0xA000 ) ||
25839 (cc >= 0x3400 && cc < 0x4E00 ) ||
25840 (cc >= 0xf900 && cc < 0xfb00 )
25845 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25846 return "&#" + cc + ";";
25853 if(this.owner.fireEvent('beforesync', this, html) !== false){
25854 this.el.dom.value = html;
25855 this.owner.fireEvent('sync', this, html);
25861 * Protected method that will not generally be called directly. Pushes the value of the textarea
25862 * into the iframe editor.
25864 pushValue : function(){
25865 if(this.initialized){
25866 var v = this.el.dom.value.trim();
25868 // if(v.length < 1){
25872 if(this.owner.fireEvent('beforepush', this, v) !== false){
25873 var d = (this.doc.body || this.doc.documentElement);
25875 this.cleanUpPaste();
25876 this.el.dom.value = d.innerHTML;
25877 this.owner.fireEvent('push', this, v);
25883 deferFocus : function(){
25884 this.focus.defer(10, this);
25888 focus : function(){
25889 if(this.win && !this.sourceEditMode){
25896 assignDocWin: function()
25898 var iframe = this.iframe;
25901 this.doc = iframe.contentWindow.document;
25902 this.win = iframe.contentWindow;
25904 // if (!Roo.get(this.frameId)) {
25907 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25908 // this.win = Roo.get(this.frameId).dom.contentWindow;
25910 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25914 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25915 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25920 initEditor : function(){
25921 //console.log("INIT EDITOR");
25922 this.assignDocWin();
25926 this.doc.designMode="on";
25928 this.doc.write(this.getDocMarkup());
25931 var dbody = (this.doc.body || this.doc.documentElement);
25932 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25933 // this copies styles from the containing element into thsi one..
25934 // not sure why we need all of this..
25935 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25937 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25938 //ss['background-attachment'] = 'fixed'; // w3c
25939 dbody.bgProperties = 'fixed'; // ie
25940 //Roo.DomHelper.applyStyles(dbody, ss);
25941 Roo.EventManager.on(this.doc, {
25942 //'mousedown': this.onEditorEvent,
25943 'mouseup': this.onEditorEvent,
25944 'dblclick': this.onEditorEvent,
25945 'click': this.onEditorEvent,
25946 'keyup': this.onEditorEvent,
25951 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25953 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25954 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25956 this.initialized = true;
25958 this.owner.fireEvent('initialize', this);
25963 onDestroy : function(){
25969 //for (var i =0; i < this.toolbars.length;i++) {
25970 // // fixme - ask toolbars for heights?
25971 // this.toolbars[i].onDestroy();
25974 //this.wrap.dom.innerHTML = '';
25975 //this.wrap.remove();
25980 onFirstFocus : function(){
25982 this.assignDocWin();
25985 this.activated = true;
25988 if(Roo.isGecko){ // prevent silly gecko errors
25990 var s = this.win.getSelection();
25991 if(!s.focusNode || s.focusNode.nodeType != 3){
25992 var r = s.getRangeAt(0);
25993 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25998 this.execCmd('useCSS', true);
25999 this.execCmd('styleWithCSS', false);
26002 this.owner.fireEvent('activate', this);
26006 adjustFont: function(btn){
26007 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26008 //if(Roo.isSafari){ // safari
26011 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26012 if(Roo.isSafari){ // safari
26013 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26014 v = (v < 10) ? 10 : v;
26015 v = (v > 48) ? 48 : v;
26016 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26021 v = Math.max(1, v+adjust);
26023 this.execCmd('FontSize', v );
26026 onEditorEvent : function(e)
26028 this.owner.fireEvent('editorevent', this, e);
26029 // this.updateToolbar();
26030 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26033 insertTag : function(tg)
26035 // could be a bit smarter... -> wrap the current selected tRoo..
26036 if (tg.toLowerCase() == 'span' ||
26037 tg.toLowerCase() == 'code' ||
26038 tg.toLowerCase() == 'sup' ||
26039 tg.toLowerCase() == 'sub'
26042 range = this.createRange(this.getSelection());
26043 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26044 wrappingNode.appendChild(range.extractContents());
26045 range.insertNode(wrappingNode);
26052 this.execCmd("formatblock", tg);
26056 insertText : function(txt)
26060 var range = this.createRange();
26061 range.deleteContents();
26062 //alert(Sender.getAttribute('label'));
26064 range.insertNode(this.doc.createTextNode(txt));
26070 * Executes a Midas editor command on the editor document and performs necessary focus and
26071 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26072 * @param {String} cmd The Midas command
26073 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26075 relayCmd : function(cmd, value){
26077 this.execCmd(cmd, value);
26078 this.owner.fireEvent('editorevent', this);
26079 //this.updateToolbar();
26080 this.owner.deferFocus();
26084 * Executes a Midas editor command directly on the editor document.
26085 * For visual commands, you should use {@link #relayCmd} instead.
26086 * <b>This should only be called after the editor is initialized.</b>
26087 * @param {String} cmd The Midas command
26088 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26090 execCmd : function(cmd, value){
26091 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26098 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26100 * @param {String} text | dom node..
26102 insertAtCursor : function(text)
26105 if(!this.activated){
26111 var r = this.doc.selection.createRange();
26122 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26126 // from jquery ui (MIT licenced)
26128 var win = this.win;
26130 if (win.getSelection && win.getSelection().getRangeAt) {
26131 range = win.getSelection().getRangeAt(0);
26132 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26133 range.insertNode(node);
26134 } else if (win.document.selection && win.document.selection.createRange) {
26135 // no firefox support
26136 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26137 win.document.selection.createRange().pasteHTML(txt);
26139 // no firefox support
26140 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26141 this.execCmd('InsertHTML', txt);
26150 mozKeyPress : function(e){
26152 var c = e.getCharCode(), cmd;
26155 c = String.fromCharCode(c).toLowerCase();
26169 this.cleanUpPaste.defer(100, this);
26177 e.preventDefault();
26185 fixKeys : function(){ // load time branching for fastest keydown performance
26187 return function(e){
26188 var k = e.getKey(), r;
26191 r = this.doc.selection.createRange();
26194 r.pasteHTML('    ');
26201 r = this.doc.selection.createRange();
26203 var target = r.parentElement();
26204 if(!target || target.tagName.toLowerCase() != 'li'){
26206 r.pasteHTML('<br />');
26212 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26213 this.cleanUpPaste.defer(100, this);
26219 }else if(Roo.isOpera){
26220 return function(e){
26221 var k = e.getKey();
26225 this.execCmd('InsertHTML','    ');
26228 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26229 this.cleanUpPaste.defer(100, this);
26234 }else if(Roo.isSafari){
26235 return function(e){
26236 var k = e.getKey();
26240 this.execCmd('InsertText','\t');
26244 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26245 this.cleanUpPaste.defer(100, this);
26253 getAllAncestors: function()
26255 var p = this.getSelectedNode();
26258 a.push(p); // push blank onto stack..
26259 p = this.getParentElement();
26263 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26267 a.push(this.doc.body);
26271 lastSelNode : false,
26274 getSelection : function()
26276 this.assignDocWin();
26277 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26280 getSelectedNode: function()
26282 // this may only work on Gecko!!!
26284 // should we cache this!!!!
26289 var range = this.createRange(this.getSelection()).cloneRange();
26292 var parent = range.parentElement();
26294 var testRange = range.duplicate();
26295 testRange.moveToElementText(parent);
26296 if (testRange.inRange(range)) {
26299 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26302 parent = parent.parentElement;
26307 // is ancestor a text element.
26308 var ac = range.commonAncestorContainer;
26309 if (ac.nodeType == 3) {
26310 ac = ac.parentNode;
26313 var ar = ac.childNodes;
26316 var other_nodes = [];
26317 var has_other_nodes = false;
26318 for (var i=0;i<ar.length;i++) {
26319 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26322 // fullly contained node.
26324 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26329 // probably selected..
26330 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26331 other_nodes.push(ar[i]);
26335 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26340 has_other_nodes = true;
26342 if (!nodes.length && other_nodes.length) {
26343 nodes= other_nodes;
26345 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26351 createRange: function(sel)
26353 // this has strange effects when using with
26354 // top toolbar - not sure if it's a great idea.
26355 //this.editor.contentWindow.focus();
26356 if (typeof sel != "undefined") {
26358 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26360 return this.doc.createRange();
26363 return this.doc.createRange();
26366 getParentElement: function()
26369 this.assignDocWin();
26370 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26372 var range = this.createRange(sel);
26375 var p = range.commonAncestorContainer;
26376 while (p.nodeType == 3) { // text node
26387 * Range intersection.. the hard stuff...
26391 * [ -- selected range --- ]
26395 * if end is before start or hits it. fail.
26396 * if start is after end or hits it fail.
26398 * if either hits (but other is outside. - then it's not
26404 // @see http://www.thismuchiknow.co.uk/?p=64.
26405 rangeIntersectsNode : function(range, node)
26407 var nodeRange = node.ownerDocument.createRange();
26409 nodeRange.selectNode(node);
26411 nodeRange.selectNodeContents(node);
26414 var rangeStartRange = range.cloneRange();
26415 rangeStartRange.collapse(true);
26417 var rangeEndRange = range.cloneRange();
26418 rangeEndRange.collapse(false);
26420 var nodeStartRange = nodeRange.cloneRange();
26421 nodeStartRange.collapse(true);
26423 var nodeEndRange = nodeRange.cloneRange();
26424 nodeEndRange.collapse(false);
26426 return rangeStartRange.compareBoundaryPoints(
26427 Range.START_TO_START, nodeEndRange) == -1 &&
26428 rangeEndRange.compareBoundaryPoints(
26429 Range.START_TO_START, nodeStartRange) == 1;
26433 rangeCompareNode : function(range, node)
26435 var nodeRange = node.ownerDocument.createRange();
26437 nodeRange.selectNode(node);
26439 nodeRange.selectNodeContents(node);
26443 range.collapse(true);
26445 nodeRange.collapse(true);
26447 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26448 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26450 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26452 var nodeIsBefore = ss == 1;
26453 var nodeIsAfter = ee == -1;
26455 if (nodeIsBefore && nodeIsAfter) {
26458 if (!nodeIsBefore && nodeIsAfter) {
26459 return 1; //right trailed.
26462 if (nodeIsBefore && !nodeIsAfter) {
26463 return 2; // left trailed.
26469 // private? - in a new class?
26470 cleanUpPaste : function()
26472 // cleans up the whole document..
26473 Roo.log('cleanuppaste');
26475 this.cleanUpChildren(this.doc.body);
26476 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26477 if (clean != this.doc.body.innerHTML) {
26478 this.doc.body.innerHTML = clean;
26483 cleanWordChars : function(input) {// change the chars to hex code
26484 var he = Roo.HtmlEditorCore;
26486 var output = input;
26487 Roo.each(he.swapCodes, function(sw) {
26488 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26490 output = output.replace(swapper, sw[1]);
26497 cleanUpChildren : function (n)
26499 if (!n.childNodes.length) {
26502 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26503 this.cleanUpChild(n.childNodes[i]);
26510 cleanUpChild : function (node)
26513 //console.log(node);
26514 if (node.nodeName == "#text") {
26515 // clean up silly Windows -- stuff?
26518 if (node.nodeName == "#comment") {
26519 node.parentNode.removeChild(node);
26520 // clean up silly Windows -- stuff?
26523 var lcname = node.tagName.toLowerCase();
26524 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26525 // whitelist of tags..
26527 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26529 node.parentNode.removeChild(node);
26534 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26536 // spans with no attributes - just remove them..
26537 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
26538 remove_keep_children = true;
26541 // remove <a name=....> as rendering on yahoo mailer is borked with this.
26542 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26544 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26545 // remove_keep_children = true;
26548 if (remove_keep_children) {
26549 this.cleanUpChildren(node);
26550 // inserts everything just before this node...
26551 while (node.childNodes.length) {
26552 var cn = node.childNodes[0];
26553 node.removeChild(cn);
26554 node.parentNode.insertBefore(cn, node);
26556 node.parentNode.removeChild(node);
26560 if (!node.attributes || !node.attributes.length) {
26565 this.cleanUpChildren(node);
26569 function cleanAttr(n,v)
26572 if (v.match(/^\./) || v.match(/^\//)) {
26575 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26578 if (v.match(/^#/)) {
26581 if (v.match(/^\{/)) { // allow template editing.
26584 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26585 node.removeAttribute(n);
26589 var cwhite = this.cwhite;
26590 var cblack = this.cblack;
26592 function cleanStyle(n,v)
26594 if (v.match(/expression/)) { //XSS?? should we even bother..
26595 node.removeAttribute(n);
26599 var parts = v.split(/;/);
26602 Roo.each(parts, function(p) {
26603 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26607 var l = p.split(':').shift().replace(/\s+/g,'');
26608 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26610 if ( cwhite.length && cblack.indexOf(l) > -1) {
26611 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26612 //node.removeAttribute(n);
26616 // only allow 'c whitelisted system attributes'
26617 if ( cwhite.length && cwhite.indexOf(l) < 0) {
26618 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26619 //node.removeAttribute(n);
26629 if (clean.length) {
26630 node.setAttribute(n, clean.join(';'));
26632 node.removeAttribute(n);
26638 for (var i = node.attributes.length-1; i > -1 ; i--) {
26639 var a = node.attributes[i];
26642 if (a.name.toLowerCase().substr(0,2)=='on') {
26643 node.removeAttribute(a.name);
26646 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26647 node.removeAttribute(a.name);
26650 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26651 cleanAttr(a.name,a.value); // fixme..
26654 if (a.name == 'style') {
26655 cleanStyle(a.name,a.value);
26658 /// clean up MS crap..
26659 // tecnically this should be a list of valid class'es..
26662 if (a.name == 'class') {
26663 if (a.value.match(/^Mso/)) {
26664 node.removeAttribute('class');
26667 if (a.value.match(/^body$/)) {
26668 node.removeAttribute('class');
26679 this.cleanUpChildren(node);
26685 * Clean up MS wordisms...
26687 cleanWord : function(node)
26690 this.cleanWord(this.doc.body);
26695 node.nodeName == 'SPAN' &&
26696 !node.hasAttributes() &&
26697 node.childNodes.length == 1 &&
26698 node.firstChild.nodeName == "#text"
26700 var textNode = node.firstChild;
26701 node.removeChild(textNode);
26702 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26703 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26705 node.parentNode.insertBefore(textNode, node);
26706 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26707 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26709 node.parentNode.removeChild(node);
26712 if (node.nodeName == "#text") {
26713 // clean up silly Windows -- stuff?
26716 if (node.nodeName == "#comment") {
26717 node.parentNode.removeChild(node);
26718 // clean up silly Windows -- stuff?
26722 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26723 node.parentNode.removeChild(node);
26726 //Roo.log(node.tagName);
26727 // remove - but keep children..
26728 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26729 //Roo.log('-- removed');
26730 while (node.childNodes.length) {
26731 var cn = node.childNodes[0];
26732 node.removeChild(cn);
26733 node.parentNode.insertBefore(cn, node);
26734 // move node to parent - and clean it..
26735 this.cleanWord(cn);
26737 node.parentNode.removeChild(node);
26738 /// no need to iterate chidlren = it's got none..
26739 //this.iterateChildren(node, this.cleanWord);
26743 if (node.className.length) {
26745 var cn = node.className.split(/\W+/);
26747 Roo.each(cn, function(cls) {
26748 if (cls.match(/Mso[a-zA-Z]+/)) {
26753 node.className = cna.length ? cna.join(' ') : '';
26755 node.removeAttribute("class");
26759 if (node.hasAttribute("lang")) {
26760 node.removeAttribute("lang");
26763 if (node.hasAttribute("style")) {
26765 var styles = node.getAttribute("style").split(";");
26767 Roo.each(styles, function(s) {
26768 if (!s.match(/:/)) {
26771 var kv = s.split(":");
26772 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26775 // what ever is left... we allow.
26778 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26779 if (!nstyle.length) {
26780 node.removeAttribute('style');
26783 this.iterateChildren(node, this.cleanWord);
26789 * iterateChildren of a Node, calling fn each time, using this as the scole..
26790 * @param {DomNode} node node to iterate children of.
26791 * @param {Function} fn method of this class to call on each item.
26793 iterateChildren : function(node, fn)
26795 if (!node.childNodes.length) {
26798 for (var i = node.childNodes.length-1; i > -1 ; i--) {
26799 fn.call(this, node.childNodes[i])
26805 * cleanTableWidths.
26807 * Quite often pasting from word etc.. results in tables with column and widths.
26808 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26811 cleanTableWidths : function(node)
26816 this.cleanTableWidths(this.doc.body);
26821 if (node.nodeName == "#text" || node.nodeName == "#comment") {
26824 Roo.log(node.tagName);
26825 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26826 this.iterateChildren(node, this.cleanTableWidths);
26829 if (node.hasAttribute('width')) {
26830 node.removeAttribute('width');
26834 if (node.hasAttribute("style")) {
26837 var styles = node.getAttribute("style").split(";");
26839 Roo.each(styles, function(s) {
26840 if (!s.match(/:/)) {
26843 var kv = s.split(":");
26844 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26847 // what ever is left... we allow.
26850 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26851 if (!nstyle.length) {
26852 node.removeAttribute('style');
26856 this.iterateChildren(node, this.cleanTableWidths);
26864 domToHTML : function(currentElement, depth, nopadtext) {
26866 depth = depth || 0;
26867 nopadtext = nopadtext || false;
26869 if (!currentElement) {
26870 return this.domToHTML(this.doc.body);
26873 //Roo.log(currentElement);
26875 var allText = false;
26876 var nodeName = currentElement.nodeName;
26877 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26879 if (nodeName == '#text') {
26881 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26886 if (nodeName != 'BODY') {
26889 // Prints the node tagName, such as <A>, <IMG>, etc
26892 for(i = 0; i < currentElement.attributes.length;i++) {
26894 var aname = currentElement.attributes.item(i).name;
26895 if (!currentElement.attributes.item(i).value.length) {
26898 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26901 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26910 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26913 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26918 // Traverse the tree
26920 var currentElementChild = currentElement.childNodes.item(i);
26921 var allText = true;
26922 var innerHTML = '';
26924 while (currentElementChild) {
26925 // Formatting code (indent the tree so it looks nice on the screen)
26926 var nopad = nopadtext;
26927 if (lastnode == 'SPAN') {
26931 if (currentElementChild.nodeName == '#text') {
26932 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26933 toadd = nopadtext ? toadd : toadd.trim();
26934 if (!nopad && toadd.length > 80) {
26935 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
26937 innerHTML += toadd;
26940 currentElementChild = currentElement.childNodes.item(i);
26946 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
26948 // Recursively traverse the tree structure of the child node
26949 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
26950 lastnode = currentElementChild.nodeName;
26952 currentElementChild=currentElement.childNodes.item(i);
26958 // The remaining code is mostly for formatting the tree
26959 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
26964 ret+= "</"+tagName+">";
26970 applyBlacklists : function()
26972 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26973 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26977 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26978 if (b.indexOf(tag) > -1) {
26981 this.white.push(tag);
26985 Roo.each(w, function(tag) {
26986 if (b.indexOf(tag) > -1) {
26989 if (this.white.indexOf(tag) > -1) {
26992 this.white.push(tag);
26997 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26998 if (w.indexOf(tag) > -1) {
27001 this.black.push(tag);
27005 Roo.each(b, function(tag) {
27006 if (w.indexOf(tag) > -1) {
27009 if (this.black.indexOf(tag) > -1) {
27012 this.black.push(tag);
27017 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27018 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27022 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27023 if (b.indexOf(tag) > -1) {
27026 this.cwhite.push(tag);
27030 Roo.each(w, function(tag) {
27031 if (b.indexOf(tag) > -1) {
27034 if (this.cwhite.indexOf(tag) > -1) {
27037 this.cwhite.push(tag);
27042 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27043 if (w.indexOf(tag) > -1) {
27046 this.cblack.push(tag);
27050 Roo.each(b, function(tag) {
27051 if (w.indexOf(tag) > -1) {
27054 if (this.cblack.indexOf(tag) > -1) {
27057 this.cblack.push(tag);
27062 setStylesheets : function(stylesheets)
27064 if(typeof(stylesheets) == 'string'){
27065 Roo.get(this.iframe.contentDocument.head).createChild({
27067 rel : 'stylesheet',
27076 Roo.each(stylesheets, function(s) {
27081 Roo.get(_this.iframe.contentDocument.head).createChild({
27083 rel : 'stylesheet',
27092 removeStylesheets : function()
27096 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27101 setStyle : function(style)
27103 Roo.get(this.iframe.contentDocument.head).createChild({
27112 // hide stuff that is not compatible
27126 * @event specialkey
27130 * @cfg {String} fieldClass @hide
27133 * @cfg {String} focusClass @hide
27136 * @cfg {String} autoCreate @hide
27139 * @cfg {String} inputType @hide
27142 * @cfg {String} invalidClass @hide
27145 * @cfg {String} invalidText @hide
27148 * @cfg {String} msgFx @hide
27151 * @cfg {String} validateOnBlur @hide
27155 Roo.HtmlEditorCore.white = [
27156 'area', 'br', 'img', 'input', 'hr', 'wbr',
27158 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27159 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27160 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27161 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27162 'table', 'ul', 'xmp',
27164 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27167 'dir', 'menu', 'ol', 'ul', 'dl',
27173 Roo.HtmlEditorCore.black = [
27174 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27176 'base', 'basefont', 'bgsound', 'blink', 'body',
27177 'frame', 'frameset', 'head', 'html', 'ilayer',
27178 'iframe', 'layer', 'link', 'meta', 'object',
27179 'script', 'style' ,'title', 'xml' // clean later..
27181 Roo.HtmlEditorCore.clean = [
27182 'script', 'style', 'title', 'xml'
27184 Roo.HtmlEditorCore.remove = [
27189 Roo.HtmlEditorCore.ablack = [
27193 Roo.HtmlEditorCore.aclean = [
27194 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27198 Roo.HtmlEditorCore.pwhite= [
27199 'http', 'https', 'mailto'
27202 // white listed style attributes.
27203 Roo.HtmlEditorCore.cwhite= [
27204 // 'text-align', /// default is to allow most things..
27210 // black listed style attributes.
27211 Roo.HtmlEditorCore.cblack= [
27212 // 'font-size' -- this can be set by the project
27216 Roo.HtmlEditorCore.swapCodes =[
27217 [ 8211, "–" ],
27218 [ 8212, "—" ],
27235 * @class Roo.bootstrap.HtmlEditor
27236 * @extends Roo.bootstrap.TextArea
27237 * Bootstrap HtmlEditor class
27240 * Create a new HtmlEditor
27241 * @param {Object} config The config object
27244 Roo.bootstrap.HtmlEditor = function(config){
27245 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27246 if (!this.toolbars) {
27247 this.toolbars = [];
27250 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27253 * @event initialize
27254 * Fires when the editor is fully initialized (including the iframe)
27255 * @param {HtmlEditor} this
27260 * Fires when the editor is first receives the focus. Any insertion must wait
27261 * until after this event.
27262 * @param {HtmlEditor} this
27266 * @event beforesync
27267 * Fires before the textarea is updated with content from the editor iframe. Return false
27268 * to cancel the sync.
27269 * @param {HtmlEditor} this
27270 * @param {String} html
27274 * @event beforepush
27275 * Fires before the iframe editor is updated with content from the textarea. Return false
27276 * to cancel the push.
27277 * @param {HtmlEditor} this
27278 * @param {String} html
27283 * Fires when the textarea is updated with content from the editor iframe.
27284 * @param {HtmlEditor} this
27285 * @param {String} html
27290 * Fires when the iframe editor is updated with content from the textarea.
27291 * @param {HtmlEditor} this
27292 * @param {String} html
27296 * @event editmodechange
27297 * Fires when the editor switches edit modes
27298 * @param {HtmlEditor} this
27299 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27301 editmodechange: true,
27303 * @event editorevent
27304 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27305 * @param {HtmlEditor} this
27309 * @event firstfocus
27310 * Fires when on first focus - needed by toolbars..
27311 * @param {HtmlEditor} this
27316 * Auto save the htmlEditor value as a file into Events
27317 * @param {HtmlEditor} this
27321 * @event savedpreview
27322 * preview the saved version of htmlEditor
27323 * @param {HtmlEditor} this
27330 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
27334 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27339 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27344 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27349 * @cfg {Number} height (in pixels)
27353 * @cfg {Number} width (in pixels)
27358 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27361 stylesheets: false,
27366 // private properties
27367 validationEvent : false,
27369 initialized : false,
27372 onFocus : Roo.emptyFn,
27374 hideMode:'offsets',
27376 tbContainer : false,
27380 toolbarContainer :function() {
27381 return this.wrap.select('.x-html-editor-tb',true).first();
27385 * Protected method that will not generally be called directly. It
27386 * is called when the editor creates its toolbar. Override this method if you need to
27387 * add custom toolbar buttons.
27388 * @param {HtmlEditor} editor
27390 createToolbar : function(){
27391 Roo.log('renewing');
27392 Roo.log("create toolbars");
27394 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27395 this.toolbars[0].render(this.toolbarContainer());
27399 // if (!editor.toolbars || !editor.toolbars.length) {
27400 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27403 // for (var i =0 ; i < editor.toolbars.length;i++) {
27404 // editor.toolbars[i] = Roo.factory(
27405 // typeof(editor.toolbars[i]) == 'string' ?
27406 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27407 // Roo.bootstrap.HtmlEditor);
27408 // editor.toolbars[i].init(editor);
27414 onRender : function(ct, position)
27416 // Roo.log("Call onRender: " + this.xtype);
27418 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27420 this.wrap = this.inputEl().wrap({
27421 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27424 this.editorcore.onRender(ct, position);
27426 if (this.resizable) {
27427 this.resizeEl = new Roo.Resizable(this.wrap, {
27431 minHeight : this.height,
27432 height: this.height,
27433 handles : this.resizable,
27436 resize : function(r, w, h) {
27437 _t.onResize(w,h); // -something
27443 this.createToolbar(this);
27446 if(!this.width && this.resizable){
27447 this.setSize(this.wrap.getSize());
27449 if (this.resizeEl) {
27450 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27451 // should trigger onReize..
27457 onResize : function(w, h)
27459 Roo.log('resize: ' +w + ',' + h );
27460 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27464 if(this.inputEl() ){
27465 if(typeof w == 'number'){
27466 var aw = w - this.wrap.getFrameWidth('lr');
27467 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27470 if(typeof h == 'number'){
27471 var tbh = -11; // fixme it needs to tool bar size!
27472 for (var i =0; i < this.toolbars.length;i++) {
27473 // fixme - ask toolbars for heights?
27474 tbh += this.toolbars[i].el.getHeight();
27475 //if (this.toolbars[i].footer) {
27476 // tbh += this.toolbars[i].footer.el.getHeight();
27484 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27485 ah -= 5; // knock a few pixes off for look..
27486 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27490 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27491 this.editorcore.onResize(ew,eh);
27496 * Toggles the editor between standard and source edit mode.
27497 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27499 toggleSourceEdit : function(sourceEditMode)
27501 this.editorcore.toggleSourceEdit(sourceEditMode);
27503 if(this.editorcore.sourceEditMode){
27504 Roo.log('editor - showing textarea');
27507 // Roo.log(this.syncValue());
27509 this.inputEl().removeClass(['hide', 'x-hidden']);
27510 this.inputEl().dom.removeAttribute('tabIndex');
27511 this.inputEl().focus();
27513 Roo.log('editor - hiding textarea');
27515 // Roo.log(this.pushValue());
27518 this.inputEl().addClass(['hide', 'x-hidden']);
27519 this.inputEl().dom.setAttribute('tabIndex', -1);
27520 //this.deferFocus();
27523 if(this.resizable){
27524 this.setSize(this.wrap.getSize());
27527 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27530 // private (for BoxComponent)
27531 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27533 // private (for BoxComponent)
27534 getResizeEl : function(){
27538 // private (for BoxComponent)
27539 getPositionEl : function(){
27544 initEvents : function(){
27545 this.originalValue = this.getValue();
27549 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27552 // markInvalid : Roo.emptyFn,
27554 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27557 // clearInvalid : Roo.emptyFn,
27559 setValue : function(v){
27560 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27561 this.editorcore.pushValue();
27566 deferFocus : function(){
27567 this.focus.defer(10, this);
27571 focus : function(){
27572 this.editorcore.focus();
27578 onDestroy : function(){
27584 for (var i =0; i < this.toolbars.length;i++) {
27585 // fixme - ask toolbars for heights?
27586 this.toolbars[i].onDestroy();
27589 this.wrap.dom.innerHTML = '';
27590 this.wrap.remove();
27595 onFirstFocus : function(){
27596 //Roo.log("onFirstFocus");
27597 this.editorcore.onFirstFocus();
27598 for (var i =0; i < this.toolbars.length;i++) {
27599 this.toolbars[i].onFirstFocus();
27605 syncValue : function()
27607 this.editorcore.syncValue();
27610 pushValue : function()
27612 this.editorcore.pushValue();
27616 // hide stuff that is not compatible
27630 * @event specialkey
27634 * @cfg {String} fieldClass @hide
27637 * @cfg {String} focusClass @hide
27640 * @cfg {String} autoCreate @hide
27643 * @cfg {String} inputType @hide
27647 * @cfg {String} invalidText @hide
27650 * @cfg {String} msgFx @hide
27653 * @cfg {String} validateOnBlur @hide
27662 Roo.namespace('Roo.bootstrap.htmleditor');
27664 * @class Roo.bootstrap.HtmlEditorToolbar1
27670 new Roo.bootstrap.HtmlEditor({
27673 new Roo.bootstrap.HtmlEditorToolbar1({
27674 disable : { fonts: 1 , format: 1, ..., ... , ...],
27680 * @cfg {Object} disable List of elements to disable..
27681 * @cfg {Array} btns List of additional buttons.
27685 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27688 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27691 Roo.apply(this, config);
27693 // default disabled, based on 'good practice'..
27694 this.disable = this.disable || {};
27695 Roo.applyIf(this.disable, {
27698 specialElements : true
27700 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27702 this.editor = config.editor;
27703 this.editorcore = config.editor.editorcore;
27705 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27707 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27708 // dont call parent... till later.
27710 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
27715 editorcore : false,
27720 "h1","h2","h3","h4","h5","h6",
27722 "abbr", "acronym", "address", "cite", "samp", "var",
27726 onRender : function(ct, position)
27728 // Roo.log("Call onRender: " + this.xtype);
27730 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27732 this.el.dom.style.marginBottom = '0';
27734 var editorcore = this.editorcore;
27735 var editor= this.editor;
27738 var btn = function(id,cmd , toggle, handler, html){
27740 var event = toggle ? 'toggle' : 'click';
27745 xns: Roo.bootstrap,
27749 enableToggle:toggle !== false,
27751 pressed : toggle ? false : null,
27754 a.listeners[toggle ? 'toggle' : 'click'] = function() {
27755 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
27761 // var cb_box = function...
27766 xns: Roo.bootstrap,
27771 xns: Roo.bootstrap,
27775 Roo.each(this.formats, function(f) {
27776 style.menu.items.push({
27778 xns: Roo.bootstrap,
27779 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27784 editorcore.insertTag(this.tagname);
27791 children.push(style);
27793 btn('bold',false,true);
27794 btn('italic',false,true);
27795 btn('align-left', 'justifyleft',true);
27796 btn('align-center', 'justifycenter',true);
27797 btn('align-right' , 'justifyright',true);
27798 btn('link', false, false, function(btn) {
27799 //Roo.log("create link?");
27800 var url = prompt(this.createLinkText, this.defaultLinkValue);
27801 if(url && url != 'http:/'+'/'){
27802 this.editorcore.relayCmd('createlink', url);
27805 btn('list','insertunorderedlist',true);
27806 btn('pencil', false,true, function(btn){
27808 this.toggleSourceEdit(btn.pressed);
27811 if (this.editor.btns.length > 0) {
27812 for (var i = 0; i<this.editor.btns.length; i++) {
27813 children.push(this.editor.btns[i]);
27821 xns: Roo.bootstrap,
27826 xns: Roo.bootstrap,
27831 cog.menu.items.push({
27833 xns: Roo.bootstrap,
27834 html : Clean styles,
27839 editorcore.insertTag(this.tagname);
27848 this.xtype = 'NavSimplebar';
27850 for(var i=0;i< children.length;i++) {
27852 this.buttons.add(this.addxtypeChild(children[i]));
27856 editor.on('editorevent', this.updateToolbar, this);
27858 onBtnClick : function(id)
27860 this.editorcore.relayCmd(id);
27861 this.editorcore.focus();
27865 * Protected method that will not generally be called directly. It triggers
27866 * a toolbar update by reading the markup state of the current selection in the editor.
27868 updateToolbar: function(){
27870 if(!this.editorcore.activated){
27871 this.editor.onFirstFocus(); // is this neeed?
27875 var btns = this.buttons;
27876 var doc = this.editorcore.doc;
27877 btns.get('bold').setActive(doc.queryCommandState('bold'));
27878 btns.get('italic').setActive(doc.queryCommandState('italic'));
27879 //btns.get('underline').setActive(doc.queryCommandState('underline'));
27881 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27882 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27883 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27885 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27886 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27889 var ans = this.editorcore.getAllAncestors();
27890 if (this.formatCombo) {
27893 var store = this.formatCombo.store;
27894 this.formatCombo.setValue("");
27895 for (var i =0; i < ans.length;i++) {
27896 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27898 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27906 // hides menus... - so this cant be on a menu...
27907 Roo.bootstrap.MenuMgr.hideAll();
27909 Roo.bootstrap.MenuMgr.hideAll();
27910 //this.editorsyncValue();
27912 onFirstFocus: function() {
27913 this.buttons.each(function(item){
27917 toggleSourceEdit : function(sourceEditMode){
27920 if(sourceEditMode){
27921 Roo.log("disabling buttons");
27922 this.buttons.each( function(item){
27923 if(item.cmd != 'pencil'){
27929 Roo.log("enabling buttons");
27930 if(this.editorcore.initialized){
27931 this.buttons.each( function(item){
27937 Roo.log("calling toggole on editor");
27938 // tell the editor that it's been pressed..
27939 this.editor.toggleSourceEdit(sourceEditMode);
27953 * @class Roo.bootstrap.Markdown
27954 * @extends Roo.bootstrap.TextArea
27955 * Bootstrap Showdown editable area
27956 * @cfg {string} content
27959 * Create a new Showdown
27962 Roo.bootstrap.Markdown = function(config){
27963 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27967 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
27971 initEvents : function()
27974 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27975 this.markdownEl = this.el.createChild({
27976 cls : 'roo-markdown-area'
27978 this.inputEl().addClass('d-none');
27979 if (this.getValue() == '') {
27980 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27983 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27985 this.markdownEl.on('click', this.toggleTextEdit, this);
27986 this.on('blur', this.toggleTextEdit, this);
27987 this.on('specialkey', this.resizeTextArea, this);
27990 toggleTextEdit : function()
27992 var sh = this.markdownEl.getHeight();
27993 this.inputEl().addClass('d-none');
27994 this.markdownEl.addClass('d-none');
27995 if (!this.editing) {
27997 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
27998 this.inputEl().removeClass('d-none');
27999 this.inputEl().focus();
28000 this.editing = true;
28003 // show showdown...
28004 this.updateMarkdown();
28005 this.markdownEl.removeClass('d-none');
28006 this.editing = false;
28009 updateMarkdown : function()
28011 if (this.getValue() == '') {
28012 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28016 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28019 resizeTextArea: function () {
28022 Roo.log([sh, this.getValue().split("\n").length * 30]);
28023 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28025 setValue : function(val)
28027 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28028 if (!this.editing) {
28029 this.updateMarkdown();
28035 if (!this.editing) {
28036 this.toggleTextEdit();
28044 * Ext JS Library 1.1.1
28045 * Copyright(c) 2006-2007, Ext JS, LLC.
28047 * Originally Released Under LGPL - original licence link has changed is not relivant.
28050 * <script type="text/javascript">
28054 * @class Roo.bootstrap.PagingToolbar
28055 * @extends Roo.bootstrap.NavSimplebar
28056 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28058 * Create a new PagingToolbar
28059 * @param {Object} config The config object
28060 * @param {Roo.data.Store} store
28062 Roo.bootstrap.PagingToolbar = function(config)
28064 // old args format still supported... - xtype is prefered..
28065 // created from xtype...
28067 this.ds = config.dataSource;
28069 if (config.store && !this.ds) {
28070 this.store= Roo.factory(config.store, Roo.data);
28071 this.ds = this.store;
28072 this.ds.xmodule = this.xmodule || false;
28075 this.toolbarItems = [];
28076 if (config.items) {
28077 this.toolbarItems = config.items;
28080 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28085 this.bind(this.ds);
28088 if (Roo.bootstrap.version == 4) {
28089 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28091 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28096 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28098 * @cfg {Roo.data.Store} dataSource
28099 * The underlying data store providing the paged data
28102 * @cfg {String/HTMLElement/Element} container
28103 * container The id or element that will contain the toolbar
28106 * @cfg {Boolean} displayInfo
28107 * True to display the displayMsg (defaults to false)
28110 * @cfg {Number} pageSize
28111 * The number of records to display per page (defaults to 20)
28115 * @cfg {String} displayMsg
28116 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28118 displayMsg : 'Displaying {0} - {1} of {2}',
28120 * @cfg {String} emptyMsg
28121 * The message to display when no records are found (defaults to "No data to display")
28123 emptyMsg : 'No data to display',
28125 * Customizable piece of the default paging text (defaults to "Page")
28128 beforePageText : "Page",
28130 * Customizable piece of the default paging text (defaults to "of %0")
28133 afterPageText : "of {0}",
28135 * Customizable piece of the default paging text (defaults to "First Page")
28138 firstText : "First Page",
28140 * Customizable piece of the default paging text (defaults to "Previous Page")
28143 prevText : "Previous Page",
28145 * Customizable piece of the default paging text (defaults to "Next Page")
28148 nextText : "Next Page",
28150 * Customizable piece of the default paging text (defaults to "Last Page")
28153 lastText : "Last Page",
28155 * Customizable piece of the default paging text (defaults to "Refresh")
28158 refreshText : "Refresh",
28162 onRender : function(ct, position)
28164 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28165 this.navgroup.parentId = this.id;
28166 this.navgroup.onRender(this.el, null);
28167 // add the buttons to the navgroup
28169 if(this.displayInfo){
28170 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28171 this.displayEl = this.el.select('.x-paging-info', true).first();
28172 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28173 // this.displayEl = navel.el.select('span',true).first();
28179 Roo.each(_this.buttons, function(e){ // this might need to use render????
28180 Roo.factory(e).render(_this.el);
28184 Roo.each(_this.toolbarItems, function(e) {
28185 _this.navgroup.addItem(e);
28189 this.first = this.navgroup.addItem({
28190 tooltip: this.firstText,
28191 cls: "prev btn-outline-secondary",
28192 html : ' <i class="fa fa-step-backward"></i>',
28194 preventDefault: true,
28195 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28198 this.prev = this.navgroup.addItem({
28199 tooltip: this.prevText,
28200 cls: "prev btn-outline-secondary",
28201 html : ' <i class="fa fa-backward"></i>',
28203 preventDefault: true,
28204 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28206 //this.addSeparator();
28209 var field = this.navgroup.addItem( {
28211 cls : 'x-paging-position btn-outline-secondary',
28213 html : this.beforePageText +
28214 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28215 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28218 this.field = field.el.select('input', true).first();
28219 this.field.on("keydown", this.onPagingKeydown, this);
28220 this.field.on("focus", function(){this.dom.select();});
28223 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28224 //this.field.setHeight(18);
28225 //this.addSeparator();
28226 this.next = this.navgroup.addItem({
28227 tooltip: this.nextText,
28228 cls: "next btn-outline-secondary",
28229 html : ' <i class="fa fa-forward"></i>',
28231 preventDefault: true,
28232 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28234 this.last = this.navgroup.addItem({
28235 tooltip: this.lastText,
28236 html : ' <i class="fa fa-step-forward"></i>',
28237 cls: "next btn-outline-secondary",
28239 preventDefault: true,
28240 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28242 //this.addSeparator();
28243 this.loading = this.navgroup.addItem({
28244 tooltip: this.refreshText,
28245 cls: "btn-outline-secondary",
28246 html : ' <i class="fa fa-refresh"></i>',
28247 preventDefault: true,
28248 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28254 updateInfo : function(){
28255 if(this.displayEl){
28256 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28257 var msg = count == 0 ?
28261 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28263 this.displayEl.update(msg);
28268 onLoad : function(ds, r, o)
28270 this.cursor = o.params && o.params.start ? o.params.start : 0;
28272 var d = this.getPageData(),
28277 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28278 this.field.dom.value = ap;
28279 this.first.setDisabled(ap == 1);
28280 this.prev.setDisabled(ap == 1);
28281 this.next.setDisabled(ap == ps);
28282 this.last.setDisabled(ap == ps);
28283 this.loading.enable();
28288 getPageData : function(){
28289 var total = this.ds.getTotalCount();
28292 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28293 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28298 onLoadError : function(){
28299 this.loading.enable();
28303 onPagingKeydown : function(e){
28304 var k = e.getKey();
28305 var d = this.getPageData();
28307 var v = this.field.dom.value, pageNum;
28308 if(!v || isNaN(pageNum = parseInt(v, 10))){
28309 this.field.dom.value = d.activePage;
28312 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28313 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28316 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))
28318 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28319 this.field.dom.value = pageNum;
28320 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28323 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28325 var v = this.field.dom.value, pageNum;
28326 var increment = (e.shiftKey) ? 10 : 1;
28327 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28330 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28331 this.field.dom.value = d.activePage;
28334 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28336 this.field.dom.value = parseInt(v, 10) + increment;
28337 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28338 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28345 beforeLoad : function(){
28347 this.loading.disable();
28352 onClick : function(which){
28361 ds.load({params:{start: 0, limit: this.pageSize}});
28364 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28367 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28370 var total = ds.getTotalCount();
28371 var extra = total % this.pageSize;
28372 var lastStart = extra ? (total - extra) : total-this.pageSize;
28373 ds.load({params:{start: lastStart, limit: this.pageSize}});
28376 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28382 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28383 * @param {Roo.data.Store} store The data store to unbind
28385 unbind : function(ds){
28386 ds.un("beforeload", this.beforeLoad, this);
28387 ds.un("load", this.onLoad, this);
28388 ds.un("loadexception", this.onLoadError, this);
28389 ds.un("remove", this.updateInfo, this);
28390 ds.un("add", this.updateInfo, this);
28391 this.ds = undefined;
28395 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28396 * @param {Roo.data.Store} store The data store to bind
28398 bind : function(ds){
28399 ds.on("beforeload", this.beforeLoad, this);
28400 ds.on("load", this.onLoad, this);
28401 ds.on("loadexception", this.onLoadError, this);
28402 ds.on("remove", this.updateInfo, this);
28403 ds.on("add", this.updateInfo, this);
28414 * @class Roo.bootstrap.MessageBar
28415 * @extends Roo.bootstrap.Component
28416 * Bootstrap MessageBar class
28417 * @cfg {String} html contents of the MessageBar
28418 * @cfg {String} weight (info | success | warning | danger) default info
28419 * @cfg {String} beforeClass insert the bar before the given class
28420 * @cfg {Boolean} closable (true | false) default false
28421 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28424 * Create a new Element
28425 * @param {Object} config The config object
28428 Roo.bootstrap.MessageBar = function(config){
28429 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28432 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28438 beforeClass: 'bootstrap-sticky-wrap',
28440 getAutoCreate : function(){
28444 cls: 'alert alert-dismissable alert-' + this.weight,
28449 html: this.html || ''
28455 cfg.cls += ' alert-messages-fixed';
28469 onRender : function(ct, position)
28471 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28474 var cfg = Roo.apply({}, this.getAutoCreate());
28478 cfg.cls += ' ' + this.cls;
28481 cfg.style = this.style;
28483 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28485 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28488 this.el.select('>button.close').on('click', this.hide, this);
28494 if (!this.rendered) {
28500 this.fireEvent('show', this);
28506 if (!this.rendered) {
28512 this.fireEvent('hide', this);
28515 update : function()
28517 // var e = this.el.dom.firstChild;
28519 // if(this.closable){
28520 // e = e.nextSibling;
28523 // e.data = this.html || '';
28525 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28541 * @class Roo.bootstrap.Graph
28542 * @extends Roo.bootstrap.Component
28543 * Bootstrap Graph class
28547 @cfg {String} graphtype bar | vbar | pie
28548 @cfg {number} g_x coodinator | centre x (pie)
28549 @cfg {number} g_y coodinator | centre y (pie)
28550 @cfg {number} g_r radius (pie)
28551 @cfg {number} g_height height of the chart (respected by all elements in the set)
28552 @cfg {number} g_width width of the chart (respected by all elements in the set)
28553 @cfg {Object} title The title of the chart
28556 -opts (object) options for the chart
28558 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28559 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28561 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.
28562 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28564 o stretch (boolean)
28566 -opts (object) options for the pie
28569 o startAngle (number)
28570 o endAngle (number)
28574 * Create a new Input
28575 * @param {Object} config The config object
28578 Roo.bootstrap.Graph = function(config){
28579 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28585 * The img click event for the img.
28586 * @param {Roo.EventObject} e
28592 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28603 //g_colors: this.colors,
28610 getAutoCreate : function(){
28621 onRender : function(ct,position){
28624 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28626 if (typeof(Raphael) == 'undefined') {
28627 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28631 this.raphael = Raphael(this.el.dom);
28633 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28634 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28635 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28636 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28638 r.text(160, 10, "Single Series Chart").attr(txtattr);
28639 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28640 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28641 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28643 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28644 r.barchart(330, 10, 300, 220, data1);
28645 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28646 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28649 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28650 // r.barchart(30, 30, 560, 250, xdata, {
28651 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28652 // axis : "0 0 1 1",
28653 // axisxlabels : xdata
28654 // //yvalues : cols,
28657 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28659 // this.load(null,xdata,{
28660 // axis : "0 0 1 1",
28661 // axisxlabels : xdata
28666 load : function(graphtype,xdata,opts)
28668 this.raphael.clear();
28670 graphtype = this.graphtype;
28675 var r = this.raphael,
28676 fin = function () {
28677 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28679 fout = function () {
28680 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28682 pfin = function() {
28683 this.sector.stop();
28684 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28687 this.label[0].stop();
28688 this.label[0].attr({ r: 7.5 });
28689 this.label[1].attr({ "font-weight": 800 });
28692 pfout = function() {
28693 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28696 this.label[0].animate({ r: 5 }, 500, "bounce");
28697 this.label[1].attr({ "font-weight": 400 });
28703 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28706 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28709 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28710 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28712 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28719 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28724 setTitle: function(o)
28729 initEvents: function() {
28732 this.el.on('click', this.onClick, this);
28736 onClick : function(e)
28738 Roo.log('img onclick');
28739 this.fireEvent('click', this, e);
28751 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28754 * @class Roo.bootstrap.dash.NumberBox
28755 * @extends Roo.bootstrap.Component
28756 * Bootstrap NumberBox class
28757 * @cfg {String} headline Box headline
28758 * @cfg {String} content Box content
28759 * @cfg {String} icon Box icon
28760 * @cfg {String} footer Footer text
28761 * @cfg {String} fhref Footer href
28764 * Create a new NumberBox
28765 * @param {Object} config The config object
28769 Roo.bootstrap.dash.NumberBox = function(config){
28770 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28774 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28783 getAutoCreate : function(){
28787 cls : 'small-box ',
28795 cls : 'roo-headline',
28796 html : this.headline
28800 cls : 'roo-content',
28801 html : this.content
28815 cls : 'ion ' + this.icon
28824 cls : 'small-box-footer',
28825 href : this.fhref || '#',
28829 cfg.cn.push(footer);
28836 onRender : function(ct,position){
28837 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28844 setHeadline: function (value)
28846 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28849 setFooter: function (value, href)
28851 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28854 this.el.select('a.small-box-footer',true).first().attr('href', href);
28859 setContent: function (value)
28861 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28864 initEvents: function()
28878 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28881 * @class Roo.bootstrap.dash.TabBox
28882 * @extends Roo.bootstrap.Component
28883 * Bootstrap TabBox class
28884 * @cfg {String} title Title of the TabBox
28885 * @cfg {String} icon Icon of the TabBox
28886 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28887 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28890 * Create a new TabBox
28891 * @param {Object} config The config object
28895 Roo.bootstrap.dash.TabBox = function(config){
28896 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28901 * When a pane is added
28902 * @param {Roo.bootstrap.dash.TabPane} pane
28906 * @event activatepane
28907 * When a pane is activated
28908 * @param {Roo.bootstrap.dash.TabPane} pane
28910 "activatepane" : true
28918 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28923 tabScrollable : false,
28925 getChildContainer : function()
28927 return this.el.select('.tab-content', true).first();
28930 getAutoCreate : function(){
28934 cls: 'pull-left header',
28942 cls: 'fa ' + this.icon
28948 cls: 'nav nav-tabs pull-right',
28954 if(this.tabScrollable){
28961 cls: 'nav nav-tabs pull-right',
28972 cls: 'nav-tabs-custom',
28977 cls: 'tab-content no-padding',
28985 initEvents : function()
28987 //Roo.log('add add pane handler');
28988 this.on('addpane', this.onAddPane, this);
28991 * Updates the box title
28992 * @param {String} html to set the title to.
28994 setTitle : function(value)
28996 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28998 onAddPane : function(pane)
29000 this.panes.push(pane);
29001 //Roo.log('addpane');
29003 // tabs are rendere left to right..
29004 if(!this.showtabs){
29008 var ctr = this.el.select('.nav-tabs', true).first();
29011 var existing = ctr.select('.nav-tab',true);
29012 var qty = existing.getCount();;
29015 var tab = ctr.createChild({
29017 cls : 'nav-tab' + (qty ? '' : ' active'),
29025 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29028 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29030 pane.el.addClass('active');
29035 onTabClick : function(ev,un,ob,pane)
29037 //Roo.log('tab - prev default');
29038 ev.preventDefault();
29041 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29042 pane.tab.addClass('active');
29043 //Roo.log(pane.title);
29044 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29045 // technically we should have a deactivate event.. but maybe add later.
29046 // and it should not de-activate the selected tab...
29047 this.fireEvent('activatepane', pane);
29048 pane.el.addClass('active');
29049 pane.fireEvent('activate');
29054 getActivePane : function()
29057 Roo.each(this.panes, function(p) {
29058 if(p.el.hasClass('active')){
29079 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29081 * @class Roo.bootstrap.TabPane
29082 * @extends Roo.bootstrap.Component
29083 * Bootstrap TabPane class
29084 * @cfg {Boolean} active (false | true) Default false
29085 * @cfg {String} title title of panel
29089 * Create a new TabPane
29090 * @param {Object} config The config object
29093 Roo.bootstrap.dash.TabPane = function(config){
29094 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29100 * When a pane is activated
29101 * @param {Roo.bootstrap.dash.TabPane} pane
29108 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29113 // the tabBox that this is attached to.
29116 getAutoCreate : function()
29124 cfg.cls += ' active';
29129 initEvents : function()
29131 //Roo.log('trigger add pane handler');
29132 this.parent().fireEvent('addpane', this)
29136 * Updates the tab title
29137 * @param {String} html to set the title to.
29139 setTitle: function(str)
29145 this.tab.select('a', true).first().dom.innerHTML = str;
29162 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29165 * @class Roo.bootstrap.menu.Menu
29166 * @extends Roo.bootstrap.Component
29167 * Bootstrap Menu class - container for Menu
29168 * @cfg {String} html Text of the menu
29169 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29170 * @cfg {String} icon Font awesome icon
29171 * @cfg {String} pos Menu align to (top | bottom) default bottom
29175 * Create a new Menu
29176 * @param {Object} config The config object
29180 Roo.bootstrap.menu.Menu = function(config){
29181 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29185 * @event beforeshow
29186 * Fires before this menu is displayed
29187 * @param {Roo.bootstrap.menu.Menu} this
29191 * @event beforehide
29192 * Fires before this menu is hidden
29193 * @param {Roo.bootstrap.menu.Menu} this
29198 * Fires after this menu is displayed
29199 * @param {Roo.bootstrap.menu.Menu} this
29204 * Fires after this menu is hidden
29205 * @param {Roo.bootstrap.menu.Menu} this
29210 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29211 * @param {Roo.bootstrap.menu.Menu} this
29212 * @param {Roo.EventObject} e
29219 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
29223 weight : 'default',
29228 getChildContainer : function() {
29229 if(this.isSubMenu){
29233 return this.el.select('ul.dropdown-menu', true).first();
29236 getAutoCreate : function()
29241 cls : 'roo-menu-text',
29249 cls : 'fa ' + this.icon
29260 cls : 'dropdown-button btn btn-' + this.weight,
29265 cls : 'dropdown-toggle btn btn-' + this.weight,
29275 cls : 'dropdown-menu'
29281 if(this.pos == 'top'){
29282 cfg.cls += ' dropup';
29285 if(this.isSubMenu){
29288 cls : 'dropdown-menu'
29295 onRender : function(ct, position)
29297 this.isSubMenu = ct.hasClass('dropdown-submenu');
29299 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29302 initEvents : function()
29304 if(this.isSubMenu){
29308 this.hidden = true;
29310 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29311 this.triggerEl.on('click', this.onTriggerPress, this);
29313 this.buttonEl = this.el.select('button.dropdown-button', true).first();
29314 this.buttonEl.on('click', this.onClick, this);
29320 if(this.isSubMenu){
29324 return this.el.select('ul.dropdown-menu', true).first();
29327 onClick : function(e)
29329 this.fireEvent("click", this, e);
29332 onTriggerPress : function(e)
29334 if (this.isVisible()) {
29341 isVisible : function(){
29342 return !this.hidden;
29347 this.fireEvent("beforeshow", this);
29349 this.hidden = false;
29350 this.el.addClass('open');
29352 Roo.get(document).on("mouseup", this.onMouseUp, this);
29354 this.fireEvent("show", this);
29361 this.fireEvent("beforehide", this);
29363 this.hidden = true;
29364 this.el.removeClass('open');
29366 Roo.get(document).un("mouseup", this.onMouseUp);
29368 this.fireEvent("hide", this);
29371 onMouseUp : function()
29385 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29388 * @class Roo.bootstrap.menu.Item
29389 * @extends Roo.bootstrap.Component
29390 * Bootstrap MenuItem class
29391 * @cfg {Boolean} submenu (true | false) default false
29392 * @cfg {String} html text of the item
29393 * @cfg {String} href the link
29394 * @cfg {Boolean} disable (true | false) default false
29395 * @cfg {Boolean} preventDefault (true | false) default true
29396 * @cfg {String} icon Font awesome icon
29397 * @cfg {String} pos Submenu align to (left | right) default right
29401 * Create a new Item
29402 * @param {Object} config The config object
29406 Roo.bootstrap.menu.Item = function(config){
29407 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29411 * Fires when the mouse is hovering over this menu
29412 * @param {Roo.bootstrap.menu.Item} this
29413 * @param {Roo.EventObject} e
29418 * Fires when the mouse exits this menu
29419 * @param {Roo.bootstrap.menu.Item} this
29420 * @param {Roo.EventObject} e
29426 * The raw click event for the entire grid.
29427 * @param {Roo.EventObject} e
29433 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
29438 preventDefault: true,
29443 getAutoCreate : function()
29448 cls : 'roo-menu-item-text',
29456 cls : 'fa ' + this.icon
29465 href : this.href || '#',
29472 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29476 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29478 if(this.pos == 'left'){
29479 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29486 initEvents : function()
29488 this.el.on('mouseover', this.onMouseOver, this);
29489 this.el.on('mouseout', this.onMouseOut, this);
29491 this.el.select('a', true).first().on('click', this.onClick, this);
29495 onClick : function(e)
29497 if(this.preventDefault){
29498 e.preventDefault();
29501 this.fireEvent("click", this, e);
29504 onMouseOver : function(e)
29506 if(this.submenu && this.pos == 'left'){
29507 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29510 this.fireEvent("mouseover", this, e);
29513 onMouseOut : function(e)
29515 this.fireEvent("mouseout", this, e);
29527 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29530 * @class Roo.bootstrap.menu.Separator
29531 * @extends Roo.bootstrap.Component
29532 * Bootstrap Separator class
29535 * Create a new Separator
29536 * @param {Object} config The config object
29540 Roo.bootstrap.menu.Separator = function(config){
29541 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29544 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29546 getAutoCreate : function(){
29549 cls: 'dropdown-divider divider'
29567 * @class Roo.bootstrap.Tooltip
29568 * Bootstrap Tooltip class
29569 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29570 * to determine which dom element triggers the tooltip.
29572 * It needs to add support for additional attributes like tooltip-position
29575 * Create a new Toolti
29576 * @param {Object} config The config object
29579 Roo.bootstrap.Tooltip = function(config){
29580 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29582 this.alignment = Roo.bootstrap.Tooltip.alignment;
29584 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29585 this.alignment = config.alignment;
29590 Roo.apply(Roo.bootstrap.Tooltip, {
29592 * @function init initialize tooltip monitoring.
29596 currentTip : false,
29597 currentRegion : false,
29603 Roo.get(document).on('mouseover', this.enter ,this);
29604 Roo.get(document).on('mouseout', this.leave, this);
29607 this.currentTip = new Roo.bootstrap.Tooltip();
29610 enter : function(ev)
29612 var dom = ev.getTarget();
29614 //Roo.log(['enter',dom]);
29615 var el = Roo.fly(dom);
29616 if (this.currentEl) {
29618 //Roo.log(this.currentEl);
29619 //Roo.log(this.currentEl.contains(dom));
29620 if (this.currentEl == el) {
29623 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29629 if (this.currentTip.el) {
29630 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29634 if(!el || el.dom == document){
29640 if (!el.attr('tooltip')) {
29641 pel = el.findParent("[tooltip]");
29643 bindEl = Roo.get(pel);
29649 // you can not look for children, as if el is the body.. then everythign is the child..
29650 if (!pel && !el.attr('tooltip')) { //
29651 if (!el.select("[tooltip]").elements.length) {
29654 // is the mouse over this child...?
29655 bindEl = el.select("[tooltip]").first();
29656 var xy = ev.getXY();
29657 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29658 //Roo.log("not in region.");
29661 //Roo.log("child element over..");
29664 this.currentEl = el;
29665 this.currentTip.bind(bindEl);
29666 this.currentRegion = Roo.lib.Region.getRegion(dom);
29667 this.currentTip.enter();
29670 leave : function(ev)
29672 var dom = ev.getTarget();
29673 //Roo.log(['leave',dom]);
29674 if (!this.currentEl) {
29679 if (dom != this.currentEl.dom) {
29682 var xy = ev.getXY();
29683 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29686 // only activate leave if mouse cursor is outside... bounding box..
29691 if (this.currentTip) {
29692 this.currentTip.leave();
29694 //Roo.log('clear currentEl');
29695 this.currentEl = false;
29700 'left' : ['r-l', [-2,0], 'right'],
29701 'right' : ['l-r', [2,0], 'left'],
29702 'bottom' : ['t-b', [0,2], 'top'],
29703 'top' : [ 'b-t', [0,-2], 'bottom']
29709 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29714 delay : null, // can be { show : 300 , hide: 500}
29718 hoverState : null, //???
29720 placement : 'bottom',
29724 getAutoCreate : function(){
29731 cls : 'tooltip-arrow arrow'
29734 cls : 'tooltip-inner'
29741 bind : function(el)
29746 initEvents : function()
29748 this.arrowEl = this.el.select('.arrow', true).first();
29749 this.innerEl = this.el.select('.tooltip-inner', true).first();
29752 enter : function () {
29754 if (this.timeout != null) {
29755 clearTimeout(this.timeout);
29758 this.hoverState = 'in';
29759 //Roo.log("enter - show");
29760 if (!this.delay || !this.delay.show) {
29765 this.timeout = setTimeout(function () {
29766 if (_t.hoverState == 'in') {
29769 }, this.delay.show);
29773 clearTimeout(this.timeout);
29775 this.hoverState = 'out';
29776 if (!this.delay || !this.delay.hide) {
29782 this.timeout = setTimeout(function () {
29783 //Roo.log("leave - timeout");
29785 if (_t.hoverState == 'out') {
29787 Roo.bootstrap.Tooltip.currentEl = false;
29792 show : function (msg)
29795 this.render(document.body);
29798 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29800 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29802 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29804 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29805 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29807 var placement = typeof this.placement == 'function' ?
29808 this.placement.call(this, this.el, on_el) :
29811 var autoToken = /\s?auto?\s?/i;
29812 var autoPlace = autoToken.test(placement);
29814 placement = placement.replace(autoToken, '') || 'top';
29818 //this.el.setXY([0,0]);
29820 //this.el.dom.style.display='block';
29822 //this.el.appendTo(on_el);
29824 var p = this.getPosition();
29825 var box = this.el.getBox();
29831 var align = this.alignment[placement];
29833 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29835 if(placement == 'top' || placement == 'bottom'){
29837 placement = 'right';
29840 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29841 placement = 'left';
29844 var scroll = Roo.select('body', true).first().getScroll();
29846 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29850 align = this.alignment[placement];
29852 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29856 var elems = document.getElementsByTagName('div');
29857 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29858 for (var i = 0; i < elems.length; i++) {
29859 var zindex = Number.parseInt(
29860 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29863 if (zindex > highest) {
29870 this.el.dom.style.zIndex = highest;
29872 this.el.alignTo(this.bindEl, align[0],align[1]);
29873 //var arrow = this.el.select('.arrow',true).first();
29874 //arrow.set(align[2],
29876 this.el.addClass(placement);
29877 this.el.addClass("bs-tooltip-"+ placement);
29879 this.el.addClass('in fade show');
29881 this.hoverState = null;
29883 if (this.el.hasClass('fade')) {
29898 //this.el.setXY([0,0]);
29899 this.el.removeClass(['show', 'in']);
29915 * @class Roo.bootstrap.LocationPicker
29916 * @extends Roo.bootstrap.Component
29917 * Bootstrap LocationPicker class
29918 * @cfg {Number} latitude Position when init default 0
29919 * @cfg {Number} longitude Position when init default 0
29920 * @cfg {Number} zoom default 15
29921 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29922 * @cfg {Boolean} mapTypeControl default false
29923 * @cfg {Boolean} disableDoubleClickZoom default false
29924 * @cfg {Boolean} scrollwheel default true
29925 * @cfg {Boolean} streetViewControl default false
29926 * @cfg {Number} radius default 0
29927 * @cfg {String} locationName
29928 * @cfg {Boolean} draggable default true
29929 * @cfg {Boolean} enableAutocomplete default false
29930 * @cfg {Boolean} enableReverseGeocode default true
29931 * @cfg {String} markerTitle
29934 * Create a new LocationPicker
29935 * @param {Object} config The config object
29939 Roo.bootstrap.LocationPicker = function(config){
29941 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29946 * Fires when the picker initialized.
29947 * @param {Roo.bootstrap.LocationPicker} this
29948 * @param {Google Location} location
29952 * @event positionchanged
29953 * Fires when the picker position changed.
29954 * @param {Roo.bootstrap.LocationPicker} this
29955 * @param {Google Location} location
29957 positionchanged : true,
29960 * Fires when the map resize.
29961 * @param {Roo.bootstrap.LocationPicker} this
29966 * Fires when the map show.
29967 * @param {Roo.bootstrap.LocationPicker} this
29972 * Fires when the map hide.
29973 * @param {Roo.bootstrap.LocationPicker} this
29978 * Fires when click the map.
29979 * @param {Roo.bootstrap.LocationPicker} this
29980 * @param {Map event} e
29984 * @event mapRightClick
29985 * Fires when right click the map.
29986 * @param {Roo.bootstrap.LocationPicker} this
29987 * @param {Map event} e
29989 mapRightClick : true,
29991 * @event markerClick
29992 * Fires when click the marker.
29993 * @param {Roo.bootstrap.LocationPicker} this
29994 * @param {Map event} e
29996 markerClick : true,
29998 * @event markerRightClick
29999 * Fires when right click the marker.
30000 * @param {Roo.bootstrap.LocationPicker} this
30001 * @param {Map event} e
30003 markerRightClick : true,
30005 * @event OverlayViewDraw
30006 * Fires when OverlayView Draw
30007 * @param {Roo.bootstrap.LocationPicker} this
30009 OverlayViewDraw : true,
30011 * @event OverlayViewOnAdd
30012 * Fires when OverlayView Draw
30013 * @param {Roo.bootstrap.LocationPicker} this
30015 OverlayViewOnAdd : true,
30017 * @event OverlayViewOnRemove
30018 * Fires when OverlayView Draw
30019 * @param {Roo.bootstrap.LocationPicker} this
30021 OverlayViewOnRemove : true,
30023 * @event OverlayViewShow
30024 * Fires when OverlayView Draw
30025 * @param {Roo.bootstrap.LocationPicker} this
30026 * @param {Pixel} cpx
30028 OverlayViewShow : true,
30030 * @event OverlayViewHide
30031 * Fires when OverlayView Draw
30032 * @param {Roo.bootstrap.LocationPicker} this
30034 OverlayViewHide : true,
30036 * @event loadexception
30037 * Fires when load google lib failed.
30038 * @param {Roo.bootstrap.LocationPicker} this
30040 loadexception : true
30045 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30047 gMapContext: false,
30053 mapTypeControl: false,
30054 disableDoubleClickZoom: false,
30056 streetViewControl: false,
30060 enableAutocomplete: false,
30061 enableReverseGeocode: true,
30064 getAutoCreate: function()
30069 cls: 'roo-location-picker'
30075 initEvents: function(ct, position)
30077 if(!this.el.getWidth() || this.isApplied()){
30081 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30086 initial: function()
30088 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30089 this.fireEvent('loadexception', this);
30093 if(!this.mapTypeId){
30094 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30097 this.gMapContext = this.GMapContext();
30099 this.initOverlayView();
30101 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30105 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30106 _this.setPosition(_this.gMapContext.marker.position);
30109 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30110 _this.fireEvent('mapClick', this, event);
30114 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30115 _this.fireEvent('mapRightClick', this, event);
30119 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30120 _this.fireEvent('markerClick', this, event);
30124 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30125 _this.fireEvent('markerRightClick', this, event);
30129 this.setPosition(this.gMapContext.location);
30131 this.fireEvent('initial', this, this.gMapContext.location);
30134 initOverlayView: function()
30138 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30142 _this.fireEvent('OverlayViewDraw', _this);
30147 _this.fireEvent('OverlayViewOnAdd', _this);
30150 onRemove: function()
30152 _this.fireEvent('OverlayViewOnRemove', _this);
30155 show: function(cpx)
30157 _this.fireEvent('OverlayViewShow', _this, cpx);
30162 _this.fireEvent('OverlayViewHide', _this);
30168 fromLatLngToContainerPixel: function(event)
30170 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30173 isApplied: function()
30175 return this.getGmapContext() == false ? false : true;
30178 getGmapContext: function()
30180 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30183 GMapContext: function()
30185 var position = new google.maps.LatLng(this.latitude, this.longitude);
30187 var _map = new google.maps.Map(this.el.dom, {
30190 mapTypeId: this.mapTypeId,
30191 mapTypeControl: this.mapTypeControl,
30192 disableDoubleClickZoom: this.disableDoubleClickZoom,
30193 scrollwheel: this.scrollwheel,
30194 streetViewControl: this.streetViewControl,
30195 locationName: this.locationName,
30196 draggable: this.draggable,
30197 enableAutocomplete: this.enableAutocomplete,
30198 enableReverseGeocode: this.enableReverseGeocode
30201 var _marker = new google.maps.Marker({
30202 position: position,
30204 title: this.markerTitle,
30205 draggable: this.draggable
30212 location: position,
30213 radius: this.radius,
30214 locationName: this.locationName,
30215 addressComponents: {
30216 formatted_address: null,
30217 addressLine1: null,
30218 addressLine2: null,
30220 streetNumber: null,
30224 stateOrProvince: null
30227 domContainer: this.el.dom,
30228 geodecoder: new google.maps.Geocoder()
30232 drawCircle: function(center, radius, options)
30234 if (this.gMapContext.circle != null) {
30235 this.gMapContext.circle.setMap(null);
30239 options = Roo.apply({}, options, {
30240 strokeColor: "#0000FF",
30241 strokeOpacity: .35,
30243 fillColor: "#0000FF",
30247 options.map = this.gMapContext.map;
30248 options.radius = radius;
30249 options.center = center;
30250 this.gMapContext.circle = new google.maps.Circle(options);
30251 return this.gMapContext.circle;
30257 setPosition: function(location)
30259 this.gMapContext.location = location;
30260 this.gMapContext.marker.setPosition(location);
30261 this.gMapContext.map.panTo(location);
30262 this.drawCircle(location, this.gMapContext.radius, {});
30266 if (this.gMapContext.settings.enableReverseGeocode) {
30267 this.gMapContext.geodecoder.geocode({
30268 latLng: this.gMapContext.location
30269 }, function(results, status) {
30271 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30272 _this.gMapContext.locationName = results[0].formatted_address;
30273 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30275 _this.fireEvent('positionchanged', this, location);
30282 this.fireEvent('positionchanged', this, location);
30287 google.maps.event.trigger(this.gMapContext.map, "resize");
30289 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30291 this.fireEvent('resize', this);
30294 setPositionByLatLng: function(latitude, longitude)
30296 this.setPosition(new google.maps.LatLng(latitude, longitude));
30299 getCurrentPosition: function()
30302 latitude: this.gMapContext.location.lat(),
30303 longitude: this.gMapContext.location.lng()
30307 getAddressName: function()
30309 return this.gMapContext.locationName;
30312 getAddressComponents: function()
30314 return this.gMapContext.addressComponents;
30317 address_component_from_google_geocode: function(address_components)
30321 for (var i = 0; i < address_components.length; i++) {
30322 var component = address_components[i];
30323 if (component.types.indexOf("postal_code") >= 0) {
30324 result.postalCode = component.short_name;
30325 } else if (component.types.indexOf("street_number") >= 0) {
30326 result.streetNumber = component.short_name;
30327 } else if (component.types.indexOf("route") >= 0) {
30328 result.streetName = component.short_name;
30329 } else if (component.types.indexOf("neighborhood") >= 0) {
30330 result.city = component.short_name;
30331 } else if (component.types.indexOf("locality") >= 0) {
30332 result.city = component.short_name;
30333 } else if (component.types.indexOf("sublocality") >= 0) {
30334 result.district = component.short_name;
30335 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30336 result.stateOrProvince = component.short_name;
30337 } else if (component.types.indexOf("country") >= 0) {
30338 result.country = component.short_name;
30342 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30343 result.addressLine2 = "";
30347 setZoomLevel: function(zoom)
30349 this.gMapContext.map.setZoom(zoom);
30362 this.fireEvent('show', this);
30373 this.fireEvent('hide', this);
30378 Roo.apply(Roo.bootstrap.LocationPicker, {
30380 OverlayView : function(map, options)
30382 options = options || {};
30389 * @class Roo.bootstrap.Alert
30390 * @extends Roo.bootstrap.Component
30391 * Bootstrap Alert class - shows an alert area box
30393 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30394 Enter a valid email address
30397 * @cfg {String} title The title of alert
30398 * @cfg {String} html The content of alert
30399 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30400 * @cfg {String} fa font-awesomeicon
30401 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30402 * @cfg {Boolean} close true to show a x closer
30406 * Create a new alert
30407 * @param {Object} config The config object
30411 Roo.bootstrap.Alert = function(config){
30412 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30416 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30422 faicon: false, // BC
30426 getAutoCreate : function()
30438 style : this.close ? '' : 'display:none'
30442 cls : 'roo-alert-icon'
30447 cls : 'roo-alert-title',
30452 cls : 'roo-alert-text',
30459 cfg.cn[0].cls += ' fa ' + this.faicon;
30462 cfg.cn[0].cls += ' fa ' + this.fa;
30466 cfg.cls += ' alert-' + this.weight;
30472 initEvents: function()
30474 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30475 this.titleEl = this.el.select('.roo-alert-title',true).first();
30476 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30477 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30478 if (this.seconds > 0) {
30479 this.hide.defer(this.seconds, this);
30483 * Set the Title Message HTML
30484 * @param {String} html
30486 setTitle : function(str)
30488 this.titleEl.dom.innerHTML = str;
30492 * Set the Body Message HTML
30493 * @param {String} html
30495 setHtml : function(str)
30497 this.htmlEl.dom.innerHTML = str;
30500 * Set the Weight of the alert
30501 * @param {String} (success|info|warning|danger) weight
30504 setWeight : function(weight)
30507 this.el.removeClass('alert-' + this.weight);
30510 this.weight = weight;
30512 this.el.addClass('alert-' + this.weight);
30515 * Set the Icon of the alert
30516 * @param {String} see fontawsome names (name without the 'fa-' bit)
30518 setIcon : function(icon)
30521 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30524 this.faicon = icon;
30526 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30551 * @class Roo.bootstrap.UploadCropbox
30552 * @extends Roo.bootstrap.Component
30553 * Bootstrap UploadCropbox class
30554 * @cfg {String} emptyText show when image has been loaded
30555 * @cfg {String} rotateNotify show when image too small to rotate
30556 * @cfg {Number} errorTimeout default 3000
30557 * @cfg {Number} minWidth default 300
30558 * @cfg {Number} minHeight default 300
30559 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30560 * @cfg {Boolean} isDocument (true|false) default false
30561 * @cfg {String} url action url
30562 * @cfg {String} paramName default 'imageUpload'
30563 * @cfg {String} method default POST
30564 * @cfg {Boolean} loadMask (true|false) default true
30565 * @cfg {Boolean} loadingText default 'Loading...'
30568 * Create a new UploadCropbox
30569 * @param {Object} config The config object
30572 Roo.bootstrap.UploadCropbox = function(config){
30573 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30577 * @event beforeselectfile
30578 * Fire before select file
30579 * @param {Roo.bootstrap.UploadCropbox} this
30581 "beforeselectfile" : true,
30584 * Fire after initEvent
30585 * @param {Roo.bootstrap.UploadCropbox} this
30590 * Fire after initEvent
30591 * @param {Roo.bootstrap.UploadCropbox} this
30592 * @param {String} data
30597 * Fire when preparing the file data
30598 * @param {Roo.bootstrap.UploadCropbox} this
30599 * @param {Object} file
30604 * Fire when get exception
30605 * @param {Roo.bootstrap.UploadCropbox} this
30606 * @param {XMLHttpRequest} xhr
30608 "exception" : true,
30610 * @event beforeloadcanvas
30611 * Fire before load the canvas
30612 * @param {Roo.bootstrap.UploadCropbox} this
30613 * @param {String} src
30615 "beforeloadcanvas" : true,
30618 * Fire when trash image
30619 * @param {Roo.bootstrap.UploadCropbox} this
30624 * Fire when download the image
30625 * @param {Roo.bootstrap.UploadCropbox} this
30629 * @event footerbuttonclick
30630 * Fire when footerbuttonclick
30631 * @param {Roo.bootstrap.UploadCropbox} this
30632 * @param {String} type
30634 "footerbuttonclick" : true,
30638 * @param {Roo.bootstrap.UploadCropbox} this
30643 * Fire when rotate the image
30644 * @param {Roo.bootstrap.UploadCropbox} this
30645 * @param {String} pos
30650 * Fire when inspect the file
30651 * @param {Roo.bootstrap.UploadCropbox} this
30652 * @param {Object} file
30657 * Fire when xhr upload the file
30658 * @param {Roo.bootstrap.UploadCropbox} this
30659 * @param {Object} data
30664 * Fire when arrange the file data
30665 * @param {Roo.bootstrap.UploadCropbox} this
30666 * @param {Object} formData
30671 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30674 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30676 emptyText : 'Click to upload image',
30677 rotateNotify : 'Image is too small to rotate',
30678 errorTimeout : 3000,
30692 cropType : 'image/jpeg',
30694 canvasLoaded : false,
30695 isDocument : false,
30697 paramName : 'imageUpload',
30699 loadingText : 'Loading...',
30702 getAutoCreate : function()
30706 cls : 'roo-upload-cropbox',
30710 cls : 'roo-upload-cropbox-selector',
30715 cls : 'roo-upload-cropbox-body',
30716 style : 'cursor:pointer',
30720 cls : 'roo-upload-cropbox-preview'
30724 cls : 'roo-upload-cropbox-thumb'
30728 cls : 'roo-upload-cropbox-empty-notify',
30729 html : this.emptyText
30733 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30734 html : this.rotateNotify
30740 cls : 'roo-upload-cropbox-footer',
30743 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30753 onRender : function(ct, position)
30755 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30757 if (this.buttons.length) {
30759 Roo.each(this.buttons, function(bb) {
30761 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30763 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30769 this.maskEl = this.el;
30773 initEvents : function()
30775 this.urlAPI = (window.createObjectURL && window) ||
30776 (window.URL && URL.revokeObjectURL && URL) ||
30777 (window.webkitURL && webkitURL);
30779 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30780 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30782 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30783 this.selectorEl.hide();
30785 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30786 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30788 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30789 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30790 this.thumbEl.hide();
30792 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30793 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30795 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30796 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30797 this.errorEl.hide();
30799 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30800 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30801 this.footerEl.hide();
30803 this.setThumbBoxSize();
30809 this.fireEvent('initial', this);
30816 window.addEventListener("resize", function() { _this.resize(); } );
30818 this.bodyEl.on('click', this.beforeSelectFile, this);
30821 this.bodyEl.on('touchstart', this.onTouchStart, this);
30822 this.bodyEl.on('touchmove', this.onTouchMove, this);
30823 this.bodyEl.on('touchend', this.onTouchEnd, this);
30827 this.bodyEl.on('mousedown', this.onMouseDown, this);
30828 this.bodyEl.on('mousemove', this.onMouseMove, this);
30829 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30830 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30831 Roo.get(document).on('mouseup', this.onMouseUp, this);
30834 this.selectorEl.on('change', this.onFileSelected, this);
30840 this.baseScale = 1;
30842 this.baseRotate = 1;
30843 this.dragable = false;
30844 this.pinching = false;
30847 this.cropData = false;
30848 this.notifyEl.dom.innerHTML = this.emptyText;
30850 this.selectorEl.dom.value = '';
30854 resize : function()
30856 if(this.fireEvent('resize', this) != false){
30857 this.setThumbBoxPosition();
30858 this.setCanvasPosition();
30862 onFooterButtonClick : function(e, el, o, type)
30865 case 'rotate-left' :
30866 this.onRotateLeft(e);
30868 case 'rotate-right' :
30869 this.onRotateRight(e);
30872 this.beforeSelectFile(e);
30887 this.fireEvent('footerbuttonclick', this, type);
30890 beforeSelectFile : function(e)
30892 e.preventDefault();
30894 if(this.fireEvent('beforeselectfile', this) != false){
30895 this.selectorEl.dom.click();
30899 onFileSelected : function(e)
30901 e.preventDefault();
30903 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30907 var file = this.selectorEl.dom.files[0];
30909 if(this.fireEvent('inspect', this, file) != false){
30910 this.prepare(file);
30915 trash : function(e)
30917 this.fireEvent('trash', this);
30920 download : function(e)
30922 this.fireEvent('download', this);
30925 loadCanvas : function(src)
30927 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30931 this.imageEl = document.createElement('img');
30935 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30937 this.imageEl.src = src;
30941 onLoadCanvas : function()
30943 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30944 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30946 this.bodyEl.un('click', this.beforeSelectFile, this);
30948 this.notifyEl.hide();
30949 this.thumbEl.show();
30950 this.footerEl.show();
30952 this.baseRotateLevel();
30954 if(this.isDocument){
30955 this.setThumbBoxSize();
30958 this.setThumbBoxPosition();
30960 this.baseScaleLevel();
30966 this.canvasLoaded = true;
30969 this.maskEl.unmask();
30974 setCanvasPosition : function()
30976 if(!this.canvasEl){
30980 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30981 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30983 this.previewEl.setLeft(pw);
30984 this.previewEl.setTop(ph);
30988 onMouseDown : function(e)
30992 this.dragable = true;
30993 this.pinching = false;
30995 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30996 this.dragable = false;
31000 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31001 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31005 onMouseMove : function(e)
31009 if(!this.canvasLoaded){
31013 if (!this.dragable){
31017 var minX = Math.ceil(this.thumbEl.getLeft(true));
31018 var minY = Math.ceil(this.thumbEl.getTop(true));
31020 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31021 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31023 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31024 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31026 x = x - this.mouseX;
31027 y = y - this.mouseY;
31029 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31030 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31032 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31033 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31035 this.previewEl.setLeft(bgX);
31036 this.previewEl.setTop(bgY);
31038 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31039 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31042 onMouseUp : function(e)
31046 this.dragable = false;
31049 onMouseWheel : function(e)
31053 this.startScale = this.scale;
31055 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31057 if(!this.zoomable()){
31058 this.scale = this.startScale;
31067 zoomable : function()
31069 var minScale = this.thumbEl.getWidth() / this.minWidth;
31071 if(this.minWidth < this.minHeight){
31072 minScale = this.thumbEl.getHeight() / this.minHeight;
31075 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31076 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31080 (this.rotate == 0 || this.rotate == 180) &&
31082 width > this.imageEl.OriginWidth ||
31083 height > this.imageEl.OriginHeight ||
31084 (width < this.minWidth && height < this.minHeight)
31092 (this.rotate == 90 || this.rotate == 270) &&
31094 width > this.imageEl.OriginWidth ||
31095 height > this.imageEl.OriginHeight ||
31096 (width < this.minHeight && height < this.minWidth)
31103 !this.isDocument &&
31104 (this.rotate == 0 || this.rotate == 180) &&
31106 width < this.minWidth ||
31107 width > this.imageEl.OriginWidth ||
31108 height < this.minHeight ||
31109 height > this.imageEl.OriginHeight
31116 !this.isDocument &&
31117 (this.rotate == 90 || this.rotate == 270) &&
31119 width < this.minHeight ||
31120 width > this.imageEl.OriginWidth ||
31121 height < this.minWidth ||
31122 height > this.imageEl.OriginHeight
31132 onRotateLeft : function(e)
31134 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31136 var minScale = this.thumbEl.getWidth() / this.minWidth;
31138 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31139 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31141 this.startScale = this.scale;
31143 while (this.getScaleLevel() < minScale){
31145 this.scale = this.scale + 1;
31147 if(!this.zoomable()){
31152 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31153 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31158 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31165 this.scale = this.startScale;
31167 this.onRotateFail();
31172 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31174 if(this.isDocument){
31175 this.setThumbBoxSize();
31176 this.setThumbBoxPosition();
31177 this.setCanvasPosition();
31182 this.fireEvent('rotate', this, 'left');
31186 onRotateRight : function(e)
31188 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31190 var minScale = this.thumbEl.getWidth() / this.minWidth;
31192 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31193 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31195 this.startScale = this.scale;
31197 while (this.getScaleLevel() < minScale){
31199 this.scale = this.scale + 1;
31201 if(!this.zoomable()){
31206 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31207 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31212 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31219 this.scale = this.startScale;
31221 this.onRotateFail();
31226 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31228 if(this.isDocument){
31229 this.setThumbBoxSize();
31230 this.setThumbBoxPosition();
31231 this.setCanvasPosition();
31236 this.fireEvent('rotate', this, 'right');
31239 onRotateFail : function()
31241 this.errorEl.show(true);
31245 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31250 this.previewEl.dom.innerHTML = '';
31252 var canvasEl = document.createElement("canvas");
31254 var contextEl = canvasEl.getContext("2d");
31256 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31257 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31258 var center = this.imageEl.OriginWidth / 2;
31260 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31261 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31262 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31263 center = this.imageEl.OriginHeight / 2;
31266 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31268 contextEl.translate(center, center);
31269 contextEl.rotate(this.rotate * Math.PI / 180);
31271 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31273 this.canvasEl = document.createElement("canvas");
31275 this.contextEl = this.canvasEl.getContext("2d");
31277 switch (this.rotate) {
31280 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31281 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31283 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31288 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31289 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31291 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31292 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);
31296 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31301 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31302 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31304 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31305 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);
31309 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);
31314 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31315 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31317 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31318 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31322 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);
31329 this.previewEl.appendChild(this.canvasEl);
31331 this.setCanvasPosition();
31336 if(!this.canvasLoaded){
31340 var imageCanvas = document.createElement("canvas");
31342 var imageContext = imageCanvas.getContext("2d");
31344 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31345 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31347 var center = imageCanvas.width / 2;
31349 imageContext.translate(center, center);
31351 imageContext.rotate(this.rotate * Math.PI / 180);
31353 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31355 var canvas = document.createElement("canvas");
31357 var context = canvas.getContext("2d");
31359 canvas.width = this.minWidth;
31360 canvas.height = this.minHeight;
31362 switch (this.rotate) {
31365 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31366 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31368 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31369 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31371 var targetWidth = this.minWidth - 2 * x;
31372 var targetHeight = this.minHeight - 2 * y;
31376 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31377 scale = targetWidth / width;
31380 if(x > 0 && y == 0){
31381 scale = targetHeight / height;
31384 if(x > 0 && y > 0){
31385 scale = targetWidth / width;
31387 if(width < height){
31388 scale = targetHeight / height;
31392 context.scale(scale, scale);
31394 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31395 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31397 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31398 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31400 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31405 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31406 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31408 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31409 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31411 var targetWidth = this.minWidth - 2 * x;
31412 var targetHeight = this.minHeight - 2 * y;
31416 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31417 scale = targetWidth / width;
31420 if(x > 0 && y == 0){
31421 scale = targetHeight / height;
31424 if(x > 0 && y > 0){
31425 scale = targetWidth / width;
31427 if(width < height){
31428 scale = targetHeight / height;
31432 context.scale(scale, scale);
31434 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31435 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31437 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31438 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31440 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31442 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31447 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31448 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31450 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31451 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31453 var targetWidth = this.minWidth - 2 * x;
31454 var targetHeight = this.minHeight - 2 * y;
31458 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31459 scale = targetWidth / width;
31462 if(x > 0 && y == 0){
31463 scale = targetHeight / height;
31466 if(x > 0 && y > 0){
31467 scale = targetWidth / width;
31469 if(width < height){
31470 scale = targetHeight / height;
31474 context.scale(scale, scale);
31476 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31477 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31479 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31480 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31482 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31483 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31485 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31490 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31491 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31493 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31494 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31496 var targetWidth = this.minWidth - 2 * x;
31497 var targetHeight = this.minHeight - 2 * y;
31501 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31502 scale = targetWidth / width;
31505 if(x > 0 && y == 0){
31506 scale = targetHeight / height;
31509 if(x > 0 && y > 0){
31510 scale = targetWidth / width;
31512 if(width < height){
31513 scale = targetHeight / height;
31517 context.scale(scale, scale);
31519 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31520 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31522 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31523 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31525 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31527 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31534 this.cropData = canvas.toDataURL(this.cropType);
31536 if(this.fireEvent('crop', this, this.cropData) !== false){
31537 this.process(this.file, this.cropData);
31544 setThumbBoxSize : function()
31548 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31549 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31550 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31552 this.minWidth = width;
31553 this.minHeight = height;
31555 if(this.rotate == 90 || this.rotate == 270){
31556 this.minWidth = height;
31557 this.minHeight = width;
31562 width = Math.ceil(this.minWidth * height / this.minHeight);
31564 if(this.minWidth > this.minHeight){
31566 height = Math.ceil(this.minHeight * width / this.minWidth);
31569 this.thumbEl.setStyle({
31570 width : width + 'px',
31571 height : height + 'px'
31578 setThumbBoxPosition : function()
31580 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31581 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31583 this.thumbEl.setLeft(x);
31584 this.thumbEl.setTop(y);
31588 baseRotateLevel : function()
31590 this.baseRotate = 1;
31593 typeof(this.exif) != 'undefined' &&
31594 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31595 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31597 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31600 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31604 baseScaleLevel : function()
31608 if(this.isDocument){
31610 if(this.baseRotate == 6 || this.baseRotate == 8){
31612 height = this.thumbEl.getHeight();
31613 this.baseScale = height / this.imageEl.OriginWidth;
31615 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31616 width = this.thumbEl.getWidth();
31617 this.baseScale = width / this.imageEl.OriginHeight;
31623 height = this.thumbEl.getHeight();
31624 this.baseScale = height / this.imageEl.OriginHeight;
31626 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31627 width = this.thumbEl.getWidth();
31628 this.baseScale = width / this.imageEl.OriginWidth;
31634 if(this.baseRotate == 6 || this.baseRotate == 8){
31636 width = this.thumbEl.getHeight();
31637 this.baseScale = width / this.imageEl.OriginHeight;
31639 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31640 height = this.thumbEl.getWidth();
31641 this.baseScale = height / this.imageEl.OriginHeight;
31644 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31645 height = this.thumbEl.getWidth();
31646 this.baseScale = height / this.imageEl.OriginHeight;
31648 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31649 width = this.thumbEl.getHeight();
31650 this.baseScale = width / this.imageEl.OriginWidth;
31657 width = this.thumbEl.getWidth();
31658 this.baseScale = width / this.imageEl.OriginWidth;
31660 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31661 height = this.thumbEl.getHeight();
31662 this.baseScale = height / this.imageEl.OriginHeight;
31665 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31667 height = this.thumbEl.getHeight();
31668 this.baseScale = height / this.imageEl.OriginHeight;
31670 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31671 width = this.thumbEl.getWidth();
31672 this.baseScale = width / this.imageEl.OriginWidth;
31680 getScaleLevel : function()
31682 return this.baseScale * Math.pow(1.1, this.scale);
31685 onTouchStart : function(e)
31687 if(!this.canvasLoaded){
31688 this.beforeSelectFile(e);
31692 var touches = e.browserEvent.touches;
31698 if(touches.length == 1){
31699 this.onMouseDown(e);
31703 if(touches.length != 2){
31709 for(var i = 0, finger; finger = touches[i]; i++){
31710 coords.push(finger.pageX, finger.pageY);
31713 var x = Math.pow(coords[0] - coords[2], 2);
31714 var y = Math.pow(coords[1] - coords[3], 2);
31716 this.startDistance = Math.sqrt(x + y);
31718 this.startScale = this.scale;
31720 this.pinching = true;
31721 this.dragable = false;
31725 onTouchMove : function(e)
31727 if(!this.pinching && !this.dragable){
31731 var touches = e.browserEvent.touches;
31738 this.onMouseMove(e);
31744 for(var i = 0, finger; finger = touches[i]; i++){
31745 coords.push(finger.pageX, finger.pageY);
31748 var x = Math.pow(coords[0] - coords[2], 2);
31749 var y = Math.pow(coords[1] - coords[3], 2);
31751 this.endDistance = Math.sqrt(x + y);
31753 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31755 if(!this.zoomable()){
31756 this.scale = this.startScale;
31764 onTouchEnd : function(e)
31766 this.pinching = false;
31767 this.dragable = false;
31771 process : function(file, crop)
31774 this.maskEl.mask(this.loadingText);
31777 this.xhr = new XMLHttpRequest();
31779 file.xhr = this.xhr;
31781 this.xhr.open(this.method, this.url, true);
31784 "Accept": "application/json",
31785 "Cache-Control": "no-cache",
31786 "X-Requested-With": "XMLHttpRequest"
31789 for (var headerName in headers) {
31790 var headerValue = headers[headerName];
31792 this.xhr.setRequestHeader(headerName, headerValue);
31798 this.xhr.onload = function()
31800 _this.xhrOnLoad(_this.xhr);
31803 this.xhr.onerror = function()
31805 _this.xhrOnError(_this.xhr);
31808 var formData = new FormData();
31810 formData.append('returnHTML', 'NO');
31813 formData.append('crop', crop);
31816 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31817 formData.append(this.paramName, file, file.name);
31820 if(typeof(file.filename) != 'undefined'){
31821 formData.append('filename', file.filename);
31824 if(typeof(file.mimetype) != 'undefined'){
31825 formData.append('mimetype', file.mimetype);
31828 if(this.fireEvent('arrange', this, formData) != false){
31829 this.xhr.send(formData);
31833 xhrOnLoad : function(xhr)
31836 this.maskEl.unmask();
31839 if (xhr.readyState !== 4) {
31840 this.fireEvent('exception', this, xhr);
31844 var response = Roo.decode(xhr.responseText);
31846 if(!response.success){
31847 this.fireEvent('exception', this, xhr);
31851 var response = Roo.decode(xhr.responseText);
31853 this.fireEvent('upload', this, response);
31857 xhrOnError : function()
31860 this.maskEl.unmask();
31863 Roo.log('xhr on error');
31865 var response = Roo.decode(xhr.responseText);
31871 prepare : function(file)
31874 this.maskEl.mask(this.loadingText);
31880 if(typeof(file) === 'string'){
31881 this.loadCanvas(file);
31885 if(!file || !this.urlAPI){
31890 this.cropType = file.type;
31894 if(this.fireEvent('prepare', this, this.file) != false){
31896 var reader = new FileReader();
31898 reader.onload = function (e) {
31899 if (e.target.error) {
31900 Roo.log(e.target.error);
31904 var buffer = e.target.result,
31905 dataView = new DataView(buffer),
31907 maxOffset = dataView.byteLength - 4,
31911 if (dataView.getUint16(0) === 0xffd8) {
31912 while (offset < maxOffset) {
31913 markerBytes = dataView.getUint16(offset);
31915 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31916 markerLength = dataView.getUint16(offset + 2) + 2;
31917 if (offset + markerLength > dataView.byteLength) {
31918 Roo.log('Invalid meta data: Invalid segment size.');
31922 if(markerBytes == 0xffe1){
31923 _this.parseExifData(
31930 offset += markerLength;
31940 var url = _this.urlAPI.createObjectURL(_this.file);
31942 _this.loadCanvas(url);
31947 reader.readAsArrayBuffer(this.file);
31953 parseExifData : function(dataView, offset, length)
31955 var tiffOffset = offset + 10,
31959 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31960 // No Exif data, might be XMP data instead
31964 // Check for the ASCII code for "Exif" (0x45786966):
31965 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31966 // No Exif data, might be XMP data instead
31969 if (tiffOffset + 8 > dataView.byteLength) {
31970 Roo.log('Invalid Exif data: Invalid segment size.');
31973 // Check for the two null bytes:
31974 if (dataView.getUint16(offset + 8) !== 0x0000) {
31975 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31978 // Check the byte alignment:
31979 switch (dataView.getUint16(tiffOffset)) {
31981 littleEndian = true;
31984 littleEndian = false;
31987 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31990 // Check for the TIFF tag marker (0x002A):
31991 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31992 Roo.log('Invalid Exif data: Missing TIFF marker.');
31995 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31996 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31998 this.parseExifTags(
32001 tiffOffset + dirOffset,
32006 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32011 if (dirOffset + 6 > dataView.byteLength) {
32012 Roo.log('Invalid Exif data: Invalid directory offset.');
32015 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32016 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32017 if (dirEndOffset + 4 > dataView.byteLength) {
32018 Roo.log('Invalid Exif data: Invalid directory size.');
32021 for (i = 0; i < tagsNumber; i += 1) {
32025 dirOffset + 2 + 12 * i, // tag offset
32029 // Return the offset to the next directory:
32030 return dataView.getUint32(dirEndOffset, littleEndian);
32033 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32035 var tag = dataView.getUint16(offset, littleEndian);
32037 this.exif[tag] = this.getExifValue(
32041 dataView.getUint16(offset + 2, littleEndian), // tag type
32042 dataView.getUint32(offset + 4, littleEndian), // tag length
32047 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32049 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32058 Roo.log('Invalid Exif data: Invalid tag type.');
32062 tagSize = tagType.size * length;
32063 // Determine if the value is contained in the dataOffset bytes,
32064 // or if the value at the dataOffset is a pointer to the actual data:
32065 dataOffset = tagSize > 4 ?
32066 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32067 if (dataOffset + tagSize > dataView.byteLength) {
32068 Roo.log('Invalid Exif data: Invalid data offset.');
32071 if (length === 1) {
32072 return tagType.getValue(dataView, dataOffset, littleEndian);
32075 for (i = 0; i < length; i += 1) {
32076 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32079 if (tagType.ascii) {
32081 // Concatenate the chars:
32082 for (i = 0; i < values.length; i += 1) {
32084 // Ignore the terminating NULL byte(s):
32085 if (c === '\u0000') {
32097 Roo.apply(Roo.bootstrap.UploadCropbox, {
32099 'Orientation': 0x0112
32103 1: 0, //'top-left',
32105 3: 180, //'bottom-right',
32106 // 4: 'bottom-left',
32108 6: 90, //'right-top',
32109 // 7: 'right-bottom',
32110 8: 270 //'left-bottom'
32114 // byte, 8-bit unsigned int:
32116 getValue: function (dataView, dataOffset) {
32117 return dataView.getUint8(dataOffset);
32121 // ascii, 8-bit byte:
32123 getValue: function (dataView, dataOffset) {
32124 return String.fromCharCode(dataView.getUint8(dataOffset));
32129 // short, 16 bit int:
32131 getValue: function (dataView, dataOffset, littleEndian) {
32132 return dataView.getUint16(dataOffset, littleEndian);
32136 // long, 32 bit int:
32138 getValue: function (dataView, dataOffset, littleEndian) {
32139 return dataView.getUint32(dataOffset, littleEndian);
32143 // rational = two long values, first is numerator, second is denominator:
32145 getValue: function (dataView, dataOffset, littleEndian) {
32146 return dataView.getUint32(dataOffset, littleEndian) /
32147 dataView.getUint32(dataOffset + 4, littleEndian);
32151 // slong, 32 bit signed int:
32153 getValue: function (dataView, dataOffset, littleEndian) {
32154 return dataView.getInt32(dataOffset, littleEndian);
32158 // srational, two slongs, first is numerator, second is denominator:
32160 getValue: function (dataView, dataOffset, littleEndian) {
32161 return dataView.getInt32(dataOffset, littleEndian) /
32162 dataView.getInt32(dataOffset + 4, littleEndian);
32172 cls : 'btn-group roo-upload-cropbox-rotate-left',
32173 action : 'rotate-left',
32177 cls : 'btn btn-default',
32178 html : '<i class="fa fa-undo"></i>'
32184 cls : 'btn-group roo-upload-cropbox-picture',
32185 action : 'picture',
32189 cls : 'btn btn-default',
32190 html : '<i class="fa fa-picture-o"></i>'
32196 cls : 'btn-group roo-upload-cropbox-rotate-right',
32197 action : 'rotate-right',
32201 cls : 'btn btn-default',
32202 html : '<i class="fa fa-repeat"></i>'
32210 cls : 'btn-group roo-upload-cropbox-rotate-left',
32211 action : 'rotate-left',
32215 cls : 'btn btn-default',
32216 html : '<i class="fa fa-undo"></i>'
32222 cls : 'btn-group roo-upload-cropbox-download',
32223 action : 'download',
32227 cls : 'btn btn-default',
32228 html : '<i class="fa fa-download"></i>'
32234 cls : 'btn-group roo-upload-cropbox-crop',
32239 cls : 'btn btn-default',
32240 html : '<i class="fa fa-crop"></i>'
32246 cls : 'btn-group roo-upload-cropbox-trash',
32251 cls : 'btn btn-default',
32252 html : '<i class="fa fa-trash"></i>'
32258 cls : 'btn-group roo-upload-cropbox-rotate-right',
32259 action : 'rotate-right',
32263 cls : 'btn btn-default',
32264 html : '<i class="fa fa-repeat"></i>'
32272 cls : 'btn-group roo-upload-cropbox-rotate-left',
32273 action : 'rotate-left',
32277 cls : 'btn btn-default',
32278 html : '<i class="fa fa-undo"></i>'
32284 cls : 'btn-group roo-upload-cropbox-rotate-right',
32285 action : 'rotate-right',
32289 cls : 'btn btn-default',
32290 html : '<i class="fa fa-repeat"></i>'
32303 * @class Roo.bootstrap.DocumentManager
32304 * @extends Roo.bootstrap.Component
32305 * Bootstrap DocumentManager class
32306 * @cfg {String} paramName default 'imageUpload'
32307 * @cfg {String} toolTipName default 'filename'
32308 * @cfg {String} method default POST
32309 * @cfg {String} url action url
32310 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32311 * @cfg {Boolean} multiple multiple upload default true
32312 * @cfg {Number} thumbSize default 300
32313 * @cfg {String} fieldLabel
32314 * @cfg {Number} labelWidth default 4
32315 * @cfg {String} labelAlign (left|top) default left
32316 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32317 * @cfg {Number} labellg set the width of label (1-12)
32318 * @cfg {Number} labelmd set the width of label (1-12)
32319 * @cfg {Number} labelsm set the width of label (1-12)
32320 * @cfg {Number} labelxs set the width of label (1-12)
32323 * Create a new DocumentManager
32324 * @param {Object} config The config object
32327 Roo.bootstrap.DocumentManager = function(config){
32328 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32331 this.delegates = [];
32336 * Fire when initial the DocumentManager
32337 * @param {Roo.bootstrap.DocumentManager} this
32342 * inspect selected file
32343 * @param {Roo.bootstrap.DocumentManager} this
32344 * @param {File} file
32349 * Fire when xhr load exception
32350 * @param {Roo.bootstrap.DocumentManager} this
32351 * @param {XMLHttpRequest} xhr
32353 "exception" : true,
32355 * @event afterupload
32356 * Fire when xhr load exception
32357 * @param {Roo.bootstrap.DocumentManager} this
32358 * @param {XMLHttpRequest} xhr
32360 "afterupload" : true,
32363 * prepare the form data
32364 * @param {Roo.bootstrap.DocumentManager} this
32365 * @param {Object} formData
32370 * Fire when remove the file
32371 * @param {Roo.bootstrap.DocumentManager} this
32372 * @param {Object} file
32377 * Fire after refresh the file
32378 * @param {Roo.bootstrap.DocumentManager} this
32383 * Fire after click the image
32384 * @param {Roo.bootstrap.DocumentManager} this
32385 * @param {Object} file
32390 * Fire when upload a image and editable set to true
32391 * @param {Roo.bootstrap.DocumentManager} this
32392 * @param {Object} file
32396 * @event beforeselectfile
32397 * Fire before select file
32398 * @param {Roo.bootstrap.DocumentManager} this
32400 "beforeselectfile" : true,
32403 * Fire before process file
32404 * @param {Roo.bootstrap.DocumentManager} this
32405 * @param {Object} file
32409 * @event previewrendered
32410 * Fire when preview rendered
32411 * @param {Roo.bootstrap.DocumentManager} this
32412 * @param {Object} file
32414 "previewrendered" : true,
32417 "previewResize" : true
32422 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32431 paramName : 'imageUpload',
32432 toolTipName : 'filename',
32435 labelAlign : 'left',
32445 getAutoCreate : function()
32447 var managerWidget = {
32449 cls : 'roo-document-manager',
32453 cls : 'roo-document-manager-selector',
32458 cls : 'roo-document-manager-uploader',
32462 cls : 'roo-document-manager-upload-btn',
32463 html : '<i class="fa fa-plus"></i>'
32474 cls : 'column col-md-12',
32479 if(this.fieldLabel.length){
32484 cls : 'column col-md-12',
32485 html : this.fieldLabel
32489 cls : 'column col-md-12',
32494 if(this.labelAlign == 'left'){
32499 html : this.fieldLabel
32508 if(this.labelWidth > 12){
32509 content[0].style = "width: " + this.labelWidth + 'px';
32512 if(this.labelWidth < 13 && this.labelmd == 0){
32513 this.labelmd = this.labelWidth;
32516 if(this.labellg > 0){
32517 content[0].cls += ' col-lg-' + this.labellg;
32518 content[1].cls += ' col-lg-' + (12 - this.labellg);
32521 if(this.labelmd > 0){
32522 content[0].cls += ' col-md-' + this.labelmd;
32523 content[1].cls += ' col-md-' + (12 - this.labelmd);
32526 if(this.labelsm > 0){
32527 content[0].cls += ' col-sm-' + this.labelsm;
32528 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32531 if(this.labelxs > 0){
32532 content[0].cls += ' col-xs-' + this.labelxs;
32533 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32541 cls : 'row clearfix',
32549 initEvents : function()
32551 this.managerEl = this.el.select('.roo-document-manager', true).first();
32552 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32554 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32555 this.selectorEl.hide();
32558 this.selectorEl.attr('multiple', 'multiple');
32561 this.selectorEl.on('change', this.onFileSelected, this);
32563 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32564 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32566 this.uploader.on('click', this.onUploaderClick, this);
32568 this.renderProgressDialog();
32572 window.addEventListener("resize", function() { _this.refresh(); } );
32574 this.fireEvent('initial', this);
32577 renderProgressDialog : function()
32581 this.progressDialog = new Roo.bootstrap.Modal({
32582 cls : 'roo-document-manager-progress-dialog',
32583 allow_close : false,
32594 btnclick : function() {
32595 _this.uploadCancel();
32601 this.progressDialog.render(Roo.get(document.body));
32603 this.progress = new Roo.bootstrap.Progress({
32604 cls : 'roo-document-manager-progress',
32609 this.progress.render(this.progressDialog.getChildContainer());
32611 this.progressBar = new Roo.bootstrap.ProgressBar({
32612 cls : 'roo-document-manager-progress-bar',
32615 aria_valuemax : 12,
32619 this.progressBar.render(this.progress.getChildContainer());
32622 onUploaderClick : function(e)
32624 e.preventDefault();
32626 if(this.fireEvent('beforeselectfile', this) != false){
32627 this.selectorEl.dom.click();
32632 onFileSelected : function(e)
32634 e.preventDefault();
32636 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32640 Roo.each(this.selectorEl.dom.files, function(file){
32641 if(this.fireEvent('inspect', this, file) != false){
32642 this.files.push(file);
32652 this.selectorEl.dom.value = '';
32654 if(!this.files || !this.files.length){
32658 if(this.boxes > 0 && this.files.length > this.boxes){
32659 this.files = this.files.slice(0, this.boxes);
32662 this.uploader.show();
32664 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32665 this.uploader.hide();
32674 Roo.each(this.files, function(file){
32676 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32677 var f = this.renderPreview(file);
32682 if(file.type.indexOf('image') != -1){
32683 this.delegates.push(
32685 _this.process(file);
32686 }).createDelegate(this)
32694 _this.process(file);
32695 }).createDelegate(this)
32700 this.files = files;
32702 this.delegates = this.delegates.concat(docs);
32704 if(!this.delegates.length){
32709 this.progressBar.aria_valuemax = this.delegates.length;
32716 arrange : function()
32718 if(!this.delegates.length){
32719 this.progressDialog.hide();
32724 var delegate = this.delegates.shift();
32726 this.progressDialog.show();
32728 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32730 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32735 refresh : function()
32737 this.uploader.show();
32739 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32740 this.uploader.hide();
32743 Roo.isTouch ? this.closable(false) : this.closable(true);
32745 this.fireEvent('refresh', this);
32748 onRemove : function(e, el, o)
32750 e.preventDefault();
32752 this.fireEvent('remove', this, o);
32756 remove : function(o)
32760 Roo.each(this.files, function(file){
32761 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32770 this.files = files;
32777 Roo.each(this.files, function(file){
32782 file.target.remove();
32791 onClick : function(e, el, o)
32793 e.preventDefault();
32795 this.fireEvent('click', this, o);
32799 closable : function(closable)
32801 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32803 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32815 xhrOnLoad : function(xhr)
32817 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32821 if (xhr.readyState !== 4) {
32823 this.fireEvent('exception', this, xhr);
32827 var response = Roo.decode(xhr.responseText);
32829 if(!response.success){
32831 this.fireEvent('exception', this, xhr);
32835 var file = this.renderPreview(response.data);
32837 this.files.push(file);
32841 this.fireEvent('afterupload', this, xhr);
32845 xhrOnError : function(xhr)
32847 Roo.log('xhr on error');
32849 var response = Roo.decode(xhr.responseText);
32856 process : function(file)
32858 if(this.fireEvent('process', this, file) !== false){
32859 if(this.editable && file.type.indexOf('image') != -1){
32860 this.fireEvent('edit', this, file);
32864 this.uploadStart(file, false);
32871 uploadStart : function(file, crop)
32873 this.xhr = new XMLHttpRequest();
32875 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32880 file.xhr = this.xhr;
32882 this.managerEl.createChild({
32884 cls : 'roo-document-manager-loading',
32888 tooltip : file.name,
32889 cls : 'roo-document-manager-thumb',
32890 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32896 this.xhr.open(this.method, this.url, true);
32899 "Accept": "application/json",
32900 "Cache-Control": "no-cache",
32901 "X-Requested-With": "XMLHttpRequest"
32904 for (var headerName in headers) {
32905 var headerValue = headers[headerName];
32907 this.xhr.setRequestHeader(headerName, headerValue);
32913 this.xhr.onload = function()
32915 _this.xhrOnLoad(_this.xhr);
32918 this.xhr.onerror = function()
32920 _this.xhrOnError(_this.xhr);
32923 var formData = new FormData();
32925 formData.append('returnHTML', 'NO');
32928 formData.append('crop', crop);
32931 formData.append(this.paramName, file, file.name);
32938 if(this.fireEvent('prepare', this, formData, options) != false){
32940 if(options.manually){
32944 this.xhr.send(formData);
32948 this.uploadCancel();
32951 uploadCancel : function()
32957 this.delegates = [];
32959 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32966 renderPreview : function(file)
32968 if(typeof(file.target) != 'undefined' && file.target){
32972 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32974 var previewEl = this.managerEl.createChild({
32976 cls : 'roo-document-manager-preview',
32980 tooltip : file[this.toolTipName],
32981 cls : 'roo-document-manager-thumb',
32982 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32987 html : '<i class="fa fa-times-circle"></i>'
32992 var close = previewEl.select('button.close', true).first();
32994 close.on('click', this.onRemove, this, file);
32996 file.target = previewEl;
32998 var image = previewEl.select('img', true).first();
33002 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33004 image.on('click', this.onClick, this, file);
33006 this.fireEvent('previewrendered', this, file);
33012 onPreviewLoad : function(file, image)
33014 if(typeof(file.target) == 'undefined' || !file.target){
33018 var width = image.dom.naturalWidth || image.dom.width;
33019 var height = image.dom.naturalHeight || image.dom.height;
33021 if(!this.previewResize) {
33025 if(width > height){
33026 file.target.addClass('wide');
33030 file.target.addClass('tall');
33035 uploadFromSource : function(file, crop)
33037 this.xhr = new XMLHttpRequest();
33039 this.managerEl.createChild({
33041 cls : 'roo-document-manager-loading',
33045 tooltip : file.name,
33046 cls : 'roo-document-manager-thumb',
33047 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33053 this.xhr.open(this.method, this.url, true);
33056 "Accept": "application/json",
33057 "Cache-Control": "no-cache",
33058 "X-Requested-With": "XMLHttpRequest"
33061 for (var headerName in headers) {
33062 var headerValue = headers[headerName];
33064 this.xhr.setRequestHeader(headerName, headerValue);
33070 this.xhr.onload = function()
33072 _this.xhrOnLoad(_this.xhr);
33075 this.xhr.onerror = function()
33077 _this.xhrOnError(_this.xhr);
33080 var formData = new FormData();
33082 formData.append('returnHTML', 'NO');
33084 formData.append('crop', crop);
33086 if(typeof(file.filename) != 'undefined'){
33087 formData.append('filename', file.filename);
33090 if(typeof(file.mimetype) != 'undefined'){
33091 formData.append('mimetype', file.mimetype);
33096 if(this.fireEvent('prepare', this, formData) != false){
33097 this.xhr.send(formData);
33107 * @class Roo.bootstrap.DocumentViewer
33108 * @extends Roo.bootstrap.Component
33109 * Bootstrap DocumentViewer class
33110 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33111 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33114 * Create a new DocumentViewer
33115 * @param {Object} config The config object
33118 Roo.bootstrap.DocumentViewer = function(config){
33119 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33124 * Fire after initEvent
33125 * @param {Roo.bootstrap.DocumentViewer} this
33131 * @param {Roo.bootstrap.DocumentViewer} this
33136 * Fire after download button
33137 * @param {Roo.bootstrap.DocumentViewer} this
33142 * Fire after trash button
33143 * @param {Roo.bootstrap.DocumentViewer} this
33150 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33152 showDownload : true,
33156 getAutoCreate : function()
33160 cls : 'roo-document-viewer',
33164 cls : 'roo-document-viewer-body',
33168 cls : 'roo-document-viewer-thumb',
33172 cls : 'roo-document-viewer-image'
33180 cls : 'roo-document-viewer-footer',
33183 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33187 cls : 'btn-group roo-document-viewer-download',
33191 cls : 'btn btn-default',
33192 html : '<i class="fa fa-download"></i>'
33198 cls : 'btn-group roo-document-viewer-trash',
33202 cls : 'btn btn-default',
33203 html : '<i class="fa fa-trash"></i>'
33216 initEvents : function()
33218 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33219 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33221 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33222 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33224 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33225 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33227 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33228 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33230 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33231 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33233 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33234 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33236 this.bodyEl.on('click', this.onClick, this);
33237 this.downloadBtn.on('click', this.onDownload, this);
33238 this.trashBtn.on('click', this.onTrash, this);
33240 this.downloadBtn.hide();
33241 this.trashBtn.hide();
33243 if(this.showDownload){
33244 this.downloadBtn.show();
33247 if(this.showTrash){
33248 this.trashBtn.show();
33251 if(!this.showDownload && !this.showTrash) {
33252 this.footerEl.hide();
33257 initial : function()
33259 this.fireEvent('initial', this);
33263 onClick : function(e)
33265 e.preventDefault();
33267 this.fireEvent('click', this);
33270 onDownload : function(e)
33272 e.preventDefault();
33274 this.fireEvent('download', this);
33277 onTrash : function(e)
33279 e.preventDefault();
33281 this.fireEvent('trash', this);
33293 * @class Roo.bootstrap.NavProgressBar
33294 * @extends Roo.bootstrap.Component
33295 * Bootstrap NavProgressBar class
33298 * Create a new nav progress bar
33299 * @param {Object} config The config object
33302 Roo.bootstrap.NavProgressBar = function(config){
33303 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33305 this.bullets = this.bullets || [];
33307 // Roo.bootstrap.NavProgressBar.register(this);
33311 * Fires when the active item changes
33312 * @param {Roo.bootstrap.NavProgressBar} this
33313 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33314 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
33321 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
33326 getAutoCreate : function()
33328 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33332 cls : 'roo-navigation-bar-group',
33336 cls : 'roo-navigation-top-bar'
33340 cls : 'roo-navigation-bullets-bar',
33344 cls : 'roo-navigation-bar'
33351 cls : 'roo-navigation-bottom-bar'
33361 initEvents: function()
33366 onRender : function(ct, position)
33368 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33370 if(this.bullets.length){
33371 Roo.each(this.bullets, function(b){
33380 addItem : function(cfg)
33382 var item = new Roo.bootstrap.NavProgressItem(cfg);
33384 item.parentId = this.id;
33385 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33388 var top = new Roo.bootstrap.Element({
33390 cls : 'roo-navigation-bar-text'
33393 var bottom = new Roo.bootstrap.Element({
33395 cls : 'roo-navigation-bar-text'
33398 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33399 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33401 var topText = new Roo.bootstrap.Element({
33403 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33406 var bottomText = new Roo.bootstrap.Element({
33408 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33411 topText.onRender(top.el, null);
33412 bottomText.onRender(bottom.el, null);
33415 item.bottomEl = bottom;
33418 this.barItems.push(item);
33423 getActive : function()
33425 var active = false;
33427 Roo.each(this.barItems, function(v){
33429 if (!v.isActive()) {
33441 setActiveItem : function(item)
33445 Roo.each(this.barItems, function(v){
33446 if (v.rid == item.rid) {
33450 if (v.isActive()) {
33451 v.setActive(false);
33456 item.setActive(true);
33458 this.fireEvent('changed', this, item, prev);
33461 getBarItem: function(rid)
33465 Roo.each(this.barItems, function(e) {
33466 if (e.rid != rid) {
33477 indexOfItem : function(item)
33481 Roo.each(this.barItems, function(v, i){
33483 if (v.rid != item.rid) {
33494 setActiveNext : function()
33496 var i = this.indexOfItem(this.getActive());
33498 if (i > this.barItems.length) {
33502 this.setActiveItem(this.barItems[i+1]);
33505 setActivePrev : function()
33507 var i = this.indexOfItem(this.getActive());
33513 this.setActiveItem(this.barItems[i-1]);
33516 format : function()
33518 if(!this.barItems.length){
33522 var width = 100 / this.barItems.length;
33524 Roo.each(this.barItems, function(i){
33525 i.el.setStyle('width', width + '%');
33526 i.topEl.el.setStyle('width', width + '%');
33527 i.bottomEl.el.setStyle('width', width + '%');
33536 * Nav Progress Item
33541 * @class Roo.bootstrap.NavProgressItem
33542 * @extends Roo.bootstrap.Component
33543 * Bootstrap NavProgressItem class
33544 * @cfg {String} rid the reference id
33545 * @cfg {Boolean} active (true|false) Is item active default false
33546 * @cfg {Boolean} disabled (true|false) Is item active default false
33547 * @cfg {String} html
33548 * @cfg {String} position (top|bottom) text position default bottom
33549 * @cfg {String} icon show icon instead of number
33552 * Create a new NavProgressItem
33553 * @param {Object} config The config object
33555 Roo.bootstrap.NavProgressItem = function(config){
33556 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33561 * The raw click event for the entire grid.
33562 * @param {Roo.bootstrap.NavProgressItem} this
33563 * @param {Roo.EventObject} e
33570 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33576 position : 'bottom',
33579 getAutoCreate : function()
33581 var iconCls = 'roo-navigation-bar-item-icon';
33583 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33587 cls: 'roo-navigation-bar-item',
33597 cfg.cls += ' active';
33600 cfg.cls += ' disabled';
33606 disable : function()
33608 this.setDisabled(true);
33611 enable : function()
33613 this.setDisabled(false);
33616 initEvents: function()
33618 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33620 this.iconEl.on('click', this.onClick, this);
33623 onClick : function(e)
33625 e.preventDefault();
33631 if(this.fireEvent('click', this, e) === false){
33635 this.parent().setActiveItem(this);
33638 isActive: function ()
33640 return this.active;
33643 setActive : function(state)
33645 if(this.active == state){
33649 this.active = state;
33652 this.el.addClass('active');
33656 this.el.removeClass('active');
33661 setDisabled : function(state)
33663 if(this.disabled == state){
33667 this.disabled = state;
33670 this.el.addClass('disabled');
33674 this.el.removeClass('disabled');
33677 tooltipEl : function()
33679 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33692 * @class Roo.bootstrap.FieldLabel
33693 * @extends Roo.bootstrap.Component
33694 * Bootstrap FieldLabel class
33695 * @cfg {String} html contents of the element
33696 * @cfg {String} tag tag of the element default label
33697 * @cfg {String} cls class of the element
33698 * @cfg {String} target label target
33699 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33700 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33701 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33702 * @cfg {String} iconTooltip default "This field is required"
33703 * @cfg {String} indicatorpos (left|right) default left
33706 * Create a new FieldLabel
33707 * @param {Object} config The config object
33710 Roo.bootstrap.FieldLabel = function(config){
33711 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33716 * Fires after the field has been marked as invalid.
33717 * @param {Roo.form.FieldLabel} this
33718 * @param {String} msg The validation message
33723 * Fires after the field has been validated with no errors.
33724 * @param {Roo.form.FieldLabel} this
33730 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33737 invalidClass : 'has-warning',
33738 validClass : 'has-success',
33739 iconTooltip : 'This field is required',
33740 indicatorpos : 'left',
33742 getAutoCreate : function(){
33745 if (!this.allowBlank) {
33751 cls : 'roo-bootstrap-field-label ' + this.cls,
33756 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33757 tooltip : this.iconTooltip
33766 if(this.indicatorpos == 'right'){
33769 cls : 'roo-bootstrap-field-label ' + this.cls,
33778 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33779 tooltip : this.iconTooltip
33788 initEvents: function()
33790 Roo.bootstrap.Element.superclass.initEvents.call(this);
33792 this.indicator = this.indicatorEl();
33794 if(this.indicator){
33795 this.indicator.removeClass('visible');
33796 this.indicator.addClass('invisible');
33799 Roo.bootstrap.FieldLabel.register(this);
33802 indicatorEl : function()
33804 var indicator = this.el.select('i.roo-required-indicator',true).first();
33815 * Mark this field as valid
33817 markValid : function()
33819 if(this.indicator){
33820 this.indicator.removeClass('visible');
33821 this.indicator.addClass('invisible');
33823 if (Roo.bootstrap.version == 3) {
33824 this.el.removeClass(this.invalidClass);
33825 this.el.addClass(this.validClass);
33827 this.el.removeClass('is-invalid');
33828 this.el.addClass('is-valid');
33832 this.fireEvent('valid', this);
33836 * Mark this field as invalid
33837 * @param {String} msg The validation message
33839 markInvalid : function(msg)
33841 if(this.indicator){
33842 this.indicator.removeClass('invisible');
33843 this.indicator.addClass('visible');
33845 if (Roo.bootstrap.version == 3) {
33846 this.el.removeClass(this.validClass);
33847 this.el.addClass(this.invalidClass);
33849 this.el.removeClass('is-valid');
33850 this.el.addClass('is-invalid');
33854 this.fireEvent('invalid', this, msg);
33860 Roo.apply(Roo.bootstrap.FieldLabel, {
33865 * register a FieldLabel Group
33866 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33868 register : function(label)
33870 if(this.groups.hasOwnProperty(label.target)){
33874 this.groups[label.target] = label;
33878 * fetch a FieldLabel Group based on the target
33879 * @param {string} target
33880 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33882 get: function(target) {
33883 if (typeof(this.groups[target]) == 'undefined') {
33887 return this.groups[target] ;
33896 * page DateSplitField.
33902 * @class Roo.bootstrap.DateSplitField
33903 * @extends Roo.bootstrap.Component
33904 * Bootstrap DateSplitField class
33905 * @cfg {string} fieldLabel - the label associated
33906 * @cfg {Number} labelWidth set the width of label (0-12)
33907 * @cfg {String} labelAlign (top|left)
33908 * @cfg {Boolean} dayAllowBlank (true|false) default false
33909 * @cfg {Boolean} monthAllowBlank (true|false) default false
33910 * @cfg {Boolean} yearAllowBlank (true|false) default false
33911 * @cfg {string} dayPlaceholder
33912 * @cfg {string} monthPlaceholder
33913 * @cfg {string} yearPlaceholder
33914 * @cfg {string} dayFormat default 'd'
33915 * @cfg {string} monthFormat default 'm'
33916 * @cfg {string} yearFormat default 'Y'
33917 * @cfg {Number} labellg set the width of label (1-12)
33918 * @cfg {Number} labelmd set the width of label (1-12)
33919 * @cfg {Number} labelsm set the width of label (1-12)
33920 * @cfg {Number} labelxs set the width of label (1-12)
33924 * Create a new DateSplitField
33925 * @param {Object} config The config object
33928 Roo.bootstrap.DateSplitField = function(config){
33929 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33935 * getting the data of years
33936 * @param {Roo.bootstrap.DateSplitField} this
33937 * @param {Object} years
33942 * getting the data of days
33943 * @param {Roo.bootstrap.DateSplitField} this
33944 * @param {Object} days
33949 * Fires after the field has been marked as invalid.
33950 * @param {Roo.form.Field} this
33951 * @param {String} msg The validation message
33956 * Fires after the field has been validated with no errors.
33957 * @param {Roo.form.Field} this
33963 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33966 labelAlign : 'top',
33968 dayAllowBlank : false,
33969 monthAllowBlank : false,
33970 yearAllowBlank : false,
33971 dayPlaceholder : '',
33972 monthPlaceholder : '',
33973 yearPlaceholder : '',
33977 isFormField : true,
33983 getAutoCreate : function()
33987 cls : 'row roo-date-split-field-group',
33992 cls : 'form-hidden-field roo-date-split-field-group-value',
33998 var labelCls = 'col-md-12';
33999 var contentCls = 'col-md-4';
34001 if(this.fieldLabel){
34005 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34009 html : this.fieldLabel
34014 if(this.labelAlign == 'left'){
34016 if(this.labelWidth > 12){
34017 label.style = "width: " + this.labelWidth + 'px';
34020 if(this.labelWidth < 13 && this.labelmd == 0){
34021 this.labelmd = this.labelWidth;
34024 if(this.labellg > 0){
34025 labelCls = ' col-lg-' + this.labellg;
34026 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34029 if(this.labelmd > 0){
34030 labelCls = ' col-md-' + this.labelmd;
34031 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34034 if(this.labelsm > 0){
34035 labelCls = ' col-sm-' + this.labelsm;
34036 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34039 if(this.labelxs > 0){
34040 labelCls = ' col-xs-' + this.labelxs;
34041 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34045 label.cls += ' ' + labelCls;
34047 cfg.cn.push(label);
34050 Roo.each(['day', 'month', 'year'], function(t){
34053 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34060 inputEl: function ()
34062 return this.el.select('.roo-date-split-field-group-value', true).first();
34065 onRender : function(ct, position)
34069 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34071 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34073 this.dayField = new Roo.bootstrap.ComboBox({
34074 allowBlank : this.dayAllowBlank,
34075 alwaysQuery : true,
34076 displayField : 'value',
34079 forceSelection : true,
34081 placeholder : this.dayPlaceholder,
34082 selectOnFocus : true,
34083 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34084 triggerAction : 'all',
34086 valueField : 'value',
34087 store : new Roo.data.SimpleStore({
34088 data : (function() {
34090 _this.fireEvent('days', _this, days);
34093 fields : [ 'value' ]
34096 select : function (_self, record, index)
34098 _this.setValue(_this.getValue());
34103 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34105 this.monthField = new Roo.bootstrap.MonthField({
34106 after : '<i class=\"fa fa-calendar\"></i>',
34107 allowBlank : this.monthAllowBlank,
34108 placeholder : this.monthPlaceholder,
34111 render : function (_self)
34113 this.el.select('span.input-group-addon', true).first().on('click', function(e){
34114 e.preventDefault();
34118 select : function (_self, oldvalue, newvalue)
34120 _this.setValue(_this.getValue());
34125 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34127 this.yearField = new Roo.bootstrap.ComboBox({
34128 allowBlank : this.yearAllowBlank,
34129 alwaysQuery : true,
34130 displayField : 'value',
34133 forceSelection : true,
34135 placeholder : this.yearPlaceholder,
34136 selectOnFocus : true,
34137 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34138 triggerAction : 'all',
34140 valueField : 'value',
34141 store : new Roo.data.SimpleStore({
34142 data : (function() {
34144 _this.fireEvent('years', _this, years);
34147 fields : [ 'value' ]
34150 select : function (_self, record, index)
34152 _this.setValue(_this.getValue());
34157 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34160 setValue : function(v, format)
34162 this.inputEl.dom.value = v;
34164 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34166 var d = Date.parseDate(v, f);
34173 this.setDay(d.format(this.dayFormat));
34174 this.setMonth(d.format(this.monthFormat));
34175 this.setYear(d.format(this.yearFormat));
34182 setDay : function(v)
34184 this.dayField.setValue(v);
34185 this.inputEl.dom.value = this.getValue();
34190 setMonth : function(v)
34192 this.monthField.setValue(v, true);
34193 this.inputEl.dom.value = this.getValue();
34198 setYear : function(v)
34200 this.yearField.setValue(v);
34201 this.inputEl.dom.value = this.getValue();
34206 getDay : function()
34208 return this.dayField.getValue();
34211 getMonth : function()
34213 return this.monthField.getValue();
34216 getYear : function()
34218 return this.yearField.getValue();
34221 getValue : function()
34223 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34225 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34235 this.inputEl.dom.value = '';
34240 validate : function()
34242 var d = this.dayField.validate();
34243 var m = this.monthField.validate();
34244 var y = this.yearField.validate();
34249 (!this.dayAllowBlank && !d) ||
34250 (!this.monthAllowBlank && !m) ||
34251 (!this.yearAllowBlank && !y)
34256 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34265 this.markInvalid();
34270 markValid : function()
34273 var label = this.el.select('label', true).first();
34274 var icon = this.el.select('i.fa-star', true).first();
34280 this.fireEvent('valid', this);
34284 * Mark this field as invalid
34285 * @param {String} msg The validation message
34287 markInvalid : function(msg)
34290 var label = this.el.select('label', true).first();
34291 var icon = this.el.select('i.fa-star', true).first();
34293 if(label && !icon){
34294 this.el.select('.roo-date-split-field-label', true).createChild({
34296 cls : 'text-danger fa fa-lg fa-star',
34297 tooltip : 'This field is required',
34298 style : 'margin-right:5px;'
34302 this.fireEvent('invalid', this, msg);
34305 clearInvalid : function()
34307 var label = this.el.select('label', true).first();
34308 var icon = this.el.select('i.fa-star', true).first();
34314 this.fireEvent('valid', this);
34317 getName: function()
34327 * http://masonry.desandro.com
34329 * The idea is to render all the bricks based on vertical width...
34331 * The original code extends 'outlayer' - we might need to use that....
34337 * @class Roo.bootstrap.LayoutMasonry
34338 * @extends Roo.bootstrap.Component
34339 * Bootstrap Layout Masonry class
34342 * Create a new Element
34343 * @param {Object} config The config object
34346 Roo.bootstrap.LayoutMasonry = function(config){
34348 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34352 Roo.bootstrap.LayoutMasonry.register(this);
34358 * Fire after layout the items
34359 * @param {Roo.bootstrap.LayoutMasonry} this
34360 * @param {Roo.EventObject} e
34367 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34370 * @cfg {Boolean} isLayoutInstant = no animation?
34372 isLayoutInstant : false, // needed?
34375 * @cfg {Number} boxWidth width of the columns
34380 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34385 * @cfg {Number} padWidth padding below box..
34390 * @cfg {Number} gutter gutter width..
34395 * @cfg {Number} maxCols maximum number of columns
34401 * @cfg {Boolean} isAutoInitial defalut true
34403 isAutoInitial : true,
34408 * @cfg {Boolean} isHorizontal defalut false
34410 isHorizontal : false,
34412 currentSize : null,
34418 bricks: null, //CompositeElement
34422 _isLayoutInited : false,
34424 // isAlternative : false, // only use for vertical layout...
34427 * @cfg {Number} alternativePadWidth padding below box..
34429 alternativePadWidth : 50,
34431 selectedBrick : [],
34433 getAutoCreate : function(){
34435 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34439 cls: 'blog-masonary-wrapper ' + this.cls,
34441 cls : 'mas-boxes masonary'
34448 getChildContainer: function( )
34450 if (this.boxesEl) {
34451 return this.boxesEl;
34454 this.boxesEl = this.el.select('.mas-boxes').first();
34456 return this.boxesEl;
34460 initEvents : function()
34464 if(this.isAutoInitial){
34465 Roo.log('hook children rendered');
34466 this.on('childrenrendered', function() {
34467 Roo.log('children rendered');
34473 initial : function()
34475 this.selectedBrick = [];
34477 this.currentSize = this.el.getBox(true);
34479 Roo.EventManager.onWindowResize(this.resize, this);
34481 if(!this.isAutoInitial){
34489 //this.layout.defer(500,this);
34493 resize : function()
34495 var cs = this.el.getBox(true);
34498 this.currentSize.width == cs.width &&
34499 this.currentSize.x == cs.x &&
34500 this.currentSize.height == cs.height &&
34501 this.currentSize.y == cs.y
34503 Roo.log("no change in with or X or Y");
34507 this.currentSize = cs;
34513 layout : function()
34515 this._resetLayout();
34517 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34519 this.layoutItems( isInstant );
34521 this._isLayoutInited = true;
34523 this.fireEvent('layout', this);
34527 _resetLayout : function()
34529 if(this.isHorizontal){
34530 this.horizontalMeasureColumns();
34534 this.verticalMeasureColumns();
34538 verticalMeasureColumns : function()
34540 this.getContainerWidth();
34542 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34543 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34547 var boxWidth = this.boxWidth + this.padWidth;
34549 if(this.containerWidth < this.boxWidth){
34550 boxWidth = this.containerWidth
34553 var containerWidth = this.containerWidth;
34555 var cols = Math.floor(containerWidth / boxWidth);
34557 this.cols = Math.max( cols, 1 );
34559 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34561 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34563 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34565 this.colWidth = boxWidth + avail - this.padWidth;
34567 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34568 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34571 horizontalMeasureColumns : function()
34573 this.getContainerWidth();
34575 var boxWidth = this.boxWidth;
34577 if(this.containerWidth < boxWidth){
34578 boxWidth = this.containerWidth;
34581 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34583 this.el.setHeight(boxWidth);
34587 getContainerWidth : function()
34589 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34592 layoutItems : function( isInstant )
34594 Roo.log(this.bricks);
34596 var items = Roo.apply([], this.bricks);
34598 if(this.isHorizontal){
34599 this._horizontalLayoutItems( items , isInstant );
34603 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34604 // this._verticalAlternativeLayoutItems( items , isInstant );
34608 this._verticalLayoutItems( items , isInstant );
34612 _verticalLayoutItems : function ( items , isInstant)
34614 if ( !items || !items.length ) {
34619 ['xs', 'xs', 'xs', 'tall'],
34620 ['xs', 'xs', 'tall'],
34621 ['xs', 'xs', 'sm'],
34622 ['xs', 'xs', 'xs'],
34628 ['sm', 'xs', 'xs'],
34632 ['tall', 'xs', 'xs', 'xs'],
34633 ['tall', 'xs', 'xs'],
34645 Roo.each(items, function(item, k){
34647 switch (item.size) {
34648 // these layouts take up a full box,
34659 boxes.push([item]);
34682 var filterPattern = function(box, length)
34690 var pattern = box.slice(0, length);
34694 Roo.each(pattern, function(i){
34695 format.push(i.size);
34698 Roo.each(standard, function(s){
34700 if(String(s) != String(format)){
34709 if(!match && length == 1){
34714 filterPattern(box, length - 1);
34718 queue.push(pattern);
34720 box = box.slice(length, box.length);
34722 filterPattern(box, 4);
34728 Roo.each(boxes, function(box, k){
34734 if(box.length == 1){
34739 filterPattern(box, 4);
34743 this._processVerticalLayoutQueue( queue, isInstant );
34747 // _verticalAlternativeLayoutItems : function( items , isInstant )
34749 // if ( !items || !items.length ) {
34753 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34757 _horizontalLayoutItems : function ( items , isInstant)
34759 if ( !items || !items.length || items.length < 3) {
34765 var eItems = items.slice(0, 3);
34767 items = items.slice(3, items.length);
34770 ['xs', 'xs', 'xs', 'wide'],
34771 ['xs', 'xs', 'wide'],
34772 ['xs', 'xs', 'sm'],
34773 ['xs', 'xs', 'xs'],
34779 ['sm', 'xs', 'xs'],
34783 ['wide', 'xs', 'xs', 'xs'],
34784 ['wide', 'xs', 'xs'],
34797 Roo.each(items, function(item, k){
34799 switch (item.size) {
34810 boxes.push([item]);
34834 var filterPattern = function(box, length)
34842 var pattern = box.slice(0, length);
34846 Roo.each(pattern, function(i){
34847 format.push(i.size);
34850 Roo.each(standard, function(s){
34852 if(String(s) != String(format)){
34861 if(!match && length == 1){
34866 filterPattern(box, length - 1);
34870 queue.push(pattern);
34872 box = box.slice(length, box.length);
34874 filterPattern(box, 4);
34880 Roo.each(boxes, function(box, k){
34886 if(box.length == 1){
34891 filterPattern(box, 4);
34898 var pos = this.el.getBox(true);
34902 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34904 var hit_end = false;
34906 Roo.each(queue, function(box){
34910 Roo.each(box, function(b){
34912 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34922 Roo.each(box, function(b){
34924 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34927 mx = Math.max(mx, b.x);
34931 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34935 Roo.each(box, function(b){
34937 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34951 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34954 /** Sets position of item in DOM
34955 * @param {Element} item
34956 * @param {Number} x - horizontal position
34957 * @param {Number} y - vertical position
34958 * @param {Boolean} isInstant - disables transitions
34960 _processVerticalLayoutQueue : function( queue, isInstant )
34962 var pos = this.el.getBox(true);
34967 for (var i = 0; i < this.cols; i++){
34971 Roo.each(queue, function(box, k){
34973 var col = k % this.cols;
34975 Roo.each(box, function(b,kk){
34977 b.el.position('absolute');
34979 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34980 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34982 if(b.size == 'md-left' || b.size == 'md-right'){
34983 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34984 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34987 b.el.setWidth(width);
34988 b.el.setHeight(height);
34990 b.el.select('iframe',true).setSize(width,height);
34994 for (var i = 0; i < this.cols; i++){
34996 if(maxY[i] < maxY[col]){
35001 col = Math.min(col, i);
35005 x = pos.x + col * (this.colWidth + this.padWidth);
35009 var positions = [];
35011 switch (box.length){
35013 positions = this.getVerticalOneBoxColPositions(x, y, box);
35016 positions = this.getVerticalTwoBoxColPositions(x, y, box);
35019 positions = this.getVerticalThreeBoxColPositions(x, y, box);
35022 positions = this.getVerticalFourBoxColPositions(x, y, box);
35028 Roo.each(box, function(b,kk){
35030 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35032 var sz = b.el.getSize();
35034 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35042 for (var i = 0; i < this.cols; i++){
35043 mY = Math.max(mY, maxY[i]);
35046 this.el.setHeight(mY - pos.y);
35050 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35052 // var pos = this.el.getBox(true);
35055 // var maxX = pos.right;
35057 // var maxHeight = 0;
35059 // Roo.each(items, function(item, k){
35063 // item.el.position('absolute');
35065 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35067 // item.el.setWidth(width);
35069 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35071 // item.el.setHeight(height);
35074 // item.el.setXY([x, y], isInstant ? false : true);
35076 // item.el.setXY([maxX - width, y], isInstant ? false : true);
35079 // y = y + height + this.alternativePadWidth;
35081 // maxHeight = maxHeight + height + this.alternativePadWidth;
35085 // this.el.setHeight(maxHeight);
35089 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35091 var pos = this.el.getBox(true);
35096 var maxX = pos.right;
35098 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35100 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35102 Roo.each(queue, function(box, k){
35104 Roo.each(box, function(b, kk){
35106 b.el.position('absolute');
35108 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35109 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35111 if(b.size == 'md-left' || b.size == 'md-right'){
35112 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35113 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35116 b.el.setWidth(width);
35117 b.el.setHeight(height);
35125 var positions = [];
35127 switch (box.length){
35129 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35132 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35135 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35138 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35144 Roo.each(box, function(b,kk){
35146 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35148 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35156 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35158 Roo.each(eItems, function(b,k){
35160 b.size = (k == 0) ? 'sm' : 'xs';
35161 b.x = (k == 0) ? 2 : 1;
35162 b.y = (k == 0) ? 2 : 1;
35164 b.el.position('absolute');
35166 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35168 b.el.setWidth(width);
35170 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35172 b.el.setHeight(height);
35176 var positions = [];
35179 x : maxX - this.unitWidth * 2 - this.gutter,
35184 x : maxX - this.unitWidth,
35185 y : minY + (this.unitWidth + this.gutter) * 2
35189 x : maxX - this.unitWidth * 3 - this.gutter * 2,
35193 Roo.each(eItems, function(b,k){
35195 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35201 getVerticalOneBoxColPositions : function(x, y, box)
35205 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35207 if(box[0].size == 'md-left'){
35211 if(box[0].size == 'md-right'){
35216 x : x + (this.unitWidth + this.gutter) * rand,
35223 getVerticalTwoBoxColPositions : function(x, y, box)
35227 if(box[0].size == 'xs'){
35231 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35235 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35249 x : x + (this.unitWidth + this.gutter) * 2,
35250 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35257 getVerticalThreeBoxColPositions : function(x, y, box)
35261 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35269 x : x + (this.unitWidth + this.gutter) * 1,
35274 x : x + (this.unitWidth + this.gutter) * 2,
35282 if(box[0].size == 'xs' && box[1].size == 'xs'){
35291 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35295 x : x + (this.unitWidth + this.gutter) * 1,
35309 x : x + (this.unitWidth + this.gutter) * 2,
35314 x : x + (this.unitWidth + this.gutter) * 2,
35315 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35322 getVerticalFourBoxColPositions : function(x, y, box)
35326 if(box[0].size == 'xs'){
35335 y : y + (this.unitHeight + this.gutter) * 1
35340 y : y + (this.unitHeight + this.gutter) * 2
35344 x : x + (this.unitWidth + this.gutter) * 1,
35358 x : x + (this.unitWidth + this.gutter) * 2,
35363 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35364 y : y + (this.unitHeight + this.gutter) * 1
35368 x : x + (this.unitWidth + this.gutter) * 2,
35369 y : y + (this.unitWidth + this.gutter) * 2
35376 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35380 if(box[0].size == 'md-left'){
35382 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35389 if(box[0].size == 'md-right'){
35391 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35392 y : minY + (this.unitWidth + this.gutter) * 1
35398 var rand = Math.floor(Math.random() * (4 - box[0].y));
35401 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35402 y : minY + (this.unitWidth + this.gutter) * rand
35409 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35413 if(box[0].size == 'xs'){
35416 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35421 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35422 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35430 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35435 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35436 y : minY + (this.unitWidth + this.gutter) * 2
35443 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35447 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35450 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35455 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35456 y : minY + (this.unitWidth + this.gutter) * 1
35460 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35461 y : minY + (this.unitWidth + this.gutter) * 2
35468 if(box[0].size == 'xs' && box[1].size == 'xs'){
35471 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35476 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35481 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35482 y : minY + (this.unitWidth + this.gutter) * 1
35490 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35495 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35496 y : minY + (this.unitWidth + this.gutter) * 2
35500 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35501 y : minY + (this.unitWidth + this.gutter) * 2
35508 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35512 if(box[0].size == 'xs'){
35515 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35520 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35525 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),
35530 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35531 y : minY + (this.unitWidth + this.gutter) * 1
35539 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35544 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35545 y : minY + (this.unitWidth + this.gutter) * 2
35549 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35550 y : minY + (this.unitWidth + this.gutter) * 2
35554 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),
35555 y : minY + (this.unitWidth + this.gutter) * 2
35563 * remove a Masonry Brick
35564 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35566 removeBrick : function(brick_id)
35572 for (var i = 0; i<this.bricks.length; i++) {
35573 if (this.bricks[i].id == brick_id) {
35574 this.bricks.splice(i,1);
35575 this.el.dom.removeChild(Roo.get(brick_id).dom);
35582 * adds a Masonry Brick
35583 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35585 addBrick : function(cfg)
35587 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35588 //this.register(cn);
35589 cn.parentId = this.id;
35590 cn.render(this.el);
35595 * register a Masonry Brick
35596 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35599 register : function(brick)
35601 this.bricks.push(brick);
35602 brick.masonryId = this.id;
35606 * clear all the Masonry Brick
35608 clearAll : function()
35611 //this.getChildContainer().dom.innerHTML = "";
35612 this.el.dom.innerHTML = '';
35615 getSelected : function()
35617 if (!this.selectedBrick) {
35621 return this.selectedBrick;
35625 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35629 * register a Masonry Layout
35630 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35633 register : function(layout)
35635 this.groups[layout.id] = layout;
35638 * fetch a Masonry Layout based on the masonry layout ID
35639 * @param {string} the masonry layout to add
35640 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35643 get: function(layout_id) {
35644 if (typeof(this.groups[layout_id]) == 'undefined') {
35647 return this.groups[layout_id] ;
35659 * http://masonry.desandro.com
35661 * The idea is to render all the bricks based on vertical width...
35663 * The original code extends 'outlayer' - we might need to use that....
35669 * @class Roo.bootstrap.LayoutMasonryAuto
35670 * @extends Roo.bootstrap.Component
35671 * Bootstrap Layout Masonry class
35674 * Create a new Element
35675 * @param {Object} config The config object
35678 Roo.bootstrap.LayoutMasonryAuto = function(config){
35679 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35682 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35685 * @cfg {Boolean} isFitWidth - resize the width..
35687 isFitWidth : false, // options..
35689 * @cfg {Boolean} isOriginLeft = left align?
35691 isOriginLeft : true,
35693 * @cfg {Boolean} isOriginTop = top align?
35695 isOriginTop : false,
35697 * @cfg {Boolean} isLayoutInstant = no animation?
35699 isLayoutInstant : false, // needed?
35701 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35703 isResizingContainer : true,
35705 * @cfg {Number} columnWidth width of the columns
35711 * @cfg {Number} maxCols maximum number of columns
35716 * @cfg {Number} padHeight padding below box..
35722 * @cfg {Boolean} isAutoInitial defalut true
35725 isAutoInitial : true,
35731 initialColumnWidth : 0,
35732 currentSize : null,
35734 colYs : null, // array.
35741 bricks: null, //CompositeElement
35742 cols : 0, // array?
35743 // element : null, // wrapped now this.el
35744 _isLayoutInited : null,
35747 getAutoCreate : function(){
35751 cls: 'blog-masonary-wrapper ' + this.cls,
35753 cls : 'mas-boxes masonary'
35760 getChildContainer: function( )
35762 if (this.boxesEl) {
35763 return this.boxesEl;
35766 this.boxesEl = this.el.select('.mas-boxes').first();
35768 return this.boxesEl;
35772 initEvents : function()
35776 if(this.isAutoInitial){
35777 Roo.log('hook children rendered');
35778 this.on('childrenrendered', function() {
35779 Roo.log('children rendered');
35786 initial : function()
35788 this.reloadItems();
35790 this.currentSize = this.el.getBox(true);
35792 /// was window resize... - let's see if this works..
35793 Roo.EventManager.onWindowResize(this.resize, this);
35795 if(!this.isAutoInitial){
35800 this.layout.defer(500,this);
35803 reloadItems: function()
35805 this.bricks = this.el.select('.masonry-brick', true);
35807 this.bricks.each(function(b) {
35808 //Roo.log(b.getSize());
35809 if (!b.attr('originalwidth')) {
35810 b.attr('originalwidth', b.getSize().width);
35815 Roo.log(this.bricks.elements.length);
35818 resize : function()
35821 var cs = this.el.getBox(true);
35823 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35824 Roo.log("no change in with or X");
35827 this.currentSize = cs;
35831 layout : function()
35834 this._resetLayout();
35835 //this._manageStamps();
35837 // don't animate first layout
35838 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35839 this.layoutItems( isInstant );
35841 // flag for initalized
35842 this._isLayoutInited = true;
35845 layoutItems : function( isInstant )
35847 //var items = this._getItemsForLayout( this.items );
35848 // original code supports filtering layout items.. we just ignore it..
35850 this._layoutItems( this.bricks , isInstant );
35852 this._postLayout();
35854 _layoutItems : function ( items , isInstant)
35856 //this.fireEvent( 'layout', this, items );
35859 if ( !items || !items.elements.length ) {
35860 // no items, emit event with empty array
35865 items.each(function(item) {
35866 Roo.log("layout item");
35868 // get x/y object from method
35869 var position = this._getItemLayoutPosition( item );
35871 position.item = item;
35872 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35873 queue.push( position );
35876 this._processLayoutQueue( queue );
35878 /** Sets position of item in DOM
35879 * @param {Element} item
35880 * @param {Number} x - horizontal position
35881 * @param {Number} y - vertical position
35882 * @param {Boolean} isInstant - disables transitions
35884 _processLayoutQueue : function( queue )
35886 for ( var i=0, len = queue.length; i < len; i++ ) {
35887 var obj = queue[i];
35888 obj.item.position('absolute');
35889 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35895 * Any logic you want to do after each layout,
35896 * i.e. size the container
35898 _postLayout : function()
35900 this.resizeContainer();
35903 resizeContainer : function()
35905 if ( !this.isResizingContainer ) {
35908 var size = this._getContainerSize();
35910 this.el.setSize(size.width,size.height);
35911 this.boxesEl.setSize(size.width,size.height);
35917 _resetLayout : function()
35919 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35920 this.colWidth = this.el.getWidth();
35921 //this.gutter = this.el.getWidth();
35923 this.measureColumns();
35929 this.colYs.push( 0 );
35935 measureColumns : function()
35937 this.getContainerWidth();
35938 // if columnWidth is 0, default to outerWidth of first item
35939 if ( !this.columnWidth ) {
35940 var firstItem = this.bricks.first();
35941 Roo.log(firstItem);
35942 this.columnWidth = this.containerWidth;
35943 if (firstItem && firstItem.attr('originalwidth') ) {
35944 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35946 // columnWidth fall back to item of first element
35947 Roo.log("set column width?");
35948 this.initialColumnWidth = this.columnWidth ;
35950 // if first elem has no width, default to size of container
35955 if (this.initialColumnWidth) {
35956 this.columnWidth = this.initialColumnWidth;
35961 // column width is fixed at the top - however if container width get's smaller we should
35964 // this bit calcs how man columns..
35966 var columnWidth = this.columnWidth += this.gutter;
35968 // calculate columns
35969 var containerWidth = this.containerWidth + this.gutter;
35971 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35972 // fix rounding errors, typically with gutters
35973 var excess = columnWidth - containerWidth % columnWidth;
35976 // if overshoot is less than a pixel, round up, otherwise floor it
35977 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35978 cols = Math[ mathMethod ]( cols );
35979 this.cols = Math.max( cols, 1 );
35980 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35982 // padding positioning..
35983 var totalColWidth = this.cols * this.columnWidth;
35984 var padavail = this.containerWidth - totalColWidth;
35985 // so for 2 columns - we need 3 'pads'
35987 var padNeeded = (1+this.cols) * this.padWidth;
35989 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35991 this.columnWidth += padExtra
35992 //this.padWidth = Math.floor(padavail / ( this.cols));
35994 // adjust colum width so that padding is fixed??
35996 // we have 3 columns ... total = width * 3
35997 // we have X left over... that should be used by
35999 //if (this.expandC) {
36007 getContainerWidth : function()
36009 /* // container is parent if fit width
36010 var container = this.isFitWidth ? this.element.parentNode : this.element;
36011 // check that this.size and size are there
36012 // IE8 triggers resize on body size change, so they might not be
36014 var size = getSize( container ); //FIXME
36015 this.containerWidth = size && size.innerWidth; //FIXME
36018 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36022 _getItemLayoutPosition : function( item ) // what is item?
36024 // we resize the item to our columnWidth..
36026 item.setWidth(this.columnWidth);
36027 item.autoBoxAdjust = false;
36029 var sz = item.getSize();
36031 // how many columns does this brick span
36032 var remainder = this.containerWidth % this.columnWidth;
36034 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36035 // round if off by 1 pixel, otherwise use ceil
36036 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
36037 colSpan = Math.min( colSpan, this.cols );
36039 // normally this should be '1' as we dont' currently allow multi width columns..
36041 var colGroup = this._getColGroup( colSpan );
36042 // get the minimum Y value from the columns
36043 var minimumY = Math.min.apply( Math, colGroup );
36044 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36046 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
36048 // position the brick
36050 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36051 y: this.currentSize.y + minimumY + this.padHeight
36055 // apply setHeight to necessary columns
36056 var setHeight = minimumY + sz.height + this.padHeight;
36057 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
36059 var setSpan = this.cols + 1 - colGroup.length;
36060 for ( var i = 0; i < setSpan; i++ ) {
36061 this.colYs[ shortColIndex + i ] = setHeight ;
36068 * @param {Number} colSpan - number of columns the element spans
36069 * @returns {Array} colGroup
36071 _getColGroup : function( colSpan )
36073 if ( colSpan < 2 ) {
36074 // if brick spans only one column, use all the column Ys
36079 // how many different places could this brick fit horizontally
36080 var groupCount = this.cols + 1 - colSpan;
36081 // for each group potential horizontal position
36082 for ( var i = 0; i < groupCount; i++ ) {
36083 // make an array of colY values for that one group
36084 var groupColYs = this.colYs.slice( i, i + colSpan );
36085 // and get the max value of the array
36086 colGroup[i] = Math.max.apply( Math, groupColYs );
36091 _manageStamp : function( stamp )
36093 var stampSize = stamp.getSize();
36094 var offset = stamp.getBox();
36095 // get the columns that this stamp affects
36096 var firstX = this.isOriginLeft ? offset.x : offset.right;
36097 var lastX = firstX + stampSize.width;
36098 var firstCol = Math.floor( firstX / this.columnWidth );
36099 firstCol = Math.max( 0, firstCol );
36101 var lastCol = Math.floor( lastX / this.columnWidth );
36102 // lastCol should not go over if multiple of columnWidth #425
36103 lastCol -= lastX % this.columnWidth ? 0 : 1;
36104 lastCol = Math.min( this.cols - 1, lastCol );
36106 // set colYs to bottom of the stamp
36107 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36110 for ( var i = firstCol; i <= lastCol; i++ ) {
36111 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36116 _getContainerSize : function()
36118 this.maxY = Math.max.apply( Math, this.colYs );
36123 if ( this.isFitWidth ) {
36124 size.width = this._getContainerFitWidth();
36130 _getContainerFitWidth : function()
36132 var unusedCols = 0;
36133 // count unused columns
36136 if ( this.colYs[i] !== 0 ) {
36141 // fit container to columns that have been used
36142 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36145 needsResizeLayout : function()
36147 var previousWidth = this.containerWidth;
36148 this.getContainerWidth();
36149 return previousWidth !== this.containerWidth;
36164 * @class Roo.bootstrap.MasonryBrick
36165 * @extends Roo.bootstrap.Component
36166 * Bootstrap MasonryBrick class
36169 * Create a new MasonryBrick
36170 * @param {Object} config The config object
36173 Roo.bootstrap.MasonryBrick = function(config){
36175 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36177 Roo.bootstrap.MasonryBrick.register(this);
36183 * When a MasonryBrick is clcik
36184 * @param {Roo.bootstrap.MasonryBrick} this
36185 * @param {Roo.EventObject} e
36191 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
36194 * @cfg {String} title
36198 * @cfg {String} html
36202 * @cfg {String} bgimage
36206 * @cfg {String} videourl
36210 * @cfg {String} cls
36214 * @cfg {String} href
36218 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36223 * @cfg {String} placetitle (center|bottom)
36228 * @cfg {Boolean} isFitContainer defalut true
36230 isFitContainer : true,
36233 * @cfg {Boolean} preventDefault defalut false
36235 preventDefault : false,
36238 * @cfg {Boolean} inverse defalut false
36240 maskInverse : false,
36242 getAutoCreate : function()
36244 if(!this.isFitContainer){
36245 return this.getSplitAutoCreate();
36248 var cls = 'masonry-brick masonry-brick-full';
36250 if(this.href.length){
36251 cls += ' masonry-brick-link';
36254 if(this.bgimage.length){
36255 cls += ' masonry-brick-image';
36258 if(this.maskInverse){
36259 cls += ' mask-inverse';
36262 if(!this.html.length && !this.maskInverse && !this.videourl.length){
36263 cls += ' enable-mask';
36267 cls += ' masonry-' + this.size + '-brick';
36270 if(this.placetitle.length){
36272 switch (this.placetitle) {
36274 cls += ' masonry-center-title';
36277 cls += ' masonry-bottom-title';
36284 if(!this.html.length && !this.bgimage.length){
36285 cls += ' masonry-center-title';
36288 if(!this.html.length && this.bgimage.length){
36289 cls += ' masonry-bottom-title';
36294 cls += ' ' + this.cls;
36298 tag: (this.href.length) ? 'a' : 'div',
36303 cls: 'masonry-brick-mask'
36307 cls: 'masonry-brick-paragraph',
36313 if(this.href.length){
36314 cfg.href = this.href;
36317 var cn = cfg.cn[1].cn;
36319 if(this.title.length){
36322 cls: 'masonry-brick-title',
36327 if(this.html.length){
36330 cls: 'masonry-brick-text',
36335 if (!this.title.length && !this.html.length) {
36336 cfg.cn[1].cls += ' hide';
36339 if(this.bgimage.length){
36342 cls: 'masonry-brick-image-view',
36347 if(this.videourl.length){
36348 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36349 // youtube support only?
36352 cls: 'masonry-brick-image-view',
36355 allowfullscreen : true
36363 getSplitAutoCreate : function()
36365 var cls = 'masonry-brick masonry-brick-split';
36367 if(this.href.length){
36368 cls += ' masonry-brick-link';
36371 if(this.bgimage.length){
36372 cls += ' masonry-brick-image';
36376 cls += ' masonry-' + this.size + '-brick';
36379 switch (this.placetitle) {
36381 cls += ' masonry-center-title';
36384 cls += ' masonry-bottom-title';
36387 if(!this.bgimage.length){
36388 cls += ' masonry-center-title';
36391 if(this.bgimage.length){
36392 cls += ' masonry-bottom-title';
36398 cls += ' ' + this.cls;
36402 tag: (this.href.length) ? 'a' : 'div',
36407 cls: 'masonry-brick-split-head',
36411 cls: 'masonry-brick-paragraph',
36418 cls: 'masonry-brick-split-body',
36424 if(this.href.length){
36425 cfg.href = this.href;
36428 if(this.title.length){
36429 cfg.cn[0].cn[0].cn.push({
36431 cls: 'masonry-brick-title',
36436 if(this.html.length){
36437 cfg.cn[1].cn.push({
36439 cls: 'masonry-brick-text',
36444 if(this.bgimage.length){
36445 cfg.cn[0].cn.push({
36447 cls: 'masonry-brick-image-view',
36452 if(this.videourl.length){
36453 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36454 // youtube support only?
36455 cfg.cn[0].cn.cn.push({
36457 cls: 'masonry-brick-image-view',
36460 allowfullscreen : true
36467 initEvents: function()
36469 switch (this.size) {
36502 this.el.on('touchstart', this.onTouchStart, this);
36503 this.el.on('touchmove', this.onTouchMove, this);
36504 this.el.on('touchend', this.onTouchEnd, this);
36505 this.el.on('contextmenu', this.onContextMenu, this);
36507 this.el.on('mouseenter' ,this.enter, this);
36508 this.el.on('mouseleave', this.leave, this);
36509 this.el.on('click', this.onClick, this);
36512 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36513 this.parent().bricks.push(this);
36518 onClick: function(e, el)
36520 var time = this.endTimer - this.startTimer;
36521 // Roo.log(e.preventDefault());
36524 e.preventDefault();
36529 if(!this.preventDefault){
36533 e.preventDefault();
36535 if (this.activeClass != '') {
36536 this.selectBrick();
36539 this.fireEvent('click', this, e);
36542 enter: function(e, el)
36544 e.preventDefault();
36546 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36550 if(this.bgimage.length && this.html.length){
36551 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36555 leave: function(e, el)
36557 e.preventDefault();
36559 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36563 if(this.bgimage.length && this.html.length){
36564 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36568 onTouchStart: function(e, el)
36570 // e.preventDefault();
36572 this.touchmoved = false;
36574 if(!this.isFitContainer){
36578 if(!this.bgimage.length || !this.html.length){
36582 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36584 this.timer = new Date().getTime();
36588 onTouchMove: function(e, el)
36590 this.touchmoved = true;
36593 onContextMenu : function(e,el)
36595 e.preventDefault();
36596 e.stopPropagation();
36600 onTouchEnd: function(e, el)
36602 // e.preventDefault();
36604 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36611 if(!this.bgimage.length || !this.html.length){
36613 if(this.href.length){
36614 window.location.href = this.href;
36620 if(!this.isFitContainer){
36624 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36626 window.location.href = this.href;
36629 //selection on single brick only
36630 selectBrick : function() {
36632 if (!this.parentId) {
36636 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36637 var index = m.selectedBrick.indexOf(this.id);
36640 m.selectedBrick.splice(index,1);
36641 this.el.removeClass(this.activeClass);
36645 for(var i = 0; i < m.selectedBrick.length; i++) {
36646 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36647 b.el.removeClass(b.activeClass);
36650 m.selectedBrick = [];
36652 m.selectedBrick.push(this.id);
36653 this.el.addClass(this.activeClass);
36657 isSelected : function(){
36658 return this.el.hasClass(this.activeClass);
36663 Roo.apply(Roo.bootstrap.MasonryBrick, {
36666 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36668 * register a Masonry Brick
36669 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36672 register : function(brick)
36674 //this.groups[brick.id] = brick;
36675 this.groups.add(brick.id, brick);
36678 * fetch a masonry brick based on the masonry brick ID
36679 * @param {string} the masonry brick to add
36680 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36683 get: function(brick_id)
36685 // if (typeof(this.groups[brick_id]) == 'undefined') {
36688 // return this.groups[brick_id] ;
36690 if(this.groups.key(brick_id)) {
36691 return this.groups.key(brick_id);
36709 * @class Roo.bootstrap.Brick
36710 * @extends Roo.bootstrap.Component
36711 * Bootstrap Brick class
36714 * Create a new Brick
36715 * @param {Object} config The config object
36718 Roo.bootstrap.Brick = function(config){
36719 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36725 * When a Brick is click
36726 * @param {Roo.bootstrap.Brick} this
36727 * @param {Roo.EventObject} e
36733 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36736 * @cfg {String} title
36740 * @cfg {String} html
36744 * @cfg {String} bgimage
36748 * @cfg {String} cls
36752 * @cfg {String} href
36756 * @cfg {String} video
36760 * @cfg {Boolean} square
36764 getAutoCreate : function()
36766 var cls = 'roo-brick';
36768 if(this.href.length){
36769 cls += ' roo-brick-link';
36772 if(this.bgimage.length){
36773 cls += ' roo-brick-image';
36776 if(!this.html.length && !this.bgimage.length){
36777 cls += ' roo-brick-center-title';
36780 if(!this.html.length && this.bgimage.length){
36781 cls += ' roo-brick-bottom-title';
36785 cls += ' ' + this.cls;
36789 tag: (this.href.length) ? 'a' : 'div',
36794 cls: 'roo-brick-paragraph',
36800 if(this.href.length){
36801 cfg.href = this.href;
36804 var cn = cfg.cn[0].cn;
36806 if(this.title.length){
36809 cls: 'roo-brick-title',
36814 if(this.html.length){
36817 cls: 'roo-brick-text',
36824 if(this.bgimage.length){
36827 cls: 'roo-brick-image-view',
36835 initEvents: function()
36837 if(this.title.length || this.html.length){
36838 this.el.on('mouseenter' ,this.enter, this);
36839 this.el.on('mouseleave', this.leave, this);
36842 Roo.EventManager.onWindowResize(this.resize, this);
36844 if(this.bgimage.length){
36845 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36846 this.imageEl.on('load', this.onImageLoad, this);
36853 onImageLoad : function()
36858 resize : function()
36860 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36862 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36864 if(this.bgimage.length){
36865 var image = this.el.select('.roo-brick-image-view', true).first();
36867 image.setWidth(paragraph.getWidth());
36870 image.setHeight(paragraph.getWidth());
36873 this.el.setHeight(image.getHeight());
36874 paragraph.setHeight(image.getHeight());
36880 enter: function(e, el)
36882 e.preventDefault();
36884 if(this.bgimage.length){
36885 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36886 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36890 leave: function(e, el)
36892 e.preventDefault();
36894 if(this.bgimage.length){
36895 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36896 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36911 * @class Roo.bootstrap.NumberField
36912 * @extends Roo.bootstrap.Input
36913 * Bootstrap NumberField class
36919 * Create a new NumberField
36920 * @param {Object} config The config object
36923 Roo.bootstrap.NumberField = function(config){
36924 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36927 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36930 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36932 allowDecimals : true,
36934 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36936 decimalSeparator : ".",
36938 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36940 decimalPrecision : 2,
36942 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36944 allowNegative : true,
36947 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36951 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36953 minValue : Number.NEGATIVE_INFINITY,
36955 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36957 maxValue : Number.MAX_VALUE,
36959 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36961 minText : "The minimum value for this field is {0}",
36963 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36965 maxText : "The maximum value for this field is {0}",
36967 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36968 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36970 nanText : "{0} is not a valid number",
36972 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36974 thousandsDelimiter : false,
36976 * @cfg {String} valueAlign alignment of value
36978 valueAlign : "left",
36980 getAutoCreate : function()
36982 var hiddenInput = {
36986 cls: 'hidden-number-input'
36990 hiddenInput.name = this.name;
36995 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36997 this.name = hiddenInput.name;
36999 if(cfg.cn.length > 0) {
37000 cfg.cn.push(hiddenInput);
37007 initEvents : function()
37009 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37011 var allowed = "0123456789";
37013 if(this.allowDecimals){
37014 allowed += this.decimalSeparator;
37017 if(this.allowNegative){
37021 if(this.thousandsDelimiter) {
37025 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37027 var keyPress = function(e){
37029 var k = e.getKey();
37031 var c = e.getCharCode();
37034 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37035 allowed.indexOf(String.fromCharCode(c)) === -1
37041 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37045 if(allowed.indexOf(String.fromCharCode(c)) === -1){
37050 this.el.on("keypress", keyPress, this);
37053 validateValue : function(value)
37056 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37060 var num = this.parseValue(value);
37063 this.markInvalid(String.format(this.nanText, value));
37067 if(num < this.minValue){
37068 this.markInvalid(String.format(this.minText, this.minValue));
37072 if(num > this.maxValue){
37073 this.markInvalid(String.format(this.maxText, this.maxValue));
37080 getValue : function()
37082 var v = this.hiddenEl().getValue();
37084 return this.fixPrecision(this.parseValue(v));
37087 parseValue : function(value)
37089 if(this.thousandsDelimiter) {
37091 r = new RegExp(",", "g");
37092 value = value.replace(r, "");
37095 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37096 return isNaN(value) ? '' : value;
37099 fixPrecision : function(value)
37101 if(this.thousandsDelimiter) {
37103 r = new RegExp(",", "g");
37104 value = value.replace(r, "");
37107 var nan = isNaN(value);
37109 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37110 return nan ? '' : value;
37112 return parseFloat(value).toFixed(this.decimalPrecision);
37115 setValue : function(v)
37117 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37123 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37125 this.inputEl().dom.value = (v == '') ? '' :
37126 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37128 if(!this.allowZero && v === '0') {
37129 this.hiddenEl().dom.value = '';
37130 this.inputEl().dom.value = '';
37137 decimalPrecisionFcn : function(v)
37139 return Math.floor(v);
37142 beforeBlur : function()
37144 var v = this.parseValue(this.getRawValue());
37146 if(v || v === 0 || v === ''){
37151 hiddenEl : function()
37153 return this.el.select('input.hidden-number-input',true).first();
37165 * @class Roo.bootstrap.DocumentSlider
37166 * @extends Roo.bootstrap.Component
37167 * Bootstrap DocumentSlider class
37170 * Create a new DocumentViewer
37171 * @param {Object} config The config object
37174 Roo.bootstrap.DocumentSlider = function(config){
37175 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37182 * Fire after initEvent
37183 * @param {Roo.bootstrap.DocumentSlider} this
37188 * Fire after update
37189 * @param {Roo.bootstrap.DocumentSlider} this
37195 * @param {Roo.bootstrap.DocumentSlider} this
37201 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
37207 getAutoCreate : function()
37211 cls : 'roo-document-slider',
37215 cls : 'roo-document-slider-header',
37219 cls : 'roo-document-slider-header-title'
37225 cls : 'roo-document-slider-body',
37229 cls : 'roo-document-slider-prev',
37233 cls : 'fa fa-chevron-left'
37239 cls : 'roo-document-slider-thumb',
37243 cls : 'roo-document-slider-image'
37249 cls : 'roo-document-slider-next',
37253 cls : 'fa fa-chevron-right'
37265 initEvents : function()
37267 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37268 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37270 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37271 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37273 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37274 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37276 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37277 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37279 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37280 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37282 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37283 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37285 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37286 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37288 this.thumbEl.on('click', this.onClick, this);
37290 this.prevIndicator.on('click', this.prev, this);
37292 this.nextIndicator.on('click', this.next, this);
37296 initial : function()
37298 if(this.files.length){
37299 this.indicator = 1;
37303 this.fireEvent('initial', this);
37306 update : function()
37308 this.imageEl.attr('src', this.files[this.indicator - 1]);
37310 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37312 this.prevIndicator.show();
37314 if(this.indicator == 1){
37315 this.prevIndicator.hide();
37318 this.nextIndicator.show();
37320 if(this.indicator == this.files.length){
37321 this.nextIndicator.hide();
37324 this.thumbEl.scrollTo('top');
37326 this.fireEvent('update', this);
37329 onClick : function(e)
37331 e.preventDefault();
37333 this.fireEvent('click', this);
37338 e.preventDefault();
37340 this.indicator = Math.max(1, this.indicator - 1);
37347 e.preventDefault();
37349 this.indicator = Math.min(this.files.length, this.indicator + 1);
37363 * @class Roo.bootstrap.RadioSet
37364 * @extends Roo.bootstrap.Input
37365 * Bootstrap RadioSet class
37366 * @cfg {String} indicatorpos (left|right) default left
37367 * @cfg {Boolean} inline (true|false) inline the element (default true)
37368 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37370 * Create a new RadioSet
37371 * @param {Object} config The config object
37374 Roo.bootstrap.RadioSet = function(config){
37376 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37380 Roo.bootstrap.RadioSet.register(this);
37385 * Fires when the element is checked or unchecked.
37386 * @param {Roo.bootstrap.RadioSet} this This radio
37387 * @param {Roo.bootstrap.Radio} item The checked item
37392 * Fires when the element is click.
37393 * @param {Roo.bootstrap.RadioSet} this This radio set
37394 * @param {Roo.bootstrap.Radio} item The checked item
37395 * @param {Roo.EventObject} e The event object
37402 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
37410 indicatorpos : 'left',
37412 getAutoCreate : function()
37416 cls : 'roo-radio-set-label',
37420 html : this.fieldLabel
37424 if (Roo.bootstrap.version == 3) {
37427 if(this.indicatorpos == 'left'){
37430 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37431 tooltip : 'This field is required'
37436 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37437 tooltip : 'This field is required'
37443 cls : 'roo-radio-set-items'
37446 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37448 if (align === 'left' && this.fieldLabel.length) {
37451 cls : "roo-radio-set-right",
37457 if(this.labelWidth > 12){
37458 label.style = "width: " + this.labelWidth + 'px';
37461 if(this.labelWidth < 13 && this.labelmd == 0){
37462 this.labelmd = this.labelWidth;
37465 if(this.labellg > 0){
37466 label.cls += ' col-lg-' + this.labellg;
37467 items.cls += ' col-lg-' + (12 - this.labellg);
37470 if(this.labelmd > 0){
37471 label.cls += ' col-md-' + this.labelmd;
37472 items.cls += ' col-md-' + (12 - this.labelmd);
37475 if(this.labelsm > 0){
37476 label.cls += ' col-sm-' + this.labelsm;
37477 items.cls += ' col-sm-' + (12 - this.labelsm);
37480 if(this.labelxs > 0){
37481 label.cls += ' col-xs-' + this.labelxs;
37482 items.cls += ' col-xs-' + (12 - this.labelxs);
37488 cls : 'roo-radio-set',
37492 cls : 'roo-radio-set-input',
37495 value : this.value ? this.value : ''
37502 if(this.weight.length){
37503 cfg.cls += ' roo-radio-' + this.weight;
37507 cfg.cls += ' roo-radio-set-inline';
37511 ['xs','sm','md','lg'].map(function(size){
37512 if (settings[size]) {
37513 cfg.cls += ' col-' + size + '-' + settings[size];
37521 initEvents : function()
37523 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37524 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37526 if(!this.fieldLabel.length){
37527 this.labelEl.hide();
37530 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37531 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37533 this.indicator = this.indicatorEl();
37535 if(this.indicator){
37536 this.indicator.addClass('invisible');
37539 this.originalValue = this.getValue();
37543 inputEl: function ()
37545 return this.el.select('.roo-radio-set-input', true).first();
37548 getChildContainer : function()
37550 return this.itemsEl;
37553 register : function(item)
37555 this.radioes.push(item);
37559 validate : function()
37561 if(this.getVisibilityEl().hasClass('hidden')){
37567 Roo.each(this.radioes, function(i){
37576 if(this.allowBlank) {
37580 if(this.disabled || valid){
37585 this.markInvalid();
37590 markValid : function()
37592 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37593 this.indicatorEl().removeClass('visible');
37594 this.indicatorEl().addClass('invisible');
37598 if (Roo.bootstrap.version == 3) {
37599 this.el.removeClass([this.invalidClass, this.validClass]);
37600 this.el.addClass(this.validClass);
37602 this.el.removeClass(['is-invalid','is-valid']);
37603 this.el.addClass(['is-valid']);
37605 this.fireEvent('valid', this);
37608 markInvalid : function(msg)
37610 if(this.allowBlank || this.disabled){
37614 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37615 this.indicatorEl().removeClass('invisible');
37616 this.indicatorEl().addClass('visible');
37618 if (Roo.bootstrap.version == 3) {
37619 this.el.removeClass([this.invalidClass, this.validClass]);
37620 this.el.addClass(this.invalidClass);
37622 this.el.removeClass(['is-invalid','is-valid']);
37623 this.el.addClass(['is-invalid']);
37626 this.fireEvent('invalid', this, msg);
37630 setValue : function(v, suppressEvent)
37632 if(this.value === v){
37639 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37642 Roo.each(this.radioes, function(i){
37644 i.el.removeClass('checked');
37647 Roo.each(this.radioes, function(i){
37649 if(i.value === v || i.value.toString() === v.toString()){
37651 i.el.addClass('checked');
37653 if(suppressEvent !== true){
37654 this.fireEvent('check', this, i);
37665 clearInvalid : function(){
37667 if(!this.el || this.preventMark){
37671 this.el.removeClass([this.invalidClass]);
37673 this.fireEvent('valid', this);
37678 Roo.apply(Roo.bootstrap.RadioSet, {
37682 register : function(set)
37684 this.groups[set.name] = set;
37687 get: function(name)
37689 if (typeof(this.groups[name]) == 'undefined') {
37693 return this.groups[name] ;
37699 * Ext JS Library 1.1.1
37700 * Copyright(c) 2006-2007, Ext JS, LLC.
37702 * Originally Released Under LGPL - original licence link has changed is not relivant.
37705 * <script type="text/javascript">
37710 * @class Roo.bootstrap.SplitBar
37711 * @extends Roo.util.Observable
37712 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37716 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37717 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37718 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37719 split.minSize = 100;
37720 split.maxSize = 600;
37721 split.animate = true;
37722 split.on('moved', splitterMoved);
37725 * Create a new SplitBar
37726 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37727 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37728 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37729 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37730 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37731 position of the SplitBar).
37733 Roo.bootstrap.SplitBar = function(cfg){
37738 // dragElement : elm
37739 // resizingElement: el,
37741 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37742 // placement : Roo.bootstrap.SplitBar.LEFT ,
37743 // existingProxy ???
37746 this.el = Roo.get(cfg.dragElement, true);
37747 this.el.dom.unselectable = "on";
37749 this.resizingEl = Roo.get(cfg.resizingElement, true);
37753 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37754 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37757 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37760 * The minimum size of the resizing element. (Defaults to 0)
37766 * The maximum size of the resizing element. (Defaults to 2000)
37769 this.maxSize = 2000;
37772 * Whether to animate the transition to the new size
37775 this.animate = false;
37778 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37781 this.useShim = false;
37786 if(!cfg.existingProxy){
37788 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37790 this.proxy = Roo.get(cfg.existingProxy).dom;
37793 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37796 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37799 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37802 this.dragSpecs = {};
37805 * @private The adapter to use to positon and resize elements
37807 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37808 this.adapter.init(this);
37810 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37812 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37813 this.el.addClass("roo-splitbar-h");
37816 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37817 this.el.addClass("roo-splitbar-v");
37823 * Fires when the splitter is moved (alias for {@link #event-moved})
37824 * @param {Roo.bootstrap.SplitBar} this
37825 * @param {Number} newSize the new width or height
37830 * Fires when the splitter is moved
37831 * @param {Roo.bootstrap.SplitBar} this
37832 * @param {Number} newSize the new width or height
37836 * @event beforeresize
37837 * Fires before the splitter is dragged
37838 * @param {Roo.bootstrap.SplitBar} this
37840 "beforeresize" : true,
37842 "beforeapply" : true
37845 Roo.util.Observable.call(this);
37848 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37849 onStartProxyDrag : function(x, y){
37850 this.fireEvent("beforeresize", this);
37852 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37854 o.enableDisplayMode("block");
37855 // all splitbars share the same overlay
37856 Roo.bootstrap.SplitBar.prototype.overlay = o;
37858 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37859 this.overlay.show();
37860 Roo.get(this.proxy).setDisplayed("block");
37861 var size = this.adapter.getElementSize(this);
37862 this.activeMinSize = this.getMinimumSize();;
37863 this.activeMaxSize = this.getMaximumSize();;
37864 var c1 = size - this.activeMinSize;
37865 var c2 = Math.max(this.activeMaxSize - size, 0);
37866 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37867 this.dd.resetConstraints();
37868 this.dd.setXConstraint(
37869 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37870 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37872 this.dd.setYConstraint(0, 0);
37874 this.dd.resetConstraints();
37875 this.dd.setXConstraint(0, 0);
37876 this.dd.setYConstraint(
37877 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37878 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37881 this.dragSpecs.startSize = size;
37882 this.dragSpecs.startPoint = [x, y];
37883 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37887 * @private Called after the drag operation by the DDProxy
37889 onEndProxyDrag : function(e){
37890 Roo.get(this.proxy).setDisplayed(false);
37891 var endPoint = Roo.lib.Event.getXY(e);
37893 this.overlay.hide();
37896 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37897 newSize = this.dragSpecs.startSize +
37898 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37899 endPoint[0] - this.dragSpecs.startPoint[0] :
37900 this.dragSpecs.startPoint[0] - endPoint[0]
37903 newSize = this.dragSpecs.startSize +
37904 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37905 endPoint[1] - this.dragSpecs.startPoint[1] :
37906 this.dragSpecs.startPoint[1] - endPoint[1]
37909 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37910 if(newSize != this.dragSpecs.startSize){
37911 if(this.fireEvent('beforeapply', this, newSize) !== false){
37912 this.adapter.setElementSize(this, newSize);
37913 this.fireEvent("moved", this, newSize);
37914 this.fireEvent("resize", this, newSize);
37920 * Get the adapter this SplitBar uses
37921 * @return The adapter object
37923 getAdapter : function(){
37924 return this.adapter;
37928 * Set the adapter this SplitBar uses
37929 * @param {Object} adapter A SplitBar adapter object
37931 setAdapter : function(adapter){
37932 this.adapter = adapter;
37933 this.adapter.init(this);
37937 * Gets the minimum size for the resizing element
37938 * @return {Number} The minimum size
37940 getMinimumSize : function(){
37941 return this.minSize;
37945 * Sets the minimum size for the resizing element
37946 * @param {Number} minSize The minimum size
37948 setMinimumSize : function(minSize){
37949 this.minSize = minSize;
37953 * Gets the maximum size for the resizing element
37954 * @return {Number} The maximum size
37956 getMaximumSize : function(){
37957 return this.maxSize;
37961 * Sets the maximum size for the resizing element
37962 * @param {Number} maxSize The maximum size
37964 setMaximumSize : function(maxSize){
37965 this.maxSize = maxSize;
37969 * Sets the initialize size for the resizing element
37970 * @param {Number} size The initial size
37972 setCurrentSize : function(size){
37973 var oldAnimate = this.animate;
37974 this.animate = false;
37975 this.adapter.setElementSize(this, size);
37976 this.animate = oldAnimate;
37980 * Destroy this splitbar.
37981 * @param {Boolean} removeEl True to remove the element
37983 destroy : function(removeEl){
37985 this.shim.remove();
37988 this.proxy.parentNode.removeChild(this.proxy);
37996 * @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.
37998 Roo.bootstrap.SplitBar.createProxy = function(dir){
37999 var proxy = new Roo.Element(document.createElement("div"));
38000 proxy.unselectable();
38001 var cls = 'roo-splitbar-proxy';
38002 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38003 document.body.appendChild(proxy.dom);
38008 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38009 * Default Adapter. It assumes the splitter and resizing element are not positioned
38010 * elements and only gets/sets the width of the element. Generally used for table based layouts.
38012 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38015 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38016 // do nothing for now
38017 init : function(s){
38021 * Called before drag operations to get the current size of the resizing element.
38022 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38024 getElementSize : function(s){
38025 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38026 return s.resizingEl.getWidth();
38028 return s.resizingEl.getHeight();
38033 * Called after drag operations to set the size of the resizing element.
38034 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38035 * @param {Number} newSize The new size to set
38036 * @param {Function} onComplete A function to be invoked when resizing is complete
38038 setElementSize : function(s, newSize, onComplete){
38039 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38041 s.resizingEl.setWidth(newSize);
38043 onComplete(s, newSize);
38046 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38051 s.resizingEl.setHeight(newSize);
38053 onComplete(s, newSize);
38056 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38063 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38064 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38065 * Adapter that moves the splitter element to align with the resized sizing element.
38066 * Used with an absolute positioned SplitBar.
38067 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38068 * document.body, make sure you assign an id to the body element.
38070 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38071 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38072 this.container = Roo.get(container);
38075 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38076 init : function(s){
38077 this.basic.init(s);
38080 getElementSize : function(s){
38081 return this.basic.getElementSize(s);
38084 setElementSize : function(s, newSize, onComplete){
38085 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38088 moveSplitter : function(s){
38089 var yes = Roo.bootstrap.SplitBar;
38090 switch(s.placement){
38092 s.el.setX(s.resizingEl.getRight());
38095 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38098 s.el.setY(s.resizingEl.getBottom());
38101 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38108 * Orientation constant - Create a vertical SplitBar
38112 Roo.bootstrap.SplitBar.VERTICAL = 1;
38115 * Orientation constant - Create a horizontal SplitBar
38119 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38122 * Placement constant - The resizing element is to the left of the splitter element
38126 Roo.bootstrap.SplitBar.LEFT = 1;
38129 * Placement constant - The resizing element is to the right of the splitter element
38133 Roo.bootstrap.SplitBar.RIGHT = 2;
38136 * Placement constant - The resizing element is positioned above the splitter element
38140 Roo.bootstrap.SplitBar.TOP = 3;
38143 * Placement constant - The resizing element is positioned under splitter element
38147 Roo.bootstrap.SplitBar.BOTTOM = 4;
38148 Roo.namespace("Roo.bootstrap.layout");/*
38150 * Ext JS Library 1.1.1
38151 * Copyright(c) 2006-2007, Ext JS, LLC.
38153 * Originally Released Under LGPL - original licence link has changed is not relivant.
38156 * <script type="text/javascript">
38160 * @class Roo.bootstrap.layout.Manager
38161 * @extends Roo.bootstrap.Component
38162 * Base class for layout managers.
38164 Roo.bootstrap.layout.Manager = function(config)
38166 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38172 /** false to disable window resize monitoring @type Boolean */
38173 this.monitorWindowResize = true;
38178 * Fires when a layout is performed.
38179 * @param {Roo.LayoutManager} this
38183 * @event regionresized
38184 * Fires when the user resizes a region.
38185 * @param {Roo.LayoutRegion} region The resized region
38186 * @param {Number} newSize The new size (width for east/west, height for north/south)
38188 "regionresized" : true,
38190 * @event regioncollapsed
38191 * Fires when a region is collapsed.
38192 * @param {Roo.LayoutRegion} region The collapsed region
38194 "regioncollapsed" : true,
38196 * @event regionexpanded
38197 * Fires when a region is expanded.
38198 * @param {Roo.LayoutRegion} region The expanded region
38200 "regionexpanded" : true
38202 this.updating = false;
38205 this.el = Roo.get(config.el);
38211 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38216 monitorWindowResize : true,
38222 onRender : function(ct, position)
38225 this.el = Roo.get(ct);
38228 //this.fireEvent('render',this);
38232 initEvents: function()
38236 // ie scrollbar fix
38237 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38238 document.body.scroll = "no";
38239 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38240 this.el.position('relative');
38242 this.id = this.el.id;
38243 this.el.addClass("roo-layout-container");
38244 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38245 if(this.el.dom != document.body ) {
38246 this.el.on('resize', this.layout,this);
38247 this.el.on('show', this.layout,this);
38253 * Returns true if this layout is currently being updated
38254 * @return {Boolean}
38256 isUpdating : function(){
38257 return this.updating;
38261 * Suspend the LayoutManager from doing auto-layouts while
38262 * making multiple add or remove calls
38264 beginUpdate : function(){
38265 this.updating = true;
38269 * Restore auto-layouts and optionally disable the manager from performing a layout
38270 * @param {Boolean} noLayout true to disable a layout update
38272 endUpdate : function(noLayout){
38273 this.updating = false;
38279 layout: function(){
38283 onRegionResized : function(region, newSize){
38284 this.fireEvent("regionresized", region, newSize);
38288 onRegionCollapsed : function(region){
38289 this.fireEvent("regioncollapsed", region);
38292 onRegionExpanded : function(region){
38293 this.fireEvent("regionexpanded", region);
38297 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38298 * performs box-model adjustments.
38299 * @return {Object} The size as an object {width: (the width), height: (the height)}
38301 getViewSize : function()
38304 if(this.el.dom != document.body){
38305 size = this.el.getSize();
38307 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38309 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38310 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38315 * Returns the Element this layout is bound to.
38316 * @return {Roo.Element}
38318 getEl : function(){
38323 * Returns the specified region.
38324 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38325 * @return {Roo.LayoutRegion}
38327 getRegion : function(target){
38328 return this.regions[target.toLowerCase()];
38331 onWindowResize : function(){
38332 if(this.monitorWindowResize){
38339 * Ext JS Library 1.1.1
38340 * Copyright(c) 2006-2007, Ext JS, LLC.
38342 * Originally Released Under LGPL - original licence link has changed is not relivant.
38345 * <script type="text/javascript">
38348 * @class Roo.bootstrap.layout.Border
38349 * @extends Roo.bootstrap.layout.Manager
38350 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38351 * please see: examples/bootstrap/nested.html<br><br>
38353 <b>The container the layout is rendered into can be either the body element or any other element.
38354 If it is not the body element, the container needs to either be an absolute positioned element,
38355 or you will need to add "position:relative" to the css of the container. You will also need to specify
38356 the container size if it is not the body element.</b>
38359 * Create a new Border
38360 * @param {Object} config Configuration options
38362 Roo.bootstrap.layout.Border = function(config){
38363 config = config || {};
38364 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38368 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38369 if(config[region]){
38370 config[region].region = region;
38371 this.addRegion(config[region]);
38377 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38379 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38381 parent : false, // this might point to a 'nest' or a ???
38384 * Creates and adds a new region if it doesn't already exist.
38385 * @param {String} target The target region key (north, south, east, west or center).
38386 * @param {Object} config The regions config object
38387 * @return {BorderLayoutRegion} The new region
38389 addRegion : function(config)
38391 if(!this.regions[config.region]){
38392 var r = this.factory(config);
38393 this.bindRegion(r);
38395 return this.regions[config.region];
38399 bindRegion : function(r){
38400 this.regions[r.config.region] = r;
38402 r.on("visibilitychange", this.layout, this);
38403 r.on("paneladded", this.layout, this);
38404 r.on("panelremoved", this.layout, this);
38405 r.on("invalidated", this.layout, this);
38406 r.on("resized", this.onRegionResized, this);
38407 r.on("collapsed", this.onRegionCollapsed, this);
38408 r.on("expanded", this.onRegionExpanded, this);
38412 * Performs a layout update.
38414 layout : function()
38416 if(this.updating) {
38420 // render all the rebions if they have not been done alreayd?
38421 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38422 if(this.regions[region] && !this.regions[region].bodyEl){
38423 this.regions[region].onRender(this.el)
38427 var size = this.getViewSize();
38428 var w = size.width;
38429 var h = size.height;
38434 //var x = 0, y = 0;
38436 var rs = this.regions;
38437 var north = rs["north"];
38438 var south = rs["south"];
38439 var west = rs["west"];
38440 var east = rs["east"];
38441 var center = rs["center"];
38442 //if(this.hideOnLayout){ // not supported anymore
38443 //c.el.setStyle("display", "none");
38445 if(north && north.isVisible()){
38446 var b = north.getBox();
38447 var m = north.getMargins();
38448 b.width = w - (m.left+m.right);
38451 centerY = b.height + b.y + m.bottom;
38452 centerH -= centerY;
38453 north.updateBox(this.safeBox(b));
38455 if(south && south.isVisible()){
38456 var b = south.getBox();
38457 var m = south.getMargins();
38458 b.width = w - (m.left+m.right);
38460 var totalHeight = (b.height + m.top + m.bottom);
38461 b.y = h - totalHeight + m.top;
38462 centerH -= totalHeight;
38463 south.updateBox(this.safeBox(b));
38465 if(west && west.isVisible()){
38466 var b = west.getBox();
38467 var m = west.getMargins();
38468 b.height = centerH - (m.top+m.bottom);
38470 b.y = centerY + m.top;
38471 var totalWidth = (b.width + m.left + m.right);
38472 centerX += totalWidth;
38473 centerW -= totalWidth;
38474 west.updateBox(this.safeBox(b));
38476 if(east && east.isVisible()){
38477 var b = east.getBox();
38478 var m = east.getMargins();
38479 b.height = centerH - (m.top+m.bottom);
38480 var totalWidth = (b.width + m.left + m.right);
38481 b.x = w - totalWidth + m.left;
38482 b.y = centerY + m.top;
38483 centerW -= totalWidth;
38484 east.updateBox(this.safeBox(b));
38487 var m = center.getMargins();
38489 x: centerX + m.left,
38490 y: centerY + m.top,
38491 width: centerW - (m.left+m.right),
38492 height: centerH - (m.top+m.bottom)
38494 //if(this.hideOnLayout){
38495 //center.el.setStyle("display", "block");
38497 center.updateBox(this.safeBox(centerBox));
38500 this.fireEvent("layout", this);
38504 safeBox : function(box){
38505 box.width = Math.max(0, box.width);
38506 box.height = Math.max(0, box.height);
38511 * Adds a ContentPanel (or subclass) to this layout.
38512 * @param {String} target The target region key (north, south, east, west or center).
38513 * @param {Roo.ContentPanel} panel The panel to add
38514 * @return {Roo.ContentPanel} The added panel
38516 add : function(target, panel){
38518 target = target.toLowerCase();
38519 return this.regions[target].add(panel);
38523 * Remove a ContentPanel (or subclass) to this layout.
38524 * @param {String} target The target region key (north, south, east, west or center).
38525 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38526 * @return {Roo.ContentPanel} The removed panel
38528 remove : function(target, panel){
38529 target = target.toLowerCase();
38530 return this.regions[target].remove(panel);
38534 * Searches all regions for a panel with the specified id
38535 * @param {String} panelId
38536 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38538 findPanel : function(panelId){
38539 var rs = this.regions;
38540 for(var target in rs){
38541 if(typeof rs[target] != "function"){
38542 var p = rs[target].getPanel(panelId);
38552 * Searches all regions for a panel with the specified id and activates (shows) it.
38553 * @param {String/ContentPanel} panelId The panels id or the panel itself
38554 * @return {Roo.ContentPanel} The shown panel or null
38556 showPanel : function(panelId) {
38557 var rs = this.regions;
38558 for(var target in rs){
38559 var r = rs[target];
38560 if(typeof r != "function"){
38561 if(r.hasPanel(panelId)){
38562 return r.showPanel(panelId);
38570 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38571 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38574 restoreState : function(provider){
38576 provider = Roo.state.Manager;
38578 var sm = new Roo.LayoutStateManager();
38579 sm.init(this, provider);
38585 * Adds a xtype elements to the layout.
38589 xtype : 'ContentPanel',
38596 xtype : 'NestedLayoutPanel',
38602 items : [ ... list of content panels or nested layout panels.. ]
38606 * @param {Object} cfg Xtype definition of item to add.
38608 addxtype : function(cfg)
38610 // basically accepts a pannel...
38611 // can accept a layout region..!?!?
38612 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38615 // theory? children can only be panels??
38617 //if (!cfg.xtype.match(/Panel$/)) {
38622 if (typeof(cfg.region) == 'undefined') {
38623 Roo.log("Failed to add Panel, region was not set");
38627 var region = cfg.region;
38633 xitems = cfg.items;
38638 if ( region == 'center') {
38639 Roo.log("Center: " + cfg.title);
38645 case 'Content': // ContentPanel (el, cfg)
38646 case 'Scroll': // ContentPanel (el, cfg)
38648 cfg.autoCreate = cfg.autoCreate || true;
38649 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38651 // var el = this.el.createChild();
38652 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38655 this.add(region, ret);
38659 case 'TreePanel': // our new panel!
38660 cfg.el = this.el.createChild();
38661 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38662 this.add(region, ret);
38667 // create a new Layout (which is a Border Layout...
38669 var clayout = cfg.layout;
38670 clayout.el = this.el.createChild();
38671 clayout.items = clayout.items || [];
38675 // replace this exitems with the clayout ones..
38676 xitems = clayout.items;
38678 // force background off if it's in center...
38679 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38680 cfg.background = false;
38682 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38685 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38686 //console.log('adding nested layout panel ' + cfg.toSource());
38687 this.add(region, ret);
38688 nb = {}; /// find first...
38693 // needs grid and region
38695 //var el = this.getRegion(region).el.createChild();
38697 *var el = this.el.createChild();
38698 // create the grid first...
38699 cfg.grid.container = el;
38700 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38703 if (region == 'center' && this.active ) {
38704 cfg.background = false;
38707 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38709 this.add(region, ret);
38711 if (cfg.background) {
38712 // render grid on panel activation (if panel background)
38713 ret.on('activate', function(gp) {
38714 if (!gp.grid.rendered) {
38715 // gp.grid.render(el);
38719 // cfg.grid.render(el);
38725 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38726 // it was the old xcomponent building that caused this before.
38727 // espeically if border is the top element in the tree.
38737 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38739 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38740 this.add(region, ret);
38744 throw "Can not add '" + cfg.xtype + "' to Border";
38750 this.beginUpdate();
38754 Roo.each(xitems, function(i) {
38755 region = nb && i.region ? i.region : false;
38757 var add = ret.addxtype(i);
38760 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38761 if (!i.background) {
38762 abn[region] = nb[region] ;
38769 // make the last non-background panel active..
38770 //if (nb) { Roo.log(abn); }
38773 for(var r in abn) {
38774 region = this.getRegion(r);
38776 // tried using nb[r], but it does not work..
38778 region.showPanel(abn[r]);
38789 factory : function(cfg)
38792 var validRegions = Roo.bootstrap.layout.Border.regions;
38794 var target = cfg.region;
38797 var r = Roo.bootstrap.layout;
38801 return new r.North(cfg);
38803 return new r.South(cfg);
38805 return new r.East(cfg);
38807 return new r.West(cfg);
38809 return new r.Center(cfg);
38811 throw 'Layout region "'+target+'" not supported.';
38818 * Ext JS Library 1.1.1
38819 * Copyright(c) 2006-2007, Ext JS, LLC.
38821 * Originally Released Under LGPL - original licence link has changed is not relivant.
38824 * <script type="text/javascript">
38828 * @class Roo.bootstrap.layout.Basic
38829 * @extends Roo.util.Observable
38830 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38831 * and does not have a titlebar, tabs or any other features. All it does is size and position
38832 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38833 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38834 * @cfg {string} region the region that it inhabits..
38835 * @cfg {bool} skipConfig skip config?
38839 Roo.bootstrap.layout.Basic = function(config){
38841 this.mgr = config.mgr;
38843 this.position = config.region;
38845 var skipConfig = config.skipConfig;
38849 * @scope Roo.BasicLayoutRegion
38853 * @event beforeremove
38854 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38855 * @param {Roo.LayoutRegion} this
38856 * @param {Roo.ContentPanel} panel The panel
38857 * @param {Object} e The cancel event object
38859 "beforeremove" : true,
38861 * @event invalidated
38862 * Fires when the layout for this region is changed.
38863 * @param {Roo.LayoutRegion} this
38865 "invalidated" : true,
38867 * @event visibilitychange
38868 * Fires when this region is shown or hidden
38869 * @param {Roo.LayoutRegion} this
38870 * @param {Boolean} visibility true or false
38872 "visibilitychange" : true,
38874 * @event paneladded
38875 * Fires when a panel is added.
38876 * @param {Roo.LayoutRegion} this
38877 * @param {Roo.ContentPanel} panel The panel
38879 "paneladded" : true,
38881 * @event panelremoved
38882 * Fires when a panel is removed.
38883 * @param {Roo.LayoutRegion} this
38884 * @param {Roo.ContentPanel} panel The panel
38886 "panelremoved" : true,
38888 * @event beforecollapse
38889 * Fires when this region before collapse.
38890 * @param {Roo.LayoutRegion} this
38892 "beforecollapse" : true,
38895 * Fires when this region is collapsed.
38896 * @param {Roo.LayoutRegion} this
38898 "collapsed" : true,
38901 * Fires when this region is expanded.
38902 * @param {Roo.LayoutRegion} this
38907 * Fires when this region is slid into view.
38908 * @param {Roo.LayoutRegion} this
38910 "slideshow" : true,
38913 * Fires when this region slides out of view.
38914 * @param {Roo.LayoutRegion} this
38916 "slidehide" : true,
38918 * @event panelactivated
38919 * Fires when a panel is activated.
38920 * @param {Roo.LayoutRegion} this
38921 * @param {Roo.ContentPanel} panel The activated panel
38923 "panelactivated" : true,
38926 * Fires when the user resizes this region.
38927 * @param {Roo.LayoutRegion} this
38928 * @param {Number} newSize The new size (width for east/west, height for north/south)
38932 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38933 this.panels = new Roo.util.MixedCollection();
38934 this.panels.getKey = this.getPanelId.createDelegate(this);
38936 this.activePanel = null;
38937 // ensure listeners are added...
38939 if (config.listeners || config.events) {
38940 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38941 listeners : config.listeners || {},
38942 events : config.events || {}
38946 if(skipConfig !== true){
38947 this.applyConfig(config);
38951 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38953 getPanelId : function(p){
38957 applyConfig : function(config){
38958 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38959 this.config = config;
38964 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38965 * the width, for horizontal (north, south) the height.
38966 * @param {Number} newSize The new width or height
38968 resizeTo : function(newSize){
38969 var el = this.el ? this.el :
38970 (this.activePanel ? this.activePanel.getEl() : null);
38972 switch(this.position){
38975 el.setWidth(newSize);
38976 this.fireEvent("resized", this, newSize);
38980 el.setHeight(newSize);
38981 this.fireEvent("resized", this, newSize);
38987 getBox : function(){
38988 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38991 getMargins : function(){
38992 return this.margins;
38995 updateBox : function(box){
38997 var el = this.activePanel.getEl();
38998 el.dom.style.left = box.x + "px";
38999 el.dom.style.top = box.y + "px";
39000 this.activePanel.setSize(box.width, box.height);
39004 * Returns the container element for this region.
39005 * @return {Roo.Element}
39007 getEl : function(){
39008 return this.activePanel;
39012 * Returns true if this region is currently visible.
39013 * @return {Boolean}
39015 isVisible : function(){
39016 return this.activePanel ? true : false;
39019 setActivePanel : function(panel){
39020 panel = this.getPanel(panel);
39021 if(this.activePanel && this.activePanel != panel){
39022 this.activePanel.setActiveState(false);
39023 this.activePanel.getEl().setLeftTop(-10000,-10000);
39025 this.activePanel = panel;
39026 panel.setActiveState(true);
39028 panel.setSize(this.box.width, this.box.height);
39030 this.fireEvent("panelactivated", this, panel);
39031 this.fireEvent("invalidated");
39035 * Show the specified panel.
39036 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39037 * @return {Roo.ContentPanel} The shown panel or null
39039 showPanel : function(panel){
39040 panel = this.getPanel(panel);
39042 this.setActivePanel(panel);
39048 * Get the active panel for this region.
39049 * @return {Roo.ContentPanel} The active panel or null
39051 getActivePanel : function(){
39052 return this.activePanel;
39056 * Add the passed ContentPanel(s)
39057 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39058 * @return {Roo.ContentPanel} The panel added (if only one was added)
39060 add : function(panel){
39061 if(arguments.length > 1){
39062 for(var i = 0, len = arguments.length; i < len; i++) {
39063 this.add(arguments[i]);
39067 if(this.hasPanel(panel)){
39068 this.showPanel(panel);
39071 var el = panel.getEl();
39072 if(el.dom.parentNode != this.mgr.el.dom){
39073 this.mgr.el.dom.appendChild(el.dom);
39075 if(panel.setRegion){
39076 panel.setRegion(this);
39078 this.panels.add(panel);
39079 el.setStyle("position", "absolute");
39080 if(!panel.background){
39081 this.setActivePanel(panel);
39082 if(this.config.initialSize && this.panels.getCount()==1){
39083 this.resizeTo(this.config.initialSize);
39086 this.fireEvent("paneladded", this, panel);
39091 * Returns true if the panel is in this region.
39092 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39093 * @return {Boolean}
39095 hasPanel : function(panel){
39096 if(typeof panel == "object"){ // must be panel obj
39097 panel = panel.getId();
39099 return this.getPanel(panel) ? true : false;
39103 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39104 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39105 * @param {Boolean} preservePanel Overrides the config preservePanel option
39106 * @return {Roo.ContentPanel} The panel that was removed
39108 remove : function(panel, preservePanel){
39109 panel = this.getPanel(panel);
39114 this.fireEvent("beforeremove", this, panel, e);
39115 if(e.cancel === true){
39118 var panelId = panel.getId();
39119 this.panels.removeKey(panelId);
39124 * Returns the panel specified or null if it's not in this region.
39125 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39126 * @return {Roo.ContentPanel}
39128 getPanel : function(id){
39129 if(typeof id == "object"){ // must be panel obj
39132 return this.panels.get(id);
39136 * Returns this regions position (north/south/east/west/center).
39139 getPosition: function(){
39140 return this.position;
39144 * Ext JS Library 1.1.1
39145 * Copyright(c) 2006-2007, Ext JS, LLC.
39147 * Originally Released Under LGPL - original licence link has changed is not relivant.
39150 * <script type="text/javascript">
39154 * @class Roo.bootstrap.layout.Region
39155 * @extends Roo.bootstrap.layout.Basic
39156 * This class represents a region in a layout manager.
39158 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39159 * @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})
39160 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
39161 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
39162 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
39163 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
39164 * @cfg {String} title The title for the region (overrides panel titles)
39165 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
39166 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39167 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
39168 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39169 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
39170 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39171 * the space available, similar to FireFox 1.5 tabs (defaults to false)
39172 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
39173 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
39174 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
39176 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
39177 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
39178 * @cfg {Boolean} disableTabTips True to disable tab tooltips
39179 * @cfg {Number} width For East/West panels
39180 * @cfg {Number} height For North/South panels
39181 * @cfg {Boolean} split To show the splitter
39182 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
39184 * @cfg {string} cls Extra CSS classes to add to region
39186 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
39187 * @cfg {string} region the region that it inhabits..
39190 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
39191 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
39193 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
39194 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
39195 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
39197 Roo.bootstrap.layout.Region = function(config)
39199 this.applyConfig(config);
39201 var mgr = config.mgr;
39202 var pos = config.region;
39203 config.skipConfig = true;
39204 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39207 this.onRender(mgr.el);
39210 this.visible = true;
39211 this.collapsed = false;
39212 this.unrendered_panels = [];
39215 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39217 position: '', // set by wrapper (eg. north/south etc..)
39218 unrendered_panels : null, // unrendered panels.
39220 tabPosition : false,
39222 mgr: false, // points to 'Border'
39225 createBody : function(){
39226 /** This region's body element
39227 * @type Roo.Element */
39228 this.bodyEl = this.el.createChild({
39230 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39234 onRender: function(ctr, pos)
39236 var dh = Roo.DomHelper;
39237 /** This region's container element
39238 * @type Roo.Element */
39239 this.el = dh.append(ctr.dom, {
39241 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39243 /** This region's title element
39244 * @type Roo.Element */
39246 this.titleEl = dh.append(this.el.dom, {
39248 unselectable: "on",
39249 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39251 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
39252 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39256 this.titleEl.enableDisplayMode();
39257 /** This region's title text element
39258 * @type HTMLElement */
39259 this.titleTextEl = this.titleEl.dom.firstChild;
39260 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39262 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39263 this.closeBtn.enableDisplayMode();
39264 this.closeBtn.on("click", this.closeClicked, this);
39265 this.closeBtn.hide();
39267 this.createBody(this.config);
39268 if(this.config.hideWhenEmpty){
39270 this.on("paneladded", this.validateVisibility, this);
39271 this.on("panelremoved", this.validateVisibility, this);
39273 if(this.autoScroll){
39274 this.bodyEl.setStyle("overflow", "auto");
39276 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39278 //if(c.titlebar !== false){
39279 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39280 this.titleEl.hide();
39282 this.titleEl.show();
39283 if(this.config.title){
39284 this.titleTextEl.innerHTML = this.config.title;
39288 if(this.config.collapsed){
39289 this.collapse(true);
39291 if(this.config.hidden){
39295 if (this.unrendered_panels && this.unrendered_panels.length) {
39296 for (var i =0;i< this.unrendered_panels.length; i++) {
39297 this.add(this.unrendered_panels[i]);
39299 this.unrendered_panels = null;
39305 applyConfig : function(c)
39308 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39309 var dh = Roo.DomHelper;
39310 if(c.titlebar !== false){
39311 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39312 this.collapseBtn.on("click", this.collapse, this);
39313 this.collapseBtn.enableDisplayMode();
39315 if(c.showPin === true || this.showPin){
39316 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39317 this.stickBtn.enableDisplayMode();
39318 this.stickBtn.on("click", this.expand, this);
39319 this.stickBtn.hide();
39324 /** This region's collapsed element
39325 * @type Roo.Element */
39328 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39329 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39332 if(c.floatable !== false){
39333 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39334 this.collapsedEl.on("click", this.collapseClick, this);
39337 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39338 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39339 id: "message", unselectable: "on", style:{"float":"left"}});
39340 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39342 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39343 this.expandBtn.on("click", this.expand, this);
39347 if(this.collapseBtn){
39348 this.collapseBtn.setVisible(c.collapsible == true);
39351 this.cmargins = c.cmargins || this.cmargins ||
39352 (this.position == "west" || this.position == "east" ?
39353 {top: 0, left: 2, right:2, bottom: 0} :
39354 {top: 2, left: 0, right:0, bottom: 2});
39356 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39359 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39361 this.autoScroll = c.autoScroll || false;
39366 this.duration = c.duration || .30;
39367 this.slideDuration = c.slideDuration || .45;
39372 * Returns true if this region is currently visible.
39373 * @return {Boolean}
39375 isVisible : function(){
39376 return this.visible;
39380 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39381 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39383 //setCollapsedTitle : function(title){
39384 // title = title || " ";
39385 // if(this.collapsedTitleTextEl){
39386 // this.collapsedTitleTextEl.innerHTML = title;
39390 getBox : function(){
39392 // if(!this.collapsed){
39393 b = this.el.getBox(false, true);
39395 // b = this.collapsedEl.getBox(false, true);
39400 getMargins : function(){
39401 return this.margins;
39402 //return this.collapsed ? this.cmargins : this.margins;
39405 highlight : function(){
39406 this.el.addClass("x-layout-panel-dragover");
39409 unhighlight : function(){
39410 this.el.removeClass("x-layout-panel-dragover");
39413 updateBox : function(box)
39415 if (!this.bodyEl) {
39416 return; // not rendered yet..
39420 if(!this.collapsed){
39421 this.el.dom.style.left = box.x + "px";
39422 this.el.dom.style.top = box.y + "px";
39423 this.updateBody(box.width, box.height);
39425 this.collapsedEl.dom.style.left = box.x + "px";
39426 this.collapsedEl.dom.style.top = box.y + "px";
39427 this.collapsedEl.setSize(box.width, box.height);
39430 this.tabs.autoSizeTabs();
39434 updateBody : function(w, h)
39437 this.el.setWidth(w);
39438 w -= this.el.getBorderWidth("rl");
39439 if(this.config.adjustments){
39440 w += this.config.adjustments[0];
39443 if(h !== null && h > 0){
39444 this.el.setHeight(h);
39445 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39446 h -= this.el.getBorderWidth("tb");
39447 if(this.config.adjustments){
39448 h += this.config.adjustments[1];
39450 this.bodyEl.setHeight(h);
39452 h = this.tabs.syncHeight(h);
39455 if(this.panelSize){
39456 w = w !== null ? w : this.panelSize.width;
39457 h = h !== null ? h : this.panelSize.height;
39459 if(this.activePanel){
39460 var el = this.activePanel.getEl();
39461 w = w !== null ? w : el.getWidth();
39462 h = h !== null ? h : el.getHeight();
39463 this.panelSize = {width: w, height: h};
39464 this.activePanel.setSize(w, h);
39466 if(Roo.isIE && this.tabs){
39467 this.tabs.el.repaint();
39472 * Returns the container element for this region.
39473 * @return {Roo.Element}
39475 getEl : function(){
39480 * Hides this region.
39483 //if(!this.collapsed){
39484 this.el.dom.style.left = "-2000px";
39487 // this.collapsedEl.dom.style.left = "-2000px";
39488 // this.collapsedEl.hide();
39490 this.visible = false;
39491 this.fireEvent("visibilitychange", this, false);
39495 * Shows this region if it was previously hidden.
39498 //if(!this.collapsed){
39501 // this.collapsedEl.show();
39503 this.visible = true;
39504 this.fireEvent("visibilitychange", this, true);
39507 closeClicked : function(){
39508 if(this.activePanel){
39509 this.remove(this.activePanel);
39513 collapseClick : function(e){
39515 e.stopPropagation();
39518 e.stopPropagation();
39524 * Collapses this region.
39525 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39528 collapse : function(skipAnim, skipCheck = false){
39529 if(this.collapsed) {
39533 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39535 this.collapsed = true;
39537 this.split.el.hide();
39539 if(this.config.animate && skipAnim !== true){
39540 this.fireEvent("invalidated", this);
39541 this.animateCollapse();
39543 this.el.setLocation(-20000,-20000);
39545 this.collapsedEl.show();
39546 this.fireEvent("collapsed", this);
39547 this.fireEvent("invalidated", this);
39553 animateCollapse : function(){
39558 * Expands this region if it was previously collapsed.
39559 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39560 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39563 expand : function(e, skipAnim){
39565 e.stopPropagation();
39567 if(!this.collapsed || this.el.hasActiveFx()) {
39571 this.afterSlideIn();
39574 this.collapsed = false;
39575 if(this.config.animate && skipAnim !== true){
39576 this.animateExpand();
39580 this.split.el.show();
39582 this.collapsedEl.setLocation(-2000,-2000);
39583 this.collapsedEl.hide();
39584 this.fireEvent("invalidated", this);
39585 this.fireEvent("expanded", this);
39589 animateExpand : function(){
39593 initTabs : function()
39595 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39597 var ts = new Roo.bootstrap.panel.Tabs({
39598 el: this.bodyEl.dom,
39600 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39601 disableTooltips: this.config.disableTabTips,
39602 toolbar : this.config.toolbar
39605 if(this.config.hideTabs){
39606 ts.stripWrap.setDisplayed(false);
39609 ts.resizeTabs = this.config.resizeTabs === true;
39610 ts.minTabWidth = this.config.minTabWidth || 40;
39611 ts.maxTabWidth = this.config.maxTabWidth || 250;
39612 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39613 ts.monitorResize = false;
39614 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39615 ts.bodyEl.addClass('roo-layout-tabs-body');
39616 this.panels.each(this.initPanelAsTab, this);
39619 initPanelAsTab : function(panel){
39620 var ti = this.tabs.addTab(
39624 this.config.closeOnTab && panel.isClosable(),
39627 if(panel.tabTip !== undefined){
39628 ti.setTooltip(panel.tabTip);
39630 ti.on("activate", function(){
39631 this.setActivePanel(panel);
39634 if(this.config.closeOnTab){
39635 ti.on("beforeclose", function(t, e){
39637 this.remove(panel);
39641 panel.tabItem = ti;
39646 updatePanelTitle : function(panel, title)
39648 if(this.activePanel == panel){
39649 this.updateTitle(title);
39652 var ti = this.tabs.getTab(panel.getEl().id);
39654 if(panel.tabTip !== undefined){
39655 ti.setTooltip(panel.tabTip);
39660 updateTitle : function(title){
39661 if(this.titleTextEl && !this.config.title){
39662 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39666 setActivePanel : function(panel)
39668 panel = this.getPanel(panel);
39669 if(this.activePanel && this.activePanel != panel){
39670 if(this.activePanel.setActiveState(false) === false){
39674 this.activePanel = panel;
39675 panel.setActiveState(true);
39676 if(this.panelSize){
39677 panel.setSize(this.panelSize.width, this.panelSize.height);
39680 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39682 this.updateTitle(panel.getTitle());
39684 this.fireEvent("invalidated", this);
39686 this.fireEvent("panelactivated", this, panel);
39690 * Shows the specified panel.
39691 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39692 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39694 showPanel : function(panel)
39696 panel = this.getPanel(panel);
39699 var tab = this.tabs.getTab(panel.getEl().id);
39700 if(tab.isHidden()){
39701 this.tabs.unhideTab(tab.id);
39705 this.setActivePanel(panel);
39712 * Get the active panel for this region.
39713 * @return {Roo.ContentPanel} The active panel or null
39715 getActivePanel : function(){
39716 return this.activePanel;
39719 validateVisibility : function(){
39720 if(this.panels.getCount() < 1){
39721 this.updateTitle(" ");
39722 this.closeBtn.hide();
39725 if(!this.isVisible()){
39732 * Adds the passed ContentPanel(s) to this region.
39733 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39734 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39736 add : function(panel)
39738 if(arguments.length > 1){
39739 for(var i = 0, len = arguments.length; i < len; i++) {
39740 this.add(arguments[i]);
39745 // if we have not been rendered yet, then we can not really do much of this..
39746 if (!this.bodyEl) {
39747 this.unrendered_panels.push(panel);
39754 if(this.hasPanel(panel)){
39755 this.showPanel(panel);
39758 panel.setRegion(this);
39759 this.panels.add(panel);
39760 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39761 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39762 // and hide them... ???
39763 this.bodyEl.dom.appendChild(panel.getEl().dom);
39764 if(panel.background !== true){
39765 this.setActivePanel(panel);
39767 this.fireEvent("paneladded", this, panel);
39774 this.initPanelAsTab(panel);
39778 if(panel.background !== true){
39779 this.tabs.activate(panel.getEl().id);
39781 this.fireEvent("paneladded", this, panel);
39786 * Hides the tab for the specified panel.
39787 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39789 hidePanel : function(panel){
39790 if(this.tabs && (panel = this.getPanel(panel))){
39791 this.tabs.hideTab(panel.getEl().id);
39796 * Unhides the tab for a previously hidden panel.
39797 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39799 unhidePanel : function(panel){
39800 if(this.tabs && (panel = this.getPanel(panel))){
39801 this.tabs.unhideTab(panel.getEl().id);
39805 clearPanels : function(){
39806 while(this.panels.getCount() > 0){
39807 this.remove(this.panels.first());
39812 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39813 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39814 * @param {Boolean} preservePanel Overrides the config preservePanel option
39815 * @return {Roo.ContentPanel} The panel that was removed
39817 remove : function(panel, preservePanel)
39819 panel = this.getPanel(panel);
39824 this.fireEvent("beforeremove", this, panel, e);
39825 if(e.cancel === true){
39828 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39829 var panelId = panel.getId();
39830 this.panels.removeKey(panelId);
39832 document.body.appendChild(panel.getEl().dom);
39835 this.tabs.removeTab(panel.getEl().id);
39836 }else if (!preservePanel){
39837 this.bodyEl.dom.removeChild(panel.getEl().dom);
39839 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39840 var p = this.panels.first();
39841 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39842 tempEl.appendChild(p.getEl().dom);
39843 this.bodyEl.update("");
39844 this.bodyEl.dom.appendChild(p.getEl().dom);
39846 this.updateTitle(p.getTitle());
39848 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39849 this.setActivePanel(p);
39851 panel.setRegion(null);
39852 if(this.activePanel == panel){
39853 this.activePanel = null;
39855 if(this.config.autoDestroy !== false && preservePanel !== true){
39856 try{panel.destroy();}catch(e){}
39858 this.fireEvent("panelremoved", this, panel);
39863 * Returns the TabPanel component used by this region
39864 * @return {Roo.TabPanel}
39866 getTabs : function(){
39870 createTool : function(parentEl, className){
39871 var btn = Roo.DomHelper.append(parentEl, {
39873 cls: "x-layout-tools-button",
39876 cls: "roo-layout-tools-button-inner " + className,
39880 btn.addClassOnOver("roo-layout-tools-button-over");
39885 * Ext JS Library 1.1.1
39886 * Copyright(c) 2006-2007, Ext JS, LLC.
39888 * Originally Released Under LGPL - original licence link has changed is not relivant.
39891 * <script type="text/javascript">
39897 * @class Roo.SplitLayoutRegion
39898 * @extends Roo.LayoutRegion
39899 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39901 Roo.bootstrap.layout.Split = function(config){
39902 this.cursor = config.cursor;
39903 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39906 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39908 splitTip : "Drag to resize.",
39909 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39910 useSplitTips : false,
39912 applyConfig : function(config){
39913 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39916 onRender : function(ctr,pos) {
39918 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39919 if(!this.config.split){
39924 var splitEl = Roo.DomHelper.append(ctr.dom, {
39926 id: this.el.id + "-split",
39927 cls: "roo-layout-split roo-layout-split-"+this.position,
39930 /** The SplitBar for this region
39931 * @type Roo.SplitBar */
39932 // does not exist yet...
39933 Roo.log([this.position, this.orientation]);
39935 this.split = new Roo.bootstrap.SplitBar({
39936 dragElement : splitEl,
39937 resizingElement: this.el,
39938 orientation : this.orientation
39941 this.split.on("moved", this.onSplitMove, this);
39942 this.split.useShim = this.config.useShim === true;
39943 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39944 if(this.useSplitTips){
39945 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39947 //if(config.collapsible){
39948 // this.split.el.on("dblclick", this.collapse, this);
39951 if(typeof this.config.minSize != "undefined"){
39952 this.split.minSize = this.config.minSize;
39954 if(typeof this.config.maxSize != "undefined"){
39955 this.split.maxSize = this.config.maxSize;
39957 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39958 this.hideSplitter();
39963 getHMaxSize : function(){
39964 var cmax = this.config.maxSize || 10000;
39965 var center = this.mgr.getRegion("center");
39966 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39969 getVMaxSize : function(){
39970 var cmax = this.config.maxSize || 10000;
39971 var center = this.mgr.getRegion("center");
39972 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39975 onSplitMove : function(split, newSize){
39976 this.fireEvent("resized", this, newSize);
39980 * Returns the {@link Roo.SplitBar} for this region.
39981 * @return {Roo.SplitBar}
39983 getSplitBar : function(){
39988 this.hideSplitter();
39989 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39992 hideSplitter : function(){
39994 this.split.el.setLocation(-2000,-2000);
39995 this.split.el.hide();
40001 this.split.el.show();
40003 Roo.bootstrap.layout.Split.superclass.show.call(this);
40006 beforeSlide: function(){
40007 if(Roo.isGecko){// firefox overflow auto bug workaround
40008 this.bodyEl.clip();
40010 this.tabs.bodyEl.clip();
40012 if(this.activePanel){
40013 this.activePanel.getEl().clip();
40015 if(this.activePanel.beforeSlide){
40016 this.activePanel.beforeSlide();
40022 afterSlide : function(){
40023 if(Roo.isGecko){// firefox overflow auto bug workaround
40024 this.bodyEl.unclip();
40026 this.tabs.bodyEl.unclip();
40028 if(this.activePanel){
40029 this.activePanel.getEl().unclip();
40030 if(this.activePanel.afterSlide){
40031 this.activePanel.afterSlide();
40037 initAutoHide : function(){
40038 if(this.autoHide !== false){
40039 if(!this.autoHideHd){
40040 var st = new Roo.util.DelayedTask(this.slideIn, this);
40041 this.autoHideHd = {
40042 "mouseout": function(e){
40043 if(!e.within(this.el, true)){
40047 "mouseover" : function(e){
40053 this.el.on(this.autoHideHd);
40057 clearAutoHide : function(){
40058 if(this.autoHide !== false){
40059 this.el.un("mouseout", this.autoHideHd.mouseout);
40060 this.el.un("mouseover", this.autoHideHd.mouseover);
40064 clearMonitor : function(){
40065 Roo.get(document).un("click", this.slideInIf, this);
40068 // these names are backwards but not changed for compat
40069 slideOut : function(){
40070 if(this.isSlid || this.el.hasActiveFx()){
40073 this.isSlid = true;
40074 if(this.collapseBtn){
40075 this.collapseBtn.hide();
40077 this.closeBtnState = this.closeBtn.getStyle('display');
40078 this.closeBtn.hide();
40080 this.stickBtn.show();
40083 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40084 this.beforeSlide();
40085 this.el.setStyle("z-index", 10001);
40086 this.el.slideIn(this.getSlideAnchor(), {
40087 callback: function(){
40089 this.initAutoHide();
40090 Roo.get(document).on("click", this.slideInIf, this);
40091 this.fireEvent("slideshow", this);
40098 afterSlideIn : function(){
40099 this.clearAutoHide();
40100 this.isSlid = false;
40101 this.clearMonitor();
40102 this.el.setStyle("z-index", "");
40103 if(this.collapseBtn){
40104 this.collapseBtn.show();
40106 this.closeBtn.setStyle('display', this.closeBtnState);
40108 this.stickBtn.hide();
40110 this.fireEvent("slidehide", this);
40113 slideIn : function(cb){
40114 if(!this.isSlid || this.el.hasActiveFx()){
40118 this.isSlid = false;
40119 this.beforeSlide();
40120 this.el.slideOut(this.getSlideAnchor(), {
40121 callback: function(){
40122 this.el.setLeftTop(-10000, -10000);
40124 this.afterSlideIn();
40132 slideInIf : function(e){
40133 if(!e.within(this.el)){
40138 animateCollapse : function(){
40139 this.beforeSlide();
40140 this.el.setStyle("z-index", 20000);
40141 var anchor = this.getSlideAnchor();
40142 this.el.slideOut(anchor, {
40143 callback : function(){
40144 this.el.setStyle("z-index", "");
40145 this.collapsedEl.slideIn(anchor, {duration:.3});
40147 this.el.setLocation(-10000,-10000);
40149 this.fireEvent("collapsed", this);
40156 animateExpand : function(){
40157 this.beforeSlide();
40158 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40159 this.el.setStyle("z-index", 20000);
40160 this.collapsedEl.hide({
40163 this.el.slideIn(this.getSlideAnchor(), {
40164 callback : function(){
40165 this.el.setStyle("z-index", "");
40168 this.split.el.show();
40170 this.fireEvent("invalidated", this);
40171 this.fireEvent("expanded", this);
40199 getAnchor : function(){
40200 return this.anchors[this.position];
40203 getCollapseAnchor : function(){
40204 return this.canchors[this.position];
40207 getSlideAnchor : function(){
40208 return this.sanchors[this.position];
40211 getAlignAdj : function(){
40212 var cm = this.cmargins;
40213 switch(this.position){
40229 getExpandAdj : function(){
40230 var c = this.collapsedEl, cm = this.cmargins;
40231 switch(this.position){
40233 return [-(cm.right+c.getWidth()+cm.left), 0];
40236 return [cm.right+c.getWidth()+cm.left, 0];
40239 return [0, -(cm.top+cm.bottom+c.getHeight())];
40242 return [0, cm.top+cm.bottom+c.getHeight()];
40248 * Ext JS Library 1.1.1
40249 * Copyright(c) 2006-2007, Ext JS, LLC.
40251 * Originally Released Under LGPL - original licence link has changed is not relivant.
40254 * <script type="text/javascript">
40257 * These classes are private internal classes
40259 Roo.bootstrap.layout.Center = function(config){
40260 config.region = "center";
40261 Roo.bootstrap.layout.Region.call(this, config);
40262 this.visible = true;
40263 this.minWidth = config.minWidth || 20;
40264 this.minHeight = config.minHeight || 20;
40267 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40269 // center panel can't be hidden
40273 // center panel can't be hidden
40276 getMinWidth: function(){
40277 return this.minWidth;
40280 getMinHeight: function(){
40281 return this.minHeight;
40295 Roo.bootstrap.layout.North = function(config)
40297 config.region = 'north';
40298 config.cursor = 'n-resize';
40300 Roo.bootstrap.layout.Split.call(this, config);
40304 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40305 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40306 this.split.el.addClass("roo-layout-split-v");
40308 //var size = config.initialSize || config.height;
40309 //if(this.el && typeof size != "undefined"){
40310 // this.el.setHeight(size);
40313 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40315 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40318 onRender : function(ctr, pos)
40320 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40321 var size = this.config.initialSize || this.config.height;
40322 if(this.el && typeof size != "undefined"){
40323 this.el.setHeight(size);
40328 getBox : function(){
40329 if(this.collapsed){
40330 return this.collapsedEl.getBox();
40332 var box = this.el.getBox();
40334 box.height += this.split.el.getHeight();
40339 updateBox : function(box){
40340 if(this.split && !this.collapsed){
40341 box.height -= this.split.el.getHeight();
40342 this.split.el.setLeft(box.x);
40343 this.split.el.setTop(box.y+box.height);
40344 this.split.el.setWidth(box.width);
40346 if(this.collapsed){
40347 this.updateBody(box.width, null);
40349 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40357 Roo.bootstrap.layout.South = function(config){
40358 config.region = 'south';
40359 config.cursor = 's-resize';
40360 Roo.bootstrap.layout.Split.call(this, config);
40362 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40363 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40364 this.split.el.addClass("roo-layout-split-v");
40369 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40370 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40372 onRender : function(ctr, pos)
40374 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40375 var size = this.config.initialSize || this.config.height;
40376 if(this.el && typeof size != "undefined"){
40377 this.el.setHeight(size);
40382 getBox : function(){
40383 if(this.collapsed){
40384 return this.collapsedEl.getBox();
40386 var box = this.el.getBox();
40388 var sh = this.split.el.getHeight();
40395 updateBox : function(box){
40396 if(this.split && !this.collapsed){
40397 var sh = this.split.el.getHeight();
40400 this.split.el.setLeft(box.x);
40401 this.split.el.setTop(box.y-sh);
40402 this.split.el.setWidth(box.width);
40404 if(this.collapsed){
40405 this.updateBody(box.width, null);
40407 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40411 Roo.bootstrap.layout.East = function(config){
40412 config.region = "east";
40413 config.cursor = "e-resize";
40414 Roo.bootstrap.layout.Split.call(this, config);
40416 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40417 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40418 this.split.el.addClass("roo-layout-split-h");
40422 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40423 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40425 onRender : function(ctr, pos)
40427 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40428 var size = this.config.initialSize || this.config.width;
40429 if(this.el && typeof size != "undefined"){
40430 this.el.setWidth(size);
40435 getBox : function(){
40436 if(this.collapsed){
40437 return this.collapsedEl.getBox();
40439 var box = this.el.getBox();
40441 var sw = this.split.el.getWidth();
40448 updateBox : function(box){
40449 if(this.split && !this.collapsed){
40450 var sw = this.split.el.getWidth();
40452 this.split.el.setLeft(box.x);
40453 this.split.el.setTop(box.y);
40454 this.split.el.setHeight(box.height);
40457 if(this.collapsed){
40458 this.updateBody(null, box.height);
40460 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40464 Roo.bootstrap.layout.West = function(config){
40465 config.region = "west";
40466 config.cursor = "w-resize";
40468 Roo.bootstrap.layout.Split.call(this, config);
40470 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40471 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40472 this.split.el.addClass("roo-layout-split-h");
40476 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40477 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40479 onRender: function(ctr, pos)
40481 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40482 var size = this.config.initialSize || this.config.width;
40483 if(typeof size != "undefined"){
40484 this.el.setWidth(size);
40488 getBox : function(){
40489 if(this.collapsed){
40490 return this.collapsedEl.getBox();
40492 var box = this.el.getBox();
40493 if (box.width == 0) {
40494 box.width = this.config.width; // kludge?
40497 box.width += this.split.el.getWidth();
40502 updateBox : function(box){
40503 if(this.split && !this.collapsed){
40504 var sw = this.split.el.getWidth();
40506 this.split.el.setLeft(box.x+box.width);
40507 this.split.el.setTop(box.y);
40508 this.split.el.setHeight(box.height);
40510 if(this.collapsed){
40511 this.updateBody(null, box.height);
40513 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40515 });Roo.namespace("Roo.bootstrap.panel");/*
40517 * Ext JS Library 1.1.1
40518 * Copyright(c) 2006-2007, Ext JS, LLC.
40520 * Originally Released Under LGPL - original licence link has changed is not relivant.
40523 * <script type="text/javascript">
40526 * @class Roo.ContentPanel
40527 * @extends Roo.util.Observable
40528 * A basic ContentPanel element.
40529 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40530 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40531 * @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
40532 * @cfg {Boolean} closable True if the panel can be closed/removed
40533 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40534 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40535 * @cfg {Toolbar} toolbar A toolbar for this panel
40536 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40537 * @cfg {String} title The title for this panel
40538 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40539 * @cfg {String} url Calls {@link #setUrl} with this value
40540 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40541 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40542 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40543 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40544 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40545 * @cfg {Boolean} badges render the badges
40546 * @cfg {String} cls extra classes to use
40547 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40550 * Create a new ContentPanel.
40551 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40552 * @param {String/Object} config A string to set only the title or a config object
40553 * @param {String} content (optional) Set the HTML content for this panel
40554 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40556 Roo.bootstrap.panel.Content = function( config){
40558 this.tpl = config.tpl || false;
40560 var el = config.el;
40561 var content = config.content;
40563 if(config.autoCreate){ // xtype is available if this is called from factory
40566 this.el = Roo.get(el);
40567 if(!this.el && config && config.autoCreate){
40568 if(typeof config.autoCreate == "object"){
40569 if(!config.autoCreate.id){
40570 config.autoCreate.id = config.id||el;
40572 this.el = Roo.DomHelper.append(document.body,
40573 config.autoCreate, true);
40577 cls: (config.cls || '') +
40578 (config.background ? ' bg-' + config.background : '') +
40579 " roo-layout-inactive-content",
40582 if (config.iframe) {
40586 style : 'border: 0px',
40587 src : 'about:blank'
40593 elcfg.html = config.html;
40597 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40598 if (config.iframe) {
40599 this.iframeEl = this.el.select('iframe',true).first();
40604 this.closable = false;
40605 this.loaded = false;
40606 this.active = false;
40609 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40611 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40613 this.wrapEl = this.el; //this.el.wrap();
40615 if (config.toolbar.items) {
40616 ti = config.toolbar.items ;
40617 delete config.toolbar.items ;
40621 this.toolbar.render(this.wrapEl, 'before');
40622 for(var i =0;i < ti.length;i++) {
40623 // Roo.log(['add child', items[i]]);
40624 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40626 this.toolbar.items = nitems;
40627 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40628 delete config.toolbar;
40632 // xtype created footer. - not sure if will work as we normally have to render first..
40633 if (this.footer && !this.footer.el && this.footer.xtype) {
40634 if (!this.wrapEl) {
40635 this.wrapEl = this.el.wrap();
40638 this.footer.container = this.wrapEl.createChild();
40640 this.footer = Roo.factory(this.footer, Roo);
40645 if(typeof config == "string"){
40646 this.title = config;
40648 Roo.apply(this, config);
40652 this.resizeEl = Roo.get(this.resizeEl, true);
40654 this.resizeEl = this.el;
40656 // handle view.xtype
40664 * Fires when this panel is activated.
40665 * @param {Roo.ContentPanel} this
40669 * @event deactivate
40670 * Fires when this panel is activated.
40671 * @param {Roo.ContentPanel} this
40673 "deactivate" : true,
40677 * Fires when this panel is resized if fitToFrame is true.
40678 * @param {Roo.ContentPanel} this
40679 * @param {Number} width The width after any component adjustments
40680 * @param {Number} height The height after any component adjustments
40686 * Fires when this tab is created
40687 * @param {Roo.ContentPanel} this
40693 * Fires when this content is scrolled
40694 * @param {Roo.ContentPanel} this
40695 * @param {Event} scrollEvent
40706 if(this.autoScroll && !this.iframe){
40707 this.resizeEl.setStyle("overflow", "auto");
40708 this.resizeEl.on('scroll', this.onScroll, this);
40710 // fix randome scrolling
40711 //this.el.on('scroll', function() {
40712 // Roo.log('fix random scolling');
40713 // this.scrollTo('top',0);
40716 content = content || this.content;
40718 this.setContent(content);
40720 if(config && config.url){
40721 this.setUrl(this.url, this.params, this.loadOnce);
40726 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40728 if (this.view && typeof(this.view.xtype) != 'undefined') {
40729 this.view.el = this.el.appendChild(document.createElement("div"));
40730 this.view = Roo.factory(this.view);
40731 this.view.render && this.view.render(false, '');
40735 this.fireEvent('render', this);
40738 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40748 /* Resize Element - use this to work out scroll etc. */
40751 setRegion : function(region){
40752 this.region = region;
40753 this.setActiveClass(region && !this.background);
40757 setActiveClass: function(state)
40760 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40761 this.el.setStyle('position','relative');
40763 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40764 this.el.setStyle('position', 'absolute');
40769 * Returns the toolbar for this Panel if one was configured.
40770 * @return {Roo.Toolbar}
40772 getToolbar : function(){
40773 return this.toolbar;
40776 setActiveState : function(active)
40778 this.active = active;
40779 this.setActiveClass(active);
40781 if(this.fireEvent("deactivate", this) === false){
40786 this.fireEvent("activate", this);
40790 * Updates this panel's element (not for iframe)
40791 * @param {String} content The new content
40792 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40794 setContent : function(content, loadScripts){
40799 this.el.update(content, loadScripts);
40802 ignoreResize : function(w, h){
40803 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40806 this.lastSize = {width: w, height: h};
40811 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40812 * @return {Roo.UpdateManager} The UpdateManager
40814 getUpdateManager : function(){
40818 return this.el.getUpdateManager();
40821 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40822 * Does not work with IFRAME contents
40823 * @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:
40826 url: "your-url.php",
40827 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40828 callback: yourFunction,
40829 scope: yourObject, //(optional scope)
40832 text: "Loading...",
40838 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40839 * 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.
40840 * @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}
40841 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40842 * @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.
40843 * @return {Roo.ContentPanel} this
40851 var um = this.el.getUpdateManager();
40852 um.update.apply(um, arguments);
40858 * 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.
40859 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40860 * @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)
40861 * @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)
40862 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40864 setUrl : function(url, params, loadOnce){
40866 this.iframeEl.dom.src = url;
40870 if(this.refreshDelegate){
40871 this.removeListener("activate", this.refreshDelegate);
40873 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40874 this.on("activate", this.refreshDelegate);
40875 return this.el.getUpdateManager();
40878 _handleRefresh : function(url, params, loadOnce){
40879 if(!loadOnce || !this.loaded){
40880 var updater = this.el.getUpdateManager();
40881 updater.update(url, params, this._setLoaded.createDelegate(this));
40885 _setLoaded : function(){
40886 this.loaded = true;
40890 * Returns this panel's id
40893 getId : function(){
40898 * Returns this panel's element - used by regiosn to add.
40899 * @return {Roo.Element}
40901 getEl : function(){
40902 return this.wrapEl || this.el;
40907 adjustForComponents : function(width, height)
40909 //Roo.log('adjustForComponents ');
40910 if(this.resizeEl != this.el){
40911 width -= this.el.getFrameWidth('lr');
40912 height -= this.el.getFrameWidth('tb');
40915 var te = this.toolbar.getEl();
40916 te.setWidth(width);
40917 height -= te.getHeight();
40920 var te = this.footer.getEl();
40921 te.setWidth(width);
40922 height -= te.getHeight();
40926 if(this.adjustments){
40927 width += this.adjustments[0];
40928 height += this.adjustments[1];
40930 return {"width": width, "height": height};
40933 setSize : function(width, height){
40934 if(this.fitToFrame && !this.ignoreResize(width, height)){
40935 if(this.fitContainer && this.resizeEl != this.el){
40936 this.el.setSize(width, height);
40938 var size = this.adjustForComponents(width, height);
40940 this.iframeEl.setSize(width,height);
40943 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40944 this.fireEvent('resize', this, size.width, size.height);
40951 * Returns this panel's title
40954 getTitle : function(){
40956 if (typeof(this.title) != 'object') {
40961 for (var k in this.title) {
40962 if (!this.title.hasOwnProperty(k)) {
40966 if (k.indexOf('-') >= 0) {
40967 var s = k.split('-');
40968 for (var i = 0; i<s.length; i++) {
40969 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40972 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40979 * Set this panel's title
40980 * @param {String} title
40982 setTitle : function(title){
40983 this.title = title;
40985 this.region.updatePanelTitle(this, title);
40990 * Returns true is this panel was configured to be closable
40991 * @return {Boolean}
40993 isClosable : function(){
40994 return this.closable;
40997 beforeSlide : function(){
40999 this.resizeEl.clip();
41002 afterSlide : function(){
41004 this.resizeEl.unclip();
41008 * Force a content refresh from the URL specified in the {@link #setUrl} method.
41009 * Will fail silently if the {@link #setUrl} method has not been called.
41010 * This does not activate the panel, just updates its content.
41012 refresh : function(){
41013 if(this.refreshDelegate){
41014 this.loaded = false;
41015 this.refreshDelegate();
41020 * Destroys this panel
41022 destroy : function(){
41023 this.el.removeAllListeners();
41024 var tempEl = document.createElement("span");
41025 tempEl.appendChild(this.el.dom);
41026 tempEl.innerHTML = "";
41032 * form - if the content panel contains a form - this is a reference to it.
41033 * @type {Roo.form.Form}
41037 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41038 * This contains a reference to it.
41044 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41054 * @param {Object} cfg Xtype definition of item to add.
41058 getChildContainer: function () {
41059 return this.getEl();
41063 onScroll : function(e)
41065 this.fireEvent('scroll', this, e);
41070 var ret = new Roo.factory(cfg);
41075 if (cfg.xtype.match(/^Form$/)) {
41078 //if (this.footer) {
41079 // el = this.footer.container.insertSibling(false, 'before');
41081 el = this.el.createChild();
41084 this.form = new Roo.form.Form(cfg);
41087 if ( this.form.allItems.length) {
41088 this.form.render(el.dom);
41092 // should only have one of theses..
41093 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41094 // views.. should not be just added - used named prop 'view''
41096 cfg.el = this.el.appendChild(document.createElement("div"));
41099 var ret = new Roo.factory(cfg);
41101 ret.render && ret.render(false, ''); // render blank..
41111 * @class Roo.bootstrap.panel.Grid
41112 * @extends Roo.bootstrap.panel.Content
41114 * Create a new GridPanel.
41115 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41116 * @param {Object} config A the config object
41122 Roo.bootstrap.panel.Grid = function(config)
41126 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41127 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41129 config.el = this.wrapper;
41130 //this.el = this.wrapper;
41132 if (config.container) {
41133 // ctor'ed from a Border/panel.grid
41136 this.wrapper.setStyle("overflow", "hidden");
41137 this.wrapper.addClass('roo-grid-container');
41142 if(config.toolbar){
41143 var tool_el = this.wrapper.createChild();
41144 this.toolbar = Roo.factory(config.toolbar);
41146 if (config.toolbar.items) {
41147 ti = config.toolbar.items ;
41148 delete config.toolbar.items ;
41152 this.toolbar.render(tool_el);
41153 for(var i =0;i < ti.length;i++) {
41154 // Roo.log(['add child', items[i]]);
41155 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41157 this.toolbar.items = nitems;
41159 delete config.toolbar;
41162 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41163 config.grid.scrollBody = true;;
41164 config.grid.monitorWindowResize = false; // turn off autosizing
41165 config.grid.autoHeight = false;
41166 config.grid.autoWidth = false;
41168 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41170 if (config.background) {
41171 // render grid on panel activation (if panel background)
41172 this.on('activate', function(gp) {
41173 if (!gp.grid.rendered) {
41174 gp.grid.render(this.wrapper);
41175 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41180 this.grid.render(this.wrapper);
41181 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
41184 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41185 // ??? needed ??? config.el = this.wrapper;
41190 // xtype created footer. - not sure if will work as we normally have to render first..
41191 if (this.footer && !this.footer.el && this.footer.xtype) {
41193 var ctr = this.grid.getView().getFooterPanel(true);
41194 this.footer.dataSource = this.grid.dataSource;
41195 this.footer = Roo.factory(this.footer, Roo);
41196 this.footer.render(ctr);
41206 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41207 getId : function(){
41208 return this.grid.id;
41212 * Returns the grid for this panel
41213 * @return {Roo.bootstrap.Table}
41215 getGrid : function(){
41219 setSize : function(width, height){
41220 if(!this.ignoreResize(width, height)){
41221 var grid = this.grid;
41222 var size = this.adjustForComponents(width, height);
41223 // tfoot is not a footer?
41226 var gridel = grid.getGridEl();
41227 gridel.setSize(size.width, size.height);
41229 var tbd = grid.getGridEl().select('tbody', true).first();
41230 var thd = grid.getGridEl().select('thead',true).first();
41231 var tbf= grid.getGridEl().select('tfoot', true).first();
41234 size.height -= tbf.getHeight();
41237 size.height -= thd.getHeight();
41240 tbd.setSize(size.width, size.height );
41241 // this is for the account management tab -seems to work there.
41242 var thd = grid.getGridEl().select('thead',true).first();
41244 // tbd.setSize(size.width, size.height - thd.getHeight());
41253 beforeSlide : function(){
41254 this.grid.getView().scroller.clip();
41257 afterSlide : function(){
41258 this.grid.getView().scroller.unclip();
41261 destroy : function(){
41262 this.grid.destroy();
41264 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41269 * @class Roo.bootstrap.panel.Nest
41270 * @extends Roo.bootstrap.panel.Content
41272 * Create a new Panel, that can contain a layout.Border.
41275 * @param {Roo.BorderLayout} layout The layout for this panel
41276 * @param {String/Object} config A string to set only the title or a config object
41278 Roo.bootstrap.panel.Nest = function(config)
41280 // construct with only one argument..
41281 /* FIXME - implement nicer consturctors
41282 if (layout.layout) {
41284 layout = config.layout;
41285 delete config.layout;
41287 if (layout.xtype && !layout.getEl) {
41288 // then layout needs constructing..
41289 layout = Roo.factory(layout, Roo);
41293 config.el = config.layout.getEl();
41295 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41297 config.layout.monitorWindowResize = false; // turn off autosizing
41298 this.layout = config.layout;
41299 this.layout.getEl().addClass("roo-layout-nested-layout");
41300 this.layout.parent = this;
41307 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41309 setSize : function(width, height){
41310 if(!this.ignoreResize(width, height)){
41311 var size = this.adjustForComponents(width, height);
41312 var el = this.layout.getEl();
41313 if (size.height < 1) {
41314 el.setWidth(size.width);
41316 el.setSize(size.width, size.height);
41318 var touch = el.dom.offsetWidth;
41319 this.layout.layout();
41320 // ie requires a double layout on the first pass
41321 if(Roo.isIE && !this.initialized){
41322 this.initialized = true;
41323 this.layout.layout();
41328 // activate all subpanels if not currently active..
41330 setActiveState : function(active){
41331 this.active = active;
41332 this.setActiveClass(active);
41335 this.fireEvent("deactivate", this);
41339 this.fireEvent("activate", this);
41340 // not sure if this should happen before or after..
41341 if (!this.layout) {
41342 return; // should not happen..
41345 for (var r in this.layout.regions) {
41346 reg = this.layout.getRegion(r);
41347 if (reg.getActivePanel()) {
41348 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41349 reg.setActivePanel(reg.getActivePanel());
41352 if (!reg.panels.length) {
41355 reg.showPanel(reg.getPanel(0));
41364 * Returns the nested BorderLayout for this panel
41365 * @return {Roo.BorderLayout}
41367 getLayout : function(){
41368 return this.layout;
41372 * Adds a xtype elements to the layout of the nested panel
41376 xtype : 'ContentPanel',
41383 xtype : 'NestedLayoutPanel',
41389 items : [ ... list of content panels or nested layout panels.. ]
41393 * @param {Object} cfg Xtype definition of item to add.
41395 addxtype : function(cfg) {
41396 return this.layout.addxtype(cfg);
41401 * Ext JS Library 1.1.1
41402 * Copyright(c) 2006-2007, Ext JS, LLC.
41404 * Originally Released Under LGPL - original licence link has changed is not relivant.
41407 * <script type="text/javascript">
41410 * @class Roo.TabPanel
41411 * @extends Roo.util.Observable
41412 * A lightweight tab container.
41416 // basic tabs 1, built from existing content
41417 var tabs = new Roo.TabPanel("tabs1");
41418 tabs.addTab("script", "View Script");
41419 tabs.addTab("markup", "View Markup");
41420 tabs.activate("script");
41422 // more advanced tabs, built from javascript
41423 var jtabs = new Roo.TabPanel("jtabs");
41424 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41426 // set up the UpdateManager
41427 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41428 var updater = tab2.getUpdateManager();
41429 updater.setDefaultUrl("ajax1.htm");
41430 tab2.on('activate', updater.refresh, updater, true);
41432 // Use setUrl for Ajax loading
41433 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41434 tab3.setUrl("ajax2.htm", null, true);
41437 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41440 jtabs.activate("jtabs-1");
41443 * Create a new TabPanel.
41444 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41445 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41447 Roo.bootstrap.panel.Tabs = function(config){
41449 * The container element for this TabPanel.
41450 * @type Roo.Element
41452 this.el = Roo.get(config.el);
41455 if(typeof config == "boolean"){
41456 this.tabPosition = config ? "bottom" : "top";
41458 Roo.apply(this, config);
41462 if(this.tabPosition == "bottom"){
41463 // if tabs are at the bottom = create the body first.
41464 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41465 this.el.addClass("roo-tabs-bottom");
41467 // next create the tabs holders
41469 if (this.tabPosition == "west"){
41471 var reg = this.region; // fake it..
41473 if (!reg.mgr.parent) {
41476 reg = reg.mgr.parent.region;
41478 Roo.log("got nest?");
41480 if (reg.mgr.getRegion('west')) {
41481 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41482 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41483 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41484 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41485 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41493 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41494 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41495 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41496 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41501 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41504 // finally - if tabs are at the top, then create the body last..
41505 if(this.tabPosition != "bottom"){
41506 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41507 * @type Roo.Element
41509 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41510 this.el.addClass("roo-tabs-top");
41514 this.bodyEl.setStyle("position", "relative");
41516 this.active = null;
41517 this.activateDelegate = this.activate.createDelegate(this);
41522 * Fires when the active tab changes
41523 * @param {Roo.TabPanel} this
41524 * @param {Roo.TabPanelItem} activePanel The new active tab
41528 * @event beforetabchange
41529 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41530 * @param {Roo.TabPanel} this
41531 * @param {Object} e Set cancel to true on this object to cancel the tab change
41532 * @param {Roo.TabPanelItem} tab The tab being changed to
41534 "beforetabchange" : true
41537 Roo.EventManager.onWindowResize(this.onResize, this);
41538 this.cpad = this.el.getPadding("lr");
41539 this.hiddenCount = 0;
41542 // toolbar on the tabbar support...
41543 if (this.toolbar) {
41544 alert("no toolbar support yet");
41545 this.toolbar = false;
41547 var tcfg = this.toolbar;
41548 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41549 this.toolbar = new Roo.Toolbar(tcfg);
41550 if (Roo.isSafari) {
41551 var tbl = tcfg.container.child('table', true);
41552 tbl.setAttribute('width', '100%');
41560 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41563 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41565 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41567 tabPosition : "top",
41569 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41571 currentTabWidth : 0,
41573 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41577 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41581 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41583 preferredTabWidth : 175,
41585 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41587 resizeTabs : false,
41589 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41591 monitorResize : true,
41593 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41595 toolbar : false, // set by caller..
41597 region : false, /// set by caller
41599 disableTooltips : true, // not used yet...
41602 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41603 * @param {String} id The id of the div to use <b>or create</b>
41604 * @param {String} text The text for the tab
41605 * @param {String} content (optional) Content to put in the TabPanelItem body
41606 * @param {Boolean} closable (optional) True to create a close icon on the tab
41607 * @return {Roo.TabPanelItem} The created TabPanelItem
41609 addTab : function(id, text, content, closable, tpl)
41611 var item = new Roo.bootstrap.panel.TabItem({
41615 closable : closable,
41618 this.addTabItem(item);
41620 item.setContent(content);
41626 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41627 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41628 * @return {Roo.TabPanelItem}
41630 getTab : function(id){
41631 return this.items[id];
41635 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41636 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41638 hideTab : function(id){
41639 var t = this.items[id];
41642 this.hiddenCount++;
41643 this.autoSizeTabs();
41648 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41649 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41651 unhideTab : function(id){
41652 var t = this.items[id];
41654 t.setHidden(false);
41655 this.hiddenCount--;
41656 this.autoSizeTabs();
41661 * Adds an existing {@link Roo.TabPanelItem}.
41662 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41664 addTabItem : function(item)
41666 this.items[item.id] = item;
41667 this.items.push(item);
41668 this.autoSizeTabs();
41669 // if(this.resizeTabs){
41670 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41671 // this.autoSizeTabs();
41673 // item.autoSize();
41678 * Removes a {@link Roo.TabPanelItem}.
41679 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41681 removeTab : function(id){
41682 var items = this.items;
41683 var tab = items[id];
41684 if(!tab) { return; }
41685 var index = items.indexOf(tab);
41686 if(this.active == tab && items.length > 1){
41687 var newTab = this.getNextAvailable(index);
41692 this.stripEl.dom.removeChild(tab.pnode.dom);
41693 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41694 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41696 items.splice(index, 1);
41697 delete this.items[tab.id];
41698 tab.fireEvent("close", tab);
41699 tab.purgeListeners();
41700 this.autoSizeTabs();
41703 getNextAvailable : function(start){
41704 var items = this.items;
41706 // look for a next tab that will slide over to
41707 // replace the one being removed
41708 while(index < items.length){
41709 var item = items[++index];
41710 if(item && !item.isHidden()){
41714 // if one isn't found select the previous tab (on the left)
41717 var item = items[--index];
41718 if(item && !item.isHidden()){
41726 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41727 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41729 disableTab : function(id){
41730 var tab = this.items[id];
41731 if(tab && this.active != tab){
41737 * Enables a {@link Roo.TabPanelItem} that is disabled.
41738 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41740 enableTab : function(id){
41741 var tab = this.items[id];
41746 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41747 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41748 * @return {Roo.TabPanelItem} The TabPanelItem.
41750 activate : function(id)
41752 //Roo.log('activite:' + id);
41754 var tab = this.items[id];
41758 if(tab == this.active || tab.disabled){
41762 this.fireEvent("beforetabchange", this, e, tab);
41763 if(e.cancel !== true && !tab.disabled){
41765 this.active.hide();
41767 this.active = this.items[id];
41768 this.active.show();
41769 this.fireEvent("tabchange", this, this.active);
41775 * Gets the active {@link Roo.TabPanelItem}.
41776 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41778 getActiveTab : function(){
41779 return this.active;
41783 * Updates the tab body element to fit the height of the container element
41784 * for overflow scrolling
41785 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41787 syncHeight : function(targetHeight){
41788 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41789 var bm = this.bodyEl.getMargins();
41790 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41791 this.bodyEl.setHeight(newHeight);
41795 onResize : function(){
41796 if(this.monitorResize){
41797 this.autoSizeTabs();
41802 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41804 beginUpdate : function(){
41805 this.updating = true;
41809 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41811 endUpdate : function(){
41812 this.updating = false;
41813 this.autoSizeTabs();
41817 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41819 autoSizeTabs : function()
41821 var count = this.items.length;
41822 var vcount = count - this.hiddenCount;
41825 this.stripEl.hide();
41827 this.stripEl.show();
41830 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41835 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41836 var availWidth = Math.floor(w / vcount);
41837 var b = this.stripBody;
41838 if(b.getWidth() > w){
41839 var tabs = this.items;
41840 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41841 if(availWidth < this.minTabWidth){
41842 /*if(!this.sleft){ // incomplete scrolling code
41843 this.createScrollButtons();
41846 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41849 if(this.currentTabWidth < this.preferredTabWidth){
41850 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41856 * Returns the number of tabs in this TabPanel.
41859 getCount : function(){
41860 return this.items.length;
41864 * Resizes all the tabs to the passed width
41865 * @param {Number} The new width
41867 setTabWidth : function(width){
41868 this.currentTabWidth = width;
41869 for(var i = 0, len = this.items.length; i < len; i++) {
41870 if(!this.items[i].isHidden()) {
41871 this.items[i].setWidth(width);
41877 * Destroys this TabPanel
41878 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41880 destroy : function(removeEl){
41881 Roo.EventManager.removeResizeListener(this.onResize, this);
41882 for(var i = 0, len = this.items.length; i < len; i++){
41883 this.items[i].purgeListeners();
41885 if(removeEl === true){
41886 this.el.update("");
41891 createStrip : function(container)
41893 var strip = document.createElement("nav");
41894 strip.className = Roo.bootstrap.version == 4 ?
41895 "navbar-light bg-light" :
41896 "navbar navbar-default"; //"x-tabs-wrap";
41897 container.appendChild(strip);
41901 createStripList : function(strip)
41903 // div wrapper for retard IE
41904 // returns the "tr" element.
41905 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41906 //'<div class="x-tabs-strip-wrap">'+
41907 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41908 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41909 return strip.firstChild; //.firstChild.firstChild.firstChild;
41911 createBody : function(container)
41913 var body = document.createElement("div");
41914 Roo.id(body, "tab-body");
41915 //Roo.fly(body).addClass("x-tabs-body");
41916 Roo.fly(body).addClass("tab-content");
41917 container.appendChild(body);
41920 createItemBody :function(bodyEl, id){
41921 var body = Roo.getDom(id);
41923 body = document.createElement("div");
41926 //Roo.fly(body).addClass("x-tabs-item-body");
41927 Roo.fly(body).addClass("tab-pane");
41928 bodyEl.insertBefore(body, bodyEl.firstChild);
41932 createStripElements : function(stripEl, text, closable, tpl)
41934 var td = document.createElement("li"); // was td..
41935 td.className = 'nav-item';
41937 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41940 stripEl.appendChild(td);
41942 td.className = "x-tabs-closable";
41943 if(!this.closeTpl){
41944 this.closeTpl = new Roo.Template(
41945 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41946 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41947 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41950 var el = this.closeTpl.overwrite(td, {"text": text});
41951 var close = el.getElementsByTagName("div")[0];
41952 var inner = el.getElementsByTagName("em")[0];
41953 return {"el": el, "close": close, "inner": inner};
41956 // not sure what this is..
41957 // if(!this.tabTpl){
41958 //this.tabTpl = new Roo.Template(
41959 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41960 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41962 // this.tabTpl = new Roo.Template(
41963 // '<a href="#">' +
41964 // '<span unselectable="on"' +
41965 // (this.disableTooltips ? '' : ' title="{text}"') +
41966 // ' >{text}</span></a>'
41972 var template = tpl || this.tabTpl || false;
41975 template = new Roo.Template(
41976 Roo.bootstrap.version == 4 ?
41978 '<a class="nav-link" href="#" unselectable="on"' +
41979 (this.disableTooltips ? '' : ' title="{text}"') +
41982 '<a class="nav-link" href="#">' +
41983 '<span unselectable="on"' +
41984 (this.disableTooltips ? '' : ' title="{text}"') +
41985 ' >{text}</span></a>'
41990 switch (typeof(template)) {
41994 template = new Roo.Template(template);
42000 var el = template.overwrite(td, {"text": text});
42002 var inner = el.getElementsByTagName("span")[0];
42004 return {"el": el, "inner": inner};
42012 * @class Roo.TabPanelItem
42013 * @extends Roo.util.Observable
42014 * Represents an individual item (tab plus body) in a TabPanel.
42015 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42016 * @param {String} id The id of this TabPanelItem
42017 * @param {String} text The text for the tab of this TabPanelItem
42018 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42020 Roo.bootstrap.panel.TabItem = function(config){
42022 * The {@link Roo.TabPanel} this TabPanelItem belongs to
42023 * @type Roo.TabPanel
42025 this.tabPanel = config.panel;
42027 * The id for this TabPanelItem
42030 this.id = config.id;
42032 this.disabled = false;
42034 this.text = config.text;
42036 this.loaded = false;
42037 this.closable = config.closable;
42040 * The body element for this TabPanelItem.
42041 * @type Roo.Element
42043 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42044 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42045 this.bodyEl.setStyle("display", "block");
42046 this.bodyEl.setStyle("zoom", "1");
42047 //this.hideAction();
42049 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42051 this.el = Roo.get(els.el);
42052 this.inner = Roo.get(els.inner, true);
42053 this.textEl = Roo.bootstrap.version == 4 ?
42054 this.el : Roo.get(this.el.dom.firstChild, true);
42056 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42057 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42060 // this.el.on("mousedown", this.onTabMouseDown, this);
42061 this.el.on("click", this.onTabClick, this);
42063 if(config.closable){
42064 var c = Roo.get(els.close, true);
42065 c.dom.title = this.closeText;
42066 c.addClassOnOver("close-over");
42067 c.on("click", this.closeClick, this);
42073 * Fires when this tab becomes the active tab.
42074 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42075 * @param {Roo.TabPanelItem} this
42079 * @event beforeclose
42080 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42081 * @param {Roo.TabPanelItem} this
42082 * @param {Object} e Set cancel to true on this object to cancel the close.
42084 "beforeclose": true,
42087 * Fires when this tab is closed.
42088 * @param {Roo.TabPanelItem} this
42092 * @event deactivate
42093 * Fires when this tab is no longer the active tab.
42094 * @param {Roo.TabPanel} tabPanel The parent TabPanel
42095 * @param {Roo.TabPanelItem} this
42097 "deactivate" : true
42099 this.hidden = false;
42101 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42104 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42106 purgeListeners : function(){
42107 Roo.util.Observable.prototype.purgeListeners.call(this);
42108 this.el.removeAllListeners();
42111 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42114 this.status_node.addClass("active");
42117 this.tabPanel.stripWrap.repaint();
42119 this.fireEvent("activate", this.tabPanel, this);
42123 * Returns true if this tab is the active tab.
42124 * @return {Boolean}
42126 isActive : function(){
42127 return this.tabPanel.getActiveTab() == this;
42131 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42134 this.status_node.removeClass("active");
42136 this.fireEvent("deactivate", this.tabPanel, this);
42139 hideAction : function(){
42140 this.bodyEl.hide();
42141 this.bodyEl.setStyle("position", "absolute");
42142 this.bodyEl.setLeft("-20000px");
42143 this.bodyEl.setTop("-20000px");
42146 showAction : function(){
42147 this.bodyEl.setStyle("position", "relative");
42148 this.bodyEl.setTop("");
42149 this.bodyEl.setLeft("");
42150 this.bodyEl.show();
42154 * Set the tooltip for the tab.
42155 * @param {String} tooltip The tab's tooltip
42157 setTooltip : function(text){
42158 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42159 this.textEl.dom.qtip = text;
42160 this.textEl.dom.removeAttribute('title');
42162 this.textEl.dom.title = text;
42166 onTabClick : function(e){
42167 e.preventDefault();
42168 this.tabPanel.activate(this.id);
42171 onTabMouseDown : function(e){
42172 e.preventDefault();
42173 this.tabPanel.activate(this.id);
42176 getWidth : function(){
42177 return this.inner.getWidth();
42180 setWidth : function(width){
42181 var iwidth = width - this.linode.getPadding("lr");
42182 this.inner.setWidth(iwidth);
42183 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42184 this.linode.setWidth(width);
42188 * Show or hide the tab
42189 * @param {Boolean} hidden True to hide or false to show.
42191 setHidden : function(hidden){
42192 this.hidden = hidden;
42193 this.linode.setStyle("display", hidden ? "none" : "");
42197 * Returns true if this tab is "hidden"
42198 * @return {Boolean}
42200 isHidden : function(){
42201 return this.hidden;
42205 * Returns the text for this tab
42208 getText : function(){
42212 autoSize : function(){
42213 //this.el.beginMeasure();
42214 this.textEl.setWidth(1);
42216 * #2804 [new] Tabs in Roojs
42217 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42219 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42220 //this.el.endMeasure();
42224 * Sets the text for the tab (Note: this also sets the tooltip text)
42225 * @param {String} text The tab's text and tooltip
42227 setText : function(text){
42229 this.textEl.update(text);
42230 this.setTooltip(text);
42231 //if(!this.tabPanel.resizeTabs){
42232 // this.autoSize();
42236 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42238 activate : function(){
42239 this.tabPanel.activate(this.id);
42243 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42245 disable : function(){
42246 if(this.tabPanel.active != this){
42247 this.disabled = true;
42248 this.status_node.addClass("disabled");
42253 * Enables this TabPanelItem if it was previously disabled.
42255 enable : function(){
42256 this.disabled = false;
42257 this.status_node.removeClass("disabled");
42261 * Sets the content for this TabPanelItem.
42262 * @param {String} content The content
42263 * @param {Boolean} loadScripts true to look for and load scripts
42265 setContent : function(content, loadScripts){
42266 this.bodyEl.update(content, loadScripts);
42270 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42271 * @return {Roo.UpdateManager} The UpdateManager
42273 getUpdateManager : function(){
42274 return this.bodyEl.getUpdateManager();
42278 * Set a URL to be used to load the content for this TabPanelItem.
42279 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42280 * @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)
42281 * @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)
42282 * @return {Roo.UpdateManager} The UpdateManager
42284 setUrl : function(url, params, loadOnce){
42285 if(this.refreshDelegate){
42286 this.un('activate', this.refreshDelegate);
42288 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42289 this.on("activate", this.refreshDelegate);
42290 return this.bodyEl.getUpdateManager();
42294 _handleRefresh : function(url, params, loadOnce){
42295 if(!loadOnce || !this.loaded){
42296 var updater = this.bodyEl.getUpdateManager();
42297 updater.update(url, params, this._setLoaded.createDelegate(this));
42302 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42303 * Will fail silently if the setUrl method has not been called.
42304 * This does not activate the panel, just updates its content.
42306 refresh : function(){
42307 if(this.refreshDelegate){
42308 this.loaded = false;
42309 this.refreshDelegate();
42314 _setLoaded : function(){
42315 this.loaded = true;
42319 closeClick : function(e){
42322 this.fireEvent("beforeclose", this, o);
42323 if(o.cancel !== true){
42324 this.tabPanel.removeTab(this.id);
42328 * The text displayed in the tooltip for the close icon.
42331 closeText : "Close this tab"
42334 * This script refer to:
42335 * Title: International Telephone Input
42336 * Author: Jack O'Connor
42337 * Code version: v12.1.12
42338 * Availability: https://github.com/jackocnr/intl-tel-input.git
42341 Roo.bootstrap.PhoneInputData = function() {
42344 "Afghanistan (افغانستان)",
42349 "Albania (Shqipëri)",
42354 "Algeria (الجزائر)",
42379 "Antigua and Barbuda",
42389 "Armenia (Հայաստան)",
42405 "Austria (Österreich)",
42410 "Azerbaijan (Azərbaycan)",
42420 "Bahrain (البحرين)",
42425 "Bangladesh (বাংলাদেশ)",
42435 "Belarus (Беларусь)",
42440 "Belgium (België)",
42470 "Bosnia and Herzegovina (Босна и Херцеговина)",
42485 "British Indian Ocean Territory",
42490 "British Virgin Islands",
42500 "Bulgaria (България)",
42510 "Burundi (Uburundi)",
42515 "Cambodia (កម្ពុជា)",
42520 "Cameroon (Cameroun)",
42529 ["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"]
42532 "Cape Verde (Kabu Verdi)",
42537 "Caribbean Netherlands",
42548 "Central African Republic (République centrafricaine)",
42568 "Christmas Island",
42574 "Cocos (Keeling) Islands",
42585 "Comoros (جزر القمر)",
42590 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42595 "Congo (Republic) (Congo-Brazzaville)",
42615 "Croatia (Hrvatska)",
42636 "Czech Republic (Česká republika)",
42641 "Denmark (Danmark)",
42656 "Dominican Republic (República Dominicana)",
42660 ["809", "829", "849"]
42678 "Equatorial Guinea (Guinea Ecuatorial)",
42698 "Falkland Islands (Islas Malvinas)",
42703 "Faroe Islands (Føroyar)",
42724 "French Guiana (Guyane française)",
42729 "French Polynesia (Polynésie française)",
42744 "Georgia (საქართველო)",
42749 "Germany (Deutschland)",
42769 "Greenland (Kalaallit Nunaat)",
42806 "Guinea-Bissau (Guiné Bissau)",
42831 "Hungary (Magyarország)",
42836 "Iceland (Ísland)",
42856 "Iraq (العراق)",
42872 "Israel (ישראל)",
42899 "Jordan (الأردن)",
42904 "Kazakhstan (Казахстан)",
42925 "Kuwait (الكويت)",
42930 "Kyrgyzstan (Кыргызстан)",
42940 "Latvia (Latvija)",
42945 "Lebanon (لبنان)",
42960 "Libya (ليبيا)",
42970 "Lithuania (Lietuva)",
42985 "Macedonia (FYROM) (Македонија)",
42990 "Madagascar (Madagasikara)",
43020 "Marshall Islands",
43030 "Mauritania (موريتانيا)",
43035 "Mauritius (Moris)",
43056 "Moldova (Republica Moldova)",
43066 "Mongolia (Монгол)",
43071 "Montenegro (Crna Gora)",
43081 "Morocco (المغرب)",
43087 "Mozambique (Moçambique)",
43092 "Myanmar (Burma) (မြန်မာ)",
43097 "Namibia (Namibië)",
43112 "Netherlands (Nederland)",
43117 "New Caledonia (Nouvelle-Calédonie)",
43152 "North Korea (조선 민주주의 인민 공화국)",
43157 "Northern Mariana Islands",
43173 "Pakistan (پاکستان)",
43183 "Palestine (فلسطين)",
43193 "Papua New Guinea",
43235 "Réunion (La Réunion)",
43241 "Romania (România)",
43257 "Saint Barthélemy",
43268 "Saint Kitts and Nevis",
43278 "Saint Martin (Saint-Martin (partie française))",
43284 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43289 "Saint Vincent and the Grenadines",
43304 "São Tomé and Príncipe (São Tomé e Príncipe)",
43309 "Saudi Arabia (المملكة العربية السعودية)",
43314 "Senegal (Sénégal)",
43344 "Slovakia (Slovensko)",
43349 "Slovenia (Slovenija)",
43359 "Somalia (Soomaaliya)",
43369 "South Korea (대한민국)",
43374 "South Sudan (جنوب السودان)",
43384 "Sri Lanka (ශ්රී ලංකාව)",
43389 "Sudan (السودان)",
43399 "Svalbard and Jan Mayen",
43410 "Sweden (Sverige)",
43415 "Switzerland (Schweiz)",
43420 "Syria (سوريا)",
43465 "Trinidad and Tobago",
43470 "Tunisia (تونس)",
43475 "Turkey (Türkiye)",
43485 "Turks and Caicos Islands",
43495 "U.S. Virgin Islands",
43505 "Ukraine (Україна)",
43510 "United Arab Emirates (الإمارات العربية المتحدة)",
43532 "Uzbekistan (Oʻzbekiston)",
43542 "Vatican City (Città del Vaticano)",
43553 "Vietnam (Việt Nam)",
43558 "Wallis and Futuna (Wallis-et-Futuna)",
43563 "Western Sahara (الصحراء الغربية)",
43569 "Yemen (اليمن)",
43593 * This script refer to:
43594 * Title: International Telephone Input
43595 * Author: Jack O'Connor
43596 * Code version: v12.1.12
43597 * Availability: https://github.com/jackocnr/intl-tel-input.git
43601 * @class Roo.bootstrap.PhoneInput
43602 * @extends Roo.bootstrap.TriggerField
43603 * An input with International dial-code selection
43605 * @cfg {String} defaultDialCode default '+852'
43606 * @cfg {Array} preferedCountries default []
43609 * Create a new PhoneInput.
43610 * @param {Object} config Configuration options
43613 Roo.bootstrap.PhoneInput = function(config) {
43614 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43617 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43619 listWidth: undefined,
43621 selectedClass: 'active',
43623 invalidClass : "has-warning",
43625 validClass: 'has-success',
43627 allowed: '0123456789',
43632 * @cfg {String} defaultDialCode The default dial code when initializing the input
43634 defaultDialCode: '+852',
43637 * @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
43639 preferedCountries: false,
43641 getAutoCreate : function()
43643 var data = Roo.bootstrap.PhoneInputData();
43644 var align = this.labelAlign || this.parentLabelAlign();
43647 this.allCountries = [];
43648 this.dialCodeMapping = [];
43650 for (var i = 0; i < data.length; i++) {
43652 this.allCountries[i] = {
43656 priority: c[3] || 0,
43657 areaCodes: c[4] || null
43659 this.dialCodeMapping[c[2]] = {
43662 priority: c[3] || 0,
43663 areaCodes: c[4] || null
43675 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43676 maxlength: this.max_length,
43677 cls : 'form-control tel-input',
43678 autocomplete: 'new-password'
43681 var hiddenInput = {
43684 cls: 'hidden-tel-input'
43688 hiddenInput.name = this.name;
43691 if (this.disabled) {
43692 input.disabled = true;
43695 var flag_container = {
43712 cls: this.hasFeedback ? 'has-feedback' : '',
43718 cls: 'dial-code-holder',
43725 cls: 'roo-select2-container input-group',
43732 if (this.fieldLabel.length) {
43735 tooltip: 'This field is required'
43741 cls: 'control-label',
43747 html: this.fieldLabel
43750 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43756 if(this.indicatorpos == 'right') {
43757 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43764 if(align == 'left') {
43772 if(this.labelWidth > 12){
43773 label.style = "width: " + this.labelWidth + 'px';
43775 if(this.labelWidth < 13 && this.labelmd == 0){
43776 this.labelmd = this.labelWidth;
43778 if(this.labellg > 0){
43779 label.cls += ' col-lg-' + this.labellg;
43780 input.cls += ' col-lg-' + (12 - this.labellg);
43782 if(this.labelmd > 0){
43783 label.cls += ' col-md-' + this.labelmd;
43784 container.cls += ' col-md-' + (12 - this.labelmd);
43786 if(this.labelsm > 0){
43787 label.cls += ' col-sm-' + this.labelsm;
43788 container.cls += ' col-sm-' + (12 - this.labelsm);
43790 if(this.labelxs > 0){
43791 label.cls += ' col-xs-' + this.labelxs;
43792 container.cls += ' col-xs-' + (12 - this.labelxs);
43802 var settings = this;
43804 ['xs','sm','md','lg'].map(function(size){
43805 if (settings[size]) {
43806 cfg.cls += ' col-' + size + '-' + settings[size];
43810 this.store = new Roo.data.Store({
43811 proxy : new Roo.data.MemoryProxy({}),
43812 reader : new Roo.data.JsonReader({
43823 'name' : 'dialCode',
43827 'name' : 'priority',
43831 'name' : 'areaCodes',
43838 if(!this.preferedCountries) {
43839 this.preferedCountries = [
43846 var p = this.preferedCountries.reverse();
43849 for (var i = 0; i < p.length; i++) {
43850 for (var j = 0; j < this.allCountries.length; j++) {
43851 if(this.allCountries[j].iso2 == p[i]) {
43852 var t = this.allCountries[j];
43853 this.allCountries.splice(j,1);
43854 this.allCountries.unshift(t);
43860 this.store.proxy.data = {
43862 data: this.allCountries
43868 initEvents : function()
43871 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43873 this.indicator = this.indicatorEl();
43874 this.flag = this.flagEl();
43875 this.dialCodeHolder = this.dialCodeHolderEl();
43877 this.trigger = this.el.select('div.flag-box',true).first();
43878 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43883 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43884 _this.list.setWidth(lw);
43887 this.list.on('mouseover', this.onViewOver, this);
43888 this.list.on('mousemove', this.onViewMove, this);
43889 this.inputEl().on("keyup", this.onKeyUp, this);
43890 this.inputEl().on("keypress", this.onKeyPress, this);
43892 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43894 this.view = new Roo.View(this.list, this.tpl, {
43895 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43898 this.view.on('click', this.onViewClick, this);
43899 this.setValue(this.defaultDialCode);
43902 onTriggerClick : function(e)
43904 Roo.log('trigger click');
43909 if(this.isExpanded()){
43911 this.hasFocus = false;
43913 this.store.load({});
43914 this.hasFocus = true;
43919 isExpanded : function()
43921 return this.list.isVisible();
43924 collapse : function()
43926 if(!this.isExpanded()){
43930 Roo.get(document).un('mousedown', this.collapseIf, this);
43931 Roo.get(document).un('mousewheel', this.collapseIf, this);
43932 this.fireEvent('collapse', this);
43936 expand : function()
43940 if(this.isExpanded() || !this.hasFocus){
43944 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43945 this.list.setWidth(lw);
43948 this.restrictHeight();
43950 Roo.get(document).on('mousedown', this.collapseIf, this);
43951 Roo.get(document).on('mousewheel', this.collapseIf, this);
43953 this.fireEvent('expand', this);
43956 restrictHeight : function()
43958 this.list.alignTo(this.inputEl(), this.listAlign);
43959 this.list.alignTo(this.inputEl(), this.listAlign);
43962 onViewOver : function(e, t)
43964 if(this.inKeyMode){
43967 var item = this.view.findItemFromChild(t);
43970 var index = this.view.indexOf(item);
43971 this.select(index, false);
43976 onViewClick : function(view, doFocus, el, e)
43978 var index = this.view.getSelectedIndexes()[0];
43980 var r = this.store.getAt(index);
43983 this.onSelect(r, index);
43985 if(doFocus !== false && !this.blockFocus){
43986 this.inputEl().focus();
43990 onViewMove : function(e, t)
43992 this.inKeyMode = false;
43995 select : function(index, scrollIntoView)
43997 this.selectedIndex = index;
43998 this.view.select(index);
43999 if(scrollIntoView !== false){
44000 var el = this.view.getNode(index);
44002 this.list.scrollChildIntoView(el, false);
44007 createList : function()
44009 this.list = Roo.get(document.body).createChild({
44011 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44012 style: 'display:none'
44015 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44018 collapseIf : function(e)
44020 var in_combo = e.within(this.el);
44021 var in_list = e.within(this.list);
44022 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44024 if (in_combo || in_list || is_list) {
44030 onSelect : function(record, index)
44032 if(this.fireEvent('beforeselect', this, record, index) !== false){
44034 this.setFlagClass(record.data.iso2);
44035 this.setDialCode(record.data.dialCode);
44036 this.hasFocus = false;
44038 this.fireEvent('select', this, record, index);
44042 flagEl : function()
44044 var flag = this.el.select('div.flag',true).first();
44051 dialCodeHolderEl : function()
44053 var d = this.el.select('input.dial-code-holder',true).first();
44060 setDialCode : function(v)
44062 this.dialCodeHolder.dom.value = '+'+v;
44065 setFlagClass : function(n)
44067 this.flag.dom.className = 'flag '+n;
44070 getValue : function()
44072 var v = this.inputEl().getValue();
44073 if(this.dialCodeHolder) {
44074 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44079 setValue : function(v)
44081 var d = this.getDialCode(v);
44083 //invalid dial code
44084 if(v.length == 0 || !d || d.length == 0) {
44086 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44087 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44093 this.setFlagClass(this.dialCodeMapping[d].iso2);
44094 this.setDialCode(d);
44095 this.inputEl().dom.value = v.replace('+'+d,'');
44096 this.hiddenEl().dom.value = this.getValue();
44101 getDialCode : function(v)
44105 if (v.length == 0) {
44106 return this.dialCodeHolder.dom.value;
44110 if (v.charAt(0) != "+") {
44113 var numericChars = "";
44114 for (var i = 1; i < v.length; i++) {
44115 var c = v.charAt(i);
44118 if (this.dialCodeMapping[numericChars]) {
44119 dialCode = v.substr(1, i);
44121 if (numericChars.length == 4) {
44131 this.setValue(this.defaultDialCode);
44135 hiddenEl : function()
44137 return this.el.select('input.hidden-tel-input',true).first();
44140 // after setting val
44141 onKeyUp : function(e){
44142 this.setValue(this.getValue());
44145 onKeyPress : function(e){
44146 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44153 * @class Roo.bootstrap.MoneyField
44154 * @extends Roo.bootstrap.ComboBox
44155 * Bootstrap MoneyField class
44158 * Create a new MoneyField.
44159 * @param {Object} config Configuration options
44162 Roo.bootstrap.MoneyField = function(config) {
44164 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44168 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44171 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44173 allowDecimals : true,
44175 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44177 decimalSeparator : ".",
44179 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44181 decimalPrecision : 0,
44183 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44185 allowNegative : true,
44187 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44191 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44193 minValue : Number.NEGATIVE_INFINITY,
44195 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44197 maxValue : Number.MAX_VALUE,
44199 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44201 minText : "The minimum value for this field is {0}",
44203 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44205 maxText : "The maximum value for this field is {0}",
44207 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
44208 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44210 nanText : "{0} is not a valid number",
44212 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44216 * @cfg {String} defaults currency of the MoneyField
44217 * value should be in lkey
44219 defaultCurrency : false,
44221 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44223 thousandsDelimiter : false,
44225 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44236 getAutoCreate : function()
44238 var align = this.labelAlign || this.parentLabelAlign();
44250 cls : 'form-control roo-money-amount-input',
44251 autocomplete: 'new-password'
44254 var hiddenInput = {
44258 cls: 'hidden-number-input'
44261 if(this.max_length) {
44262 input.maxlength = this.max_length;
44266 hiddenInput.name = this.name;
44269 if (this.disabled) {
44270 input.disabled = true;
44273 var clg = 12 - this.inputlg;
44274 var cmd = 12 - this.inputmd;
44275 var csm = 12 - this.inputsm;
44276 var cxs = 12 - this.inputxs;
44280 cls : 'row roo-money-field',
44284 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44288 cls: 'roo-select2-container input-group',
44292 cls : 'form-control roo-money-currency-input',
44293 autocomplete: 'new-password',
44295 name : this.currencyName
44299 cls : 'input-group-addon',
44313 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44317 cls: this.hasFeedback ? 'has-feedback' : '',
44328 if (this.fieldLabel.length) {
44331 tooltip: 'This field is required'
44337 cls: 'control-label',
44343 html: this.fieldLabel
44346 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44352 if(this.indicatorpos == 'right') {
44353 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44360 if(align == 'left') {
44368 if(this.labelWidth > 12){
44369 label.style = "width: " + this.labelWidth + 'px';
44371 if(this.labelWidth < 13 && this.labelmd == 0){
44372 this.labelmd = this.labelWidth;
44374 if(this.labellg > 0){
44375 label.cls += ' col-lg-' + this.labellg;
44376 input.cls += ' col-lg-' + (12 - this.labellg);
44378 if(this.labelmd > 0){
44379 label.cls += ' col-md-' + this.labelmd;
44380 container.cls += ' col-md-' + (12 - this.labelmd);
44382 if(this.labelsm > 0){
44383 label.cls += ' col-sm-' + this.labelsm;
44384 container.cls += ' col-sm-' + (12 - this.labelsm);
44386 if(this.labelxs > 0){
44387 label.cls += ' col-xs-' + this.labelxs;
44388 container.cls += ' col-xs-' + (12 - this.labelxs);
44399 var settings = this;
44401 ['xs','sm','md','lg'].map(function(size){
44402 if (settings[size]) {
44403 cfg.cls += ' col-' + size + '-' + settings[size];
44410 initEvents : function()
44412 this.indicator = this.indicatorEl();
44414 this.initCurrencyEvent();
44416 this.initNumberEvent();
44419 initCurrencyEvent : function()
44422 throw "can not find store for combo";
44425 this.store = Roo.factory(this.store, Roo.data);
44426 this.store.parent = this;
44430 this.triggerEl = this.el.select('.input-group-addon', true).first();
44432 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44437 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44438 _this.list.setWidth(lw);
44441 this.list.on('mouseover', this.onViewOver, this);
44442 this.list.on('mousemove', this.onViewMove, this);
44443 this.list.on('scroll', this.onViewScroll, this);
44446 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44449 this.view = new Roo.View(this.list, this.tpl, {
44450 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44453 this.view.on('click', this.onViewClick, this);
44455 this.store.on('beforeload', this.onBeforeLoad, this);
44456 this.store.on('load', this.onLoad, this);
44457 this.store.on('loadexception', this.onLoadException, this);
44459 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44460 "up" : function(e){
44461 this.inKeyMode = true;
44465 "down" : function(e){
44466 if(!this.isExpanded()){
44467 this.onTriggerClick();
44469 this.inKeyMode = true;
44474 "enter" : function(e){
44477 if(this.fireEvent("specialkey", this, e)){
44478 this.onViewClick(false);
44484 "esc" : function(e){
44488 "tab" : function(e){
44491 if(this.fireEvent("specialkey", this, e)){
44492 this.onViewClick(false);
44500 doRelay : function(foo, bar, hname){
44501 if(hname == 'down' || this.scope.isExpanded()){
44502 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44510 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44514 initNumberEvent : function(e)
44516 this.inputEl().on("keydown" , this.fireKey, this);
44517 this.inputEl().on("focus", this.onFocus, this);
44518 this.inputEl().on("blur", this.onBlur, this);
44520 this.inputEl().relayEvent('keyup', this);
44522 if(this.indicator){
44523 this.indicator.addClass('invisible');
44526 this.originalValue = this.getValue();
44528 if(this.validationEvent == 'keyup'){
44529 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44530 this.inputEl().on('keyup', this.filterValidation, this);
44532 else if(this.validationEvent !== false){
44533 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44536 if(this.selectOnFocus){
44537 this.on("focus", this.preFocus, this);
44540 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44541 this.inputEl().on("keypress", this.filterKeys, this);
44543 this.inputEl().relayEvent('keypress', this);
44546 var allowed = "0123456789";
44548 if(this.allowDecimals){
44549 allowed += this.decimalSeparator;
44552 if(this.allowNegative){
44556 if(this.thousandsDelimiter) {
44560 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44562 var keyPress = function(e){
44564 var k = e.getKey();
44566 var c = e.getCharCode();
44569 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44570 allowed.indexOf(String.fromCharCode(c)) === -1
44576 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44580 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44585 this.inputEl().on("keypress", keyPress, this);
44589 onTriggerClick : function(e)
44596 this.loadNext = false;
44598 if(this.isExpanded()){
44603 this.hasFocus = true;
44605 if(this.triggerAction == 'all') {
44606 this.doQuery(this.allQuery, true);
44610 this.doQuery(this.getRawValue());
44613 getCurrency : function()
44615 var v = this.currencyEl().getValue();
44620 restrictHeight : function()
44622 this.list.alignTo(this.currencyEl(), this.listAlign);
44623 this.list.alignTo(this.currencyEl(), this.listAlign);
44626 onViewClick : function(view, doFocus, el, e)
44628 var index = this.view.getSelectedIndexes()[0];
44630 var r = this.store.getAt(index);
44633 this.onSelect(r, index);
44637 onSelect : function(record, index){
44639 if(this.fireEvent('beforeselect', this, record, index) !== false){
44641 this.setFromCurrencyData(index > -1 ? record.data : false);
44645 this.fireEvent('select', this, record, index);
44649 setFromCurrencyData : function(o)
44653 this.lastCurrency = o;
44655 if (this.currencyField) {
44656 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44658 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44661 this.lastSelectionText = currency;
44663 //setting default currency
44664 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44665 this.setCurrency(this.defaultCurrency);
44669 this.setCurrency(currency);
44672 setFromData : function(o)
44676 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44678 this.setFromCurrencyData(c);
44683 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44685 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44688 this.setValue(value);
44692 setCurrency : function(v)
44694 this.currencyValue = v;
44697 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44702 setValue : function(v)
44704 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44710 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44712 this.inputEl().dom.value = (v == '') ? '' :
44713 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44715 if(!this.allowZero && v === '0') {
44716 this.hiddenEl().dom.value = '';
44717 this.inputEl().dom.value = '';
44724 getRawValue : function()
44726 var v = this.inputEl().getValue();
44731 getValue : function()
44733 return this.fixPrecision(this.parseValue(this.getRawValue()));
44736 parseValue : function(value)
44738 if(this.thousandsDelimiter) {
44740 r = new RegExp(",", "g");
44741 value = value.replace(r, "");
44744 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44745 return isNaN(value) ? '' : value;
44749 fixPrecision : function(value)
44751 if(this.thousandsDelimiter) {
44753 r = new RegExp(",", "g");
44754 value = value.replace(r, "");
44757 var nan = isNaN(value);
44759 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44760 return nan ? '' : value;
44762 return parseFloat(value).toFixed(this.decimalPrecision);
44765 decimalPrecisionFcn : function(v)
44767 return Math.floor(v);
44770 validateValue : function(value)
44772 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44776 var num = this.parseValue(value);
44779 this.markInvalid(String.format(this.nanText, value));
44783 if(num < this.minValue){
44784 this.markInvalid(String.format(this.minText, this.minValue));
44788 if(num > this.maxValue){
44789 this.markInvalid(String.format(this.maxText, this.maxValue));
44796 validate : function()
44798 if(this.disabled || this.allowBlank){
44803 var currency = this.getCurrency();
44805 if(this.validateValue(this.getRawValue()) && currency.length){
44810 this.markInvalid();
44814 getName: function()
44819 beforeBlur : function()
44825 var v = this.parseValue(this.getRawValue());
44832 onBlur : function()
44836 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44837 //this.el.removeClass(this.focusClass);
44840 this.hasFocus = false;
44842 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44846 var v = this.getValue();
44848 if(String(v) !== String(this.startValue)){
44849 this.fireEvent('change', this, v, this.startValue);
44852 this.fireEvent("blur", this);
44855 inputEl : function()
44857 return this.el.select('.roo-money-amount-input', true).first();
44860 currencyEl : function()
44862 return this.el.select('.roo-money-currency-input', true).first();
44865 hiddenEl : function()
44867 return this.el.select('input.hidden-number-input',true).first();
44871 * @class Roo.bootstrap.BezierSignature
44872 * @extends Roo.bootstrap.Component
44873 * Bootstrap BezierSignature class
44874 * This script refer to:
44875 * Title: Signature Pad
44877 * Availability: https://github.com/szimek/signature_pad
44880 * Create a new BezierSignature
44881 * @param {Object} config The config object
44884 Roo.bootstrap.BezierSignature = function(config){
44885 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44891 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44898 mouse_btn_down: true,
44901 * @cfg {int} canvas height
44903 canvas_height: '200px',
44906 * @cfg {float|function} Radius of a single dot.
44911 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44916 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44921 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44926 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44931 * @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.
44933 bg_color: 'rgba(0, 0, 0, 0)',
44936 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44938 dot_color: 'black',
44941 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44943 velocity_filter_weight: 0.7,
44946 * @cfg {function} Callback when stroke begin.
44951 * @cfg {function} Callback when stroke end.
44955 getAutoCreate : function()
44957 var cls = 'roo-signature column';
44960 cls += ' ' + this.cls;
44970 for(var i = 0; i < col_sizes.length; i++) {
44971 if(this[col_sizes[i]]) {
44972 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44982 cls: 'roo-signature-body',
44986 cls: 'roo-signature-body-canvas',
44987 height: this.canvas_height,
44988 width: this.canvas_width
44995 style: 'display: none'
45003 initEvents: function()
45005 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45007 var canvas = this.canvasEl();
45009 // mouse && touch event swapping...
45010 canvas.dom.style.touchAction = 'none';
45011 canvas.dom.style.msTouchAction = 'none';
45013 this.mouse_btn_down = false;
45014 canvas.on('mousedown', this._handleMouseDown, this);
45015 canvas.on('mousemove', this._handleMouseMove, this);
45016 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45018 if (window.PointerEvent) {
45019 canvas.on('pointerdown', this._handleMouseDown, this);
45020 canvas.on('pointermove', this._handleMouseMove, this);
45021 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45024 if ('ontouchstart' in window) {
45025 canvas.on('touchstart', this._handleTouchStart, this);
45026 canvas.on('touchmove', this._handleTouchMove, this);
45027 canvas.on('touchend', this._handleTouchEnd, this);
45030 Roo.EventManager.onWindowResize(this.resize, this, true);
45032 // file input event
45033 this.fileEl().on('change', this.uploadImage, this);
45040 resize: function(){
45042 var canvas = this.canvasEl().dom;
45043 var ctx = this.canvasElCtx();
45044 var img_data = false;
45046 if(canvas.width > 0) {
45047 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45049 // setting canvas width will clean img data
45052 var style = window.getComputedStyle ?
45053 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45055 var padding_left = parseInt(style.paddingLeft) || 0;
45056 var padding_right = parseInt(style.paddingRight) || 0;
45058 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45061 ctx.putImageData(img_data, 0, 0);
45065 _handleMouseDown: function(e)
45067 if (e.browserEvent.which === 1) {
45068 this.mouse_btn_down = true;
45069 this.strokeBegin(e);
45073 _handleMouseMove: function (e)
45075 if (this.mouse_btn_down) {
45076 this.strokeMoveUpdate(e);
45080 _handleMouseUp: function (e)
45082 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45083 this.mouse_btn_down = false;
45088 _handleTouchStart: function (e) {
45090 e.preventDefault();
45091 if (e.browserEvent.targetTouches.length === 1) {
45092 // var touch = e.browserEvent.changedTouches[0];
45093 // this.strokeBegin(touch);
45095 this.strokeBegin(e); // assume e catching the correct xy...
45099 _handleTouchMove: function (e) {
45100 e.preventDefault();
45101 // var touch = event.targetTouches[0];
45102 // _this._strokeMoveUpdate(touch);
45103 this.strokeMoveUpdate(e);
45106 _handleTouchEnd: function (e) {
45107 var wasCanvasTouched = e.target === this.canvasEl().dom;
45108 if (wasCanvasTouched) {
45109 e.preventDefault();
45110 // var touch = event.changedTouches[0];
45111 // _this._strokeEnd(touch);
45116 reset: function () {
45117 this._lastPoints = [];
45118 this._lastVelocity = 0;
45119 this._lastWidth = (this.min_width + this.max_width) / 2;
45120 this.canvasElCtx().fillStyle = this.dot_color;
45123 strokeMoveUpdate: function(e)
45125 this.strokeUpdate(e);
45127 if (this.throttle) {
45128 this.throttleStroke(this.strokeUpdate, this.throttle);
45131 this.strokeUpdate(e);
45135 strokeBegin: function(e)
45137 var newPointGroup = {
45138 color: this.dot_color,
45142 if (typeof this.onBegin === 'function') {
45146 this.curve_data.push(newPointGroup);
45148 this.strokeUpdate(e);
45151 strokeUpdate: function(e)
45153 var rect = this.canvasEl().dom.getBoundingClientRect();
45154 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45155 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45156 var lastPoints = lastPointGroup.points;
45157 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45158 var isLastPointTooClose = lastPoint
45159 ? point.distanceTo(lastPoint) <= this.min_distance
45161 var color = lastPointGroup.color;
45162 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45163 var curve = this.addPoint(point);
45165 this.drawDot({color: color, point: point});
45168 this.drawCurve({color: color, curve: curve});
45178 strokeEnd: function(e)
45180 this.strokeUpdate(e);
45181 if (typeof this.onEnd === 'function') {
45186 addPoint: function (point) {
45187 var _lastPoints = this._lastPoints;
45188 _lastPoints.push(point);
45189 if (_lastPoints.length > 2) {
45190 if (_lastPoints.length === 3) {
45191 _lastPoints.unshift(_lastPoints[0]);
45193 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45194 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45195 _lastPoints.shift();
45201 calculateCurveWidths: function (startPoint, endPoint) {
45202 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45203 (1 - this.velocity_filter_weight) * this._lastVelocity;
45205 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45208 start: this._lastWidth
45211 this._lastVelocity = velocity;
45212 this._lastWidth = newWidth;
45216 drawDot: function (_a) {
45217 var color = _a.color, point = _a.point;
45218 var ctx = this.canvasElCtx();
45219 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45221 this.drawCurveSegment(point.x, point.y, width);
45223 ctx.fillStyle = color;
45227 drawCurve: function (_a) {
45228 var color = _a.color, curve = _a.curve;
45229 var ctx = this.canvasElCtx();
45230 var widthDelta = curve.endWidth - curve.startWidth;
45231 var drawSteps = Math.floor(curve.length()) * 2;
45233 ctx.fillStyle = color;
45234 for (var i = 0; i < drawSteps; i += 1) {
45235 var t = i / drawSteps;
45241 var x = uuu * curve.startPoint.x;
45242 x += 3 * uu * t * curve.control1.x;
45243 x += 3 * u * tt * curve.control2.x;
45244 x += ttt * curve.endPoint.x;
45245 var y = uuu * curve.startPoint.y;
45246 y += 3 * uu * t * curve.control1.y;
45247 y += 3 * u * tt * curve.control2.y;
45248 y += ttt * curve.endPoint.y;
45249 var width = curve.startWidth + ttt * widthDelta;
45250 this.drawCurveSegment(x, y, width);
45256 drawCurveSegment: function (x, y, width) {
45257 var ctx = this.canvasElCtx();
45259 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45260 this.is_empty = false;
45265 var ctx = this.canvasElCtx();
45266 var canvas = this.canvasEl().dom;
45267 ctx.fillStyle = this.bg_color;
45268 ctx.clearRect(0, 0, canvas.width, canvas.height);
45269 ctx.fillRect(0, 0, canvas.width, canvas.height);
45270 this.curve_data = [];
45272 this.is_empty = true;
45277 return this.el.select('input',true).first();
45280 canvasEl: function()
45282 return this.el.select('canvas',true).first();
45285 canvasElCtx: function()
45287 return this.el.select('canvas',true).first().dom.getContext('2d');
45290 getImage: function(type)
45292 if(this.is_empty) {
45297 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45300 drawFromImage: function(img_src)
45302 var img = new Image();
45304 img.onload = function(){
45305 this.canvasElCtx().drawImage(img, 0, 0);
45310 this.is_empty = false;
45313 selectImage: function()
45315 this.fileEl().dom.click();
45318 uploadImage: function(e)
45320 var reader = new FileReader();
45322 reader.onload = function(e){
45323 var img = new Image();
45324 img.onload = function(){
45326 this.canvasElCtx().drawImage(img, 0, 0);
45328 img.src = e.target.result;
45331 reader.readAsDataURL(e.target.files[0]);
45334 // Bezier Point Constructor
45335 Point: (function () {
45336 function Point(x, y, time) {
45339 this.time = time || Date.now();
45341 Point.prototype.distanceTo = function (start) {
45342 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45344 Point.prototype.equals = function (other) {
45345 return this.x === other.x && this.y === other.y && this.time === other.time;
45347 Point.prototype.velocityFrom = function (start) {
45348 return this.time !== start.time
45349 ? this.distanceTo(start) / (this.time - start.time)
45356 // Bezier Constructor
45357 Bezier: (function () {
45358 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45359 this.startPoint = startPoint;
45360 this.control2 = control2;
45361 this.control1 = control1;
45362 this.endPoint = endPoint;
45363 this.startWidth = startWidth;
45364 this.endWidth = endWidth;
45366 Bezier.fromPoints = function (points, widths, scope) {
45367 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45368 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45369 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45371 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45372 var dx1 = s1.x - s2.x;
45373 var dy1 = s1.y - s2.y;
45374 var dx2 = s2.x - s3.x;
45375 var dy2 = s2.y - s3.y;
45376 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45377 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45378 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45379 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45380 var dxm = m1.x - m2.x;
45381 var dym = m1.y - m2.y;
45382 var k = l2 / (l1 + l2);
45383 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45384 var tx = s2.x - cm.x;
45385 var ty = s2.y - cm.y;
45387 c1: new scope.Point(m1.x + tx, m1.y + ty),
45388 c2: new scope.Point(m2.x + tx, m2.y + ty)
45391 Bezier.prototype.length = function () {
45396 for (var i = 0; i <= steps; i += 1) {
45398 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45399 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45401 var xdiff = cx - px;
45402 var ydiff = cy - py;
45403 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45410 Bezier.prototype.point = function (t, start, c1, c2, end) {
45411 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45412 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45413 + (3.0 * c2 * (1.0 - t) * t * t)
45414 + (end * t * t * t);
45419 throttleStroke: function(fn, wait) {
45420 if (wait === void 0) { wait = 250; }
45422 var timeout = null;
45426 var later = function () {
45427 previous = Date.now();
45429 result = fn.apply(storedContext, storedArgs);
45431 storedContext = null;
45435 return function wrapper() {
45437 for (var _i = 0; _i < arguments.length; _i++) {
45438 args[_i] = arguments[_i];
45440 var now = Date.now();
45441 var remaining = wait - (now - previous);
45442 storedContext = this;
45444 if (remaining <= 0 || remaining > wait) {
45446 clearTimeout(timeout);
45450 result = fn.apply(storedContext, storedArgs);
45452 storedContext = null;
45456 else if (!timeout) {
45457 timeout = window.setTimeout(later, remaining);