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 = 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
3048 * Create a new Input
3049 * @param {Object} config The config object
3052 Roo.bootstrap.Img = function(config){
3053 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3059 * The img click event for the img.
3060 * @param {Roo.EventObject} e
3066 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3068 imgResponsive: true,
3078 getAutoCreate : function()
3080 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3081 return this.createSingleImg();
3086 cls: 'roo-image-responsive-group',
3091 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3093 if(!_this[size + 'Url']){
3099 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3100 html: _this.html || cfg.html,
3101 src: _this[size + 'Url']
3104 img.cls += ' roo-image-responsive-' + size;
3106 var s = ['xs', 'sm', 'md', 'lg'];
3108 s.splice(s.indexOf(size), 1);
3110 Roo.each(s, function(ss){
3111 img.cls += ' hidden-' + ss;
3114 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3115 cfg.cls += ' img-' + _this.border;
3119 cfg.alt = _this.alt;
3132 a.target = _this.target;
3136 cfg.cn.push((_this.href) ? a : img);
3143 createSingleImg : function()
3147 cls: (this.imgResponsive) ? 'img-responsive' : '',
3149 src : 'about:blank' // just incase src get's set to undefined?!?
3152 cfg.html = this.html || cfg.html;
3154 cfg.src = this.src || cfg.src;
3156 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3157 cfg.cls += ' img-' + this.border;
3174 a.target = this.target;
3179 return (this.href) ? a : cfg;
3182 initEvents: function()
3185 this.el.on('click', this.onClick, this);
3190 onClick : function(e)
3192 Roo.log('img onclick');
3193 this.fireEvent('click', this, e);
3196 * Sets the url of the image - used to update it
3197 * @param {String} url the url of the image
3200 setSrc : function(url)
3204 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205 this.el.dom.src = url;
3209 this.el.select('img', true).first().dom.src = url;
3225 * @class Roo.bootstrap.Link
3226 * @extends Roo.bootstrap.Component
3227 * Bootstrap Link Class
3228 * @cfg {String} alt image alternative text
3229 * @cfg {String} href a tag href
3230 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3231 * @cfg {String} html the content of the link.
3232 * @cfg {String} anchor name for the anchor link
3233 * @cfg {String} fa - favicon
3235 * @cfg {Boolean} preventDefault (true | false) default false
3239 * Create a new Input
3240 * @param {Object} config The config object
3243 Roo.bootstrap.Link = function(config){
3244 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3250 * The img click event for the img.
3251 * @param {Roo.EventObject} e
3257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3261 preventDefault: false,
3267 getAutoCreate : function()
3269 var html = this.html || '';
3271 if (this.fa !== false) {
3272 html = '<i class="fa fa-' + this.fa + '"></i>';
3277 // anchor's do not require html/href...
3278 if (this.anchor === false) {
3280 cfg.href = this.href || '#';
3282 cfg.name = this.anchor;
3283 if (this.html !== false || this.fa !== false) {
3286 if (this.href !== false) {
3287 cfg.href = this.href;
3291 if(this.alt !== false){
3296 if(this.target !== false) {
3297 cfg.target = this.target;
3303 initEvents: function() {
3305 if(!this.href || this.preventDefault){
3306 this.el.on('click', this.onClick, this);
3310 onClick : function(e)
3312 if(this.preventDefault){
3315 //Roo.log('img onclick');
3316 this.fireEvent('click', this, e);
3329 * @class Roo.bootstrap.Header
3330 * @extends Roo.bootstrap.Component
3331 * Bootstrap Header class
3332 * @cfg {String} html content of header
3333 * @cfg {Number} level (1|2|3|4|5|6) default 1
3336 * Create a new Header
3337 * @param {Object} config The config object
3341 Roo.bootstrap.Header = function(config){
3342 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3345 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3353 getAutoCreate : function(){
3358 tag: 'h' + (1 *this.level),
3359 html: this.html || ''
3371 * Ext JS Library 1.1.1
3372 * Copyright(c) 2006-2007, Ext JS, LLC.
3374 * Originally Released Under LGPL - original licence link has changed is not relivant.
3377 * <script type="text/javascript">
3381 * @class Roo.bootstrap.MenuMgr
3382 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3385 Roo.bootstrap.MenuMgr = function(){
3386 var menus, active, groups = {}, attached = false, lastShow = new Date();
3388 // private - called when first menu is created
3391 active = new Roo.util.MixedCollection();
3392 Roo.get(document).addKeyListener(27, function(){
3393 if(active.length > 0){
3401 if(active && active.length > 0){
3402 var c = active.clone();
3412 if(active.length < 1){
3413 Roo.get(document).un("mouseup", onMouseDown);
3421 var last = active.last();
3422 lastShow = new Date();
3425 Roo.get(document).on("mouseup", onMouseDown);
3430 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3431 m.parentMenu.activeChild = m;
3432 }else if(last && last.isVisible()){
3433 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3438 function onBeforeHide(m){
3440 m.activeChild.hide();
3442 if(m.autoHideTimer){
3443 clearTimeout(m.autoHideTimer);
3444 delete m.autoHideTimer;
3449 function onBeforeShow(m){
3450 var pm = m.parentMenu;
3451 if(!pm && !m.allowOtherMenus){
3453 }else if(pm && pm.activeChild && active != m){
3454 pm.activeChild.hide();
3458 // private this should really trigger on mouseup..
3459 function onMouseDown(e){
3460 Roo.log("on Mouse Up");
3462 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3463 Roo.log("MenuManager hideAll");
3472 function onBeforeCheck(mi, state){
3474 var g = groups[mi.group];
3475 for(var i = 0, l = g.length; i < l; i++){
3477 g[i].setChecked(false);
3486 * Hides all menus that are currently visible
3488 hideAll : function(){
3493 register : function(menu){
3497 menus[menu.id] = menu;
3498 menu.on("beforehide", onBeforeHide);
3499 menu.on("hide", onHide);
3500 menu.on("beforeshow", onBeforeShow);
3501 menu.on("show", onShow);
3503 if(g && menu.events["checkchange"]){
3507 groups[g].push(menu);
3508 menu.on("checkchange", onCheck);
3513 * Returns a {@link Roo.menu.Menu} object
3514 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3515 * be used to generate and return a new Menu instance.
3517 get : function(menu){
3518 if(typeof menu == "string"){ // menu id
3520 }else if(menu.events){ // menu instance
3523 /*else if(typeof menu.length == 'number'){ // array of menu items?
3524 return new Roo.bootstrap.Menu({items:menu});
3525 }else{ // otherwise, must be a config
3526 return new Roo.bootstrap.Menu(menu);
3533 unregister : function(menu){
3534 delete menus[menu.id];
3535 menu.un("beforehide", onBeforeHide);
3536 menu.un("hide", onHide);
3537 menu.un("beforeshow", onBeforeShow);
3538 menu.un("show", onShow);
3540 if(g && menu.events["checkchange"]){
3541 groups[g].remove(menu);
3542 menu.un("checkchange", onCheck);
3547 registerCheckable : function(menuItem){
3548 var g = menuItem.group;
3553 groups[g].push(menuItem);
3554 menuItem.on("beforecheckchange", onBeforeCheck);
3559 unregisterCheckable : function(menuItem){
3560 var g = menuItem.group;
3562 groups[g].remove(menuItem);
3563 menuItem.un("beforecheckchange", onBeforeCheck);
3575 * @class Roo.bootstrap.Menu
3576 * @extends Roo.bootstrap.Component
3577 * Bootstrap Menu class - container for MenuItems
3578 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3579 * @cfg {bool} hidden if the menu should be hidden when rendered.
3580 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3581 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3582 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3583 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3587 * @param {Object} config The config object
3591 Roo.bootstrap.Menu = function(config){
3593 if (config.type == 'treeview') {
3594 // normally menu's are drawn attached to the document to handle layering etc..
3595 // however treeview (used by the docs menu is drawn into the parent element)
3596 this.container_method = 'getChildContainer';
3599 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3600 if (this.registerMenu && this.type != 'treeview') {
3601 Roo.bootstrap.MenuMgr.register(this);
3608 * Fires before this menu is displayed (return false to block)
3609 * @param {Roo.menu.Menu} this
3614 * Fires before this menu is hidden (return false to block)
3615 * @param {Roo.menu.Menu} this
3620 * Fires after this menu is displayed
3621 * @param {Roo.menu.Menu} this
3626 * Fires after this menu is hidden
3627 * @param {Roo.menu.Menu} this
3632 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3633 * @param {Roo.menu.Menu} this
3634 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3635 * @param {Roo.EventObject} e
3640 * Fires when the mouse is hovering over this menu
3641 * @param {Roo.menu.Menu} this
3642 * @param {Roo.EventObject} e
3643 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3648 * Fires when the mouse exits this menu
3649 * @param {Roo.menu.Menu} this
3650 * @param {Roo.EventObject} e
3651 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3656 * Fires when a menu item contained in this menu is clicked
3657 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3658 * @param {Roo.EventObject} e
3662 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3665 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3669 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3672 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3674 registerMenu : true,
3676 menuItems :false, // stores the menu items..
3686 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3688 hideTrigger : false,
3693 getChildContainer : function() {
3697 getAutoCreate : function(){
3699 //if (['right'].indexOf(this.align)!==-1) {
3700 // cfg.cn[1].cls += ' pull-right'
3705 cls : 'dropdown-menu shadow' ,
3706 style : 'z-index:1000'
3710 if (this.type === 'submenu') {
3711 cfg.cls = 'submenu active';
3713 if (this.type === 'treeview') {
3714 cfg.cls = 'treeview-menu';
3719 initEvents : function() {
3721 // Roo.log("ADD event");
3722 // Roo.log(this.triggerEl.dom);
3723 if (this.triggerEl) {
3725 this.triggerEl.on('click', this.onTriggerClick, this);
3727 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3729 if (!this.hideTrigger) {
3730 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3731 // dropdown toggle on the 'a' in BS4?
3732 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3734 this.triggerEl.addClass('dropdown-toggle');
3740 this.el.on('touchstart' , this.onTouch, this);
3742 this.el.on('click' , this.onClick, this);
3744 this.el.on("mouseover", this.onMouseOver, this);
3745 this.el.on("mouseout", this.onMouseOut, this);
3749 findTargetItem : function(e)
3751 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3755 //Roo.log(t); Roo.log(t.id);
3757 //Roo.log(this.menuitems);
3758 return this.menuitems.get(t.id);
3760 //return this.items.get(t.menuItemId);
3766 onTouch : function(e)
3768 Roo.log("menu.onTouch");
3769 //e.stopEvent(); this make the user popdown broken
3773 onClick : function(e)
3775 Roo.log("menu.onClick");
3777 var t = this.findTargetItem(e);
3778 if(!t || t.isContainer){
3783 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3784 if(t == this.activeItem && t.shouldDeactivate(e)){
3785 this.activeItem.deactivate();
3786 delete this.activeItem;
3790 this.setActiveItem(t, true);
3798 Roo.log('pass click event');
3802 this.fireEvent("click", this, t, e);
3806 if(!t.href.length || t.href == '#'){
3807 (function() { _this.hide(); }).defer(100);
3812 onMouseOver : function(e){
3813 var t = this.findTargetItem(e);
3816 // if(t.canActivate && !t.disabled){
3817 // this.setActiveItem(t, true);
3821 this.fireEvent("mouseover", this, e, t);
3823 isVisible : function(){
3824 return !this.hidden;
3826 onMouseOut : function(e){
3827 var t = this.findTargetItem(e);
3830 // if(t == this.activeItem && t.shouldDeactivate(e)){
3831 // this.activeItem.deactivate();
3832 // delete this.activeItem;
3835 this.fireEvent("mouseout", this, e, t);
3840 * Displays this menu relative to another element
3841 * @param {String/HTMLElement/Roo.Element} element The element to align to
3842 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3843 * the element (defaults to this.defaultAlign)
3844 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3846 show : function(el, pos, parentMenu)
3848 if (false === this.fireEvent("beforeshow", this)) {
3849 Roo.log("show canceled");
3852 this.parentMenu = parentMenu;
3856 this.el.addClass('show'); // show otherwise we do not know how big we are..
3858 var xy = this.el.getAlignToXY(el, pos);
3860 // bl-tl << left align below
3861 // tl-bl << left align
3863 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3864 // if it goes to far to the right.. -> align left.
3865 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3868 // was left align - go right?
3869 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3872 // goes down the bottom
3873 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3875 var a = this.align.replace('?', '').split('-');
3876 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3880 this.showAt( xy , parentMenu, false);
3883 * Displays this menu at a specific xy position
3884 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3885 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3887 showAt : function(xy, parentMenu, /* private: */_e){
3888 this.parentMenu = parentMenu;
3893 this.fireEvent("beforeshow", this);
3894 //xy = this.el.adjustForConstraints(xy);
3898 this.hideMenuItems();
3899 this.hidden = false;
3900 if (this.triggerEl) {
3901 this.triggerEl.addClass('open');
3904 this.el.addClass('show');
3908 // reassign x when hitting right
3910 // reassign y when hitting bottom
3912 // but the list may align on trigger left or trigger top... should it be a properity?
3914 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3919 this.fireEvent("show", this);
3925 this.doFocus.defer(50, this);
3929 doFocus : function(){
3931 this.focusEl.focus();
3936 * Hides this menu and optionally all parent menus
3937 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3939 hide : function(deep)
3941 if (false === this.fireEvent("beforehide", this)) {
3942 Roo.log("hide canceled");
3945 this.hideMenuItems();
3946 if(this.el && this.isVisible()){
3948 if(this.activeItem){
3949 this.activeItem.deactivate();
3950 this.activeItem = null;
3952 this.triggerEl.removeClass('open');;
3953 this.el.removeClass('show');
3955 this.fireEvent("hide", this);
3957 if(deep === true && this.parentMenu){
3958 this.parentMenu.hide(true);
3962 onTriggerClick : function(e)
3964 Roo.log('trigger click');
3966 var target = e.getTarget();
3968 Roo.log(target.nodeName.toLowerCase());
3970 if(target.nodeName.toLowerCase() === 'i'){
3976 onTriggerPress : function(e)
3978 Roo.log('trigger press');
3979 //Roo.log(e.getTarget());
3980 // Roo.log(this.triggerEl.dom);
3982 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3983 var pel = Roo.get(e.getTarget());
3984 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3985 Roo.log('is treeview or dropdown?');
3989 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3993 if (this.isVisible()) {
3999 this.show(this.triggerEl, this.align, false);
4002 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4009 hideMenuItems : function()
4011 Roo.log("hide Menu Items");
4016 this.el.select('.open',true).each(function(aa) {
4018 aa.removeClass('open');
4022 addxtypeChild : function (tree, cntr) {
4023 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4025 this.menuitems.add(comp);
4037 this.getEl().dom.innerHTML = '';
4038 this.menuitems.clear();
4052 * @class Roo.bootstrap.MenuItem
4053 * @extends Roo.bootstrap.Component
4054 * Bootstrap MenuItem class
4055 * @cfg {String} html the menu label
4056 * @cfg {String} href the link
4057 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4058 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4059 * @cfg {Boolean} active used on sidebars to highlight active itesm
4060 * @cfg {String} fa favicon to show on left of menu item.
4061 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4065 * Create a new MenuItem
4066 * @param {Object} config The config object
4070 Roo.bootstrap.MenuItem = function(config){
4071 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4076 * The raw click event for the entire grid.
4077 * @param {Roo.bootstrap.MenuItem} this
4078 * @param {Roo.EventObject} e
4084 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4088 preventDefault: false,
4089 isContainer : false,
4093 getAutoCreate : function(){
4095 if(this.isContainer){
4098 cls: 'dropdown-menu-item '
4108 cls : 'dropdown-item',
4113 if (this.fa !== false) {
4116 cls : 'fa fa-' + this.fa
4125 cls: 'dropdown-menu-item',
4128 if (this.parent().type == 'treeview') {
4129 cfg.cls = 'treeview-menu';
4132 cfg.cls += ' active';
4137 anc.href = this.href || cfg.cn[0].href ;
4138 ctag.html = this.html || cfg.cn[0].html ;
4142 initEvents: function()
4144 if (this.parent().type == 'treeview') {
4145 this.el.select('a').on('click', this.onClick, this);
4149 this.menu.parentType = this.xtype;
4150 this.menu.triggerEl = this.el;
4151 this.menu = this.addxtype(Roo.apply({}, this.menu));
4155 onClick : function(e)
4157 Roo.log('item on click ');
4159 if(this.preventDefault){
4162 //this.parent().hideMenuItems();
4164 this.fireEvent('click', this, e);
4183 * @class Roo.bootstrap.MenuSeparator
4184 * @extends Roo.bootstrap.Component
4185 * Bootstrap MenuSeparator class
4188 * Create a new MenuItem
4189 * @param {Object} config The config object
4193 Roo.bootstrap.MenuSeparator = function(config){
4194 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4197 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4199 getAutoCreate : function(){
4218 * @class Roo.bootstrap.Modal
4219 * @extends Roo.bootstrap.Component
4220 * Bootstrap Modal class
4221 * @cfg {String} title Title of dialog
4222 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4223 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4224 * @cfg {Boolean} specificTitle default false
4225 * @cfg {Array} buttons Array of buttons or standard button set..
4226 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4227 * @cfg {Boolean} animate default true
4228 * @cfg {Boolean} allow_close default true
4229 * @cfg {Boolean} fitwindow default false
4230 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4231 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4232 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4233 * @cfg {String} size (sm|lg|xl) default empty
4234 * @cfg {Number} max_width set the max width of modal
4235 * @cfg {Boolean} editableTitle can the title be edited
4240 * Create a new Modal Dialog
4241 * @param {Object} config The config object
4244 Roo.bootstrap.Modal = function(config){
4245 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4250 * The raw btnclick event for the button
4251 * @param {Roo.EventObject} e
4256 * Fire when dialog resize
4257 * @param {Roo.bootstrap.Modal} this
4258 * @param {Roo.EventObject} e
4262 * @event titlechanged
4263 * Fire when the editable title has been changed
4264 * @param {Roo.bootstrap.Modal} this
4265 * @param {Roo.EventObject} value
4267 "titlechanged" : true
4270 this.buttons = this.buttons || [];
4273 this.tmpl = Roo.factory(this.tmpl);
4278 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4280 title : 'test dialog',
4290 specificTitle: false,
4292 buttonPosition: 'right',
4314 editableTitle : false,
4316 onRender : function(ct, position)
4318 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4321 var cfg = Roo.apply({}, this.getAutoCreate());
4324 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4326 //if (!cfg.name.length) {
4330 cfg.cls += ' ' + this.cls;
4333 cfg.style = this.style;
4335 this.el = Roo.get(document.body).createChild(cfg, position);
4337 //var type = this.el.dom.type;
4340 if(this.tabIndex !== undefined){
4341 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4344 this.dialogEl = this.el.select('.modal-dialog',true).first();
4345 this.bodyEl = this.el.select('.modal-body',true).first();
4346 this.closeEl = this.el.select('.modal-header .close', true).first();
4347 this.headerEl = this.el.select('.modal-header',true).first();
4348 this.titleEl = this.el.select('.modal-title',true).first();
4349 this.footerEl = this.el.select('.modal-footer',true).first();
4351 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4353 //this.el.addClass("x-dlg-modal");
4355 if (this.buttons.length) {
4356 Roo.each(this.buttons, function(bb) {
4357 var b = Roo.apply({}, bb);
4358 b.xns = b.xns || Roo.bootstrap;
4359 b.xtype = b.xtype || 'Button';
4360 if (typeof(b.listeners) == 'undefined') {
4361 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4364 var btn = Roo.factory(b);
4366 btn.render(this.getButtonContainer());
4370 // render the children.
4373 if(typeof(this.items) != 'undefined'){
4374 var items = this.items;
4377 for(var i =0;i < items.length;i++) {
4378 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4382 this.items = nitems;
4384 // where are these used - they used to be body/close/footer
4388 //this.el.addClass([this.fieldClass, this.cls]);
4392 getAutoCreate : function()
4394 // we will default to modal-body-overflow - might need to remove or make optional later.
4396 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4397 html : this.html || ''
4402 cls : 'modal-title',
4406 if(this.specificTitle){ // WTF is this?
4411 if (this.allow_close && Roo.bootstrap.version == 3) {
4421 if (this.editableTitle) {
4423 cls: 'form-control roo-editable-title d-none',
4429 if (this.allow_close && Roo.bootstrap.version == 4) {
4439 if(this.size.length){
4440 size = 'modal-' + this.size;
4443 var footer = Roo.bootstrap.version == 3 ?
4445 cls : 'modal-footer',
4449 cls: 'btn-' + this.buttonPosition
4454 { // BS4 uses mr-auto on left buttons....
4455 cls : 'modal-footer'
4466 cls: "modal-dialog " + size,
4469 cls : "modal-content",
4472 cls : 'modal-header',
4487 modal.cls += ' fade';
4493 getChildContainer : function() {
4498 getButtonContainer : function() {
4500 return Roo.bootstrap.version == 4 ?
4501 this.el.select('.modal-footer',true).first()
4502 : this.el.select('.modal-footer div',true).first();
4505 initEvents : function()
4507 if (this.allow_close) {
4508 this.closeEl.on('click', this.hide, this);
4510 Roo.EventManager.onWindowResize(this.resize, this, true);
4511 if (this.editableTitle) {
4512 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4513 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4514 this.headerEditEl.on('keyup', function(e) {
4515 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4516 this.toggleHeaderInput(false)
4519 this.headerEditEl.on('blur', function(e) {
4520 this.toggleHeaderInput(false)
4529 this.maskEl.setSize(
4530 Roo.lib.Dom.getViewWidth(true),
4531 Roo.lib.Dom.getViewHeight(true)
4534 if (this.fitwindow) {
4536 this.dialogEl.setStyle( { 'max-width' : '100%' });
4538 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4539 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4544 if(this.max_width !== 0) {
4546 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4549 this.setSize(w, this.height);
4553 if(this.max_height) {
4554 this.setSize(w,Math.min(
4556 Roo.lib.Dom.getViewportHeight(true) - 60
4562 if(!this.fit_content) {
4563 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4567 this.setSize(w, Math.min(
4569 this.headerEl.getHeight() +
4570 this.footerEl.getHeight() +
4571 this.getChildHeight(this.bodyEl.dom.childNodes),
4572 Roo.lib.Dom.getViewportHeight(true) - 60)
4578 setSize : function(w,h)
4589 if (!this.rendered) {
4592 this.toggleHeaderInput(false);
4593 //this.el.setStyle('display', 'block');
4594 this.el.removeClass('hideing');
4595 this.el.dom.style.display='block';
4597 Roo.get(document.body).addClass('modal-open');
4599 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4602 this.el.addClass('show');
4603 this.el.addClass('in');
4606 this.el.addClass('show');
4607 this.el.addClass('in');
4610 // not sure how we can show data in here..
4612 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4615 Roo.get(document.body).addClass("x-body-masked");
4617 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4618 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4619 this.maskEl.dom.style.display = 'block';
4620 this.maskEl.addClass('show');
4625 this.fireEvent('show', this);
4627 // set zindex here - otherwise it appears to be ignored...
4628 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4631 this.items.forEach( function(e) {
4632 e.layout ? e.layout() : false;
4640 if(this.fireEvent("beforehide", this) !== false){
4642 this.maskEl.removeClass('show');
4644 this.maskEl.dom.style.display = '';
4645 Roo.get(document.body).removeClass("x-body-masked");
4646 this.el.removeClass('in');
4647 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4649 if(this.animate){ // why
4650 this.el.addClass('hideing');
4651 this.el.removeClass('show');
4653 if (!this.el.hasClass('hideing')) {
4654 return; // it's been shown again...
4657 this.el.dom.style.display='';
4659 Roo.get(document.body).removeClass('modal-open');
4660 this.el.removeClass('hideing');
4664 this.el.removeClass('show');
4665 this.el.dom.style.display='';
4666 Roo.get(document.body).removeClass('modal-open');
4669 this.fireEvent('hide', this);
4672 isVisible : function()
4675 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4679 addButton : function(str, cb)
4683 var b = Roo.apply({}, { html : str } );
4684 b.xns = b.xns || Roo.bootstrap;
4685 b.xtype = b.xtype || 'Button';
4686 if (typeof(b.listeners) == 'undefined') {
4687 b.listeners = { click : cb.createDelegate(this) };
4690 var btn = Roo.factory(b);
4692 btn.render(this.getButtonContainer());
4698 setDefaultButton : function(btn)
4700 //this.el.select('.modal-footer').()
4703 resizeTo: function(w,h)
4705 this.dialogEl.setWidth(w);
4707 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4709 this.bodyEl.setHeight(h - diff);
4711 this.fireEvent('resize', this);
4714 setContentSize : function(w, h)
4718 onButtonClick: function(btn,e)
4721 this.fireEvent('btnclick', btn.name, e);
4724 * Set the title of the Dialog
4725 * @param {String} str new Title
4727 setTitle: function(str) {
4728 this.titleEl.dom.innerHTML = str;
4732 * Set the body of the Dialog
4733 * @param {String} str new Title
4735 setBody: function(str) {
4736 this.bodyEl.dom.innerHTML = str;
4739 * Set the body of the Dialog using the template
4740 * @param {Obj} data - apply this data to the template and replace the body contents.
4742 applyBody: function(obj)
4745 Roo.log("Error - using apply Body without a template");
4748 this.tmpl.overwrite(this.bodyEl, obj);
4751 getChildHeight : function(child_nodes)
4755 child_nodes.length == 0
4760 var child_height = 0;
4762 for(var i = 0; i < child_nodes.length; i++) {
4765 * for modal with tabs...
4766 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4768 var layout_childs = child_nodes[i].childNodes;
4770 for(var j = 0; j < layout_childs.length; j++) {
4772 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4774 var layout_body_childs = layout_childs[j].childNodes;
4776 for(var k = 0; k < layout_body_childs.length; k++) {
4778 if(layout_body_childs[k].classList.contains('navbar')) {
4779 child_height += layout_body_childs[k].offsetHeight;
4783 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4785 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4787 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4789 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4790 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4805 child_height += child_nodes[i].offsetHeight;
4806 // Roo.log(child_nodes[i].offsetHeight);
4809 return child_height;
4811 toggleHeaderInput : function(is_edit)
4813 if (!this.editableTitle) {
4814 return; // not editable.
4816 if (is_edit && this.is_header_editing) {
4817 return; // already editing..
4821 this.headerEditEl.dom.value = this.title;
4822 this.headerEditEl.removeClass('d-none');
4823 this.headerEditEl.dom.focus();
4824 this.titleEl.addClass('d-none');
4826 this.is_header_editing = true;
4829 // flip back to not editing.
4830 this.title = this.headerEditEl.dom.value;
4831 this.headerEditEl.addClass('d-none');
4832 this.titleEl.removeClass('d-none');
4833 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4834 this.is_header_editing = false;
4835 this.fireEvent('titlechanged', this, this.title);
4844 Roo.apply(Roo.bootstrap.Modal, {
4846 * Button config that displays a single OK button
4855 * Button config that displays Yes and No buttons
4871 * Button config that displays OK and Cancel buttons
4886 * Button config that displays Yes, No and Cancel buttons
4911 * messagebox - can be used as a replace
4915 * @class Roo.MessageBox
4916 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4920 Roo.Msg.alert('Status', 'Changes saved successfully.');
4922 // Prompt for user data:
4923 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4925 // process text value...
4929 // Show a dialog using config options:
4931 title:'Save Changes?',
4932 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4933 buttons: Roo.Msg.YESNOCANCEL,
4940 Roo.bootstrap.MessageBox = function(){
4941 var dlg, opt, mask, waitTimer;
4942 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4943 var buttons, activeTextEl, bwidth;
4947 var handleButton = function(button){
4949 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4953 var handleHide = function(){
4955 dlg.el.removeClass(opt.cls);
4958 // Roo.TaskMgr.stop(waitTimer);
4959 // waitTimer = null;
4964 var updateButtons = function(b){
4967 buttons["ok"].hide();
4968 buttons["cancel"].hide();
4969 buttons["yes"].hide();
4970 buttons["no"].hide();
4971 dlg.footerEl.hide();
4975 dlg.footerEl.show();
4976 for(var k in buttons){
4977 if(typeof buttons[k] != "function"){
4980 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4981 width += buttons[k].el.getWidth()+15;
4991 var handleEsc = function(d, k, e){
4992 if(opt && opt.closable !== false){
5002 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5003 * @return {Roo.BasicDialog} The BasicDialog element
5005 getDialog : function(){
5007 dlg = new Roo.bootstrap.Modal( {
5010 //constraintoviewport:false,
5012 //collapsible : false,
5017 //buttonAlign:"center",
5018 closeClick : function(){
5019 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5022 handleButton("cancel");
5027 dlg.on("hide", handleHide);
5029 //dlg.addKeyListener(27, handleEsc);
5031 this.buttons = buttons;
5032 var bt = this.buttonText;
5033 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5034 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5035 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5036 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5038 bodyEl = dlg.bodyEl.createChild({
5040 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5041 '<textarea class="roo-mb-textarea"></textarea>' +
5042 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5044 msgEl = bodyEl.dom.firstChild;
5045 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5046 textboxEl.enableDisplayMode();
5047 textboxEl.addKeyListener([10,13], function(){
5048 if(dlg.isVisible() && opt && opt.buttons){
5051 }else if(opt.buttons.yes){
5052 handleButton("yes");
5056 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5057 textareaEl.enableDisplayMode();
5058 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5059 progressEl.enableDisplayMode();
5061 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5062 var pf = progressEl.dom.firstChild;
5064 pp = Roo.get(pf.firstChild);
5065 pp.setHeight(pf.offsetHeight);
5073 * Updates the message box body text
5074 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5075 * the XHTML-compliant non-breaking space character '&#160;')
5076 * @return {Roo.MessageBox} This message box
5078 updateText : function(text)
5080 if(!dlg.isVisible() && !opt.width){
5081 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5082 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5084 msgEl.innerHTML = text || ' ';
5086 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5087 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5089 Math.min(opt.width || cw , this.maxWidth),
5090 Math.max(opt.minWidth || this.minWidth, bwidth)
5093 activeTextEl.setWidth(w);
5095 if(dlg.isVisible()){
5096 dlg.fixedcenter = false;
5098 // to big, make it scroll. = But as usual stupid IE does not support
5101 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5102 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5103 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5105 bodyEl.dom.style.height = '';
5106 bodyEl.dom.style.overflowY = '';
5109 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5111 bodyEl.dom.style.overflowX = '';
5114 dlg.setContentSize(w, bodyEl.getHeight());
5115 if(dlg.isVisible()){
5116 dlg.fixedcenter = true;
5122 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5123 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5124 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5125 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5126 * @return {Roo.MessageBox} This message box
5128 updateProgress : function(value, text){
5130 this.updateText(text);
5133 if (pp) { // weird bug on my firefox - for some reason this is not defined
5134 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5135 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5141 * Returns true if the message box is currently displayed
5142 * @return {Boolean} True if the message box is visible, else false
5144 isVisible : function(){
5145 return dlg && dlg.isVisible();
5149 * Hides the message box if it is displayed
5152 if(this.isVisible()){
5158 * Displays a new message box, or reinitializes an existing message box, based on the config options
5159 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5160 * The following config object properties are supported:
5162 Property Type Description
5163 ---------- --------------- ------------------------------------------------------------------------------------
5164 animEl String/Element An id or Element from which the message box should animate as it opens and
5165 closes (defaults to undefined)
5166 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5167 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5168 closable Boolean False to hide the top-right close button (defaults to true). Note that
5169 progress and wait dialogs will ignore this property and always hide the
5170 close button as they can only be closed programmatically.
5171 cls String A custom CSS class to apply to the message box element
5172 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5173 displayed (defaults to 75)
5174 fn Function A callback function to execute after closing the dialog. The arguments to the
5175 function will be btn (the name of the button that was clicked, if applicable,
5176 e.g. "ok"), and text (the value of the active text field, if applicable).
5177 Progress and wait dialogs will ignore this option since they do not respond to
5178 user actions and can only be closed programmatically, so any required function
5179 should be called by the same code after it closes the dialog.
5180 icon String A CSS class that provides a background image to be used as an icon for
5181 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5182 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5183 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5184 modal Boolean False to allow user interaction with the page while the message box is
5185 displayed (defaults to true)
5186 msg String A string that will replace the existing message box body text (defaults
5187 to the XHTML-compliant non-breaking space character ' ')
5188 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5189 progress Boolean True to display a progress bar (defaults to false)
5190 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5191 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5192 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5193 title String The title text
5194 value String The string value to set into the active textbox element if displayed
5195 wait Boolean True to display a progress bar (defaults to false)
5196 width Number The width of the dialog in pixels
5203 msg: 'Please enter your address:',
5205 buttons: Roo.MessageBox.OKCANCEL,
5208 animEl: 'addAddressBtn'
5211 * @param {Object} config Configuration options
5212 * @return {Roo.MessageBox} This message box
5214 show : function(options)
5217 // this causes nightmares if you show one dialog after another
5218 // especially on callbacks..
5220 if(this.isVisible()){
5223 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5224 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5225 Roo.log("New Dialog Message:" + options.msg )
5226 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5227 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5230 var d = this.getDialog();
5232 d.setTitle(opt.title || " ");
5233 d.closeEl.setDisplayed(opt.closable !== false);
5234 activeTextEl = textboxEl;
5235 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5240 textareaEl.setHeight(typeof opt.multiline == "number" ?
5241 opt.multiline : this.defaultTextHeight);
5242 activeTextEl = textareaEl;
5251 progressEl.setDisplayed(opt.progress === true);
5253 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5255 this.updateProgress(0);
5256 activeTextEl.dom.value = opt.value || "";
5258 dlg.setDefaultButton(activeTextEl);
5260 var bs = opt.buttons;
5264 }else if(bs && bs.yes){
5265 db = buttons["yes"];
5267 dlg.setDefaultButton(db);
5269 bwidth = updateButtons(opt.buttons);
5270 this.updateText(opt.msg);
5272 d.el.addClass(opt.cls);
5274 d.proxyDrag = opt.proxyDrag === true;
5275 d.modal = opt.modal !== false;
5276 d.mask = opt.modal !== false ? mask : false;
5278 // force it to the end of the z-index stack so it gets a cursor in FF
5279 document.body.appendChild(dlg.el.dom);
5280 d.animateTarget = null;
5281 d.show(options.animEl);
5287 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5288 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5289 * and closing the message box when the process is complete.
5290 * @param {String} title The title bar text
5291 * @param {String} msg The message box body text
5292 * @return {Roo.MessageBox} This message box
5294 progress : function(title, msg){
5301 minWidth: this.minProgressWidth,
5308 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5309 * If a callback function is passed it will be called after the user clicks the button, and the
5310 * id of the button that was clicked will be passed as the only parameter to the callback
5311 * (could also be the top-right close button).
5312 * @param {String} title The title bar text
5313 * @param {String} msg The message box body text
5314 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5315 * @param {Object} scope (optional) The scope of the callback function
5316 * @return {Roo.MessageBox} This message box
5318 alert : function(title, msg, fn, scope)
5333 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5334 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5335 * You are responsible for closing the message box when the process is complete.
5336 * @param {String} msg The message box body text
5337 * @param {String} title (optional) The title bar text
5338 * @return {Roo.MessageBox} This message box
5340 wait : function(msg, title){
5351 waitTimer = Roo.TaskMgr.start({
5353 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5361 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5362 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5363 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5364 * @param {String} title The title bar text
5365 * @param {String} msg The message box body text
5366 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5367 * @param {Object} scope (optional) The scope of the callback function
5368 * @return {Roo.MessageBox} This message box
5370 confirm : function(title, msg, fn, scope){
5374 buttons: this.YESNO,
5383 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5384 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5385 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5386 * (could also be the top-right close button) and the text that was entered will be passed as the two
5387 * parameters to the callback.
5388 * @param {String} title The title bar text
5389 * @param {String} msg The message box body text
5390 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5391 * @param {Object} scope (optional) The scope of the callback function
5392 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5393 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5394 * @return {Roo.MessageBox} This message box
5396 prompt : function(title, msg, fn, scope, multiline){
5400 buttons: this.OKCANCEL,
5405 multiline: multiline,
5412 * Button config that displays a single OK button
5417 * Button config that displays Yes and No buttons
5420 YESNO : {yes:true, no:true},
5422 * Button config that displays OK and Cancel buttons
5425 OKCANCEL : {ok:true, cancel:true},
5427 * Button config that displays Yes, No and Cancel buttons
5430 YESNOCANCEL : {yes:true, no:true, cancel:true},
5433 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5436 defaultTextHeight : 75,
5438 * The maximum width in pixels of the message box (defaults to 600)
5443 * The minimum width in pixels of the message box (defaults to 100)
5448 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5449 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5452 minProgressWidth : 250,
5454 * An object containing the default button text strings that can be overriden for localized language support.
5455 * Supported properties are: ok, cancel, yes and no.
5456 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5469 * Shorthand for {@link Roo.MessageBox}
5471 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5472 Roo.Msg = Roo.Msg || Roo.MessageBox;
5481 * @class Roo.bootstrap.Navbar
5482 * @extends Roo.bootstrap.Component
5483 * Bootstrap Navbar class
5486 * Create a new Navbar
5487 * @param {Object} config The config object
5491 Roo.bootstrap.Navbar = function(config){
5492 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5496 * @event beforetoggle
5497 * Fire before toggle the menu
5498 * @param {Roo.EventObject} e
5500 "beforetoggle" : true
5504 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5513 getAutoCreate : function(){
5516 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5520 initEvents :function ()
5522 //Roo.log(this.el.select('.navbar-toggle',true));
5523 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5530 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5532 var size = this.el.getSize();
5533 this.maskEl.setSize(size.width, size.height);
5534 this.maskEl.enableDisplayMode("block");
5543 getChildContainer : function()
5545 if (this.el && this.el.select('.collapse').getCount()) {
5546 return this.el.select('.collapse',true).first();
5561 onToggle : function()
5564 if(this.fireEvent('beforetoggle', this) === false){
5567 var ce = this.el.select('.navbar-collapse',true).first();
5569 if (!ce.hasClass('show')) {
5579 * Expand the navbar pulldown
5581 expand : function ()
5584 var ce = this.el.select('.navbar-collapse',true).first();
5585 if (ce.hasClass('collapsing')) {
5588 ce.dom.style.height = '';
5590 ce.addClass('in'); // old...
5591 ce.removeClass('collapse');
5592 ce.addClass('show');
5593 var h = ce.getHeight();
5595 ce.removeClass('show');
5596 // at this point we should be able to see it..
5597 ce.addClass('collapsing');
5599 ce.setHeight(0); // resize it ...
5600 ce.on('transitionend', function() {
5601 //Roo.log('done transition');
5602 ce.removeClass('collapsing');
5603 ce.addClass('show');
5604 ce.removeClass('collapse');
5606 ce.dom.style.height = '';
5607 }, this, { single: true} );
5609 ce.dom.scrollTop = 0;
5612 * Collapse the navbar pulldown
5614 collapse : function()
5616 var ce = this.el.select('.navbar-collapse',true).first();
5618 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5619 // it's collapsed or collapsing..
5622 ce.removeClass('in'); // old...
5623 ce.setHeight(ce.getHeight());
5624 ce.removeClass('show');
5625 ce.addClass('collapsing');
5627 ce.on('transitionend', function() {
5628 ce.dom.style.height = '';
5629 ce.removeClass('collapsing');
5630 ce.addClass('collapse');
5631 }, this, { single: true} );
5651 * @class Roo.bootstrap.NavSimplebar
5652 * @extends Roo.bootstrap.Navbar
5653 * Bootstrap Sidebar class
5655 * @cfg {Boolean} inverse is inverted color
5657 * @cfg {String} type (nav | pills | tabs)
5658 * @cfg {Boolean} arrangement stacked | justified
5659 * @cfg {String} align (left | right) alignment
5661 * @cfg {Boolean} main (true|false) main nav bar? default false
5662 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5664 * @cfg {String} tag (header|footer|nav|div) default is nav
5666 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5670 * Create a new Sidebar
5671 * @param {Object} config The config object
5675 Roo.bootstrap.NavSimplebar = function(config){
5676 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5679 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5695 getAutoCreate : function(){
5699 tag : this.tag || 'div',
5700 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5702 if (['light','white'].indexOf(this.weight) > -1) {
5703 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5705 cfg.cls += ' bg-' + this.weight;
5708 cfg.cls += ' navbar-inverse';
5712 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5714 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5723 cls: 'nav nav-' + this.xtype,
5729 this.type = this.type || 'nav';
5730 if (['tabs','pills'].indexOf(this.type) != -1) {
5731 cfg.cn[0].cls += ' nav-' + this.type
5735 if (this.type!=='nav') {
5736 Roo.log('nav type must be nav/tabs/pills')
5738 cfg.cn[0].cls += ' navbar-nav'
5744 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5745 cfg.cn[0].cls += ' nav-' + this.arrangement;
5749 if (this.align === 'right') {
5750 cfg.cn[0].cls += ' navbar-right';
5775 * navbar-expand-md fixed-top
5779 * @class Roo.bootstrap.NavHeaderbar
5780 * @extends Roo.bootstrap.NavSimplebar
5781 * Bootstrap Sidebar class
5783 * @cfg {String} brand what is brand
5784 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5785 * @cfg {String} brand_href href of the brand
5786 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5787 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5788 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5789 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5792 * Create a new Sidebar
5793 * @param {Object} config The config object
5797 Roo.bootstrap.NavHeaderbar = function(config){
5798 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5802 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5809 desktopCenter : false,
5812 getAutoCreate : function(){
5815 tag: this.nav || 'nav',
5816 cls: 'navbar navbar-expand-md',
5822 if (this.desktopCenter) {
5823 cn.push({cls : 'container', cn : []});
5831 cls: 'navbar-toggle navbar-toggler',
5832 'data-toggle': 'collapse',
5837 html: 'Toggle navigation'
5841 cls: 'icon-bar navbar-toggler-icon'
5854 cn.push( Roo.bootstrap.version == 4 ? btn : {
5856 cls: 'navbar-header',
5865 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5869 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5871 if (['light','white'].indexOf(this.weight) > -1) {
5872 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5874 cfg.cls += ' bg-' + this.weight;
5877 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5878 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5880 // tag can override this..
5882 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5885 if (this.brand !== '') {
5886 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5887 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5889 href: this.brand_href ? this.brand_href : '#',
5890 cls: 'navbar-brand',
5898 cfg.cls += ' main-nav';
5906 getHeaderChildContainer : function()
5908 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5909 return this.el.select('.navbar-header',true).first();
5912 return this.getChildContainer();
5915 getChildContainer : function()
5918 return this.el.select('.roo-navbar-collapse',true).first();
5923 initEvents : function()
5925 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5927 if (this.autohide) {
5932 Roo.get(document).on('scroll',function(e) {
5933 var ns = Roo.get(document).getScroll().top;
5934 var os = prevScroll;
5938 ft.removeClass('slideDown');
5939 ft.addClass('slideUp');
5942 ft.removeClass('slideUp');
5943 ft.addClass('slideDown');
5964 * @class Roo.bootstrap.NavSidebar
5965 * @extends Roo.bootstrap.Navbar
5966 * Bootstrap Sidebar class
5969 * Create a new Sidebar
5970 * @param {Object} config The config object
5974 Roo.bootstrap.NavSidebar = function(config){
5975 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5978 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5980 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5982 getAutoCreate : function(){
5987 cls: 'sidebar sidebar-nav'
6009 * @class Roo.bootstrap.NavGroup
6010 * @extends Roo.bootstrap.Component
6011 * Bootstrap NavGroup class
6012 * @cfg {String} align (left|right)
6013 * @cfg {Boolean} inverse
6014 * @cfg {String} type (nav|pills|tab) default nav
6015 * @cfg {String} navId - reference Id for navbar.
6016 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6019 * Create a new nav group
6020 * @param {Object} config The config object
6023 Roo.bootstrap.NavGroup = function(config){
6024 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6027 Roo.bootstrap.NavGroup.register(this);
6031 * Fires when the active item changes
6032 * @param {Roo.bootstrap.NavGroup} this
6033 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6034 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6041 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6053 getAutoCreate : function()
6055 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6061 if (Roo.bootstrap.version == 4) {
6062 if (['tabs','pills'].indexOf(this.type) != -1) {
6063 cfg.cls += ' nav-' + this.type;
6065 // trying to remove so header bar can right align top?
6066 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6067 // do not use on header bar...
6068 cfg.cls += ' navbar-nav';
6073 if (['tabs','pills'].indexOf(this.type) != -1) {
6074 cfg.cls += ' nav-' + this.type
6076 if (this.type !== 'nav') {
6077 Roo.log('nav type must be nav/tabs/pills')
6079 cfg.cls += ' navbar-nav'
6083 if (this.parent() && this.parent().sidebar) {
6086 cls: 'dashboard-menu sidebar-menu'
6092 if (this.form === true) {
6095 cls: 'navbar-form form-inline'
6097 //nav navbar-right ml-md-auto
6098 if (this.align === 'right') {
6099 cfg.cls += ' navbar-right ml-md-auto';
6101 cfg.cls += ' navbar-left';
6105 if (this.align === 'right') {
6106 cfg.cls += ' navbar-right ml-md-auto';
6108 cfg.cls += ' mr-auto';
6112 cfg.cls += ' navbar-inverse';
6120 * sets the active Navigation item
6121 * @param {Roo.bootstrap.NavItem} the new current navitem
6123 setActiveItem : function(item)
6126 Roo.each(this.navItems, function(v){
6131 v.setActive(false, true);
6138 item.setActive(true, true);
6139 this.fireEvent('changed', this, item, prev);
6144 * gets the active Navigation item
6145 * @return {Roo.bootstrap.NavItem} the current navitem
6147 getActive : function()
6151 Roo.each(this.navItems, function(v){
6162 indexOfNav : function()
6166 Roo.each(this.navItems, function(v,i){
6177 * adds a Navigation item
6178 * @param {Roo.bootstrap.NavItem} the navitem to add
6180 addItem : function(cfg)
6182 if (this.form && Roo.bootstrap.version == 4) {
6185 var cn = new Roo.bootstrap.NavItem(cfg);
6187 cn.parentId = this.id;
6188 cn.onRender(this.el, null);
6192 * register a Navigation item
6193 * @param {Roo.bootstrap.NavItem} the navitem to add
6195 register : function(item)
6197 this.navItems.push( item);
6198 item.navId = this.navId;
6203 * clear all the Navigation item
6206 clearAll : function()
6209 this.el.dom.innerHTML = '';
6212 getNavItem: function(tabId)
6215 Roo.each(this.navItems, function(e) {
6216 if (e.tabId == tabId) {
6226 setActiveNext : function()
6228 var i = this.indexOfNav(this.getActive());
6229 if (i > this.navItems.length) {
6232 this.setActiveItem(this.navItems[i+1]);
6234 setActivePrev : function()
6236 var i = this.indexOfNav(this.getActive());
6240 this.setActiveItem(this.navItems[i-1]);
6242 clearWasActive : function(except) {
6243 Roo.each(this.navItems, function(e) {
6244 if (e.tabId != except.tabId && e.was_active) {
6245 e.was_active = false;
6252 getWasActive : function ()
6255 Roo.each(this.navItems, function(e) {
6270 Roo.apply(Roo.bootstrap.NavGroup, {
6274 * register a Navigation Group
6275 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6277 register : function(navgrp)
6279 this.groups[navgrp.navId] = navgrp;
6283 * fetch a Navigation Group based on the navigation ID
6284 * @param {string} the navgroup to add
6285 * @returns {Roo.bootstrap.NavGroup} the navgroup
6287 get: function(navId) {
6288 if (typeof(this.groups[navId]) == 'undefined') {
6290 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6292 return this.groups[navId] ;
6307 * @class Roo.bootstrap.NavItem
6308 * @extends Roo.bootstrap.Component
6309 * Bootstrap Navbar.NavItem class
6310 * @cfg {String} href link to
6311 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6312 * @cfg {Boolean} button_outline show and outlined button
6313 * @cfg {String} html content of button
6314 * @cfg {String} badge text inside badge
6315 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6316 * @cfg {String} glyphicon DEPRICATED - use fa
6317 * @cfg {String} icon DEPRICATED - use fa
6318 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6319 * @cfg {Boolean} active Is item active
6320 * @cfg {Boolean} disabled Is item disabled
6321 * @cfg {String} linkcls Link Class
6322 * @cfg {Boolean} preventDefault (true | false) default false
6323 * @cfg {String} tabId the tab that this item activates.
6324 * @cfg {String} tagtype (a|span) render as a href or span?
6325 * @cfg {Boolean} animateRef (true|false) link to element default false
6328 * Create a new Navbar Item
6329 * @param {Object} config The config object
6331 Roo.bootstrap.NavItem = function(config){
6332 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6337 * The raw click event for the entire grid.
6338 * @param {Roo.EventObject} e
6343 * Fires when the active item active state changes
6344 * @param {Roo.bootstrap.NavItem} this
6345 * @param {boolean} state the new state
6351 * Fires when scroll to element
6352 * @param {Roo.bootstrap.NavItem} this
6353 * @param {Object} options
6354 * @param {Roo.EventObject} e
6362 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6371 preventDefault : false,
6379 button_outline : false,
6383 getAutoCreate : function(){
6390 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6393 cfg.cls += ' active' ;
6395 if (this.disabled) {
6396 cfg.cls += ' disabled';
6400 if (this.button_weight.length) {
6401 cfg.tag = this.href ? 'a' : 'button';
6402 cfg.html = this.html || '';
6403 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6405 cfg.href = this.href;
6408 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6410 cfg.cls += " nav-html";
6413 // menu .. should add dropdown-menu class - so no need for carat..
6415 if (this.badge !== '') {
6417 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6422 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6426 href : this.href || "#",
6427 html: this.html || '',
6431 if (this.tagtype == 'a') {
6432 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6436 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6437 } else if (this.fa) {
6438 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6439 } else if(this.glyphicon) {
6440 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6442 cfg.cn[0].cls += " nav-html";
6446 cfg.cn[0].html += " <span class='caret'></span>";
6450 if (this.badge !== '') {
6451 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6459 onRender : function(ct, position)
6461 // Roo.log("Call onRender: " + this.xtype);
6462 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6466 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6467 this.navLink = this.el.select('.nav-link',true).first();
6468 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6473 initEvents: function()
6475 if (typeof (this.menu) != 'undefined') {
6476 this.menu.parentType = this.xtype;
6477 this.menu.triggerEl = this.el;
6478 this.menu = this.addxtype(Roo.apply({}, this.menu));
6481 this.el.on('click', this.onClick, this);
6483 //if(this.tagtype == 'span'){
6484 // this.el.select('span',true).on('click', this.onClick, this);
6487 // at this point parent should be available..
6488 this.parent().register(this);
6491 onClick : function(e)
6493 if (e.getTarget('.dropdown-menu-item')) {
6494 // did you click on a menu itemm.... - then don't trigger onclick..
6499 this.preventDefault ||
6502 Roo.log("NavItem - prevent Default?");
6506 if (this.disabled) {
6510 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6511 if (tg && tg.transition) {
6512 Roo.log("waiting for the transitionend");
6518 //Roo.log("fire event clicked");
6519 if(this.fireEvent('click', this, e) === false){
6523 if(this.tagtype == 'span'){
6527 //Roo.log(this.href);
6528 var ael = this.el.select('a',true).first();
6531 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6532 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6533 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6534 return; // ignore... - it's a 'hash' to another page.
6536 Roo.log("NavItem - prevent Default?");
6538 this.scrollToElement(e);
6542 var p = this.parent();
6544 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6545 if (typeof(p.setActiveItem) !== 'undefined') {
6546 p.setActiveItem(this);
6550 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6551 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6552 // remove the collapsed menu expand...
6553 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6557 isActive: function () {
6560 setActive : function(state, fire, is_was_active)
6562 if (this.active && !state && this.navId) {
6563 this.was_active = true;
6564 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6566 nv.clearWasActive(this);
6570 this.active = state;
6573 this.el.removeClass('active');
6574 this.navLink ? this.navLink.removeClass('active') : false;
6575 } else if (!this.el.hasClass('active')) {
6577 this.el.addClass('active');
6578 if (Roo.bootstrap.version == 4 && this.navLink ) {
6579 this.navLink.addClass('active');
6584 this.fireEvent('changed', this, state);
6587 // show a panel if it's registered and related..
6589 if (!this.navId || !this.tabId || !state || is_was_active) {
6593 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6597 var pan = tg.getPanelByName(this.tabId);
6601 // if we can not flip to new panel - go back to old nav highlight..
6602 if (false == tg.showPanel(pan)) {
6603 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6605 var onav = nv.getWasActive();
6607 onav.setActive(true, false, true);
6616 // this should not be here...
6617 setDisabled : function(state)
6619 this.disabled = state;
6621 this.el.removeClass('disabled');
6622 } else if (!this.el.hasClass('disabled')) {
6623 this.el.addClass('disabled');
6629 * Fetch the element to display the tooltip on.
6630 * @return {Roo.Element} defaults to this.el
6632 tooltipEl : function()
6634 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6637 scrollToElement : function(e)
6639 var c = document.body;
6642 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6644 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6645 c = document.documentElement;
6648 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6654 var o = target.calcOffsetsTo(c);
6661 this.fireEvent('scrollto', this, options, e);
6663 Roo.get(c).scrollTo('top', options.value, true);
6668 * Set the HTML (text content) of the item
6669 * @param {string} html content for the nav item
6671 setHtml : function(html)
6674 this.htmlEl.dom.innerHTML = html;
6686 * <span> icon </span>
6687 * <span> text </span>
6688 * <span>badge </span>
6692 * @class Roo.bootstrap.NavSidebarItem
6693 * @extends Roo.bootstrap.NavItem
6694 * Bootstrap Navbar.NavSidebarItem class
6695 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6696 * {Boolean} open is the menu open
6697 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6698 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6699 * {String} buttonSize (sm|md|lg)the extra classes for the button
6700 * {Boolean} showArrow show arrow next to the text (default true)
6702 * Create a new Navbar Button
6703 * @param {Object} config The config object
6705 Roo.bootstrap.NavSidebarItem = function(config){
6706 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6711 * The raw click event for the entire grid.
6712 * @param {Roo.EventObject} e
6717 * Fires when the active item active state changes
6718 * @param {Roo.bootstrap.NavSidebarItem} this
6719 * @param {boolean} state the new state
6727 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6729 badgeWeight : 'default',
6735 buttonWeight : 'default',
6741 getAutoCreate : function(){
6746 href : this.href || '#',
6752 if(this.buttonView){
6755 href : this.href || '#',
6756 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6769 cfg.cls += ' active';
6772 if (this.disabled) {
6773 cfg.cls += ' disabled';
6776 cfg.cls += ' open x-open';
6779 if (this.glyphicon || this.icon) {
6780 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6781 a.cn.push({ tag : 'i', cls : c }) ;
6784 if(!this.buttonView){
6787 html : this.html || ''
6794 if (this.badge !== '') {
6795 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6801 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6804 a.cls += ' dropdown-toggle treeview' ;
6810 initEvents : function()
6812 if (typeof (this.menu) != 'undefined') {
6813 this.menu.parentType = this.xtype;
6814 this.menu.triggerEl = this.el;
6815 this.menu = this.addxtype(Roo.apply({}, this.menu));
6818 this.el.on('click', this.onClick, this);
6820 if(this.badge !== ''){
6821 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6826 onClick : function(e)
6833 if(this.preventDefault){
6837 this.fireEvent('click', this, e);
6840 disable : function()
6842 this.setDisabled(true);
6847 this.setDisabled(false);
6850 setDisabled : function(state)
6852 if(this.disabled == state){
6856 this.disabled = state;
6859 this.el.addClass('disabled');
6863 this.el.removeClass('disabled');
6868 setActive : function(state)
6870 if(this.active == state){
6874 this.active = state;
6877 this.el.addClass('active');
6881 this.el.removeClass('active');
6886 isActive: function ()
6891 setBadge : function(str)
6897 this.badgeEl.dom.innerHTML = str;
6912 Roo.namespace('Roo.bootstrap.breadcrumb');
6916 * @class Roo.bootstrap.breadcrumb.Nav
6917 * @extends Roo.bootstrap.Component
6918 * Bootstrap Breadcrumb Nav Class
6920 * @children Roo.bootstrap.breadcrumb.Item
6923 * Create a new breadcrumb.Nav
6924 * @param {Object} config The config object
6928 Roo.bootstrap.breadcrumb.Nav = function(config){
6929 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6934 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6936 getAutoCreate : function()
6953 initEvents: function()
6955 this.olEl = this.el.select('ol',true).first();
6957 getChildContainer : function()
6973 * @class Roo.bootstrap.breadcrumb.Nav
6974 * @extends Roo.bootstrap.Component
6975 * Bootstrap Breadcrumb Nav Class
6977 * @children Roo.bootstrap.breadcrumb.Component
6978 * @cfg {String} html the content of the link.
6979 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6980 * @cfg {Boolean} active is it active
6984 * Create a new breadcrumb.Nav
6985 * @param {Object} config The config object
6988 Roo.bootstrap.breadcrumb.Item = function(config){
6989 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6994 * The img click event for the img.
6995 * @param {Roo.EventObject} e
7002 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7007 getAutoCreate : function()
7012 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7014 if (this.href !== false) {
7021 cfg.html = this.html;
7027 initEvents: function()
7030 this.el.select('a', true).first().on('click',this.onClick, this)
7034 onClick : function(e)
7037 this.fireEvent('click',this, e);
7050 * @class Roo.bootstrap.Row
7051 * @extends Roo.bootstrap.Component
7052 * Bootstrap Row class (contains columns...)
7056 * @param {Object} config The config object
7059 Roo.bootstrap.Row = function(config){
7060 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7063 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7065 getAutoCreate : function(){
7084 * @class Roo.bootstrap.Pagination
7085 * @extends Roo.bootstrap.Component
7086 * Bootstrap Pagination class
7087 * @cfg {String} size xs | sm | md | lg
7088 * @cfg {Boolean} inverse false | true
7091 * Create a new Pagination
7092 * @param {Object} config The config object
7095 Roo.bootstrap.Pagination = function(config){
7096 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7099 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7105 getAutoCreate : function(){
7111 cfg.cls += ' inverse';
7117 cfg.cls += " " + this.cls;
7135 * @class Roo.bootstrap.PaginationItem
7136 * @extends Roo.bootstrap.Component
7137 * Bootstrap PaginationItem class
7138 * @cfg {String} html text
7139 * @cfg {String} href the link
7140 * @cfg {Boolean} preventDefault (true | false) default true
7141 * @cfg {Boolean} active (true | false) default false
7142 * @cfg {Boolean} disabled default false
7146 * Create a new PaginationItem
7147 * @param {Object} config The config object
7151 Roo.bootstrap.PaginationItem = function(config){
7152 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7157 * The raw click event for the entire grid.
7158 * @param {Roo.EventObject} e
7164 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7168 preventDefault: true,
7173 getAutoCreate : function(){
7179 href : this.href ? this.href : '#',
7180 html : this.html ? this.html : ''
7190 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7194 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7200 initEvents: function() {
7202 this.el.on('click', this.onClick, this);
7205 onClick : function(e)
7207 Roo.log('PaginationItem on click ');
7208 if(this.preventDefault){
7216 this.fireEvent('click', this, e);
7232 * @class Roo.bootstrap.Slider
7233 * @extends Roo.bootstrap.Component
7234 * Bootstrap Slider class
7237 * Create a new Slider
7238 * @param {Object} config The config object
7241 Roo.bootstrap.Slider = function(config){
7242 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7245 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7247 getAutoCreate : function(){
7251 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7255 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7267 * Ext JS Library 1.1.1
7268 * Copyright(c) 2006-2007, Ext JS, LLC.
7270 * Originally Released Under LGPL - original licence link has changed is not relivant.
7273 * <script type="text/javascript">
7278 * @class Roo.grid.ColumnModel
7279 * @extends Roo.util.Observable
7280 * This is the default implementation of a ColumnModel used by the Grid. It defines
7281 * the columns in the grid.
7284 var colModel = new Roo.grid.ColumnModel([
7285 {header: "Ticker", width: 60, sortable: true, locked: true},
7286 {header: "Company Name", width: 150, sortable: true},
7287 {header: "Market Cap.", width: 100, sortable: true},
7288 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7289 {header: "Employees", width: 100, sortable: true, resizable: false}
7294 * The config options listed for this class are options which may appear in each
7295 * individual column definition.
7296 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7298 * @param {Object} config An Array of column config objects. See this class's
7299 * config objects for details.
7301 Roo.grid.ColumnModel = function(config){
7303 * The config passed into the constructor
7305 this.config = config;
7308 // if no id, create one
7309 // if the column does not have a dataIndex mapping,
7310 // map it to the order it is in the config
7311 for(var i = 0, len = config.length; i < len; i++){
7313 if(typeof c.dataIndex == "undefined"){
7316 if(typeof c.renderer == "string"){
7317 c.renderer = Roo.util.Format[c.renderer];
7319 if(typeof c.id == "undefined"){
7322 if(c.editor && c.editor.xtype){
7323 c.editor = Roo.factory(c.editor, Roo.grid);
7325 if(c.editor && c.editor.isFormField){
7326 c.editor = new Roo.grid.GridEditor(c.editor);
7328 this.lookup[c.id] = c;
7332 * The width of columns which have no width specified (defaults to 100)
7335 this.defaultWidth = 100;
7338 * Default sortable of columns which have no sortable specified (defaults to false)
7341 this.defaultSortable = false;
7345 * @event widthchange
7346 * Fires when the width of a column changes.
7347 * @param {ColumnModel} this
7348 * @param {Number} columnIndex The column index
7349 * @param {Number} newWidth The new width
7351 "widthchange": true,
7353 * @event headerchange
7354 * Fires when the text of a header changes.
7355 * @param {ColumnModel} this
7356 * @param {Number} columnIndex The column index
7357 * @param {Number} newText The new header text
7359 "headerchange": true,
7361 * @event hiddenchange
7362 * Fires when a column is hidden or "unhidden".
7363 * @param {ColumnModel} this
7364 * @param {Number} columnIndex The column index
7365 * @param {Boolean} hidden true if hidden, false otherwise
7367 "hiddenchange": true,
7369 * @event columnmoved
7370 * Fires when a column is moved.
7371 * @param {ColumnModel} this
7372 * @param {Number} oldIndex
7373 * @param {Number} newIndex
7375 "columnmoved" : true,
7377 * @event columlockchange
7378 * Fires when a column's locked state is changed
7379 * @param {ColumnModel} this
7380 * @param {Number} colIndex
7381 * @param {Boolean} locked true if locked
7383 "columnlockchange" : true
7385 Roo.grid.ColumnModel.superclass.constructor.call(this);
7387 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7389 * @cfg {String} header The header text to display in the Grid view.
7392 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7393 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7394 * specified, the column's index is used as an index into the Record's data Array.
7397 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7398 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7401 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7402 * Defaults to the value of the {@link #defaultSortable} property.
7403 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7406 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7409 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7412 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7415 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7418 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7419 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7420 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7421 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7424 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7427 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7430 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7433 * @cfg {String} cursor (Optional)
7436 * @cfg {String} tooltip (Optional)
7439 * @cfg {Number} xs (Optional)
7442 * @cfg {Number} sm (Optional)
7445 * @cfg {Number} md (Optional)
7448 * @cfg {Number} lg (Optional)
7451 * Returns the id of the column at the specified index.
7452 * @param {Number} index The column index
7453 * @return {String} the id
7455 getColumnId : function(index){
7456 return this.config[index].id;
7460 * Returns the column for a specified id.
7461 * @param {String} id The column id
7462 * @return {Object} the column
7464 getColumnById : function(id){
7465 return this.lookup[id];
7470 * Returns the column for a specified dataIndex.
7471 * @param {String} dataIndex The column dataIndex
7472 * @return {Object|Boolean} the column or false if not found
7474 getColumnByDataIndex: function(dataIndex){
7475 var index = this.findColumnIndex(dataIndex);
7476 return index > -1 ? this.config[index] : false;
7480 * Returns the index for a specified column id.
7481 * @param {String} id The column id
7482 * @return {Number} the index, or -1 if not found
7484 getIndexById : function(id){
7485 for(var i = 0, len = this.config.length; i < len; i++){
7486 if(this.config[i].id == id){
7494 * Returns the index for a specified column dataIndex.
7495 * @param {String} dataIndex The column dataIndex
7496 * @return {Number} the index, or -1 if not found
7499 findColumnIndex : function(dataIndex){
7500 for(var i = 0, len = this.config.length; i < len; i++){
7501 if(this.config[i].dataIndex == dataIndex){
7509 moveColumn : function(oldIndex, newIndex){
7510 var c = this.config[oldIndex];
7511 this.config.splice(oldIndex, 1);
7512 this.config.splice(newIndex, 0, c);
7513 this.dataMap = null;
7514 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7517 isLocked : function(colIndex){
7518 return this.config[colIndex].locked === true;
7521 setLocked : function(colIndex, value, suppressEvent){
7522 if(this.isLocked(colIndex) == value){
7525 this.config[colIndex].locked = value;
7527 this.fireEvent("columnlockchange", this, colIndex, value);
7531 getTotalLockedWidth : function(){
7533 for(var i = 0; i < this.config.length; i++){
7534 if(this.isLocked(i) && !this.isHidden(i)){
7535 this.totalWidth += this.getColumnWidth(i);
7541 getLockedCount : function(){
7542 for(var i = 0, len = this.config.length; i < len; i++){
7543 if(!this.isLocked(i)){
7548 return this.config.length;
7552 * Returns the number of columns.
7555 getColumnCount : function(visibleOnly){
7556 if(visibleOnly === true){
7558 for(var i = 0, len = this.config.length; i < len; i++){
7559 if(!this.isHidden(i)){
7565 return this.config.length;
7569 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7570 * @param {Function} fn
7571 * @param {Object} scope (optional)
7572 * @return {Array} result
7574 getColumnsBy : function(fn, scope){
7576 for(var i = 0, len = this.config.length; i < len; i++){
7577 var c = this.config[i];
7578 if(fn.call(scope||this, c, i) === true){
7586 * Returns true if the specified column is sortable.
7587 * @param {Number} col The column index
7590 isSortable : function(col){
7591 if(typeof this.config[col].sortable == "undefined"){
7592 return this.defaultSortable;
7594 return this.config[col].sortable;
7598 * Returns the rendering (formatting) function defined for the column.
7599 * @param {Number} col The column index.
7600 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7602 getRenderer : function(col){
7603 if(!this.config[col].renderer){
7604 return Roo.grid.ColumnModel.defaultRenderer;
7606 return this.config[col].renderer;
7610 * Sets the rendering (formatting) function for a column.
7611 * @param {Number} col The column index
7612 * @param {Function} fn The function to use to process the cell's raw data
7613 * to return HTML markup for the grid view. The render function is called with
7614 * the following parameters:<ul>
7615 * <li>Data value.</li>
7616 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7617 * <li>css A CSS style string to apply to the table cell.</li>
7618 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7619 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7620 * <li>Row index</li>
7621 * <li>Column index</li>
7622 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7624 setRenderer : function(col, fn){
7625 this.config[col].renderer = fn;
7629 * Returns the width for the specified column.
7630 * @param {Number} col The column index
7633 getColumnWidth : function(col){
7634 return this.config[col].width * 1 || this.defaultWidth;
7638 * Sets the width for a column.
7639 * @param {Number} col The column index
7640 * @param {Number} width The new width
7642 setColumnWidth : function(col, width, suppressEvent){
7643 this.config[col].width = width;
7644 this.totalWidth = null;
7646 this.fireEvent("widthchange", this, col, width);
7651 * Returns the total width of all columns.
7652 * @param {Boolean} includeHidden True to include hidden column widths
7655 getTotalWidth : function(includeHidden){
7656 if(!this.totalWidth){
7657 this.totalWidth = 0;
7658 for(var i = 0, len = this.config.length; i < len; i++){
7659 if(includeHidden || !this.isHidden(i)){
7660 this.totalWidth += this.getColumnWidth(i);
7664 return this.totalWidth;
7668 * Returns the header for the specified column.
7669 * @param {Number} col The column index
7672 getColumnHeader : function(col){
7673 return this.config[col].header;
7677 * Sets the header for a column.
7678 * @param {Number} col The column index
7679 * @param {String} header The new header
7681 setColumnHeader : function(col, header){
7682 this.config[col].header = header;
7683 this.fireEvent("headerchange", this, col, header);
7687 * Returns the tooltip for the specified column.
7688 * @param {Number} col The column index
7691 getColumnTooltip : function(col){
7692 return this.config[col].tooltip;
7695 * Sets the tooltip for a column.
7696 * @param {Number} col The column index
7697 * @param {String} tooltip The new tooltip
7699 setColumnTooltip : function(col, tooltip){
7700 this.config[col].tooltip = tooltip;
7704 * Returns the dataIndex for the specified column.
7705 * @param {Number} col The column index
7708 getDataIndex : function(col){
7709 return this.config[col].dataIndex;
7713 * Sets the dataIndex for a column.
7714 * @param {Number} col The column index
7715 * @param {Number} dataIndex The new dataIndex
7717 setDataIndex : function(col, dataIndex){
7718 this.config[col].dataIndex = dataIndex;
7724 * Returns true if the cell is editable.
7725 * @param {Number} colIndex The column index
7726 * @param {Number} rowIndex The row index - this is nto actually used..?
7729 isCellEditable : function(colIndex, rowIndex){
7730 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7734 * Returns the editor defined for the cell/column.
7735 * return false or null to disable editing.
7736 * @param {Number} colIndex The column index
7737 * @param {Number} rowIndex The row index
7740 getCellEditor : function(colIndex, rowIndex){
7741 return this.config[colIndex].editor;
7745 * Sets if a column is editable.
7746 * @param {Number} col The column index
7747 * @param {Boolean} editable True if the column is editable
7749 setEditable : function(col, editable){
7750 this.config[col].editable = editable;
7755 * Returns true if the column is hidden.
7756 * @param {Number} colIndex The column index
7759 isHidden : function(colIndex){
7760 return this.config[colIndex].hidden;
7765 * Returns true if the column width cannot be changed
7767 isFixed : function(colIndex){
7768 return this.config[colIndex].fixed;
7772 * Returns true if the column can be resized
7775 isResizable : function(colIndex){
7776 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7779 * Sets if a column is hidden.
7780 * @param {Number} colIndex The column index
7781 * @param {Boolean} hidden True if the column is hidden
7783 setHidden : function(colIndex, hidden){
7784 this.config[colIndex].hidden = hidden;
7785 this.totalWidth = null;
7786 this.fireEvent("hiddenchange", this, colIndex, hidden);
7790 * Sets the editor for a column.
7791 * @param {Number} col The column index
7792 * @param {Object} editor The editor object
7794 setEditor : function(col, editor){
7795 this.config[col].editor = editor;
7799 Roo.grid.ColumnModel.defaultRenderer = function(value)
7801 if(typeof value == "object") {
7804 if(typeof value == "string" && value.length < 1){
7808 return String.format("{0}", value);
7811 // Alias for backwards compatibility
7812 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7815 * Ext JS Library 1.1.1
7816 * Copyright(c) 2006-2007, Ext JS, LLC.
7818 * Originally Released Under LGPL - original licence link has changed is not relivant.
7821 * <script type="text/javascript">
7825 * @class Roo.LoadMask
7826 * A simple utility class for generically masking elements while loading data. If the element being masked has
7827 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7828 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7829 * element's UpdateManager load indicator and will be destroyed after the initial load.
7831 * Create a new LoadMask
7832 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7833 * @param {Object} config The config object
7835 Roo.LoadMask = function(el, config){
7836 this.el = Roo.get(el);
7837 Roo.apply(this, config);
7839 this.store.on('beforeload', this.onBeforeLoad, this);
7840 this.store.on('load', this.onLoad, this);
7841 this.store.on('loadexception', this.onLoadException, this);
7842 this.removeMask = false;
7844 var um = this.el.getUpdateManager();
7845 um.showLoadIndicator = false; // disable the default indicator
7846 um.on('beforeupdate', this.onBeforeLoad, this);
7847 um.on('update', this.onLoad, this);
7848 um.on('failure', this.onLoad, this);
7849 this.removeMask = true;
7853 Roo.LoadMask.prototype = {
7855 * @cfg {Boolean} removeMask
7856 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7857 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7861 * The text to display in a centered loading message box (defaults to 'Loading...')
7865 * @cfg {String} msgCls
7866 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7868 msgCls : 'x-mask-loading',
7871 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7877 * Disables the mask to prevent it from being displayed
7879 disable : function(){
7880 this.disabled = true;
7884 * Enables the mask so that it can be displayed
7886 enable : function(){
7887 this.disabled = false;
7890 onLoadException : function()
7894 if (typeof(arguments[3]) != 'undefined') {
7895 Roo.MessageBox.alert("Error loading",arguments[3]);
7899 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7900 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7907 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7912 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7916 onBeforeLoad : function(){
7918 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7923 destroy : function(){
7925 this.store.un('beforeload', this.onBeforeLoad, this);
7926 this.store.un('load', this.onLoad, this);
7927 this.store.un('loadexception', this.onLoadException, this);
7929 var um = this.el.getUpdateManager();
7930 um.un('beforeupdate', this.onBeforeLoad, this);
7931 um.un('update', this.onLoad, this);
7932 um.un('failure', this.onLoad, this);
7943 * @class Roo.bootstrap.Table
7944 * @extends Roo.bootstrap.Component
7945 * Bootstrap Table class
7946 * @cfg {String} cls table class
7947 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7948 * @cfg {String} bgcolor Specifies the background color for a table
7949 * @cfg {Number} border Specifies whether the table cells should have borders or not
7950 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7951 * @cfg {Number} cellspacing Specifies the space between cells
7952 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7953 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7954 * @cfg {String} sortable Specifies that the table should be sortable
7955 * @cfg {String} summary Specifies a summary of the content of a table
7956 * @cfg {Number} width Specifies the width of a table
7957 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7959 * @cfg {boolean} striped Should the rows be alternative striped
7960 * @cfg {boolean} bordered Add borders to the table
7961 * @cfg {boolean} hover Add hover highlighting
7962 * @cfg {boolean} condensed Format condensed
7963 * @cfg {boolean} responsive Format condensed
7964 * @cfg {Boolean} loadMask (true|false) default false
7965 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7966 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7967 * @cfg {Boolean} rowSelection (true|false) default false
7968 * @cfg {Boolean} cellSelection (true|false) default false
7969 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7970 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7971 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7972 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7976 * Create a new Table
7977 * @param {Object} config The config object
7980 Roo.bootstrap.Table = function(config){
7981 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7986 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7987 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7988 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7989 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7991 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7993 this.sm.grid = this;
7994 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7995 this.sm = this.selModel;
7996 this.sm.xmodule = this.xmodule || false;
7999 if (this.cm && typeof(this.cm.config) == 'undefined') {
8000 this.colModel = new Roo.grid.ColumnModel(this.cm);
8001 this.cm = this.colModel;
8002 this.cm.xmodule = this.xmodule || false;
8005 this.store= Roo.factory(this.store, Roo.data);
8006 this.ds = this.store;
8007 this.ds.xmodule = this.xmodule || false;
8010 if (this.footer && this.store) {
8011 this.footer.dataSource = this.ds;
8012 this.footer = Roo.factory(this.footer);
8019 * Fires when a cell is clicked
8020 * @param {Roo.bootstrap.Table} this
8021 * @param {Roo.Element} el
8022 * @param {Number} rowIndex
8023 * @param {Number} columnIndex
8024 * @param {Roo.EventObject} e
8028 * @event celldblclick
8029 * Fires when a cell is double clicked
8030 * @param {Roo.bootstrap.Table} this
8031 * @param {Roo.Element} el
8032 * @param {Number} rowIndex
8033 * @param {Number} columnIndex
8034 * @param {Roo.EventObject} e
8036 "celldblclick" : true,
8039 * Fires when a row is clicked
8040 * @param {Roo.bootstrap.Table} this
8041 * @param {Roo.Element} el
8042 * @param {Number} rowIndex
8043 * @param {Roo.EventObject} e
8047 * @event rowdblclick
8048 * Fires when a row is double clicked
8049 * @param {Roo.bootstrap.Table} this
8050 * @param {Roo.Element} el
8051 * @param {Number} rowIndex
8052 * @param {Roo.EventObject} e
8054 "rowdblclick" : true,
8057 * Fires when a mouseover occur
8058 * @param {Roo.bootstrap.Table} this
8059 * @param {Roo.Element} el
8060 * @param {Number} rowIndex
8061 * @param {Number} columnIndex
8062 * @param {Roo.EventObject} e
8067 * Fires when a mouseout occur
8068 * @param {Roo.bootstrap.Table} this
8069 * @param {Roo.Element} el
8070 * @param {Number} rowIndex
8071 * @param {Number} columnIndex
8072 * @param {Roo.EventObject} e
8077 * Fires when a row is rendered, so you can change add a style to it.
8078 * @param {Roo.bootstrap.Table} this
8079 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8083 * @event rowsrendered
8084 * Fires when all the rows have been rendered
8085 * @param {Roo.bootstrap.Table} this
8087 'rowsrendered' : true,
8089 * @event contextmenu
8090 * The raw contextmenu event for the entire grid.
8091 * @param {Roo.EventObject} e
8093 "contextmenu" : true,
8095 * @event rowcontextmenu
8096 * Fires when a row is right clicked
8097 * @param {Roo.bootstrap.Table} this
8098 * @param {Number} rowIndex
8099 * @param {Roo.EventObject} e
8101 "rowcontextmenu" : true,
8103 * @event cellcontextmenu
8104 * Fires when a cell is right clicked
8105 * @param {Roo.bootstrap.Table} this
8106 * @param {Number} rowIndex
8107 * @param {Number} cellIndex
8108 * @param {Roo.EventObject} e
8110 "cellcontextmenu" : true,
8112 * @event headercontextmenu
8113 * Fires when a header is right clicked
8114 * @param {Roo.bootstrap.Table} this
8115 * @param {Number} columnIndex
8116 * @param {Roo.EventObject} e
8118 "headercontextmenu" : true
8122 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8148 rowSelection : false,
8149 cellSelection : false,
8152 // Roo.Element - the tbody
8154 // Roo.Element - thead element
8157 container: false, // used by gridpanel...
8163 auto_hide_footer : false,
8165 getAutoCreate : function()
8167 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8174 if (this.scrollBody) {
8175 cfg.cls += ' table-body-fixed';
8178 cfg.cls += ' table-striped';
8182 cfg.cls += ' table-hover';
8184 if (this.bordered) {
8185 cfg.cls += ' table-bordered';
8187 if (this.condensed) {
8188 cfg.cls += ' table-condensed';
8190 if (this.responsive) {
8191 cfg.cls += ' table-responsive';
8195 cfg.cls+= ' ' +this.cls;
8198 // this lot should be simplifed...
8211 ].forEach(function(k) {
8219 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8222 if(this.store || this.cm){
8223 if(this.headerShow){
8224 cfg.cn.push(this.renderHeader());
8227 cfg.cn.push(this.renderBody());
8229 if(this.footerShow){
8230 cfg.cn.push(this.renderFooter());
8232 // where does this come from?
8233 //cfg.cls+= ' TableGrid';
8236 return { cn : [ cfg ] };
8239 initEvents : function()
8241 if(!this.store || !this.cm){
8244 if (this.selModel) {
8245 this.selModel.initEvents();
8249 //Roo.log('initEvents with ds!!!!');
8251 this.mainBody = this.el.select('tbody', true).first();
8252 this.mainHead = this.el.select('thead', true).first();
8253 this.mainFoot = this.el.select('tfoot', true).first();
8259 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8260 e.on('click', _this.sort, _this);
8263 this.mainBody.on("click", this.onClick, this);
8264 this.mainBody.on("dblclick", this.onDblClick, this);
8266 // why is this done????? = it breaks dialogs??
8267 //this.parent().el.setStyle('position', 'relative');
8271 this.footer.parentId = this.id;
8272 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8275 this.el.select('tfoot tr td').first().addClass('hide');
8280 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8283 this.store.on('load', this.onLoad, this);
8284 this.store.on('beforeload', this.onBeforeLoad, this);
8285 this.store.on('update', this.onUpdate, this);
8286 this.store.on('add', this.onAdd, this);
8287 this.store.on("clear", this.clear, this);
8289 this.el.on("contextmenu", this.onContextMenu, this);
8291 this.mainBody.on('scroll', this.onBodyScroll, this);
8293 this.cm.on("headerchange", this.onHeaderChange, this);
8295 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8299 onContextMenu : function(e, t)
8301 this.processEvent("contextmenu", e);
8304 processEvent : function(name, e)
8306 if (name != 'touchstart' ) {
8307 this.fireEvent(name, e);
8310 var t = e.getTarget();
8312 var cell = Roo.get(t);
8318 if(cell.findParent('tfoot', false, true)){
8322 if(cell.findParent('thead', false, true)){
8324 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8325 cell = Roo.get(t).findParent('th', false, true);
8327 Roo.log("failed to find th in thead?");
8328 Roo.log(e.getTarget());
8333 var cellIndex = cell.dom.cellIndex;
8335 var ename = name == 'touchstart' ? 'click' : name;
8336 this.fireEvent("header" + ename, this, cellIndex, e);
8341 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8342 cell = Roo.get(t).findParent('td', false, true);
8344 Roo.log("failed to find th in tbody?");
8345 Roo.log(e.getTarget());
8350 var row = cell.findParent('tr', false, true);
8351 var cellIndex = cell.dom.cellIndex;
8352 var rowIndex = row.dom.rowIndex - 1;
8356 this.fireEvent("row" + name, this, rowIndex, e);
8360 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8366 onMouseover : function(e, el)
8368 var cell = Roo.get(el);
8374 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8375 cell = cell.findParent('td', false, true);
8378 var row = cell.findParent('tr', false, true);
8379 var cellIndex = cell.dom.cellIndex;
8380 var rowIndex = row.dom.rowIndex - 1; // start from 0
8382 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8386 onMouseout : function(e, el)
8388 var cell = Roo.get(el);
8394 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8395 cell = cell.findParent('td', false, true);
8398 var row = cell.findParent('tr', false, true);
8399 var cellIndex = cell.dom.cellIndex;
8400 var rowIndex = row.dom.rowIndex - 1; // start from 0
8402 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8406 onClick : function(e, el)
8408 var cell = Roo.get(el);
8410 if(!cell || (!this.cellSelection && !this.rowSelection)){
8414 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8415 cell = cell.findParent('td', false, true);
8418 if(!cell || typeof(cell) == 'undefined'){
8422 var row = cell.findParent('tr', false, true);
8424 if(!row || typeof(row) == 'undefined'){
8428 var cellIndex = cell.dom.cellIndex;
8429 var rowIndex = this.getRowIndex(row);
8431 // why??? - should these not be based on SelectionModel?
8432 if(this.cellSelection){
8433 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8436 if(this.rowSelection){
8437 this.fireEvent('rowclick', this, row, rowIndex, e);
8443 onDblClick : function(e,el)
8445 var cell = Roo.get(el);
8447 if(!cell || (!this.cellSelection && !this.rowSelection)){
8451 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8452 cell = cell.findParent('td', false, true);
8455 if(!cell || typeof(cell) == 'undefined'){
8459 var row = cell.findParent('tr', false, true);
8461 if(!row || typeof(row) == 'undefined'){
8465 var cellIndex = cell.dom.cellIndex;
8466 var rowIndex = this.getRowIndex(row);
8468 if(this.cellSelection){
8469 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8472 if(this.rowSelection){
8473 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8477 sort : function(e,el)
8479 var col = Roo.get(el);
8481 if(!col.hasClass('sortable')){
8485 var sort = col.attr('sort');
8488 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8492 this.store.sortInfo = {field : sort, direction : dir};
8495 Roo.log("calling footer first");
8496 this.footer.onClick('first');
8499 this.store.load({ params : { start : 0 } });
8503 renderHeader : function()
8511 this.totalWidth = 0;
8513 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8515 var config = cm.config[i];
8519 cls : 'x-hcol-' + i,
8521 html: cm.getColumnHeader(i)
8526 if(typeof(config.sortable) != 'undefined' && config.sortable){
8528 c.html = '<i class="glyphicon"></i>' + c.html;
8531 // could use BS4 hidden-..-down
8533 if(typeof(config.lgHeader) != 'undefined'){
8534 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8537 if(typeof(config.mdHeader) != 'undefined'){
8538 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8541 if(typeof(config.smHeader) != 'undefined'){
8542 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8545 if(typeof(config.xsHeader) != 'undefined'){
8546 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8553 if(typeof(config.tooltip) != 'undefined'){
8554 c.tooltip = config.tooltip;
8557 if(typeof(config.colspan) != 'undefined'){
8558 c.colspan = config.colspan;
8561 if(typeof(config.hidden) != 'undefined' && config.hidden){
8562 c.style += ' display:none;';
8565 if(typeof(config.dataIndex) != 'undefined'){
8566 c.sort = config.dataIndex;
8571 if(typeof(config.align) != 'undefined' && config.align.length){
8572 c.style += ' text-align:' + config.align + ';';
8575 if(typeof(config.width) != 'undefined'){
8576 c.style += ' width:' + config.width + 'px;';
8577 this.totalWidth += config.width;
8579 this.totalWidth += 100; // assume minimum of 100 per column?
8582 if(typeof(config.cls) != 'undefined'){
8583 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8586 ['xs','sm','md','lg'].map(function(size){
8588 if(typeof(config[size]) == 'undefined'){
8592 if (!config[size]) { // 0 = hidden
8593 // BS 4 '0' is treated as hide that column and below.
8594 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8598 c.cls += ' col-' + size + '-' + config[size] + (
8599 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8611 renderBody : function()
8621 colspan : this.cm.getColumnCount()
8631 renderFooter : function()
8641 colspan : this.cm.getColumnCount()
8655 // Roo.log('ds onload');
8660 var ds = this.store;
8662 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8663 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8664 if (_this.store.sortInfo) {
8666 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8667 e.select('i', true).addClass(['glyphicon-arrow-up']);
8670 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8671 e.select('i', true).addClass(['glyphicon-arrow-down']);
8676 var tbody = this.mainBody;
8678 if(ds.getCount() > 0){
8679 ds.data.each(function(d,rowIndex){
8680 var row = this.renderRow(cm, ds, rowIndex);
8682 tbody.createChild(row);
8686 if(row.cellObjects.length){
8687 Roo.each(row.cellObjects, function(r){
8688 _this.renderCellObject(r);
8695 var tfoot = this.el.select('tfoot', true).first();
8697 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8699 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8701 var total = this.ds.getTotalCount();
8703 if(this.footer.pageSize < total){
8704 this.mainFoot.show();
8708 Roo.each(this.el.select('tbody td', true).elements, function(e){
8709 e.on('mouseover', _this.onMouseover, _this);
8712 Roo.each(this.el.select('tbody td', true).elements, function(e){
8713 e.on('mouseout', _this.onMouseout, _this);
8715 this.fireEvent('rowsrendered', this);
8721 onUpdate : function(ds,record)
8723 this.refreshRow(record);
8727 onRemove : function(ds, record, index, isUpdate){
8728 if(isUpdate !== true){
8729 this.fireEvent("beforerowremoved", this, index, record);
8731 var bt = this.mainBody.dom;
8733 var rows = this.el.select('tbody > tr', true).elements;
8735 if(typeof(rows[index]) != 'undefined'){
8736 bt.removeChild(rows[index].dom);
8739 // if(bt.rows[index]){
8740 // bt.removeChild(bt.rows[index]);
8743 if(isUpdate !== true){
8744 //this.stripeRows(index);
8745 //this.syncRowHeights(index, index);
8747 this.fireEvent("rowremoved", this, index, record);
8751 onAdd : function(ds, records, rowIndex)
8753 //Roo.log('on Add called');
8754 // - note this does not handle multiple adding very well..
8755 var bt = this.mainBody.dom;
8756 for (var i =0 ; i < records.length;i++) {
8757 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8758 //Roo.log(records[i]);
8759 //Roo.log(this.store.getAt(rowIndex+i));
8760 this.insertRow(this.store, rowIndex + i, false);
8767 refreshRow : function(record){
8768 var ds = this.store, index;
8769 if(typeof record == 'number'){
8771 record = ds.getAt(index);
8773 index = ds.indexOf(record);
8775 return; // should not happen - but seems to
8778 this.insertRow(ds, index, true);
8780 this.onRemove(ds, record, index+1, true);
8782 //this.syncRowHeights(index, index);
8784 this.fireEvent("rowupdated", this, index, record);
8787 insertRow : function(dm, rowIndex, isUpdate){
8790 this.fireEvent("beforerowsinserted", this, rowIndex);
8792 //var s = this.getScrollState();
8793 var row = this.renderRow(this.cm, this.store, rowIndex);
8794 // insert before rowIndex..
8795 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8799 if(row.cellObjects.length){
8800 Roo.each(row.cellObjects, function(r){
8801 _this.renderCellObject(r);
8806 this.fireEvent("rowsinserted", this, rowIndex);
8807 //this.syncRowHeights(firstRow, lastRow);
8808 //this.stripeRows(firstRow);
8815 getRowDom : function(rowIndex)
8817 var rows = this.el.select('tbody > tr', true).elements;
8819 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8822 // returns the object tree for a tr..
8825 renderRow : function(cm, ds, rowIndex)
8827 var d = ds.getAt(rowIndex);
8831 cls : 'x-row-' + rowIndex,
8835 var cellObjects = [];
8837 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8838 var config = cm.config[i];
8840 var renderer = cm.getRenderer(i);
8844 if(typeof(renderer) !== 'undefined'){
8845 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8847 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8848 // and are rendered into the cells after the row is rendered - using the id for the element.
8850 if(typeof(value) === 'object'){
8860 rowIndex : rowIndex,
8865 this.fireEvent('rowclass', this, rowcfg);
8869 cls : rowcfg.rowClass + ' x-col-' + i,
8871 html: (typeof(value) === 'object') ? '' : value
8878 if(typeof(config.colspan) != 'undefined'){
8879 td.colspan = config.colspan;
8882 if(typeof(config.hidden) != 'undefined' && config.hidden){
8883 td.style += ' display:none;';
8886 if(typeof(config.align) != 'undefined' && config.align.length){
8887 td.style += ' text-align:' + config.align + ';';
8889 if(typeof(config.valign) != 'undefined' && config.valign.length){
8890 td.style += ' vertical-align:' + config.valign + ';';
8893 if(typeof(config.width) != 'undefined'){
8894 td.style += ' width:' + config.width + 'px;';
8897 if(typeof(config.cursor) != 'undefined'){
8898 td.style += ' cursor:' + config.cursor + ';';
8901 if(typeof(config.cls) != 'undefined'){
8902 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8905 ['xs','sm','md','lg'].map(function(size){
8907 if(typeof(config[size]) == 'undefined'){
8913 if (!config[size]) { // 0 = hidden
8914 // BS 4 '0' is treated as hide that column and below.
8915 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8919 td.cls += ' col-' + size + '-' + config[size] + (
8920 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8930 row.cellObjects = cellObjects;
8938 onBeforeLoad : function()
8947 this.el.select('tbody', true).first().dom.innerHTML = '';
8950 * Show or hide a row.
8951 * @param {Number} rowIndex to show or hide
8952 * @param {Boolean} state hide
8954 setRowVisibility : function(rowIndex, state)
8956 var bt = this.mainBody.dom;
8958 var rows = this.el.select('tbody > tr', true).elements;
8960 if(typeof(rows[rowIndex]) == 'undefined'){
8963 rows[rowIndex].dom.style.display = state ? '' : 'none';
8967 getSelectionModel : function(){
8969 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8971 return this.selModel;
8974 * Render the Roo.bootstrap object from renderder
8976 renderCellObject : function(r)
8980 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8982 var t = r.cfg.render(r.container);
8985 Roo.each(r.cfg.cn, function(c){
8987 container: t.getChildContainer(),
8990 _this.renderCellObject(child);
8995 getRowIndex : function(row)
8999 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9010 * Returns the grid's underlying element = used by panel.Grid
9011 * @return {Element} The element
9013 getGridEl : function(){
9017 * Forces a resize - used by panel.Grid
9018 * @return {Element} The element
9020 autoSize : function()
9022 //var ctr = Roo.get(this.container.dom.parentElement);
9023 var ctr = Roo.get(this.el.dom);
9025 var thd = this.getGridEl().select('thead',true).first();
9026 var tbd = this.getGridEl().select('tbody', true).first();
9027 var tfd = this.getGridEl().select('tfoot', true).first();
9029 var cw = ctr.getWidth();
9030 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9034 tbd.setWidth(ctr.getWidth());
9035 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9036 // this needs fixing for various usage - currently only hydra job advers I think..
9038 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9040 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9043 cw = Math.max(cw, this.totalWidth);
9044 this.getGridEl().select('tbody tr',true).setWidth(cw);
9046 // resize 'expandable coloumn?
9048 return; // we doe not have a view in this design..
9051 onBodyScroll: function()
9053 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9055 this.mainHead.setStyle({
9056 'position' : 'relative',
9057 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9063 var scrollHeight = this.mainBody.dom.scrollHeight;
9065 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9067 var height = this.mainBody.getHeight();
9069 if(scrollHeight - height == scrollTop) {
9071 var total = this.ds.getTotalCount();
9073 if(this.footer.cursor + this.footer.pageSize < total){
9075 this.footer.ds.load({
9077 start : this.footer.cursor + this.footer.pageSize,
9078 limit : this.footer.pageSize
9088 onHeaderChange : function()
9090 var header = this.renderHeader();
9091 var table = this.el.select('table', true).first();
9093 this.mainHead.remove();
9094 this.mainHead = table.createChild(header, this.mainBody, false);
9097 onHiddenChange : function(colModel, colIndex, hidden)
9099 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9100 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9102 this.CSS.updateRule(thSelector, "display", "");
9103 this.CSS.updateRule(tdSelector, "display", "");
9106 this.CSS.updateRule(thSelector, "display", "none");
9107 this.CSS.updateRule(tdSelector, "display", "none");
9110 this.onHeaderChange();
9114 setColumnWidth: function(col_index, width)
9116 // width = "md-2 xs-2..."
9117 if(!this.colModel.config[col_index]) {
9121 var w = width.split(" ");
9123 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9125 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9128 for(var j = 0; j < w.length; j++) {
9134 var size_cls = w[j].split("-");
9136 if(!Number.isInteger(size_cls[1] * 1)) {
9140 if(!this.colModel.config[col_index][size_cls[0]]) {
9144 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9148 h_row[0].classList.replace(
9149 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9150 "col-"+size_cls[0]+"-"+size_cls[1]
9153 for(var i = 0; i < rows.length; i++) {
9155 var size_cls = w[j].split("-");
9157 if(!Number.isInteger(size_cls[1] * 1)) {
9161 if(!this.colModel.config[col_index][size_cls[0]]) {
9165 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9169 rows[i].classList.replace(
9170 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9171 "col-"+size_cls[0]+"-"+size_cls[1]
9175 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9190 * @class Roo.bootstrap.TableCell
9191 * @extends Roo.bootstrap.Component
9192 * Bootstrap TableCell class
9193 * @cfg {String} html cell contain text
9194 * @cfg {String} cls cell class
9195 * @cfg {String} tag cell tag (td|th) default td
9196 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9197 * @cfg {String} align Aligns the content in a cell
9198 * @cfg {String} axis Categorizes cells
9199 * @cfg {String} bgcolor Specifies the background color of a cell
9200 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9201 * @cfg {Number} colspan Specifies the number of columns a cell should span
9202 * @cfg {String} headers Specifies one or more header cells a cell is related to
9203 * @cfg {Number} height Sets the height of a cell
9204 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9205 * @cfg {Number} rowspan Sets the number of rows a cell should span
9206 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9207 * @cfg {String} valign Vertical aligns the content in a cell
9208 * @cfg {Number} width Specifies the width of a cell
9211 * Create a new TableCell
9212 * @param {Object} config The config object
9215 Roo.bootstrap.TableCell = function(config){
9216 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9219 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9239 getAutoCreate : function(){
9240 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9260 cfg.align=this.align
9266 cfg.bgcolor=this.bgcolor
9269 cfg.charoff=this.charoff
9272 cfg.colspan=this.colspan
9275 cfg.headers=this.headers
9278 cfg.height=this.height
9281 cfg.nowrap=this.nowrap
9284 cfg.rowspan=this.rowspan
9287 cfg.scope=this.scope
9290 cfg.valign=this.valign
9293 cfg.width=this.width
9312 * @class Roo.bootstrap.TableRow
9313 * @extends Roo.bootstrap.Component
9314 * Bootstrap TableRow class
9315 * @cfg {String} cls row class
9316 * @cfg {String} align Aligns the content in a table row
9317 * @cfg {String} bgcolor Specifies a background color for a table row
9318 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9319 * @cfg {String} valign Vertical aligns the content in a table row
9322 * Create a new TableRow
9323 * @param {Object} config The config object
9326 Roo.bootstrap.TableRow = function(config){
9327 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9330 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9338 getAutoCreate : function(){
9339 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9349 cfg.align = this.align;
9352 cfg.bgcolor = this.bgcolor;
9355 cfg.charoff = this.charoff;
9358 cfg.valign = this.valign;
9376 * @class Roo.bootstrap.TableBody
9377 * @extends Roo.bootstrap.Component
9378 * Bootstrap TableBody class
9379 * @cfg {String} cls element class
9380 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9381 * @cfg {String} align Aligns the content inside the element
9382 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9383 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9386 * Create a new TableBody
9387 * @param {Object} config The config object
9390 Roo.bootstrap.TableBody = function(config){
9391 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9394 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9402 getAutoCreate : function(){
9403 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9417 cfg.align = this.align;
9420 cfg.charoff = this.charoff;
9423 cfg.valign = this.valign;
9430 // initEvents : function()
9437 // this.store = Roo.factory(this.store, Roo.data);
9438 // this.store.on('load', this.onLoad, this);
9440 // this.store.load();
9444 // onLoad: function ()
9446 // this.fireEvent('load', this);
9456 * Ext JS Library 1.1.1
9457 * Copyright(c) 2006-2007, Ext JS, LLC.
9459 * Originally Released Under LGPL - original licence link has changed is not relivant.
9462 * <script type="text/javascript">
9465 // as we use this in bootstrap.
9466 Roo.namespace('Roo.form');
9468 * @class Roo.form.Action
9469 * Internal Class used to handle form actions
9471 * @param {Roo.form.BasicForm} el The form element or its id
9472 * @param {Object} config Configuration options
9477 // define the action interface
9478 Roo.form.Action = function(form, options){
9480 this.options = options || {};
9483 * Client Validation Failed
9486 Roo.form.Action.CLIENT_INVALID = 'client';
9488 * Server Validation Failed
9491 Roo.form.Action.SERVER_INVALID = 'server';
9493 * Connect to Server Failed
9496 Roo.form.Action.CONNECT_FAILURE = 'connect';
9498 * Reading Data from Server Failed
9501 Roo.form.Action.LOAD_FAILURE = 'load';
9503 Roo.form.Action.prototype = {
9505 failureType : undefined,
9506 response : undefined,
9510 run : function(options){
9515 success : function(response){
9520 handleResponse : function(response){
9524 // default connection failure
9525 failure : function(response){
9527 this.response = response;
9528 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9529 this.form.afterAction(this, false);
9532 processResponse : function(response){
9533 this.response = response;
9534 if(!response.responseText){
9537 this.result = this.handleResponse(response);
9541 // utility functions used internally
9542 getUrl : function(appendParams){
9543 var url = this.options.url || this.form.url || this.form.el.dom.action;
9545 var p = this.getParams();
9547 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9553 getMethod : function(){
9554 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9557 getParams : function(){
9558 var bp = this.form.baseParams;
9559 var p = this.options.params;
9561 if(typeof p == "object"){
9562 p = Roo.urlEncode(Roo.applyIf(p, bp));
9563 }else if(typeof p == 'string' && bp){
9564 p += '&' + Roo.urlEncode(bp);
9567 p = Roo.urlEncode(bp);
9572 createCallback : function(){
9574 success: this.success,
9575 failure: this.failure,
9577 timeout: (this.form.timeout*1000),
9578 upload: this.form.fileUpload ? this.success : undefined
9583 Roo.form.Action.Submit = function(form, options){
9584 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9587 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9590 haveProgress : false,
9591 uploadComplete : false,
9593 // uploadProgress indicator.
9594 uploadProgress : function()
9596 if (!this.form.progressUrl) {
9600 if (!this.haveProgress) {
9601 Roo.MessageBox.progress("Uploading", "Uploading");
9603 if (this.uploadComplete) {
9604 Roo.MessageBox.hide();
9608 this.haveProgress = true;
9610 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9612 var c = new Roo.data.Connection();
9614 url : this.form.progressUrl,
9619 success : function(req){
9620 //console.log(data);
9624 rdata = Roo.decode(req.responseText)
9626 Roo.log("Invalid data from server..");
9630 if (!rdata || !rdata.success) {
9632 Roo.MessageBox.alert(Roo.encode(rdata));
9635 var data = rdata.data;
9637 if (this.uploadComplete) {
9638 Roo.MessageBox.hide();
9643 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9644 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9647 this.uploadProgress.defer(2000,this);
9650 failure: function(data) {
9651 Roo.log('progress url failed ');
9662 // run get Values on the form, so it syncs any secondary forms.
9663 this.form.getValues();
9665 var o = this.options;
9666 var method = this.getMethod();
9667 var isPost = method == 'POST';
9668 if(o.clientValidation === false || this.form.isValid()){
9670 if (this.form.progressUrl) {
9671 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9672 (new Date() * 1) + '' + Math.random());
9677 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9678 form:this.form.el.dom,
9679 url:this.getUrl(!isPost),
9681 params:isPost ? this.getParams() : null,
9682 isUpload: this.form.fileUpload,
9683 formData : this.form.formData
9686 this.uploadProgress();
9688 }else if (o.clientValidation !== false){ // client validation failed
9689 this.failureType = Roo.form.Action.CLIENT_INVALID;
9690 this.form.afterAction(this, false);
9694 success : function(response)
9696 this.uploadComplete= true;
9697 if (this.haveProgress) {
9698 Roo.MessageBox.hide();
9702 var result = this.processResponse(response);
9703 if(result === true || result.success){
9704 this.form.afterAction(this, true);
9708 this.form.markInvalid(result.errors);
9709 this.failureType = Roo.form.Action.SERVER_INVALID;
9711 this.form.afterAction(this, false);
9713 failure : function(response)
9715 this.uploadComplete= true;
9716 if (this.haveProgress) {
9717 Roo.MessageBox.hide();
9720 this.response = response;
9721 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9722 this.form.afterAction(this, false);
9725 handleResponse : function(response){
9726 if(this.form.errorReader){
9727 var rs = this.form.errorReader.read(response);
9730 for(var i = 0, len = rs.records.length; i < len; i++) {
9731 var r = rs.records[i];
9735 if(errors.length < 1){
9739 success : rs.success,
9745 ret = Roo.decode(response.responseText);
9749 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9759 Roo.form.Action.Load = function(form, options){
9760 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9761 this.reader = this.form.reader;
9764 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9769 Roo.Ajax.request(Roo.apply(
9770 this.createCallback(), {
9771 method:this.getMethod(),
9772 url:this.getUrl(false),
9773 params:this.getParams()
9777 success : function(response){
9779 var result = this.processResponse(response);
9780 if(result === true || !result.success || !result.data){
9781 this.failureType = Roo.form.Action.LOAD_FAILURE;
9782 this.form.afterAction(this, false);
9785 this.form.clearInvalid();
9786 this.form.setValues(result.data);
9787 this.form.afterAction(this, true);
9790 handleResponse : function(response){
9791 if(this.form.reader){
9792 var rs = this.form.reader.read(response);
9793 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9795 success : rs.success,
9799 return Roo.decode(response.responseText);
9803 Roo.form.Action.ACTION_TYPES = {
9804 'load' : Roo.form.Action.Load,
9805 'submit' : Roo.form.Action.Submit
9814 * @class Roo.bootstrap.Form
9815 * @extends Roo.bootstrap.Component
9816 * Bootstrap Form class
9817 * @cfg {String} method GET | POST (default POST)
9818 * @cfg {String} labelAlign top | left (default top)
9819 * @cfg {String} align left | right - for navbars
9820 * @cfg {Boolean} loadMask load mask when submit (default true)
9825 * @param {Object} config The config object
9829 Roo.bootstrap.Form = function(config){
9831 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9833 Roo.bootstrap.Form.popover.apply();
9837 * @event clientvalidation
9838 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9839 * @param {Form} this
9840 * @param {Boolean} valid true if the form has passed client-side validation
9842 clientvalidation: true,
9844 * @event beforeaction
9845 * Fires before any action is performed. Return false to cancel the action.
9846 * @param {Form} this
9847 * @param {Action} action The action to be performed
9851 * @event actionfailed
9852 * Fires when an action fails.
9853 * @param {Form} this
9854 * @param {Action} action The action that failed
9856 actionfailed : true,
9858 * @event actioncomplete
9859 * Fires when an action is completed.
9860 * @param {Form} this
9861 * @param {Action} action The action that completed
9863 actioncomplete : true
9867 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9870 * @cfg {String} method
9871 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9876 * The URL to use for form actions if one isn't supplied in the action options.
9879 * @cfg {Boolean} fileUpload
9880 * Set to true if this form is a file upload.
9884 * @cfg {Object} baseParams
9885 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9889 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9893 * @cfg {Sting} align (left|right) for navbar forms
9898 activeAction : null,
9901 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9902 * element by passing it or its id or mask the form itself by passing in true.
9905 waitMsgTarget : false,
9910 * @cfg {Boolean} errorMask (true|false) default false
9915 * @cfg {Number} maskOffset Default 100
9920 * @cfg {Boolean} maskBody
9924 getAutoCreate : function(){
9928 method : this.method || 'POST',
9929 id : this.id || Roo.id(),
9932 if (this.parent().xtype.match(/^Nav/)) {
9933 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9937 if (this.labelAlign == 'left' ) {
9938 cfg.cls += ' form-horizontal';
9944 initEvents : function()
9946 this.el.on('submit', this.onSubmit, this);
9947 // this was added as random key presses on the form where triggering form submit.
9948 this.el.on('keypress', function(e) {
9949 if (e.getCharCode() != 13) {
9952 // we might need to allow it for textareas.. and some other items.
9953 // check e.getTarget().
9955 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9959 Roo.log("keypress blocked");
9967 onSubmit : function(e){
9972 * Returns true if client-side validation on the form is successful.
9975 isValid : function(){
9976 var items = this.getItems();
9980 items.each(function(f){
9986 Roo.log('invalid field: ' + f.name);
9990 if(!target && f.el.isVisible(true)){
9996 if(this.errorMask && !valid){
9997 Roo.bootstrap.Form.popover.mask(this, target);
10004 * Returns true if any fields in this form have changed since their original load.
10007 isDirty : function(){
10009 var items = this.getItems();
10010 items.each(function(f){
10020 * Performs a predefined action (submit or load) or custom actions you define on this form.
10021 * @param {String} actionName The name of the action type
10022 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10023 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10024 * accept other config options):
10026 Property Type Description
10027 ---------------- --------------- ----------------------------------------------------------------------------------
10028 url String The url for the action (defaults to the form's url)
10029 method String The form method to use (defaults to the form's method, or POST if not defined)
10030 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10031 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10032 validate the form on the client (defaults to false)
10034 * @return {BasicForm} this
10036 doAction : function(action, options){
10037 if(typeof action == 'string'){
10038 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10040 if(this.fireEvent('beforeaction', this, action) !== false){
10041 this.beforeAction(action);
10042 action.run.defer(100, action);
10048 beforeAction : function(action){
10049 var o = action.options;
10054 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10056 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10059 // not really supported yet.. ??
10061 //if(this.waitMsgTarget === true){
10062 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10063 //}else if(this.waitMsgTarget){
10064 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10065 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10067 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10073 afterAction : function(action, success){
10074 this.activeAction = null;
10075 var o = action.options;
10080 Roo.get(document.body).unmask();
10086 //if(this.waitMsgTarget === true){
10087 // this.el.unmask();
10088 //}else if(this.waitMsgTarget){
10089 // this.waitMsgTarget.unmask();
10091 // Roo.MessageBox.updateProgress(1);
10092 // Roo.MessageBox.hide();
10099 Roo.callback(o.success, o.scope, [this, action]);
10100 this.fireEvent('actioncomplete', this, action);
10104 // failure condition..
10105 // we have a scenario where updates need confirming.
10106 // eg. if a locking scenario exists..
10107 // we look for { errors : { needs_confirm : true }} in the response.
10109 (typeof(action.result) != 'undefined') &&
10110 (typeof(action.result.errors) != 'undefined') &&
10111 (typeof(action.result.errors.needs_confirm) != 'undefined')
10114 Roo.log("not supported yet");
10117 Roo.MessageBox.confirm(
10118 "Change requires confirmation",
10119 action.result.errorMsg,
10124 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10134 Roo.callback(o.failure, o.scope, [this, action]);
10135 // show an error message if no failed handler is set..
10136 if (!this.hasListener('actionfailed')) {
10137 Roo.log("need to add dialog support");
10139 Roo.MessageBox.alert("Error",
10140 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10141 action.result.errorMsg :
10142 "Saving Failed, please check your entries or try again"
10147 this.fireEvent('actionfailed', this, action);
10152 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10153 * @param {String} id The value to search for
10156 findField : function(id){
10157 var items = this.getItems();
10158 var field = items.get(id);
10160 items.each(function(f){
10161 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10168 return field || null;
10171 * Mark fields in this form invalid in bulk.
10172 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10173 * @return {BasicForm} this
10175 markInvalid : function(errors){
10176 if(errors instanceof Array){
10177 for(var i = 0, len = errors.length; i < len; i++){
10178 var fieldError = errors[i];
10179 var f = this.findField(fieldError.id);
10181 f.markInvalid(fieldError.msg);
10187 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10188 field.markInvalid(errors[id]);
10192 //Roo.each(this.childForms || [], function (f) {
10193 // f.markInvalid(errors);
10200 * Set values for fields in this form in bulk.
10201 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10202 * @return {BasicForm} this
10204 setValues : function(values){
10205 if(values instanceof Array){ // array of objects
10206 for(var i = 0, len = values.length; i < len; i++){
10208 var f = this.findField(v.id);
10210 f.setValue(v.value);
10211 if(this.trackResetOnLoad){
10212 f.originalValue = f.getValue();
10216 }else{ // object hash
10219 if(typeof values[id] != 'function' && (field = this.findField(id))){
10221 if (field.setFromData &&
10222 field.valueField &&
10223 field.displayField &&
10224 // combos' with local stores can
10225 // be queried via setValue()
10226 // to set their value..
10227 (field.store && !field.store.isLocal)
10231 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10232 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10233 field.setFromData(sd);
10235 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10237 field.setFromData(values);
10240 field.setValue(values[id]);
10244 if(this.trackResetOnLoad){
10245 field.originalValue = field.getValue();
10251 //Roo.each(this.childForms || [], function (f) {
10252 // f.setValues(values);
10259 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10260 * they are returned as an array.
10261 * @param {Boolean} asString
10264 getValues : function(asString){
10265 //if (this.childForms) {
10266 // copy values from the child forms
10267 // Roo.each(this.childForms, function (f) {
10268 // this.setValues(f.getValues());
10274 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10275 if(asString === true){
10278 return Roo.urlDecode(fs);
10282 * Returns the fields in this form as an object with key/value pairs.
10283 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10286 getFieldValues : function(with_hidden)
10288 var items = this.getItems();
10290 items.each(function(f){
10292 if (!f.getName()) {
10296 var v = f.getValue();
10298 if (f.inputType =='radio') {
10299 if (typeof(ret[f.getName()]) == 'undefined') {
10300 ret[f.getName()] = ''; // empty..
10303 if (!f.el.dom.checked) {
10307 v = f.el.dom.value;
10311 if(f.xtype == 'MoneyField'){
10312 ret[f.currencyName] = f.getCurrency();
10315 // not sure if this supported any more..
10316 if ((typeof(v) == 'object') && f.getRawValue) {
10317 v = f.getRawValue() ; // dates..
10319 // combo boxes where name != hiddenName...
10320 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10321 ret[f.name] = f.getRawValue();
10323 ret[f.getName()] = v;
10330 * Clears all invalid messages in this form.
10331 * @return {BasicForm} this
10333 clearInvalid : function(){
10334 var items = this.getItems();
10336 items.each(function(f){
10344 * Resets this form.
10345 * @return {BasicForm} this
10347 reset : function(){
10348 var items = this.getItems();
10349 items.each(function(f){
10353 Roo.each(this.childForms || [], function (f) {
10361 getItems : function()
10363 var r=new Roo.util.MixedCollection(false, function(o){
10364 return o.id || (o.id = Roo.id());
10366 var iter = function(el) {
10373 Roo.each(el.items,function(e) {
10382 hideFields : function(items)
10384 Roo.each(items, function(i){
10386 var f = this.findField(i);
10397 showFields : function(items)
10399 Roo.each(items, function(i){
10401 var f = this.findField(i);
10414 Roo.apply(Roo.bootstrap.Form, {
10430 intervalID : false,
10436 if(this.isApplied){
10441 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10442 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10443 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10444 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10447 this.maskEl.top.enableDisplayMode("block");
10448 this.maskEl.left.enableDisplayMode("block");
10449 this.maskEl.bottom.enableDisplayMode("block");
10450 this.maskEl.right.enableDisplayMode("block");
10452 this.toolTip = new Roo.bootstrap.Tooltip({
10453 cls : 'roo-form-error-popover',
10455 'left' : ['r-l', [-2,0], 'right'],
10456 'right' : ['l-r', [2,0], 'left'],
10457 'bottom' : ['tl-bl', [0,2], 'top'],
10458 'top' : [ 'bl-tl', [0,-2], 'bottom']
10462 this.toolTip.render(Roo.get(document.body));
10464 this.toolTip.el.enableDisplayMode("block");
10466 Roo.get(document.body).on('click', function(){
10470 Roo.get(document.body).on('touchstart', function(){
10474 this.isApplied = true
10477 mask : function(form, target)
10481 this.target = target;
10483 if(!this.form.errorMask || !target.el){
10487 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10489 Roo.log(scrollable);
10491 var ot = this.target.el.calcOffsetsTo(scrollable);
10493 var scrollTo = ot[1] - this.form.maskOffset;
10495 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10497 scrollable.scrollTo('top', scrollTo);
10499 var box = this.target.el.getBox();
10501 var zIndex = Roo.bootstrap.Modal.zIndex++;
10504 this.maskEl.top.setStyle('position', 'absolute');
10505 this.maskEl.top.setStyle('z-index', zIndex);
10506 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10507 this.maskEl.top.setLeft(0);
10508 this.maskEl.top.setTop(0);
10509 this.maskEl.top.show();
10511 this.maskEl.left.setStyle('position', 'absolute');
10512 this.maskEl.left.setStyle('z-index', zIndex);
10513 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10514 this.maskEl.left.setLeft(0);
10515 this.maskEl.left.setTop(box.y - this.padding);
10516 this.maskEl.left.show();
10518 this.maskEl.bottom.setStyle('position', 'absolute');
10519 this.maskEl.bottom.setStyle('z-index', zIndex);
10520 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10521 this.maskEl.bottom.setLeft(0);
10522 this.maskEl.bottom.setTop(box.bottom + this.padding);
10523 this.maskEl.bottom.show();
10525 this.maskEl.right.setStyle('position', 'absolute');
10526 this.maskEl.right.setStyle('z-index', zIndex);
10527 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10528 this.maskEl.right.setLeft(box.right + this.padding);
10529 this.maskEl.right.setTop(box.y - this.padding);
10530 this.maskEl.right.show();
10532 this.toolTip.bindEl = this.target.el;
10534 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10536 var tip = this.target.blankText;
10538 if(this.target.getValue() !== '' ) {
10540 if (this.target.invalidText.length) {
10541 tip = this.target.invalidText;
10542 } else if (this.target.regexText.length){
10543 tip = this.target.regexText;
10547 this.toolTip.show(tip);
10549 this.intervalID = window.setInterval(function() {
10550 Roo.bootstrap.Form.popover.unmask();
10553 window.onwheel = function(){ return false;};
10555 (function(){ this.isMasked = true; }).defer(500, this);
10559 unmask : function()
10561 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10565 this.maskEl.top.setStyle('position', 'absolute');
10566 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10567 this.maskEl.top.hide();
10569 this.maskEl.left.setStyle('position', 'absolute');
10570 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10571 this.maskEl.left.hide();
10573 this.maskEl.bottom.setStyle('position', 'absolute');
10574 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10575 this.maskEl.bottom.hide();
10577 this.maskEl.right.setStyle('position', 'absolute');
10578 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10579 this.maskEl.right.hide();
10581 this.toolTip.hide();
10583 this.toolTip.el.hide();
10585 window.onwheel = function(){ return true;};
10587 if(this.intervalID){
10588 window.clearInterval(this.intervalID);
10589 this.intervalID = false;
10592 this.isMasked = false;
10602 * Ext JS Library 1.1.1
10603 * Copyright(c) 2006-2007, Ext JS, LLC.
10605 * Originally Released Under LGPL - original licence link has changed is not relivant.
10608 * <script type="text/javascript">
10611 * @class Roo.form.VTypes
10612 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10615 Roo.form.VTypes = function(){
10616 // closure these in so they are only created once.
10617 var alpha = /^[a-zA-Z_]+$/;
10618 var alphanum = /^[a-zA-Z0-9_]+$/;
10619 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10620 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10622 // All these messages and functions are configurable
10625 * The function used to validate email addresses
10626 * @param {String} value The email address
10628 'email' : function(v){
10629 return email.test(v);
10632 * The error text to display when the email validation function returns false
10635 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10637 * The keystroke filter mask to be applied on email input
10640 'emailMask' : /[a-z0-9_\.\-@]/i,
10643 * The function used to validate URLs
10644 * @param {String} value The URL
10646 'url' : function(v){
10647 return url.test(v);
10650 * The error text to display when the url validation function returns false
10653 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10656 * The function used to validate alpha values
10657 * @param {String} value The value
10659 'alpha' : function(v){
10660 return alpha.test(v);
10663 * The error text to display when the alpha validation function returns false
10666 'alphaText' : 'This field should only contain letters and _',
10668 * The keystroke filter mask to be applied on alpha input
10671 'alphaMask' : /[a-z_]/i,
10674 * The function used to validate alphanumeric values
10675 * @param {String} value The value
10677 'alphanum' : function(v){
10678 return alphanum.test(v);
10681 * The error text to display when the alphanumeric validation function returns false
10684 'alphanumText' : 'This field should only contain letters, numbers and _',
10686 * The keystroke filter mask to be applied on alphanumeric input
10689 'alphanumMask' : /[a-z0-9_]/i
10699 * @class Roo.bootstrap.Input
10700 * @extends Roo.bootstrap.Component
10701 * Bootstrap Input class
10702 * @cfg {Boolean} disabled is it disabled
10703 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10704 * @cfg {String} name name of the input
10705 * @cfg {string} fieldLabel - the label associated
10706 * @cfg {string} placeholder - placeholder to put in text.
10707 * @cfg {string} before - input group add on before
10708 * @cfg {string} after - input group add on after
10709 * @cfg {string} size - (lg|sm) or leave empty..
10710 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10711 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10712 * @cfg {Number} md colspan out of 12 for computer-sized screens
10713 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10714 * @cfg {string} value default value of the input
10715 * @cfg {Number} labelWidth set the width of label
10716 * @cfg {Number} labellg set the width of label (1-12)
10717 * @cfg {Number} labelmd set the width of label (1-12)
10718 * @cfg {Number} labelsm set the width of label (1-12)
10719 * @cfg {Number} labelxs set the width of label (1-12)
10720 * @cfg {String} labelAlign (top|left)
10721 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10722 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10723 * @cfg {String} indicatorpos (left|right) default left
10724 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10725 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10726 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10728 * @cfg {String} align (left|center|right) Default left
10729 * @cfg {Boolean} forceFeedback (true|false) Default false
10732 * Create a new Input
10733 * @param {Object} config The config object
10736 Roo.bootstrap.Input = function(config){
10738 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10743 * Fires when this field receives input focus.
10744 * @param {Roo.form.Field} this
10749 * Fires when this field loses input focus.
10750 * @param {Roo.form.Field} this
10754 * @event specialkey
10755 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10756 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10757 * @param {Roo.form.Field} this
10758 * @param {Roo.EventObject} e The event object
10763 * Fires just before the field blurs if the field value has changed.
10764 * @param {Roo.form.Field} this
10765 * @param {Mixed} newValue The new value
10766 * @param {Mixed} oldValue The original value
10771 * Fires after the field has been marked as invalid.
10772 * @param {Roo.form.Field} this
10773 * @param {String} msg The validation message
10778 * Fires after the field has been validated with no errors.
10779 * @param {Roo.form.Field} this
10784 * Fires after the key up
10785 * @param {Roo.form.Field} this
10786 * @param {Roo.EventObject} e The event Object
10791 * Fires after the user pastes into input
10792 * @param {Roo.form.Field} this
10793 * @param {Roo.EventObject} e The event Object
10799 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10801 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10802 automatic validation (defaults to "keyup").
10804 validationEvent : "keyup",
10806 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10808 validateOnBlur : true,
10810 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10812 validationDelay : 250,
10814 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10816 focusClass : "x-form-focus", // not needed???
10820 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10822 invalidClass : "has-warning",
10825 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10827 validClass : "has-success",
10830 * @cfg {Boolean} hasFeedback (true|false) default true
10832 hasFeedback : true,
10835 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10837 invalidFeedbackClass : "glyphicon-warning-sign",
10840 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10842 validFeedbackClass : "glyphicon-ok",
10845 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10847 selectOnFocus : false,
10850 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10854 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10859 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10861 disableKeyFilter : false,
10864 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10868 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10872 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10874 blankText : "Please complete this mandatory field",
10877 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10881 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10883 maxLength : Number.MAX_VALUE,
10885 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10887 minLengthText : "The minimum length for this field is {0}",
10889 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10891 maxLengthText : "The maximum length for this field is {0}",
10895 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10896 * If available, this function will be called only after the basic validators all return true, and will be passed the
10897 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10901 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10902 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10903 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10907 * @cfg {String} regexText -- Depricated - use Invalid Text
10912 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10918 autocomplete: false,
10922 inputType : 'text',
10925 placeholder: false,
10930 preventMark: false,
10931 isFormField : true,
10934 labelAlign : false,
10937 formatedValue : false,
10938 forceFeedback : false,
10940 indicatorpos : 'left',
10950 parentLabelAlign : function()
10953 while (parent.parent()) {
10954 parent = parent.parent();
10955 if (typeof(parent.labelAlign) !='undefined') {
10956 return parent.labelAlign;
10963 getAutoCreate : function()
10965 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10971 if(this.inputType != 'hidden'){
10972 cfg.cls = 'form-group' //input-group
10978 type : this.inputType,
10979 value : this.value,
10980 cls : 'form-control',
10981 placeholder : this.placeholder || '',
10982 autocomplete : this.autocomplete || 'new-password'
10984 if (this.inputType == 'file') {
10985 input.style = 'overflow:hidden'; // why not in CSS?
10988 if(this.capture.length){
10989 input.capture = this.capture;
10992 if(this.accept.length){
10993 input.accept = this.accept + "/*";
10997 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11000 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11001 input.maxLength = this.maxLength;
11004 if (this.disabled) {
11005 input.disabled=true;
11008 if (this.readOnly) {
11009 input.readonly=true;
11013 input.name = this.name;
11017 input.cls += ' input-' + this.size;
11021 ['xs','sm','md','lg'].map(function(size){
11022 if (settings[size]) {
11023 cfg.cls += ' col-' + size + '-' + settings[size];
11027 var inputblock = input;
11031 cls: 'glyphicon form-control-feedback'
11034 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11037 cls : 'has-feedback',
11045 if (this.before || this.after) {
11048 cls : 'input-group',
11052 if (this.before && typeof(this.before) == 'string') {
11054 inputblock.cn.push({
11056 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11060 if (this.before && typeof(this.before) == 'object') {
11061 this.before = Roo.factory(this.before);
11063 inputblock.cn.push({
11065 cls : 'roo-input-before input-group-prepend input-group-' +
11066 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11070 inputblock.cn.push(input);
11072 if (this.after && typeof(this.after) == 'string') {
11073 inputblock.cn.push({
11075 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11079 if (this.after && typeof(this.after) == 'object') {
11080 this.after = Roo.factory(this.after);
11082 inputblock.cn.push({
11084 cls : 'roo-input-after input-group-append input-group-' +
11085 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11089 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11090 inputblock.cls += ' has-feedback';
11091 inputblock.cn.push(feedback);
11096 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11097 tooltip : 'This field is required'
11099 if (this.allowBlank ) {
11100 indicator.style = this.allowBlank ? ' display:none' : '';
11102 if (align ==='left' && this.fieldLabel.length) {
11104 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11111 cls : 'control-label col-form-label',
11112 html : this.fieldLabel
11123 var labelCfg = cfg.cn[1];
11124 var contentCfg = cfg.cn[2];
11126 if(this.indicatorpos == 'right'){
11131 cls : 'control-label col-form-label',
11135 html : this.fieldLabel
11149 labelCfg = cfg.cn[0];
11150 contentCfg = cfg.cn[1];
11154 if(this.labelWidth > 12){
11155 labelCfg.style = "width: " + this.labelWidth + 'px';
11158 if(this.labelWidth < 13 && this.labelmd == 0){
11159 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11162 if(this.labellg > 0){
11163 labelCfg.cls += ' col-lg-' + this.labellg;
11164 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11167 if(this.labelmd > 0){
11168 labelCfg.cls += ' col-md-' + this.labelmd;
11169 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11172 if(this.labelsm > 0){
11173 labelCfg.cls += ' col-sm-' + this.labelsm;
11174 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11177 if(this.labelxs > 0){
11178 labelCfg.cls += ' col-xs-' + this.labelxs;
11179 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11183 } else if ( this.fieldLabel.length) {
11190 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11191 tooltip : 'This field is required',
11192 style : this.allowBlank ? ' display:none' : ''
11196 //cls : 'input-group-addon',
11197 html : this.fieldLabel
11205 if(this.indicatorpos == 'right'){
11210 //cls : 'input-group-addon',
11211 html : this.fieldLabel
11216 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11217 tooltip : 'This field is required',
11218 style : this.allowBlank ? ' display:none' : ''
11238 if (this.parentType === 'Navbar' && this.parent().bar) {
11239 cfg.cls += ' navbar-form';
11242 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11243 // on BS4 we do this only if not form
11244 cfg.cls += ' navbar-form';
11252 * return the real input element.
11254 inputEl: function ()
11256 return this.el.select('input.form-control',true).first();
11259 tooltipEl : function()
11261 return this.inputEl();
11264 indicatorEl : function()
11266 if (Roo.bootstrap.version == 4) {
11267 return false; // not enabled in v4 yet.
11270 var indicator = this.el.select('i.roo-required-indicator',true).first();
11280 setDisabled : function(v)
11282 var i = this.inputEl().dom;
11284 i.removeAttribute('disabled');
11288 i.setAttribute('disabled','true');
11290 initEvents : function()
11293 this.inputEl().on("keydown" , this.fireKey, this);
11294 this.inputEl().on("focus", this.onFocus, this);
11295 this.inputEl().on("blur", this.onBlur, this);
11297 this.inputEl().relayEvent('keyup', this);
11298 this.inputEl().relayEvent('paste', this);
11300 this.indicator = this.indicatorEl();
11302 if(this.indicator){
11303 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11306 // reference to original value for reset
11307 this.originalValue = this.getValue();
11308 //Roo.form.TextField.superclass.initEvents.call(this);
11309 if(this.validationEvent == 'keyup'){
11310 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11311 this.inputEl().on('keyup', this.filterValidation, this);
11313 else if(this.validationEvent !== false){
11314 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11317 if(this.selectOnFocus){
11318 this.on("focus", this.preFocus, this);
11321 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11322 this.inputEl().on("keypress", this.filterKeys, this);
11324 this.inputEl().relayEvent('keypress', this);
11327 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11328 this.el.on("click", this.autoSize, this);
11331 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11332 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11335 if (typeof(this.before) == 'object') {
11336 this.before.render(this.el.select('.roo-input-before',true).first());
11338 if (typeof(this.after) == 'object') {
11339 this.after.render(this.el.select('.roo-input-after',true).first());
11342 this.inputEl().on('change', this.onChange, this);
11345 filterValidation : function(e){
11346 if(!e.isNavKeyPress()){
11347 this.validationTask.delay(this.validationDelay);
11351 * Validates the field value
11352 * @return {Boolean} True if the value is valid, else false
11354 validate : function(){
11355 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11356 if(this.disabled || this.validateValue(this.getRawValue())){
11361 this.markInvalid();
11367 * Validates a value according to the field's validation rules and marks the field as invalid
11368 * if the validation fails
11369 * @param {Mixed} value The value to validate
11370 * @return {Boolean} True if the value is valid, else false
11372 validateValue : function(value)
11374 if(this.getVisibilityEl().hasClass('hidden')){
11378 if(value.length < 1) { // if it's blank
11379 if(this.allowBlank){
11385 if(value.length < this.minLength){
11388 if(value.length > this.maxLength){
11392 var vt = Roo.form.VTypes;
11393 if(!vt[this.vtype](value, this)){
11397 if(typeof this.validator == "function"){
11398 var msg = this.validator(value);
11402 if (typeof(msg) == 'string') {
11403 this.invalidText = msg;
11407 if(this.regex && !this.regex.test(value)){
11415 fireKey : function(e){
11416 //Roo.log('field ' + e.getKey());
11417 if(e.isNavKeyPress()){
11418 this.fireEvent("specialkey", this, e);
11421 focus : function (selectText){
11423 this.inputEl().focus();
11424 if(selectText === true){
11425 this.inputEl().dom.select();
11431 onFocus : function(){
11432 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11433 // this.el.addClass(this.focusClass);
11435 if(!this.hasFocus){
11436 this.hasFocus = true;
11437 this.startValue = this.getValue();
11438 this.fireEvent("focus", this);
11442 beforeBlur : Roo.emptyFn,
11446 onBlur : function(){
11448 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11449 //this.el.removeClass(this.focusClass);
11451 this.hasFocus = false;
11452 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11455 var v = this.getValue();
11456 if(String(v) !== String(this.startValue)){
11457 this.fireEvent('change', this, v, this.startValue);
11459 this.fireEvent("blur", this);
11462 onChange : function(e)
11464 var v = this.getValue();
11465 if(String(v) !== String(this.startValue)){
11466 this.fireEvent('change', this, v, this.startValue);
11472 * Resets the current field value to the originally loaded value and clears any validation messages
11474 reset : function(){
11475 this.setValue(this.originalValue);
11479 * Returns the name of the field
11480 * @return {Mixed} name The name field
11482 getName: function(){
11486 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11487 * @return {Mixed} value The field value
11489 getValue : function(){
11491 var v = this.inputEl().getValue();
11496 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11497 * @return {Mixed} value The field value
11499 getRawValue : function(){
11500 var v = this.inputEl().getValue();
11506 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11507 * @param {Mixed} value The value to set
11509 setRawValue : function(v){
11510 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11513 selectText : function(start, end){
11514 var v = this.getRawValue();
11516 start = start === undefined ? 0 : start;
11517 end = end === undefined ? v.length : end;
11518 var d = this.inputEl().dom;
11519 if(d.setSelectionRange){
11520 d.setSelectionRange(start, end);
11521 }else if(d.createTextRange){
11522 var range = d.createTextRange();
11523 range.moveStart("character", start);
11524 range.moveEnd("character", v.length-end);
11531 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11532 * @param {Mixed} value The value to set
11534 setValue : function(v){
11537 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11543 processValue : function(value){
11544 if(this.stripCharsRe){
11545 var newValue = value.replace(this.stripCharsRe, '');
11546 if(newValue !== value){
11547 this.setRawValue(newValue);
11554 preFocus : function(){
11556 if(this.selectOnFocus){
11557 this.inputEl().dom.select();
11560 filterKeys : function(e){
11561 var k = e.getKey();
11562 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11565 var c = e.getCharCode(), cc = String.fromCharCode(c);
11566 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11569 if(!this.maskRe.test(cc)){
11574 * Clear any invalid styles/messages for this field
11576 clearInvalid : function(){
11578 if(!this.el || this.preventMark){ // not rendered
11583 this.el.removeClass([this.invalidClass, 'is-invalid']);
11585 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11587 var feedback = this.el.select('.form-control-feedback', true).first();
11590 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11595 if(this.indicator){
11596 this.indicator.removeClass('visible');
11597 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11600 this.fireEvent('valid', this);
11604 * Mark this field as valid
11606 markValid : function()
11608 if(!this.el || this.preventMark){ // not rendered...
11612 this.el.removeClass([this.invalidClass, this.validClass]);
11613 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11615 var feedback = this.el.select('.form-control-feedback', true).first();
11618 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11621 if(this.indicator){
11622 this.indicator.removeClass('visible');
11623 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11631 if(this.allowBlank && !this.getRawValue().length){
11634 if (Roo.bootstrap.version == 3) {
11635 this.el.addClass(this.validClass);
11637 this.inputEl().addClass('is-valid');
11640 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11642 var feedback = this.el.select('.form-control-feedback', true).first();
11645 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11646 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11651 this.fireEvent('valid', this);
11655 * Mark this field as invalid
11656 * @param {String} msg The validation message
11658 markInvalid : function(msg)
11660 if(!this.el || this.preventMark){ // not rendered
11664 this.el.removeClass([this.invalidClass, this.validClass]);
11665 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11667 var feedback = this.el.select('.form-control-feedback', true).first();
11670 this.el.select('.form-control-feedback', true).first().removeClass(
11671 [this.invalidFeedbackClass, this.validFeedbackClass]);
11678 if(this.allowBlank && !this.getRawValue().length){
11682 if(this.indicator){
11683 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11684 this.indicator.addClass('visible');
11686 if (Roo.bootstrap.version == 3) {
11687 this.el.addClass(this.invalidClass);
11689 this.inputEl().addClass('is-invalid');
11694 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11696 var feedback = this.el.select('.form-control-feedback', true).first();
11699 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11701 if(this.getValue().length || this.forceFeedback){
11702 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11709 this.fireEvent('invalid', this, msg);
11712 SafariOnKeyDown : function(event)
11714 // this is a workaround for a password hang bug on chrome/ webkit.
11715 if (this.inputEl().dom.type != 'password') {
11719 var isSelectAll = false;
11721 if(this.inputEl().dom.selectionEnd > 0){
11722 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11724 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11725 event.preventDefault();
11730 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11732 event.preventDefault();
11733 // this is very hacky as keydown always get's upper case.
11735 var cc = String.fromCharCode(event.getCharCode());
11736 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11740 adjustWidth : function(tag, w){
11741 tag = tag.toLowerCase();
11742 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11743 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11744 if(tag == 'input'){
11747 if(tag == 'textarea'){
11750 }else if(Roo.isOpera){
11751 if(tag == 'input'){
11754 if(tag == 'textarea'){
11762 setFieldLabel : function(v)
11764 if(!this.rendered){
11768 if(this.indicatorEl()){
11769 var ar = this.el.select('label > span',true);
11771 if (ar.elements.length) {
11772 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11773 this.fieldLabel = v;
11777 var br = this.el.select('label',true);
11779 if(br.elements.length) {
11780 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11781 this.fieldLabel = v;
11785 Roo.log('Cannot Found any of label > span || label in input');
11789 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11790 this.fieldLabel = v;
11805 * @class Roo.bootstrap.TextArea
11806 * @extends Roo.bootstrap.Input
11807 * Bootstrap TextArea class
11808 * @cfg {Number} cols Specifies the visible width of a text area
11809 * @cfg {Number} rows Specifies the visible number of lines in a text area
11810 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11811 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11812 * @cfg {string} html text
11815 * Create a new TextArea
11816 * @param {Object} config The config object
11819 Roo.bootstrap.TextArea = function(config){
11820 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11824 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11834 getAutoCreate : function(){
11836 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11842 if(this.inputType != 'hidden'){
11843 cfg.cls = 'form-group' //input-group
11851 value : this.value || '',
11852 html: this.html || '',
11853 cls : 'form-control',
11854 placeholder : this.placeholder || ''
11858 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11859 input.maxLength = this.maxLength;
11863 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11867 input.cols = this.cols;
11870 if (this.readOnly) {
11871 input.readonly = true;
11875 input.name = this.name;
11879 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11883 ['xs','sm','md','lg'].map(function(size){
11884 if (settings[size]) {
11885 cfg.cls += ' col-' + size + '-' + settings[size];
11889 var inputblock = input;
11891 if(this.hasFeedback && !this.allowBlank){
11895 cls: 'glyphicon form-control-feedback'
11899 cls : 'has-feedback',
11908 if (this.before || this.after) {
11911 cls : 'input-group',
11915 inputblock.cn.push({
11917 cls : 'input-group-addon',
11922 inputblock.cn.push(input);
11924 if(this.hasFeedback && !this.allowBlank){
11925 inputblock.cls += ' has-feedback';
11926 inputblock.cn.push(feedback);
11930 inputblock.cn.push({
11932 cls : 'input-group-addon',
11939 if (align ==='left' && this.fieldLabel.length) {
11944 cls : 'control-label',
11945 html : this.fieldLabel
11956 if(this.labelWidth > 12){
11957 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11960 if(this.labelWidth < 13 && this.labelmd == 0){
11961 this.labelmd = this.labelWidth;
11964 if(this.labellg > 0){
11965 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11966 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11969 if(this.labelmd > 0){
11970 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11971 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11974 if(this.labelsm > 0){
11975 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11976 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11979 if(this.labelxs > 0){
11980 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11981 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11984 } else if ( this.fieldLabel.length) {
11989 //cls : 'input-group-addon',
11990 html : this.fieldLabel
12008 if (this.disabled) {
12009 input.disabled=true;
12016 * return the real textarea element.
12018 inputEl: function ()
12020 return this.el.select('textarea.form-control',true).first();
12024 * Clear any invalid styles/messages for this field
12026 clearInvalid : function()
12029 if(!this.el || this.preventMark){ // not rendered
12033 var label = this.el.select('label', true).first();
12034 var icon = this.el.select('i.fa-star', true).first();
12039 this.el.removeClass( this.validClass);
12040 this.inputEl().removeClass('is-invalid');
12042 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12044 var feedback = this.el.select('.form-control-feedback', true).first();
12047 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12052 this.fireEvent('valid', this);
12056 * Mark this field as valid
12058 markValid : function()
12060 if(!this.el || this.preventMark){ // not rendered
12064 this.el.removeClass([this.invalidClass, this.validClass]);
12065 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12067 var feedback = this.el.select('.form-control-feedback', true).first();
12070 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12073 if(this.disabled || this.allowBlank){
12077 var label = this.el.select('label', true).first();
12078 var icon = this.el.select('i.fa-star', true).first();
12083 if (Roo.bootstrap.version == 3) {
12084 this.el.addClass(this.validClass);
12086 this.inputEl().addClass('is-valid');
12090 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12092 var feedback = this.el.select('.form-control-feedback', true).first();
12095 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12096 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12101 this.fireEvent('valid', this);
12105 * Mark this field as invalid
12106 * @param {String} msg The validation message
12108 markInvalid : function(msg)
12110 if(!this.el || this.preventMark){ // not rendered
12114 this.el.removeClass([this.invalidClass, this.validClass]);
12115 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12117 var feedback = this.el.select('.form-control-feedback', true).first();
12120 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12123 if(this.disabled || this.allowBlank){
12127 var label = this.el.select('label', true).first();
12128 var icon = this.el.select('i.fa-star', true).first();
12130 if(!this.getValue().length && label && !icon){
12131 this.el.createChild({
12133 cls : 'text-danger fa fa-lg fa-star',
12134 tooltip : 'This field is required',
12135 style : 'margin-right:5px;'
12139 if (Roo.bootstrap.version == 3) {
12140 this.el.addClass(this.invalidClass);
12142 this.inputEl().addClass('is-invalid');
12145 // fixme ... this may be depricated need to test..
12146 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12148 var feedback = this.el.select('.form-control-feedback', true).first();
12151 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12153 if(this.getValue().length || this.forceFeedback){
12154 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12161 this.fireEvent('invalid', this, msg);
12169 * trigger field - base class for combo..
12174 * @class Roo.bootstrap.TriggerField
12175 * @extends Roo.bootstrap.Input
12176 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12177 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12178 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12179 * for which you can provide a custom implementation. For example:
12181 var trigger = new Roo.bootstrap.TriggerField();
12182 trigger.onTriggerClick = myTriggerFn;
12183 trigger.applyTo('my-field');
12186 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12187 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12188 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12189 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12190 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12193 * Create a new TriggerField.
12194 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12195 * to the base TextField)
12197 Roo.bootstrap.TriggerField = function(config){
12198 this.mimicing = false;
12199 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12202 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12204 * @cfg {String} triggerClass A CSS class to apply to the trigger
12207 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12212 * @cfg {Boolean} removable (true|false) special filter default false
12216 /** @cfg {Boolean} grow @hide */
12217 /** @cfg {Number} growMin @hide */
12218 /** @cfg {Number} growMax @hide */
12224 autoSize: Roo.emptyFn,
12228 deferHeight : true,
12231 actionMode : 'wrap',
12236 getAutoCreate : function(){
12238 var align = this.labelAlign || this.parentLabelAlign();
12243 cls: 'form-group' //input-group
12250 type : this.inputType,
12251 cls : 'form-control',
12252 autocomplete: 'new-password',
12253 placeholder : this.placeholder || ''
12257 input.name = this.name;
12260 input.cls += ' input-' + this.size;
12263 if (this.disabled) {
12264 input.disabled=true;
12267 var inputblock = input;
12269 if(this.hasFeedback && !this.allowBlank){
12273 cls: 'glyphicon form-control-feedback'
12276 if(this.removable && !this.editable ){
12278 cls : 'has-feedback',
12284 cls : 'roo-combo-removable-btn close'
12291 cls : 'has-feedback',
12300 if(this.removable && !this.editable ){
12302 cls : 'roo-removable',
12308 cls : 'roo-combo-removable-btn close'
12315 if (this.before || this.after) {
12318 cls : 'input-group',
12322 inputblock.cn.push({
12324 cls : 'input-group-addon input-group-prepend input-group-text',
12329 inputblock.cn.push(input);
12331 if(this.hasFeedback && !this.allowBlank){
12332 inputblock.cls += ' has-feedback';
12333 inputblock.cn.push(feedback);
12337 inputblock.cn.push({
12339 cls : 'input-group-addon input-group-append input-group-text',
12348 var ibwrap = inputblock;
12353 cls: 'roo-select2-choices',
12357 cls: 'roo-select2-search-field',
12369 cls: 'roo-select2-container input-group',
12374 cls: 'form-hidden-field'
12380 if(!this.multiple && this.showToggleBtn){
12386 if (this.caret != false) {
12389 cls: 'fa fa-' + this.caret
12396 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12398 Roo.bootstrap.version == 3 ? caret : '',
12401 cls: 'combobox-clear',
12415 combobox.cls += ' roo-select2-container-multi';
12419 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12420 tooltip : 'This field is required'
12422 if (Roo.bootstrap.version == 4) {
12425 style : 'display:none'
12430 if (align ==='left' && this.fieldLabel.length) {
12432 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12439 cls : 'control-label',
12440 html : this.fieldLabel
12452 var labelCfg = cfg.cn[1];
12453 var contentCfg = cfg.cn[2];
12455 if(this.indicatorpos == 'right'){
12460 cls : 'control-label',
12464 html : this.fieldLabel
12478 labelCfg = cfg.cn[0];
12479 contentCfg = cfg.cn[1];
12482 if(this.labelWidth > 12){
12483 labelCfg.style = "width: " + this.labelWidth + 'px';
12486 if(this.labelWidth < 13 && this.labelmd == 0){
12487 this.labelmd = this.labelWidth;
12490 if(this.labellg > 0){
12491 labelCfg.cls += ' col-lg-' + this.labellg;
12492 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12495 if(this.labelmd > 0){
12496 labelCfg.cls += ' col-md-' + this.labelmd;
12497 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12500 if(this.labelsm > 0){
12501 labelCfg.cls += ' col-sm-' + this.labelsm;
12502 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12505 if(this.labelxs > 0){
12506 labelCfg.cls += ' col-xs-' + this.labelxs;
12507 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12510 } else if ( this.fieldLabel.length) {
12511 // Roo.log(" label");
12516 //cls : 'input-group-addon',
12517 html : this.fieldLabel
12525 if(this.indicatorpos == 'right'){
12533 html : this.fieldLabel
12547 // Roo.log(" no label && no align");
12554 ['xs','sm','md','lg'].map(function(size){
12555 if (settings[size]) {
12556 cfg.cls += ' col-' + size + '-' + settings[size];
12567 onResize : function(w, h){
12568 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12569 // if(typeof w == 'number'){
12570 // var x = w - this.trigger.getWidth();
12571 // this.inputEl().setWidth(this.adjustWidth('input', x));
12572 // this.trigger.setStyle('left', x+'px');
12577 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12580 getResizeEl : function(){
12581 return this.inputEl();
12585 getPositionEl : function(){
12586 return this.inputEl();
12590 alignErrorIcon : function(){
12591 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12595 initEvents : function(){
12599 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12600 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12601 if(!this.multiple && this.showToggleBtn){
12602 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12603 if(this.hideTrigger){
12604 this.trigger.setDisplayed(false);
12606 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12610 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12613 if(this.removable && !this.editable && !this.tickable){
12614 var close = this.closeTriggerEl();
12617 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12618 close.on('click', this.removeBtnClick, this, close);
12622 //this.trigger.addClassOnOver('x-form-trigger-over');
12623 //this.trigger.addClassOnClick('x-form-trigger-click');
12626 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12630 closeTriggerEl : function()
12632 var close = this.el.select('.roo-combo-removable-btn', true).first();
12633 return close ? close : false;
12636 removeBtnClick : function(e, h, el)
12638 e.preventDefault();
12640 if(this.fireEvent("remove", this) !== false){
12642 this.fireEvent("afterremove", this)
12646 createList : function()
12648 this.list = Roo.get(document.body).createChild({
12649 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12650 cls: 'typeahead typeahead-long dropdown-menu shadow',
12651 style: 'display:none'
12654 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12659 initTrigger : function(){
12664 onDestroy : function(){
12666 this.trigger.removeAllListeners();
12667 // this.trigger.remove();
12670 // this.wrap.remove();
12672 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12676 onFocus : function(){
12677 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12679 if(!this.mimicing){
12680 this.wrap.addClass('x-trigger-wrap-focus');
12681 this.mimicing = true;
12682 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12683 if(this.monitorTab){
12684 this.el.on("keydown", this.checkTab, this);
12691 checkTab : function(e){
12692 if(e.getKey() == e.TAB){
12693 this.triggerBlur();
12698 onBlur : function(){
12703 mimicBlur : function(e, t){
12705 if(!this.wrap.contains(t) && this.validateBlur()){
12706 this.triggerBlur();
12712 triggerBlur : function(){
12713 this.mimicing = false;
12714 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12715 if(this.monitorTab){
12716 this.el.un("keydown", this.checkTab, this);
12718 //this.wrap.removeClass('x-trigger-wrap-focus');
12719 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12723 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12724 validateBlur : function(e, t){
12729 onDisable : function(){
12730 this.inputEl().dom.disabled = true;
12731 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12733 // this.wrap.addClass('x-item-disabled');
12738 onEnable : function(){
12739 this.inputEl().dom.disabled = false;
12740 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12742 // this.el.removeClass('x-item-disabled');
12747 onShow : function(){
12748 var ae = this.getActionEl();
12751 ae.dom.style.display = '';
12752 ae.dom.style.visibility = 'visible';
12758 onHide : function(){
12759 var ae = this.getActionEl();
12760 ae.dom.style.display = 'none';
12764 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12765 * by an implementing function.
12767 * @param {EventObject} e
12769 onTriggerClick : Roo.emptyFn
12777 * @class Roo.bootstrap.CardUploader
12778 * @extends Roo.bootstrap.Button
12779 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12780 * @cfg {Number} errorTimeout default 3000
12781 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12782 * @cfg {Array} html The button text.
12786 * Create a new CardUploader
12787 * @param {Object} config The config object
12790 Roo.bootstrap.CardUploader = function(config){
12794 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12797 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12805 * When a image is clicked on - and needs to display a slideshow or similar..
12806 * @param {Roo.bootstrap.Card} this
12807 * @param {Object} The image information data
12813 * When a the download link is clicked
12814 * @param {Roo.bootstrap.Card} this
12815 * @param {Object} The image information data contains
12822 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12825 errorTimeout : 3000,
12829 fileCollection : false,
12832 getAutoCreate : function()
12836 cls :'form-group' ,
12841 //cls : 'input-group-addon',
12842 html : this.fieldLabel
12850 value : this.value,
12851 cls : 'd-none form-control'
12856 multiple : 'multiple',
12858 cls : 'd-none roo-card-upload-selector'
12862 cls : 'roo-card-uploader-button-container w-100 mb-2'
12865 cls : 'card-columns roo-card-uploader-container'
12875 getChildContainer : function() /// what children are added to.
12877 return this.containerEl;
12880 getButtonContainer : function() /// what children are added to.
12882 return this.el.select(".roo-card-uploader-button-container").first();
12885 initEvents : function()
12888 Roo.bootstrap.Input.prototype.initEvents.call(this);
12892 xns: Roo.bootstrap,
12895 container_method : 'getButtonContainer' ,
12896 html : this.html, // fix changable?
12899 'click' : function(btn, e) {
12908 this.urlAPI = (window.createObjectURL && window) ||
12909 (window.URL && URL.revokeObjectURL && URL) ||
12910 (window.webkitURL && webkitURL);
12915 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12917 this.selectorEl.on('change', this.onFileSelected, this);
12920 this.images.forEach(function(img) {
12923 this.images = false;
12925 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12931 onClick : function(e)
12933 e.preventDefault();
12935 this.selectorEl.dom.click();
12939 onFileSelected : function(e)
12941 e.preventDefault();
12943 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12947 Roo.each(this.selectorEl.dom.files, function(file){
12948 this.addFile(file);
12957 addFile : function(file)
12960 if(typeof(file) === 'string'){
12961 throw "Add file by name?"; // should not happen
12965 if(!file || !this.urlAPI){
12975 var url = _this.urlAPI.createObjectURL( file);
12978 id : Roo.bootstrap.CardUploader.ID--,
12979 is_uploaded : false,
12983 mimetype : file.type,
12991 * addCard - add an Attachment to the uploader
12992 * @param data - the data about the image to upload
12996 title : "Title of file",
12997 is_uploaded : false,
12998 src : "http://.....",
12999 srcfile : { the File upload object },
13000 mimetype : file.type,
13003 .. any other data...
13009 addCard : function (data)
13011 // hidden input element?
13012 // if the file is not an image...
13013 //then we need to use something other that and header_image
13018 xns : Roo.bootstrap,
13019 xtype : 'CardFooter',
13022 xns : Roo.bootstrap,
13028 xns : Roo.bootstrap,
13030 html : String.format("<small>{0}</small>", data.title),
13031 cls : 'col-10 text-left',
13036 click : function() {
13038 t.fireEvent( "download", t, data );
13044 xns : Roo.bootstrap,
13046 style: 'max-height: 28px; ',
13052 click : function() {
13053 t.removeCard(data.id)
13065 var cn = this.addxtype(
13068 xns : Roo.bootstrap,
13071 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13072 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13073 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13078 initEvents : function() {
13079 Roo.bootstrap.Card.prototype.initEvents.call(this);
13081 this.imgEl = this.el.select('.card-img-top').first();
13083 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13084 this.imgEl.set({ 'pointer' : 'cursor' });
13087 this.getCardFooter().addClass('p-1');
13094 // dont' really need ot update items.
13095 // this.items.push(cn);
13096 this.fileCollection.add(cn);
13098 if (!data.srcfile) {
13099 this.updateInput();
13104 var reader = new FileReader();
13105 reader.addEventListener("load", function() {
13106 data.srcdata = reader.result;
13109 reader.readAsDataURL(data.srcfile);
13114 removeCard : function(id)
13117 var card = this.fileCollection.get(id);
13118 card.data.is_deleted = 1;
13119 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13120 //this.fileCollection.remove(card);
13121 //this.items = this.items.filter(function(e) { return e != card });
13122 // dont' really need ot update items.
13123 card.el.dom.parentNode.removeChild(card.el.dom);
13124 this.updateInput();
13130 this.fileCollection.each(function(card) {
13131 if (card.el.dom && card.el.dom.parentNode) {
13132 card.el.dom.parentNode.removeChild(card.el.dom);
13135 this.fileCollection.clear();
13136 this.updateInput();
13139 updateInput : function()
13142 this.fileCollection.each(function(e) {
13146 this.inputEl().dom.value = JSON.stringify(data);
13156 Roo.bootstrap.CardUploader.ID = -1;/*
13158 * Ext JS Library 1.1.1
13159 * Copyright(c) 2006-2007, Ext JS, LLC.
13161 * Originally Released Under LGPL - original licence link has changed is not relivant.
13164 * <script type="text/javascript">
13169 * @class Roo.data.SortTypes
13171 * Defines the default sorting (casting?) comparison functions used when sorting data.
13173 Roo.data.SortTypes = {
13175 * Default sort that does nothing
13176 * @param {Mixed} s The value being converted
13177 * @return {Mixed} The comparison value
13179 none : function(s){
13184 * The regular expression used to strip tags
13188 stripTagsRE : /<\/?[^>]+>/gi,
13191 * Strips all HTML tags to sort on text only
13192 * @param {Mixed} s The value being converted
13193 * @return {String} The comparison value
13195 asText : function(s){
13196 return String(s).replace(this.stripTagsRE, "");
13200 * Strips all HTML tags to sort on text only - Case insensitive
13201 * @param {Mixed} s The value being converted
13202 * @return {String} The comparison value
13204 asUCText : function(s){
13205 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13209 * Case insensitive string
13210 * @param {Mixed} s The value being converted
13211 * @return {String} The comparison value
13213 asUCString : function(s) {
13214 return String(s).toUpperCase();
13219 * @param {Mixed} s The value being converted
13220 * @return {Number} The comparison value
13222 asDate : function(s) {
13226 if(s instanceof Date){
13227 return s.getTime();
13229 return Date.parse(String(s));
13234 * @param {Mixed} s The value being converted
13235 * @return {Float} The comparison value
13237 asFloat : function(s) {
13238 var val = parseFloat(String(s).replace(/,/g, ""));
13247 * @param {Mixed} s The value being converted
13248 * @return {Number} The comparison value
13250 asInt : function(s) {
13251 var val = parseInt(String(s).replace(/,/g, ""));
13259 * Ext JS Library 1.1.1
13260 * Copyright(c) 2006-2007, Ext JS, LLC.
13262 * Originally Released Under LGPL - original licence link has changed is not relivant.
13265 * <script type="text/javascript">
13269 * @class Roo.data.Record
13270 * Instances of this class encapsulate both record <em>definition</em> information, and record
13271 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13272 * to access Records cached in an {@link Roo.data.Store} object.<br>
13274 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13275 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13278 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13280 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13281 * {@link #create}. The parameters are the same.
13282 * @param {Array} data An associative Array of data values keyed by the field name.
13283 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13284 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13285 * not specified an integer id is generated.
13287 Roo.data.Record = function(data, id){
13288 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13293 * Generate a constructor for a specific record layout.
13294 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13295 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13296 * Each field definition object may contain the following properties: <ul>
13297 * <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,
13298 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13299 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13300 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13301 * is being used, then this is a string containing the javascript expression to reference the data relative to
13302 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13303 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13304 * this may be omitted.</p></li>
13305 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13306 * <ul><li>auto (Default, implies no conversion)</li>
13311 * <li>date</li></ul></p></li>
13312 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13313 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13314 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13315 * by the Reader into an object that will be stored in the Record. It is passed the
13316 * following parameters:<ul>
13317 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13319 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13321 * <br>usage:<br><pre><code>
13322 var TopicRecord = Roo.data.Record.create(
13323 {name: 'title', mapping: 'topic_title'},
13324 {name: 'author', mapping: 'username'},
13325 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13326 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13327 {name: 'lastPoster', mapping: 'user2'},
13328 {name: 'excerpt', mapping: 'post_text'}
13331 var myNewRecord = new TopicRecord({
13332 title: 'Do my job please',
13335 lastPost: new Date(),
13336 lastPoster: 'Animal',
13337 excerpt: 'No way dude!'
13339 myStore.add(myNewRecord);
13344 Roo.data.Record.create = function(o){
13345 var f = function(){
13346 f.superclass.constructor.apply(this, arguments);
13348 Roo.extend(f, Roo.data.Record);
13349 var p = f.prototype;
13350 p.fields = new Roo.util.MixedCollection(false, function(field){
13353 for(var i = 0, len = o.length; i < len; i++){
13354 p.fields.add(new Roo.data.Field(o[i]));
13356 f.getField = function(name){
13357 return p.fields.get(name);
13362 Roo.data.Record.AUTO_ID = 1000;
13363 Roo.data.Record.EDIT = 'edit';
13364 Roo.data.Record.REJECT = 'reject';
13365 Roo.data.Record.COMMIT = 'commit';
13367 Roo.data.Record.prototype = {
13369 * Readonly flag - true if this record has been modified.
13378 join : function(store){
13379 this.store = store;
13383 * Set the named field to the specified value.
13384 * @param {String} name The name of the field to set.
13385 * @param {Object} value The value to set the field to.
13387 set : function(name, value){
13388 if(this.data[name] == value){
13392 if(!this.modified){
13393 this.modified = {};
13395 if(typeof this.modified[name] == 'undefined'){
13396 this.modified[name] = this.data[name];
13398 this.data[name] = value;
13399 if(!this.editing && this.store){
13400 this.store.afterEdit(this);
13405 * Get the value of the named field.
13406 * @param {String} name The name of the field to get the value of.
13407 * @return {Object} The value of the field.
13409 get : function(name){
13410 return this.data[name];
13414 beginEdit : function(){
13415 this.editing = true;
13416 this.modified = {};
13420 cancelEdit : function(){
13421 this.editing = false;
13422 delete this.modified;
13426 endEdit : function(){
13427 this.editing = false;
13428 if(this.dirty && this.store){
13429 this.store.afterEdit(this);
13434 * Usually called by the {@link Roo.data.Store} which owns the Record.
13435 * Rejects all changes made to the Record since either creation, or the last commit operation.
13436 * Modified fields are reverted to their original values.
13438 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13439 * of reject operations.
13441 reject : function(){
13442 var m = this.modified;
13444 if(typeof m[n] != "function"){
13445 this.data[n] = m[n];
13448 this.dirty = false;
13449 delete this.modified;
13450 this.editing = false;
13452 this.store.afterReject(this);
13457 * Usually called by the {@link Roo.data.Store} which owns the Record.
13458 * Commits all changes made to the Record since either creation, or the last commit operation.
13460 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13461 * of commit operations.
13463 commit : function(){
13464 this.dirty = false;
13465 delete this.modified;
13466 this.editing = false;
13468 this.store.afterCommit(this);
13473 hasError : function(){
13474 return this.error != null;
13478 clearError : function(){
13483 * Creates a copy of this record.
13484 * @param {String} id (optional) A new record id if you don't want to use this record's id
13487 copy : function(newId) {
13488 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13492 * Ext JS Library 1.1.1
13493 * Copyright(c) 2006-2007, Ext JS, LLC.
13495 * Originally Released Under LGPL - original licence link has changed is not relivant.
13498 * <script type="text/javascript">
13504 * @class Roo.data.Store
13505 * @extends Roo.util.Observable
13506 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13507 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13509 * 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
13510 * has no knowledge of the format of the data returned by the Proxy.<br>
13512 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13513 * instances from the data object. These records are cached and made available through accessor functions.
13515 * Creates a new Store.
13516 * @param {Object} config A config object containing the objects needed for the Store to access data,
13517 * and read the data into Records.
13519 Roo.data.Store = function(config){
13520 this.data = new Roo.util.MixedCollection(false);
13521 this.data.getKey = function(o){
13524 this.baseParams = {};
13526 this.paramNames = {
13531 "multisort" : "_multisort"
13534 if(config && config.data){
13535 this.inlineData = config.data;
13536 delete config.data;
13539 Roo.apply(this, config);
13541 if(this.reader){ // reader passed
13542 this.reader = Roo.factory(this.reader, Roo.data);
13543 this.reader.xmodule = this.xmodule || false;
13544 if(!this.recordType){
13545 this.recordType = this.reader.recordType;
13547 if(this.reader.onMetaChange){
13548 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13552 if(this.recordType){
13553 this.fields = this.recordType.prototype.fields;
13555 this.modified = [];
13559 * @event datachanged
13560 * Fires when the data cache has changed, and a widget which is using this Store
13561 * as a Record cache should refresh its view.
13562 * @param {Store} this
13564 datachanged : true,
13566 * @event metachange
13567 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13568 * @param {Store} this
13569 * @param {Object} meta The JSON metadata
13574 * Fires when Records have been added to the Store
13575 * @param {Store} this
13576 * @param {Roo.data.Record[]} records The array of Records added
13577 * @param {Number} index The index at which the record(s) were added
13582 * Fires when a Record has been removed from the Store
13583 * @param {Store} this
13584 * @param {Roo.data.Record} record The Record that was removed
13585 * @param {Number} index The index at which the record was removed
13590 * Fires when a Record has been updated
13591 * @param {Store} this
13592 * @param {Roo.data.Record} record The Record that was updated
13593 * @param {String} operation The update operation being performed. Value may be one of:
13595 Roo.data.Record.EDIT
13596 Roo.data.Record.REJECT
13597 Roo.data.Record.COMMIT
13603 * Fires when the data cache has been cleared.
13604 * @param {Store} this
13608 * @event beforeload
13609 * Fires before a request is made for a new data object. If the beforeload handler returns false
13610 * the load action will be canceled.
13611 * @param {Store} this
13612 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13616 * @event beforeloadadd
13617 * Fires after a new set of Records has been loaded.
13618 * @param {Store} this
13619 * @param {Roo.data.Record[]} records The Records that were loaded
13620 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13622 beforeloadadd : true,
13625 * Fires after a new set of Records has been loaded, before they are added to the store.
13626 * @param {Store} this
13627 * @param {Roo.data.Record[]} records The Records that were loaded
13628 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13629 * @params {Object} return from reader
13633 * @event loadexception
13634 * Fires if an exception occurs in the Proxy during loading.
13635 * Called with the signature of the Proxy's "loadexception" event.
13636 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13639 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13640 * @param {Object} load options
13641 * @param {Object} jsonData from your request (normally this contains the Exception)
13643 loadexception : true
13647 this.proxy = Roo.factory(this.proxy, Roo.data);
13648 this.proxy.xmodule = this.xmodule || false;
13649 this.relayEvents(this.proxy, ["loadexception"]);
13651 this.sortToggle = {};
13652 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13654 Roo.data.Store.superclass.constructor.call(this);
13656 if(this.inlineData){
13657 this.loadData(this.inlineData);
13658 delete this.inlineData;
13662 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13664 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13665 * without a remote query - used by combo/forms at present.
13669 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13672 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13675 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13676 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13679 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13680 * on any HTTP request
13683 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13686 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13690 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13691 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13693 remoteSort : false,
13696 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13697 * loaded or when a record is removed. (defaults to false).
13699 pruneModifiedRecords : false,
13702 lastOptions : null,
13705 * Add Records to the Store and fires the add event.
13706 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13708 add : function(records){
13709 records = [].concat(records);
13710 for(var i = 0, len = records.length; i < len; i++){
13711 records[i].join(this);
13713 var index = this.data.length;
13714 this.data.addAll(records);
13715 this.fireEvent("add", this, records, index);
13719 * Remove a Record from the Store and fires the remove event.
13720 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13722 remove : function(record){
13723 var index = this.data.indexOf(record);
13724 this.data.removeAt(index);
13726 if(this.pruneModifiedRecords){
13727 this.modified.remove(record);
13729 this.fireEvent("remove", this, record, index);
13733 * Remove all Records from the Store and fires the clear event.
13735 removeAll : function(){
13737 if(this.pruneModifiedRecords){
13738 this.modified = [];
13740 this.fireEvent("clear", this);
13744 * Inserts Records to the Store at the given index and fires the add event.
13745 * @param {Number} index The start index at which to insert the passed Records.
13746 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13748 insert : function(index, records){
13749 records = [].concat(records);
13750 for(var i = 0, len = records.length; i < len; i++){
13751 this.data.insert(index, records[i]);
13752 records[i].join(this);
13754 this.fireEvent("add", this, records, index);
13758 * Get the index within the cache of the passed Record.
13759 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13760 * @return {Number} The index of the passed Record. Returns -1 if not found.
13762 indexOf : function(record){
13763 return this.data.indexOf(record);
13767 * Get the index within the cache of the Record with the passed id.
13768 * @param {String} id The id of the Record to find.
13769 * @return {Number} The index of the Record. Returns -1 if not found.
13771 indexOfId : function(id){
13772 return this.data.indexOfKey(id);
13776 * Get the Record with the specified id.
13777 * @param {String} id The id of the Record to find.
13778 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13780 getById : function(id){
13781 return this.data.key(id);
13785 * Get the Record at the specified index.
13786 * @param {Number} index The index of the Record to find.
13787 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13789 getAt : function(index){
13790 return this.data.itemAt(index);
13794 * Returns a range of Records between specified indices.
13795 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13796 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13797 * @return {Roo.data.Record[]} An array of Records
13799 getRange : function(start, end){
13800 return this.data.getRange(start, end);
13804 storeOptions : function(o){
13805 o = Roo.apply({}, o);
13808 this.lastOptions = o;
13812 * Loads the Record cache from the configured Proxy using the configured Reader.
13814 * If using remote paging, then the first load call must specify the <em>start</em>
13815 * and <em>limit</em> properties in the options.params property to establish the initial
13816 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13818 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13819 * and this call will return before the new data has been loaded. Perform any post-processing
13820 * in a callback function, or in a "load" event handler.</strong>
13822 * @param {Object} options An object containing properties which control loading options:<ul>
13823 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13824 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13825 * passed the following arguments:<ul>
13826 * <li>r : Roo.data.Record[]</li>
13827 * <li>options: Options object from the load call</li>
13828 * <li>success: Boolean success indicator</li></ul></li>
13829 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13830 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13833 load : function(options){
13834 options = options || {};
13835 if(this.fireEvent("beforeload", this, options) !== false){
13836 this.storeOptions(options);
13837 var p = Roo.apply(options.params || {}, this.baseParams);
13838 // if meta was not loaded from remote source.. try requesting it.
13839 if (!this.reader.metaFromRemote) {
13840 p._requestMeta = 1;
13842 if(this.sortInfo && this.remoteSort){
13843 var pn = this.paramNames;
13844 p[pn["sort"]] = this.sortInfo.field;
13845 p[pn["dir"]] = this.sortInfo.direction;
13847 if (this.multiSort) {
13848 var pn = this.paramNames;
13849 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13852 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13857 * Reloads the Record cache from the configured Proxy using the configured Reader and
13858 * the options from the last load operation performed.
13859 * @param {Object} options (optional) An object containing properties which may override the options
13860 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13861 * the most recently used options are reused).
13863 reload : function(options){
13864 this.load(Roo.applyIf(options||{}, this.lastOptions));
13868 // Called as a callback by the Reader during a load operation.
13869 loadRecords : function(o, options, success){
13870 if(!o || success === false){
13871 if(success !== false){
13872 this.fireEvent("load", this, [], options, o);
13874 if(options.callback){
13875 options.callback.call(options.scope || this, [], options, false);
13879 // if data returned failure - throw an exception.
13880 if (o.success === false) {
13881 // show a message if no listener is registered.
13882 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13883 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13885 // loadmask wil be hooked into this..
13886 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13889 var r = o.records, t = o.totalRecords || r.length;
13891 this.fireEvent("beforeloadadd", this, r, options, o);
13893 if(!options || options.add !== true){
13894 if(this.pruneModifiedRecords){
13895 this.modified = [];
13897 for(var i = 0, len = r.length; i < len; i++){
13901 this.data = this.snapshot;
13902 delete this.snapshot;
13905 this.data.addAll(r);
13906 this.totalLength = t;
13908 this.fireEvent("datachanged", this);
13910 this.totalLength = Math.max(t, this.data.length+r.length);
13914 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13916 var e = new Roo.data.Record({});
13918 e.set(this.parent.displayField, this.parent.emptyTitle);
13919 e.set(this.parent.valueField, '');
13924 this.fireEvent("load", this, r, options, o);
13925 if(options.callback){
13926 options.callback.call(options.scope || this, r, options, true);
13932 * Loads data from a passed data block. A Reader which understands the format of the data
13933 * must have been configured in the constructor.
13934 * @param {Object} data The data block from which to read the Records. The format of the data expected
13935 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13936 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13938 loadData : function(o, append){
13939 var r = this.reader.readRecords(o);
13940 this.loadRecords(r, {add: append}, true);
13944 * using 'cn' the nested child reader read the child array into it's child stores.
13945 * @param {Object} rec The record with a 'children array
13947 loadDataFromChildren : function(rec)
13949 this.loadData(this.reader.toLoadData(rec));
13954 * Gets the number of cached records.
13956 * <em>If using paging, this may not be the total size of the dataset. If the data object
13957 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13958 * the data set size</em>
13960 getCount : function(){
13961 return this.data.length || 0;
13965 * Gets the total number of records in the dataset as returned by the server.
13967 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13968 * the dataset size</em>
13970 getTotalCount : function(){
13971 return this.totalLength || 0;
13975 * Returns the sort state of the Store as an object with two properties:
13977 field {String} The name of the field by which the Records are sorted
13978 direction {String} The sort order, "ASC" or "DESC"
13981 getSortState : function(){
13982 return this.sortInfo;
13986 applySort : function(){
13987 if(this.sortInfo && !this.remoteSort){
13988 var s = this.sortInfo, f = s.field;
13989 var st = this.fields.get(f).sortType;
13990 var fn = function(r1, r2){
13991 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13992 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13994 this.data.sort(s.direction, fn);
13995 if(this.snapshot && this.snapshot != this.data){
13996 this.snapshot.sort(s.direction, fn);
14002 * Sets the default sort column and order to be used by the next load operation.
14003 * @param {String} fieldName The name of the field to sort by.
14004 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14006 setDefaultSort : function(field, dir){
14007 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14011 * Sort the Records.
14012 * If remote sorting is used, the sort is performed on the server, and the cache is
14013 * reloaded. If local sorting is used, the cache is sorted internally.
14014 * @param {String} fieldName The name of the field to sort by.
14015 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14017 sort : function(fieldName, dir){
14018 var f = this.fields.get(fieldName);
14020 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14022 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14023 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14028 this.sortToggle[f.name] = dir;
14029 this.sortInfo = {field: f.name, direction: dir};
14030 if(!this.remoteSort){
14032 this.fireEvent("datachanged", this);
14034 this.load(this.lastOptions);
14039 * Calls the specified function for each of the Records in the cache.
14040 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14041 * Returning <em>false</em> aborts and exits the iteration.
14042 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14044 each : function(fn, scope){
14045 this.data.each(fn, scope);
14049 * Gets all records modified since the last commit. Modified records are persisted across load operations
14050 * (e.g., during paging).
14051 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14053 getModifiedRecords : function(){
14054 return this.modified;
14058 createFilterFn : function(property, value, anyMatch){
14059 if(!value.exec){ // not a regex
14060 value = String(value);
14061 if(value.length == 0){
14064 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14066 return function(r){
14067 return value.test(r.data[property]);
14072 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14073 * @param {String} property A field on your records
14074 * @param {Number} start The record index to start at (defaults to 0)
14075 * @param {Number} end The last record index to include (defaults to length - 1)
14076 * @return {Number} The sum
14078 sum : function(property, start, end){
14079 var rs = this.data.items, v = 0;
14080 start = start || 0;
14081 end = (end || end === 0) ? end : rs.length-1;
14083 for(var i = start; i <= end; i++){
14084 v += (rs[i].data[property] || 0);
14090 * Filter the records by a specified property.
14091 * @param {String} field A field on your records
14092 * @param {String/RegExp} value Either a string that the field
14093 * should start with or a RegExp to test against the field
14094 * @param {Boolean} anyMatch True to match any part not just the beginning
14096 filter : function(property, value, anyMatch){
14097 var fn = this.createFilterFn(property, value, anyMatch);
14098 return fn ? this.filterBy(fn) : this.clearFilter();
14102 * Filter by a function. The specified function will be called with each
14103 * record in this data source. If the function returns true the record is included,
14104 * otherwise it is filtered.
14105 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14106 * @param {Object} scope (optional) The scope of the function (defaults to this)
14108 filterBy : function(fn, scope){
14109 this.snapshot = this.snapshot || this.data;
14110 this.data = this.queryBy(fn, scope||this);
14111 this.fireEvent("datachanged", this);
14115 * Query the records by a specified property.
14116 * @param {String} field A field on your records
14117 * @param {String/RegExp} value Either a string that the field
14118 * should start with or a RegExp to test against the field
14119 * @param {Boolean} anyMatch True to match any part not just the beginning
14120 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14122 query : function(property, value, anyMatch){
14123 var fn = this.createFilterFn(property, value, anyMatch);
14124 return fn ? this.queryBy(fn) : this.data.clone();
14128 * Query by a function. The specified function will be called with each
14129 * record in this data source. If the function returns true the record is included
14131 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14132 * @param {Object} scope (optional) The scope of the function (defaults to this)
14133 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14135 queryBy : function(fn, scope){
14136 var data = this.snapshot || this.data;
14137 return data.filterBy(fn, scope||this);
14141 * Collects unique values for a particular dataIndex from this store.
14142 * @param {String} dataIndex The property to collect
14143 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14144 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14145 * @return {Array} An array of the unique values
14147 collect : function(dataIndex, allowNull, bypassFilter){
14148 var d = (bypassFilter === true && this.snapshot) ?
14149 this.snapshot.items : this.data.items;
14150 var v, sv, r = [], l = {};
14151 for(var i = 0, len = d.length; i < len; i++){
14152 v = d[i].data[dataIndex];
14154 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14163 * Revert to a view of the Record cache with no filtering applied.
14164 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14166 clearFilter : function(suppressEvent){
14167 if(this.snapshot && this.snapshot != this.data){
14168 this.data = this.snapshot;
14169 delete this.snapshot;
14170 if(suppressEvent !== true){
14171 this.fireEvent("datachanged", this);
14177 afterEdit : function(record){
14178 if(this.modified.indexOf(record) == -1){
14179 this.modified.push(record);
14181 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14185 afterReject : function(record){
14186 this.modified.remove(record);
14187 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14191 afterCommit : function(record){
14192 this.modified.remove(record);
14193 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14197 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14198 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14200 commitChanges : function(){
14201 var m = this.modified.slice(0);
14202 this.modified = [];
14203 for(var i = 0, len = m.length; i < len; i++){
14209 * Cancel outstanding changes on all changed records.
14211 rejectChanges : function(){
14212 var m = this.modified.slice(0);
14213 this.modified = [];
14214 for(var i = 0, len = m.length; i < len; i++){
14219 onMetaChange : function(meta, rtype, o){
14220 this.recordType = rtype;
14221 this.fields = rtype.prototype.fields;
14222 delete this.snapshot;
14223 this.sortInfo = meta.sortInfo || this.sortInfo;
14224 this.modified = [];
14225 this.fireEvent('metachange', this, this.reader.meta);
14228 moveIndex : function(data, type)
14230 var index = this.indexOf(data);
14232 var newIndex = index + type;
14236 this.insert(newIndex, data);
14241 * Ext JS Library 1.1.1
14242 * Copyright(c) 2006-2007, Ext JS, LLC.
14244 * Originally Released Under LGPL - original licence link has changed is not relivant.
14247 * <script type="text/javascript">
14251 * @class Roo.data.SimpleStore
14252 * @extends Roo.data.Store
14253 * Small helper class to make creating Stores from Array data easier.
14254 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14255 * @cfg {Array} fields An array of field definition objects, or field name strings.
14256 * @cfg {Object} an existing reader (eg. copied from another store)
14257 * @cfg {Array} data The multi-dimensional array of data
14259 * @param {Object} config
14261 Roo.data.SimpleStore = function(config)
14263 Roo.data.SimpleStore.superclass.constructor.call(this, {
14265 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14268 Roo.data.Record.create(config.fields)
14270 proxy : new Roo.data.MemoryProxy(config.data)
14274 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14276 * Ext JS Library 1.1.1
14277 * Copyright(c) 2006-2007, Ext JS, LLC.
14279 * Originally Released Under LGPL - original licence link has changed is not relivant.
14282 * <script type="text/javascript">
14287 * @extends Roo.data.Store
14288 * @class Roo.data.JsonStore
14289 * Small helper class to make creating Stores for JSON data easier. <br/>
14291 var store = new Roo.data.JsonStore({
14292 url: 'get-images.php',
14294 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14297 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14298 * JsonReader and HttpProxy (unless inline data is provided).</b>
14299 * @cfg {Array} fields An array of field definition objects, or field name strings.
14301 * @param {Object} config
14303 Roo.data.JsonStore = function(c){
14304 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14305 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14306 reader: new Roo.data.JsonReader(c, c.fields)
14309 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14311 * Ext JS Library 1.1.1
14312 * Copyright(c) 2006-2007, Ext JS, LLC.
14314 * Originally Released Under LGPL - original licence link has changed is not relivant.
14317 * <script type="text/javascript">
14321 Roo.data.Field = function(config){
14322 if(typeof config == "string"){
14323 config = {name: config};
14325 Roo.apply(this, config);
14328 this.type = "auto";
14331 var st = Roo.data.SortTypes;
14332 // named sortTypes are supported, here we look them up
14333 if(typeof this.sortType == "string"){
14334 this.sortType = st[this.sortType];
14337 // set default sortType for strings and dates
14338 if(!this.sortType){
14341 this.sortType = st.asUCString;
14344 this.sortType = st.asDate;
14347 this.sortType = st.none;
14352 var stripRe = /[\$,%]/g;
14354 // prebuilt conversion function for this field, instead of
14355 // switching every time we're reading a value
14357 var cv, dateFormat = this.dateFormat;
14362 cv = function(v){ return v; };
14365 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14369 return v !== undefined && v !== null && v !== '' ?
14370 parseInt(String(v).replace(stripRe, ""), 10) : '';
14375 return v !== undefined && v !== null && v !== '' ?
14376 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14381 cv = function(v){ return v === true || v === "true" || v == 1; };
14388 if(v instanceof Date){
14392 if(dateFormat == "timestamp"){
14393 return new Date(v*1000);
14395 return Date.parseDate(v, dateFormat);
14397 var parsed = Date.parse(v);
14398 return parsed ? new Date(parsed) : null;
14407 Roo.data.Field.prototype = {
14415 * Ext JS Library 1.1.1
14416 * Copyright(c) 2006-2007, Ext JS, LLC.
14418 * Originally Released Under LGPL - original licence link has changed is not relivant.
14421 * <script type="text/javascript">
14424 // Base class for reading structured data from a data source. This class is intended to be
14425 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14428 * @class Roo.data.DataReader
14429 * Base class for reading structured data from a data source. This class is intended to be
14430 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14433 Roo.data.DataReader = function(meta, recordType){
14437 this.recordType = recordType instanceof Array ?
14438 Roo.data.Record.create(recordType) : recordType;
14441 Roo.data.DataReader.prototype = {
14444 readerType : 'Data',
14446 * Create an empty record
14447 * @param {Object} data (optional) - overlay some values
14448 * @return {Roo.data.Record} record created.
14450 newRow : function(d) {
14452 this.recordType.prototype.fields.each(function(c) {
14454 case 'int' : da[c.name] = 0; break;
14455 case 'date' : da[c.name] = new Date(); break;
14456 case 'float' : da[c.name] = 0.0; break;
14457 case 'boolean' : da[c.name] = false; break;
14458 default : da[c.name] = ""; break;
14462 return new this.recordType(Roo.apply(da, d));
14468 * Ext JS Library 1.1.1
14469 * Copyright(c) 2006-2007, Ext JS, LLC.
14471 * Originally Released Under LGPL - original licence link has changed is not relivant.
14474 * <script type="text/javascript">
14478 * @class Roo.data.DataProxy
14479 * @extends Roo.data.Observable
14480 * This class is an abstract base class for implementations which provide retrieval of
14481 * unformatted data objects.<br>
14483 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14484 * (of the appropriate type which knows how to parse the data object) to provide a block of
14485 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14487 * Custom implementations must implement the load method as described in
14488 * {@link Roo.data.HttpProxy#load}.
14490 Roo.data.DataProxy = function(){
14493 * @event beforeload
14494 * Fires before a network request is made to retrieve a data object.
14495 * @param {Object} This DataProxy object.
14496 * @param {Object} params The params parameter to the load function.
14501 * Fires before the load method's callback is called.
14502 * @param {Object} This DataProxy object.
14503 * @param {Object} o The data object.
14504 * @param {Object} arg The callback argument object passed to the load function.
14508 * @event loadexception
14509 * Fires if an Exception occurs during data retrieval.
14510 * @param {Object} This DataProxy object.
14511 * @param {Object} o The data object.
14512 * @param {Object} arg The callback argument object passed to the load function.
14513 * @param {Object} e The Exception.
14515 loadexception : true
14517 Roo.data.DataProxy.superclass.constructor.call(this);
14520 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14523 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14527 * Ext JS Library 1.1.1
14528 * Copyright(c) 2006-2007, Ext JS, LLC.
14530 * Originally Released Under LGPL - original licence link has changed is not relivant.
14533 * <script type="text/javascript">
14536 * @class Roo.data.MemoryProxy
14537 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14538 * to the Reader when its load method is called.
14540 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14542 Roo.data.MemoryProxy = function(data){
14546 Roo.data.MemoryProxy.superclass.constructor.call(this);
14550 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14553 * Load data from the requested source (in this case an in-memory
14554 * data object passed to the constructor), read the data object into
14555 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14556 * process that block using the passed callback.
14557 * @param {Object} params This parameter is not used by the MemoryProxy class.
14558 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14559 * object into a block of Roo.data.Records.
14560 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14561 * The function must be passed <ul>
14562 * <li>The Record block object</li>
14563 * <li>The "arg" argument from the load function</li>
14564 * <li>A boolean success indicator</li>
14566 * @param {Object} scope The scope in which to call the callback
14567 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14569 load : function(params, reader, callback, scope, arg){
14570 params = params || {};
14573 result = reader.readRecords(params.data ? params.data :this.data);
14575 this.fireEvent("loadexception", this, arg, null, e);
14576 callback.call(scope, null, arg, false);
14579 callback.call(scope, result, arg, true);
14583 update : function(params, records){
14588 * Ext JS Library 1.1.1
14589 * Copyright(c) 2006-2007, Ext JS, LLC.
14591 * Originally Released Under LGPL - original licence link has changed is not relivant.
14594 * <script type="text/javascript">
14597 * @class Roo.data.HttpProxy
14598 * @extends Roo.data.DataProxy
14599 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14600 * configured to reference a certain URL.<br><br>
14602 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14603 * from which the running page was served.<br><br>
14605 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14607 * Be aware that to enable the browser to parse an XML document, the server must set
14608 * the Content-Type header in the HTTP response to "text/xml".
14610 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14611 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14612 * will be used to make the request.
14614 Roo.data.HttpProxy = function(conn){
14615 Roo.data.HttpProxy.superclass.constructor.call(this);
14616 // is conn a conn config or a real conn?
14618 this.useAjax = !conn || !conn.events;
14622 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14623 // thse are take from connection...
14626 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14629 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14630 * extra parameters to each request made by this object. (defaults to undefined)
14633 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14634 * to each request made by this object. (defaults to undefined)
14637 * @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)
14640 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14643 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14649 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14653 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14654 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14655 * a finer-grained basis than the DataProxy events.
14657 getConnection : function(){
14658 return this.useAjax ? Roo.Ajax : this.conn;
14662 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14663 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14664 * process that block using the passed callback.
14665 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14666 * for the request to the remote server.
14667 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14668 * object into a block of Roo.data.Records.
14669 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14670 * The function must be passed <ul>
14671 * <li>The Record block object</li>
14672 * <li>The "arg" argument from the load function</li>
14673 * <li>A boolean success indicator</li>
14675 * @param {Object} scope The scope in which to call the callback
14676 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14678 load : function(params, reader, callback, scope, arg){
14679 if(this.fireEvent("beforeload", this, params) !== false){
14681 params : params || {},
14683 callback : callback,
14688 callback : this.loadResponse,
14692 Roo.applyIf(o, this.conn);
14693 if(this.activeRequest){
14694 Roo.Ajax.abort(this.activeRequest);
14696 this.activeRequest = Roo.Ajax.request(o);
14698 this.conn.request(o);
14701 callback.call(scope||this, null, arg, false);
14706 loadResponse : function(o, success, response){
14707 delete this.activeRequest;
14709 this.fireEvent("loadexception", this, o, response);
14710 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14715 result = o.reader.read(response);
14717 this.fireEvent("loadexception", this, o, response, e);
14718 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14722 this.fireEvent("load", this, o, o.request.arg);
14723 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14727 update : function(dataSet){
14732 updateResponse : function(dataSet){
14737 * Ext JS Library 1.1.1
14738 * Copyright(c) 2006-2007, Ext JS, LLC.
14740 * Originally Released Under LGPL - original licence link has changed is not relivant.
14743 * <script type="text/javascript">
14747 * @class Roo.data.ScriptTagProxy
14748 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14749 * other than the originating domain of the running page.<br><br>
14751 * <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
14752 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14754 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14755 * source code that is used as the source inside a <script> tag.<br><br>
14757 * In order for the browser to process the returned data, the server must wrap the data object
14758 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14759 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14760 * depending on whether the callback name was passed:
14763 boolean scriptTag = false;
14764 String cb = request.getParameter("callback");
14767 response.setContentType("text/javascript");
14769 response.setContentType("application/x-json");
14771 Writer out = response.getWriter();
14773 out.write(cb + "(");
14775 out.print(dataBlock.toJsonString());
14782 * @param {Object} config A configuration object.
14784 Roo.data.ScriptTagProxy = function(config){
14785 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14786 Roo.apply(this, config);
14787 this.head = document.getElementsByTagName("head")[0];
14790 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14792 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14794 * @cfg {String} url The URL from which to request the data object.
14797 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14801 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14802 * the server the name of the callback function set up by the load call to process the returned data object.
14803 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14804 * javascript output which calls this named function passing the data object as its only parameter.
14806 callbackParam : "callback",
14808 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14809 * name to the request.
14814 * Load data from the configured URL, read the data object into
14815 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14816 * process that block using the passed callback.
14817 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14818 * for the request to the remote server.
14819 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14820 * object into a block of Roo.data.Records.
14821 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14822 * The function must be passed <ul>
14823 * <li>The Record block object</li>
14824 * <li>The "arg" argument from the load function</li>
14825 * <li>A boolean success indicator</li>
14827 * @param {Object} scope The scope in which to call the callback
14828 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14830 load : function(params, reader, callback, scope, arg){
14831 if(this.fireEvent("beforeload", this, params) !== false){
14833 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14835 var url = this.url;
14836 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14838 url += "&_dc=" + (new Date().getTime());
14840 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14843 cb : "stcCallback"+transId,
14844 scriptId : "stcScript"+transId,
14848 callback : callback,
14854 window[trans.cb] = function(o){
14855 conn.handleResponse(o, trans);
14858 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14860 if(this.autoAbort !== false){
14864 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14866 var script = document.createElement("script");
14867 script.setAttribute("src", url);
14868 script.setAttribute("type", "text/javascript");
14869 script.setAttribute("id", trans.scriptId);
14870 this.head.appendChild(script);
14872 this.trans = trans;
14874 callback.call(scope||this, null, arg, false);
14879 isLoading : function(){
14880 return this.trans ? true : false;
14884 * Abort the current server request.
14886 abort : function(){
14887 if(this.isLoading()){
14888 this.destroyTrans(this.trans);
14893 destroyTrans : function(trans, isLoaded){
14894 this.head.removeChild(document.getElementById(trans.scriptId));
14895 clearTimeout(trans.timeoutId);
14897 window[trans.cb] = undefined;
14899 delete window[trans.cb];
14902 // if hasn't been loaded, wait for load to remove it to prevent script error
14903 window[trans.cb] = function(){
14904 window[trans.cb] = undefined;
14906 delete window[trans.cb];
14913 handleResponse : function(o, trans){
14914 this.trans = false;
14915 this.destroyTrans(trans, true);
14918 result = trans.reader.readRecords(o);
14920 this.fireEvent("loadexception", this, o, trans.arg, e);
14921 trans.callback.call(trans.scope||window, null, trans.arg, false);
14924 this.fireEvent("load", this, o, trans.arg);
14925 trans.callback.call(trans.scope||window, result, trans.arg, true);
14929 handleFailure : function(trans){
14930 this.trans = false;
14931 this.destroyTrans(trans, false);
14932 this.fireEvent("loadexception", this, null, trans.arg);
14933 trans.callback.call(trans.scope||window, null, trans.arg, false);
14937 * Ext JS Library 1.1.1
14938 * Copyright(c) 2006-2007, Ext JS, LLC.
14940 * Originally Released Under LGPL - original licence link has changed is not relivant.
14943 * <script type="text/javascript">
14947 * @class Roo.data.JsonReader
14948 * @extends Roo.data.DataReader
14949 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14950 * based on mappings in a provided Roo.data.Record constructor.
14952 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14953 * in the reply previously.
14958 var RecordDef = Roo.data.Record.create([
14959 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14960 {name: 'occupation'} // This field will use "occupation" as the mapping.
14962 var myReader = new Roo.data.JsonReader({
14963 totalProperty: "results", // The property which contains the total dataset size (optional)
14964 root: "rows", // The property which contains an Array of row objects
14965 id: "id" // The property within each row object that provides an ID for the record (optional)
14969 * This would consume a JSON file like this:
14971 { 'results': 2, 'rows': [
14972 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14973 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14976 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14977 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14978 * paged from the remote server.
14979 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14980 * @cfg {String} root name of the property which contains the Array of row objects.
14981 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14982 * @cfg {Array} fields Array of field definition objects
14984 * Create a new JsonReader
14985 * @param {Object} meta Metadata configuration options
14986 * @param {Object} recordType Either an Array of field definition objects,
14987 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14989 Roo.data.JsonReader = function(meta, recordType){
14992 // set some defaults:
14993 Roo.applyIf(meta, {
14994 totalProperty: 'total',
14995 successProperty : 'success',
15000 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15002 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15004 readerType : 'Json',
15007 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15008 * Used by Store query builder to append _requestMeta to params.
15011 metaFromRemote : false,
15013 * This method is only used by a DataProxy which has retrieved data from a remote server.
15014 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15015 * @return {Object} data A data block which is used by an Roo.data.Store object as
15016 * a cache of Roo.data.Records.
15018 read : function(response){
15019 var json = response.responseText;
15021 var o = /* eval:var:o */ eval("("+json+")");
15023 throw {message: "JsonReader.read: Json object not found"};
15029 this.metaFromRemote = true;
15030 this.meta = o.metaData;
15031 this.recordType = Roo.data.Record.create(o.metaData.fields);
15032 this.onMetaChange(this.meta, this.recordType, o);
15034 return this.readRecords(o);
15037 // private function a store will implement
15038 onMetaChange : function(meta, recordType, o){
15045 simpleAccess: function(obj, subsc) {
15052 getJsonAccessor: function(){
15054 return function(expr) {
15056 return(re.test(expr))
15057 ? new Function("obj", "return obj." + expr)
15062 return Roo.emptyFn;
15067 * Create a data block containing Roo.data.Records from an XML document.
15068 * @param {Object} o An object which contains an Array of row objects in the property specified
15069 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15070 * which contains the total size of the dataset.
15071 * @return {Object} data A data block which is used by an Roo.data.Store object as
15072 * a cache of Roo.data.Records.
15074 readRecords : function(o){
15076 * After any data loads, the raw JSON data is available for further custom processing.
15080 var s = this.meta, Record = this.recordType,
15081 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15083 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15085 if(s.totalProperty) {
15086 this.getTotal = this.getJsonAccessor(s.totalProperty);
15088 if(s.successProperty) {
15089 this.getSuccess = this.getJsonAccessor(s.successProperty);
15091 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15093 var g = this.getJsonAccessor(s.id);
15094 this.getId = function(rec) {
15096 return (r === undefined || r === "") ? null : r;
15099 this.getId = function(){return null;};
15102 for(var jj = 0; jj < fl; jj++){
15104 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15105 this.ef[jj] = this.getJsonAccessor(map);
15109 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15110 if(s.totalProperty){
15111 var vt = parseInt(this.getTotal(o), 10);
15116 if(s.successProperty){
15117 var vs = this.getSuccess(o);
15118 if(vs === false || vs === 'false'){
15123 for(var i = 0; i < c; i++){
15126 var id = this.getId(n);
15127 for(var j = 0; j < fl; j++){
15129 var v = this.ef[j](n);
15131 Roo.log('missing convert for ' + f.name);
15135 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15137 var record = new Record(values, id);
15139 records[i] = record;
15145 totalRecords : totalRecords
15148 // used when loading children.. @see loadDataFromChildren
15149 toLoadData: function(rec)
15151 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15152 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15153 return { data : data, total : data.length };
15158 * Ext JS Library 1.1.1
15159 * Copyright(c) 2006-2007, Ext JS, LLC.
15161 * Originally Released Under LGPL - original licence link has changed is not relivant.
15164 * <script type="text/javascript">
15168 * @class Roo.data.ArrayReader
15169 * @extends Roo.data.DataReader
15170 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15171 * Each element of that Array represents a row of data fields. The
15172 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15173 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15177 var RecordDef = Roo.data.Record.create([
15178 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15179 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15181 var myReader = new Roo.data.ArrayReader({
15182 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15186 * This would consume an Array like this:
15188 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15192 * Create a new JsonReader
15193 * @param {Object} meta Metadata configuration options.
15194 * @param {Object|Array} recordType Either an Array of field definition objects
15196 * @cfg {Array} fields Array of field definition objects
15197 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15198 * as specified to {@link Roo.data.Record#create},
15199 * or an {@link Roo.data.Record} object
15202 * created using {@link Roo.data.Record#create}.
15204 Roo.data.ArrayReader = function(meta, recordType)
15206 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15209 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15212 * Create a data block containing Roo.data.Records from an XML document.
15213 * @param {Object} o An Array of row objects which represents the dataset.
15214 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15215 * a cache of Roo.data.Records.
15217 readRecords : function(o)
15219 var sid = this.meta ? this.meta.id : null;
15220 var recordType = this.recordType, fields = recordType.prototype.fields;
15223 for(var i = 0; i < root.length; i++){
15226 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15227 for(var j = 0, jlen = fields.length; j < jlen; j++){
15228 var f = fields.items[j];
15229 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15230 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15232 values[f.name] = v;
15234 var record = new recordType(values, id);
15236 records[records.length] = record;
15240 totalRecords : records.length
15243 // used when loading children.. @see loadDataFromChildren
15244 toLoadData: function(rec)
15246 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15247 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15258 * @class Roo.bootstrap.ComboBox
15259 * @extends Roo.bootstrap.TriggerField
15260 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15261 * @cfg {Boolean} append (true|false) default false
15262 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15263 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15264 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15265 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15266 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15267 * @cfg {Boolean} animate default true
15268 * @cfg {Boolean} emptyResultText only for touch device
15269 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15270 * @cfg {String} emptyTitle default ''
15271 * @cfg {Number} width fixed with? experimental
15273 * Create a new ComboBox.
15274 * @param {Object} config Configuration options
15276 Roo.bootstrap.ComboBox = function(config){
15277 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15281 * Fires when the dropdown list is expanded
15282 * @param {Roo.bootstrap.ComboBox} combo This combo box
15287 * Fires when the dropdown list is collapsed
15288 * @param {Roo.bootstrap.ComboBox} combo This combo box
15292 * @event beforeselect
15293 * Fires before a list item is selected. Return false to cancel the selection.
15294 * @param {Roo.bootstrap.ComboBox} combo This combo box
15295 * @param {Roo.data.Record} record The data record returned from the underlying store
15296 * @param {Number} index The index of the selected item in the dropdown list
15298 'beforeselect' : true,
15301 * Fires when a list item is selected
15302 * @param {Roo.bootstrap.ComboBox} combo This combo box
15303 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15304 * @param {Number} index The index of the selected item in the dropdown list
15308 * @event beforequery
15309 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15310 * The event object passed has these properties:
15311 * @param {Roo.bootstrap.ComboBox} combo This combo box
15312 * @param {String} query The query
15313 * @param {Boolean} forceAll true to force "all" query
15314 * @param {Boolean} cancel true to cancel the query
15315 * @param {Object} e The query event object
15317 'beforequery': true,
15320 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15321 * @param {Roo.bootstrap.ComboBox} combo This combo box
15326 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15327 * @param {Roo.bootstrap.ComboBox} combo This combo box
15328 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15333 * Fires when the remove value from the combobox array
15334 * @param {Roo.bootstrap.ComboBox} combo This combo box
15338 * @event afterremove
15339 * Fires when the remove value from the combobox array
15340 * @param {Roo.bootstrap.ComboBox} combo This combo box
15342 'afterremove' : true,
15344 * @event specialfilter
15345 * Fires when specialfilter
15346 * @param {Roo.bootstrap.ComboBox} combo This combo box
15348 'specialfilter' : true,
15351 * Fires when tick the element
15352 * @param {Roo.bootstrap.ComboBox} combo This combo box
15356 * @event touchviewdisplay
15357 * Fires when touch view require special display (default is using displayField)
15358 * @param {Roo.bootstrap.ComboBox} combo This combo box
15359 * @param {Object} cfg set html .
15361 'touchviewdisplay' : true
15366 this.tickItems = [];
15368 this.selectedIndex = -1;
15369 if(this.mode == 'local'){
15370 if(config.queryDelay === undefined){
15371 this.queryDelay = 10;
15373 if(config.minChars === undefined){
15379 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15382 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15383 * rendering into an Roo.Editor, defaults to false)
15386 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15387 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15390 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15393 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15394 * the dropdown list (defaults to undefined, with no header element)
15398 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15402 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15404 listWidth: undefined,
15406 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15407 * mode = 'remote' or 'text' if mode = 'local')
15409 displayField: undefined,
15412 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15413 * mode = 'remote' or 'value' if mode = 'local').
15414 * Note: use of a valueField requires the user make a selection
15415 * in order for a value to be mapped.
15417 valueField: undefined,
15419 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15424 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15425 * field's data value (defaults to the underlying DOM element's name)
15427 hiddenName: undefined,
15429 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15433 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15435 selectedClass: 'active',
15438 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15442 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15443 * anchor positions (defaults to 'tl-bl')
15445 listAlign: 'tl-bl?',
15447 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15451 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15452 * query specified by the allQuery config option (defaults to 'query')
15454 triggerAction: 'query',
15456 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15457 * (defaults to 4, does not apply if editable = false)
15461 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15462 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15466 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15467 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15471 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15472 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15476 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15477 * when editable = true (defaults to false)
15479 selectOnFocus:false,
15481 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15483 queryParam: 'query',
15485 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15486 * when mode = 'remote' (defaults to 'Loading...')
15488 loadingText: 'Loading...',
15490 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15494 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15498 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15499 * traditional select (defaults to true)
15503 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15507 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15511 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15512 * listWidth has a higher value)
15516 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15517 * allow the user to set arbitrary text into the field (defaults to false)
15519 forceSelection:false,
15521 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15522 * if typeAhead = true (defaults to 250)
15524 typeAheadDelay : 250,
15526 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15527 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15529 valueNotFoundText : undefined,
15531 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15533 blockFocus : false,
15536 * @cfg {Boolean} disableClear Disable showing of clear button.
15538 disableClear : false,
15540 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15542 alwaysQuery : false,
15545 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15550 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15552 invalidClass : "has-warning",
15555 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15557 validClass : "has-success",
15560 * @cfg {Boolean} specialFilter (true|false) special filter default false
15562 specialFilter : false,
15565 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15567 mobileTouchView : true,
15570 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15572 useNativeIOS : false,
15575 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15577 mobile_restrict_height : false,
15579 ios_options : false,
15591 btnPosition : 'right',
15592 triggerList : true,
15593 showToggleBtn : true,
15595 emptyResultText: 'Empty',
15596 triggerText : 'Select',
15600 // element that contains real text value.. (when hidden is used..)
15602 getAutoCreate : function()
15607 * Render classic select for iso
15610 if(Roo.isIOS && this.useNativeIOS){
15611 cfg = this.getAutoCreateNativeIOS();
15619 if(Roo.isTouch && this.mobileTouchView){
15620 cfg = this.getAutoCreateTouchView();
15627 if(!this.tickable){
15628 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15633 * ComboBox with tickable selections
15636 var align = this.labelAlign || this.parentLabelAlign();
15639 cls : 'form-group roo-combobox-tickable' //input-group
15642 var btn_text_select = '';
15643 var btn_text_done = '';
15644 var btn_text_cancel = '';
15646 if (this.btn_text_show) {
15647 btn_text_select = 'Select';
15648 btn_text_done = 'Done';
15649 btn_text_cancel = 'Cancel';
15654 cls : 'tickable-buttons',
15659 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15660 //html : this.triggerText
15661 html: btn_text_select
15667 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15669 html: btn_text_done
15675 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15677 html: btn_text_cancel
15683 buttons.cn.unshift({
15685 cls: 'roo-select2-search-field-input'
15691 Roo.each(buttons.cn, function(c){
15693 c.cls += ' btn-' + _this.size;
15696 if (_this.disabled) {
15703 style : 'display: contents',
15708 cls: 'form-hidden-field'
15712 cls: 'roo-select2-choices',
15716 cls: 'roo-select2-search-field',
15727 cls: 'roo-select2-container input-group roo-select2-container-multi',
15733 // cls: 'typeahead typeahead-long dropdown-menu',
15734 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15739 if(this.hasFeedback && !this.allowBlank){
15743 cls: 'glyphicon form-control-feedback'
15746 combobox.cn.push(feedback);
15753 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15754 tooltip : 'This field is required'
15756 if (Roo.bootstrap.version == 4) {
15759 style : 'display:none'
15762 if (align ==='left' && this.fieldLabel.length) {
15764 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15771 cls : 'control-label col-form-label',
15772 html : this.fieldLabel
15784 var labelCfg = cfg.cn[1];
15785 var contentCfg = cfg.cn[2];
15788 if(this.indicatorpos == 'right'){
15794 cls : 'control-label col-form-label',
15798 html : this.fieldLabel
15814 labelCfg = cfg.cn[0];
15815 contentCfg = cfg.cn[1];
15819 if(this.labelWidth > 12){
15820 labelCfg.style = "width: " + this.labelWidth + 'px';
15822 if(this.width * 1 > 0){
15823 contentCfg.style = "width: " + this.width + 'px';
15825 if(this.labelWidth < 13 && this.labelmd == 0){
15826 this.labelmd = this.labelWidth;
15829 if(this.labellg > 0){
15830 labelCfg.cls += ' col-lg-' + this.labellg;
15831 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15834 if(this.labelmd > 0){
15835 labelCfg.cls += ' col-md-' + this.labelmd;
15836 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15839 if(this.labelsm > 0){
15840 labelCfg.cls += ' col-sm-' + this.labelsm;
15841 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15844 if(this.labelxs > 0){
15845 labelCfg.cls += ' col-xs-' + this.labelxs;
15846 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15850 } else if ( this.fieldLabel.length) {
15851 // Roo.log(" label");
15856 //cls : 'input-group-addon',
15857 html : this.fieldLabel
15862 if(this.indicatorpos == 'right'){
15866 //cls : 'input-group-addon',
15867 html : this.fieldLabel
15877 // Roo.log(" no label && no align");
15884 ['xs','sm','md','lg'].map(function(size){
15885 if (settings[size]) {
15886 cfg.cls += ' col-' + size + '-' + settings[size];
15894 _initEventsCalled : false,
15897 initEvents: function()
15899 if (this._initEventsCalled) { // as we call render... prevent looping...
15902 this._initEventsCalled = true;
15905 throw "can not find store for combo";
15908 this.indicator = this.indicatorEl();
15910 this.store = Roo.factory(this.store, Roo.data);
15911 this.store.parent = this;
15913 // if we are building from html. then this element is so complex, that we can not really
15914 // use the rendered HTML.
15915 // so we have to trash and replace the previous code.
15916 if (Roo.XComponent.build_from_html) {
15917 // remove this element....
15918 var e = this.el.dom, k=0;
15919 while (e ) { e = e.previousSibling; ++k;}
15924 this.rendered = false;
15926 this.render(this.parent().getChildContainer(true), k);
15929 if(Roo.isIOS && this.useNativeIOS){
15930 this.initIOSView();
15938 if(Roo.isTouch && this.mobileTouchView){
15939 this.initTouchView();
15944 this.initTickableEvents();
15948 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15950 if(this.hiddenName){
15952 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15954 this.hiddenField.dom.value =
15955 this.hiddenValue !== undefined ? this.hiddenValue :
15956 this.value !== undefined ? this.value : '';
15958 // prevent input submission
15959 this.el.dom.removeAttribute('name');
15960 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15965 // this.el.dom.setAttribute('autocomplete', 'off');
15968 var cls = 'x-combo-list';
15970 //this.list = new Roo.Layer({
15971 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15977 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15978 _this.list.setWidth(lw);
15981 this.list.on('mouseover', this.onViewOver, this);
15982 this.list.on('mousemove', this.onViewMove, this);
15983 this.list.on('scroll', this.onViewScroll, this);
15986 this.list.swallowEvent('mousewheel');
15987 this.assetHeight = 0;
15990 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15991 this.assetHeight += this.header.getHeight();
15994 this.innerList = this.list.createChild({cls:cls+'-inner'});
15995 this.innerList.on('mouseover', this.onViewOver, this);
15996 this.innerList.on('mousemove', this.onViewMove, this);
15997 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15999 if(this.allowBlank && !this.pageSize && !this.disableClear){
16000 this.footer = this.list.createChild({cls:cls+'-ft'});
16001 this.pageTb = new Roo.Toolbar(this.footer);
16005 this.footer = this.list.createChild({cls:cls+'-ft'});
16006 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16007 {pageSize: this.pageSize});
16011 if (this.pageTb && this.allowBlank && !this.disableClear) {
16013 this.pageTb.add(new Roo.Toolbar.Fill(), {
16014 cls: 'x-btn-icon x-btn-clear',
16016 handler: function()
16019 _this.clearValue();
16020 _this.onSelect(false, -1);
16025 this.assetHeight += this.footer.getHeight();
16030 this.tpl = Roo.bootstrap.version == 4 ?
16031 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16032 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16035 this.view = new Roo.View(this.list, this.tpl, {
16036 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16038 //this.view.wrapEl.setDisplayed(false);
16039 this.view.on('click', this.onViewClick, this);
16042 this.store.on('beforeload', this.onBeforeLoad, this);
16043 this.store.on('load', this.onLoad, this);
16044 this.store.on('loadexception', this.onLoadException, this);
16046 if(this.resizable){
16047 this.resizer = new Roo.Resizable(this.list, {
16048 pinned:true, handles:'se'
16050 this.resizer.on('resize', function(r, w, h){
16051 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16052 this.listWidth = w;
16053 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16054 this.restrictHeight();
16056 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16059 if(!this.editable){
16060 this.editable = true;
16061 this.setEditable(false);
16066 if (typeof(this.events.add.listeners) != 'undefined') {
16068 this.addicon = this.wrap.createChild(
16069 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16071 this.addicon.on('click', function(e) {
16072 this.fireEvent('add', this);
16075 if (typeof(this.events.edit.listeners) != 'undefined') {
16077 this.editicon = this.wrap.createChild(
16078 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16079 if (this.addicon) {
16080 this.editicon.setStyle('margin-left', '40px');
16082 this.editicon.on('click', function(e) {
16084 // we fire even if inothing is selected..
16085 this.fireEvent('edit', this, this.lastData );
16091 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16092 "up" : function(e){
16093 this.inKeyMode = true;
16097 "down" : function(e){
16098 if(!this.isExpanded()){
16099 this.onTriggerClick();
16101 this.inKeyMode = true;
16106 "enter" : function(e){
16107 // this.onViewClick();
16111 if(this.fireEvent("specialkey", this, e)){
16112 this.onViewClick(false);
16118 "esc" : function(e){
16122 "tab" : function(e){
16125 if(this.fireEvent("specialkey", this, e)){
16126 this.onViewClick(false);
16134 doRelay : function(foo, bar, hname){
16135 if(hname == 'down' || this.scope.isExpanded()){
16136 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16145 this.queryDelay = Math.max(this.queryDelay || 10,
16146 this.mode == 'local' ? 10 : 250);
16149 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16151 if(this.typeAhead){
16152 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16154 if(this.editable !== false){
16155 this.inputEl().on("keyup", this.onKeyUp, this);
16157 if(this.forceSelection){
16158 this.inputEl().on('blur', this.doForce, this);
16162 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16163 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16167 initTickableEvents: function()
16171 if(this.hiddenName){
16173 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16175 this.hiddenField.dom.value =
16176 this.hiddenValue !== undefined ? this.hiddenValue :
16177 this.value !== undefined ? this.value : '';
16179 // prevent input submission
16180 this.el.dom.removeAttribute('name');
16181 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16186 // this.list = this.el.select('ul.dropdown-menu',true).first();
16188 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16189 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16190 if(this.triggerList){
16191 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16194 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16195 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16197 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16198 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16200 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16201 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16203 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16204 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16205 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16208 this.cancelBtn.hide();
16213 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16214 _this.list.setWidth(lw);
16217 this.list.on('mouseover', this.onViewOver, this);
16218 this.list.on('mousemove', this.onViewMove, this);
16220 this.list.on('scroll', this.onViewScroll, this);
16223 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16224 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16227 this.view = new Roo.View(this.list, this.tpl, {
16232 selectedClass: this.selectedClass
16235 //this.view.wrapEl.setDisplayed(false);
16236 this.view.on('click', this.onViewClick, this);
16240 this.store.on('beforeload', this.onBeforeLoad, this);
16241 this.store.on('load', this.onLoad, this);
16242 this.store.on('loadexception', this.onLoadException, this);
16245 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16246 "up" : function(e){
16247 this.inKeyMode = true;
16251 "down" : function(e){
16252 this.inKeyMode = true;
16256 "enter" : function(e){
16257 if(this.fireEvent("specialkey", this, e)){
16258 this.onViewClick(false);
16264 "esc" : function(e){
16265 this.onTickableFooterButtonClick(e, false, false);
16268 "tab" : function(e){
16269 this.fireEvent("specialkey", this, e);
16271 this.onTickableFooterButtonClick(e, false, false);
16278 doRelay : function(e, fn, key){
16279 if(this.scope.isExpanded()){
16280 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16289 this.queryDelay = Math.max(this.queryDelay || 10,
16290 this.mode == 'local' ? 10 : 250);
16293 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16295 if(this.typeAhead){
16296 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16299 if(this.editable !== false){
16300 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16303 this.indicator = this.indicatorEl();
16305 if(this.indicator){
16306 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16307 this.indicator.hide();
16312 onDestroy : function(){
16314 this.view.setStore(null);
16315 this.view.el.removeAllListeners();
16316 this.view.el.remove();
16317 this.view.purgeListeners();
16320 this.list.dom.innerHTML = '';
16324 this.store.un('beforeload', this.onBeforeLoad, this);
16325 this.store.un('load', this.onLoad, this);
16326 this.store.un('loadexception', this.onLoadException, this);
16328 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16332 fireKey : function(e){
16333 if(e.isNavKeyPress() && !this.list.isVisible()){
16334 this.fireEvent("specialkey", this, e);
16339 onResize: function(w, h)
16343 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16345 // if(typeof w != 'number'){
16346 // // we do not handle it!?!?
16349 // var tw = this.trigger.getWidth();
16350 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16351 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16353 // this.inputEl().setWidth( this.adjustWidth('input', x));
16355 // //this.trigger.setStyle('left', x+'px');
16357 // if(this.list && this.listWidth === undefined){
16358 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16359 // this.list.setWidth(lw);
16360 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16368 * Allow or prevent the user from directly editing the field text. If false is passed,
16369 * the user will only be able to select from the items defined in the dropdown list. This method
16370 * is the runtime equivalent of setting the 'editable' config option at config time.
16371 * @param {Boolean} value True to allow the user to directly edit the field text
16373 setEditable : function(value){
16374 if(value == this.editable){
16377 this.editable = value;
16379 this.inputEl().dom.setAttribute('readOnly', true);
16380 this.inputEl().on('mousedown', this.onTriggerClick, this);
16381 this.inputEl().addClass('x-combo-noedit');
16383 this.inputEl().dom.removeAttribute('readOnly');
16384 this.inputEl().un('mousedown', this.onTriggerClick, this);
16385 this.inputEl().removeClass('x-combo-noedit');
16391 onBeforeLoad : function(combo,opts){
16392 if(!this.hasFocus){
16396 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16398 this.restrictHeight();
16399 this.selectedIndex = -1;
16403 onLoad : function(){
16405 this.hasQuery = false;
16407 if(!this.hasFocus){
16411 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16412 this.loading.hide();
16415 if(this.store.getCount() > 0){
16418 this.restrictHeight();
16419 if(this.lastQuery == this.allQuery){
16420 if(this.editable && !this.tickable){
16421 this.inputEl().dom.select();
16425 !this.selectByValue(this.value, true) &&
16428 !this.store.lastOptions ||
16429 typeof(this.store.lastOptions.add) == 'undefined' ||
16430 this.store.lastOptions.add != true
16433 this.select(0, true);
16436 if(this.autoFocus){
16439 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16440 this.taTask.delay(this.typeAheadDelay);
16444 this.onEmptyResults();
16450 onLoadException : function()
16452 this.hasQuery = false;
16454 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16455 this.loading.hide();
16458 if(this.tickable && this.editable){
16463 // only causes errors at present
16464 //Roo.log(this.store.reader.jsonData);
16465 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16467 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16473 onTypeAhead : function(){
16474 if(this.store.getCount() > 0){
16475 var r = this.store.getAt(0);
16476 var newValue = r.data[this.displayField];
16477 var len = newValue.length;
16478 var selStart = this.getRawValue().length;
16480 if(selStart != len){
16481 this.setRawValue(newValue);
16482 this.selectText(selStart, newValue.length);
16488 onSelect : function(record, index){
16490 if(this.fireEvent('beforeselect', this, record, index) !== false){
16492 this.setFromData(index > -1 ? record.data : false);
16495 this.fireEvent('select', this, record, index);
16500 * Returns the currently selected field value or empty string if no value is set.
16501 * @return {String} value The selected value
16503 getValue : function()
16505 if(Roo.isIOS && this.useNativeIOS){
16506 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16510 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16513 if(this.valueField){
16514 return typeof this.value != 'undefined' ? this.value : '';
16516 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16520 getRawValue : function()
16522 if(Roo.isIOS && this.useNativeIOS){
16523 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16526 var v = this.inputEl().getValue();
16532 * Clears any text/value currently set in the field
16534 clearValue : function(){
16536 if(this.hiddenField){
16537 this.hiddenField.dom.value = '';
16540 this.setRawValue('');
16541 this.lastSelectionText = '';
16542 this.lastData = false;
16544 var close = this.closeTriggerEl();
16555 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16556 * will be displayed in the field. If the value does not match the data value of an existing item,
16557 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16558 * Otherwise the field will be blank (although the value will still be set).
16559 * @param {String} value The value to match
16561 setValue : function(v)
16563 if(Roo.isIOS && this.useNativeIOS){
16564 this.setIOSValue(v);
16574 if(this.valueField){
16575 var r = this.findRecord(this.valueField, v);
16577 text = r.data[this.displayField];
16578 }else if(this.valueNotFoundText !== undefined){
16579 text = this.valueNotFoundText;
16582 this.lastSelectionText = text;
16583 if(this.hiddenField){
16584 this.hiddenField.dom.value = v;
16586 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16589 var close = this.closeTriggerEl();
16592 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16598 * @property {Object} the last set data for the element
16603 * Sets the value of the field based on a object which is related to the record format for the store.
16604 * @param {Object} value the value to set as. or false on reset?
16606 setFromData : function(o){
16613 var dv = ''; // display value
16614 var vv = ''; // value value..
16616 if (this.displayField) {
16617 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16619 // this is an error condition!!!
16620 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16623 if(this.valueField){
16624 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16627 var close = this.closeTriggerEl();
16630 if(dv.length || vv * 1 > 0){
16632 this.blockFocus=true;
16638 if(this.hiddenField){
16639 this.hiddenField.dom.value = vv;
16641 this.lastSelectionText = dv;
16642 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16646 // no hidden field.. - we store the value in 'value', but still display
16647 // display field!!!!
16648 this.lastSelectionText = dv;
16649 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16656 reset : function(){
16657 // overridden so that last data is reset..
16664 this.setValue(this.originalValue);
16665 //this.clearInvalid();
16666 this.lastData = false;
16668 this.view.clearSelections();
16674 findRecord : function(prop, value){
16676 if(this.store.getCount() > 0){
16677 this.store.each(function(r){
16678 if(r.data[prop] == value){
16688 getName: function()
16690 // returns hidden if it's set..
16691 if (!this.rendered) {return ''};
16692 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16696 onViewMove : function(e, t){
16697 this.inKeyMode = false;
16701 onViewOver : function(e, t){
16702 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16705 var item = this.view.findItemFromChild(t);
16708 var index = this.view.indexOf(item);
16709 this.select(index, false);
16714 onViewClick : function(view, doFocus, el, e)
16716 var index = this.view.getSelectedIndexes()[0];
16718 var r = this.store.getAt(index);
16722 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16729 Roo.each(this.tickItems, function(v,k){
16731 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16733 _this.tickItems.splice(k, 1);
16735 if(typeof(e) == 'undefined' && view == false){
16736 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16748 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16749 this.tickItems.push(r.data);
16752 if(typeof(e) == 'undefined' && view == false){
16753 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16760 this.onSelect(r, index);
16762 if(doFocus !== false && !this.blockFocus){
16763 this.inputEl().focus();
16768 restrictHeight : function(){
16769 //this.innerList.dom.style.height = '';
16770 //var inner = this.innerList.dom;
16771 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16772 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16773 //this.list.beginUpdate();
16774 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16775 this.list.alignTo(this.inputEl(), this.listAlign);
16776 this.list.alignTo(this.inputEl(), this.listAlign);
16777 //this.list.endUpdate();
16781 onEmptyResults : function(){
16783 if(this.tickable && this.editable){
16784 this.hasFocus = false;
16785 this.restrictHeight();
16793 * Returns true if the dropdown list is expanded, else false.
16795 isExpanded : function(){
16796 return this.list.isVisible();
16800 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16801 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16802 * @param {String} value The data value of the item to select
16803 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16804 * selected item if it is not currently in view (defaults to true)
16805 * @return {Boolean} True if the value matched an item in the list, else false
16807 selectByValue : function(v, scrollIntoView){
16808 if(v !== undefined && v !== null){
16809 var r = this.findRecord(this.valueField || this.displayField, v);
16811 this.select(this.store.indexOf(r), scrollIntoView);
16819 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16820 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16821 * @param {Number} index The zero-based index of the list item to select
16822 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16823 * selected item if it is not currently in view (defaults to true)
16825 select : function(index, scrollIntoView){
16826 this.selectedIndex = index;
16827 this.view.select(index);
16828 if(scrollIntoView !== false){
16829 var el = this.view.getNode(index);
16831 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16834 this.list.scrollChildIntoView(el, false);
16840 selectNext : function(){
16841 var ct = this.store.getCount();
16843 if(this.selectedIndex == -1){
16845 }else if(this.selectedIndex < ct-1){
16846 this.select(this.selectedIndex+1);
16852 selectPrev : function(){
16853 var ct = this.store.getCount();
16855 if(this.selectedIndex == -1){
16857 }else if(this.selectedIndex != 0){
16858 this.select(this.selectedIndex-1);
16864 onKeyUp : function(e){
16865 if(this.editable !== false && !e.isSpecialKey()){
16866 this.lastKey = e.getKey();
16867 this.dqTask.delay(this.queryDelay);
16872 validateBlur : function(){
16873 return !this.list || !this.list.isVisible();
16877 initQuery : function(){
16879 var v = this.getRawValue();
16881 if(this.tickable && this.editable){
16882 v = this.tickableInputEl().getValue();
16889 doForce : function(){
16890 if(this.inputEl().dom.value.length > 0){
16891 this.inputEl().dom.value =
16892 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16898 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16899 * query allowing the query action to be canceled if needed.
16900 * @param {String} query The SQL query to execute
16901 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16902 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16903 * saved in the current store (defaults to false)
16905 doQuery : function(q, forceAll){
16907 if(q === undefined || q === null){
16912 forceAll: forceAll,
16916 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16921 forceAll = qe.forceAll;
16922 if(forceAll === true || (q.length >= this.minChars)){
16924 this.hasQuery = true;
16926 if(this.lastQuery != q || this.alwaysQuery){
16927 this.lastQuery = q;
16928 if(this.mode == 'local'){
16929 this.selectedIndex = -1;
16931 this.store.clearFilter();
16934 if(this.specialFilter){
16935 this.fireEvent('specialfilter', this);
16940 this.store.filter(this.displayField, q);
16943 this.store.fireEvent("datachanged", this.store);
16950 this.store.baseParams[this.queryParam] = q;
16952 var options = {params : this.getParams(q)};
16955 options.add = true;
16956 options.params.start = this.page * this.pageSize;
16959 this.store.load(options);
16962 * this code will make the page width larger, at the beginning, the list not align correctly,
16963 * we should expand the list on onLoad
16964 * so command out it
16969 this.selectedIndex = -1;
16974 this.loadNext = false;
16978 getParams : function(q){
16980 //p[this.queryParam] = q;
16984 p.limit = this.pageSize;
16990 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16992 collapse : function(){
16993 if(!this.isExpanded()){
16999 this.hasFocus = false;
17003 this.cancelBtn.hide();
17004 this.trigger.show();
17007 this.tickableInputEl().dom.value = '';
17008 this.tickableInputEl().blur();
17013 Roo.get(document).un('mousedown', this.collapseIf, this);
17014 Roo.get(document).un('mousewheel', this.collapseIf, this);
17015 if (!this.editable) {
17016 Roo.get(document).un('keydown', this.listKeyPress, this);
17018 this.fireEvent('collapse', this);
17024 collapseIf : function(e){
17025 var in_combo = e.within(this.el);
17026 var in_list = e.within(this.list);
17027 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17029 if (in_combo || in_list || is_list) {
17030 //e.stopPropagation();
17035 this.onTickableFooterButtonClick(e, false, false);
17043 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17045 expand : function(){
17047 if(this.isExpanded() || !this.hasFocus){
17051 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17052 this.list.setWidth(lw);
17058 this.restrictHeight();
17062 this.tickItems = Roo.apply([], this.item);
17065 this.cancelBtn.show();
17066 this.trigger.hide();
17069 this.tickableInputEl().focus();
17074 Roo.get(document).on('mousedown', this.collapseIf, this);
17075 Roo.get(document).on('mousewheel', this.collapseIf, this);
17076 if (!this.editable) {
17077 Roo.get(document).on('keydown', this.listKeyPress, this);
17080 this.fireEvent('expand', this);
17084 // Implements the default empty TriggerField.onTriggerClick function
17085 onTriggerClick : function(e)
17087 Roo.log('trigger click');
17089 if(this.disabled || !this.triggerList){
17094 this.loadNext = false;
17096 if(this.isExpanded()){
17098 if (!this.blockFocus) {
17099 this.inputEl().focus();
17103 this.hasFocus = true;
17104 if(this.triggerAction == 'all') {
17105 this.doQuery(this.allQuery, true);
17107 this.doQuery(this.getRawValue());
17109 if (!this.blockFocus) {
17110 this.inputEl().focus();
17115 onTickableTriggerClick : function(e)
17122 this.loadNext = false;
17123 this.hasFocus = true;
17125 if(this.triggerAction == 'all') {
17126 this.doQuery(this.allQuery, true);
17128 this.doQuery(this.getRawValue());
17132 onSearchFieldClick : function(e)
17134 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17135 this.onTickableFooterButtonClick(e, false, false);
17139 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17144 this.loadNext = false;
17145 this.hasFocus = true;
17147 if(this.triggerAction == 'all') {
17148 this.doQuery(this.allQuery, true);
17150 this.doQuery(this.getRawValue());
17154 listKeyPress : function(e)
17156 //Roo.log('listkeypress');
17157 // scroll to first matching element based on key pres..
17158 if (e.isSpecialKey()) {
17161 var k = String.fromCharCode(e.getKey()).toUpperCase();
17164 var csel = this.view.getSelectedNodes();
17165 var cselitem = false;
17167 var ix = this.view.indexOf(csel[0]);
17168 cselitem = this.store.getAt(ix);
17169 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17175 this.store.each(function(v) {
17177 // start at existing selection.
17178 if (cselitem.id == v.id) {
17184 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17185 match = this.store.indexOf(v);
17191 if (match === false) {
17192 return true; // no more action?
17195 this.view.select(match);
17196 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17197 sn.scrollIntoView(sn.dom.parentNode, false);
17200 onViewScroll : function(e, t){
17202 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){
17206 this.hasQuery = true;
17208 this.loading = this.list.select('.loading', true).first();
17210 if(this.loading === null){
17211 this.list.createChild({
17213 cls: 'loading roo-select2-more-results roo-select2-active',
17214 html: 'Loading more results...'
17217 this.loading = this.list.select('.loading', true).first();
17219 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17221 this.loading.hide();
17224 this.loading.show();
17229 this.loadNext = true;
17231 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17236 addItem : function(o)
17238 var dv = ''; // display value
17240 if (this.displayField) {
17241 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17243 // this is an error condition!!!
17244 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17251 var choice = this.choices.createChild({
17253 cls: 'roo-select2-search-choice',
17262 cls: 'roo-select2-search-choice-close fa fa-times',
17267 }, this.searchField);
17269 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17271 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17279 this.inputEl().dom.value = '';
17284 onRemoveItem : function(e, _self, o)
17286 e.preventDefault();
17288 this.lastItem = Roo.apply([], this.item);
17290 var index = this.item.indexOf(o.data) * 1;
17293 Roo.log('not this item?!');
17297 this.item.splice(index, 1);
17302 this.fireEvent('remove', this, e);
17308 syncValue : function()
17310 if(!this.item.length){
17317 Roo.each(this.item, function(i){
17318 if(_this.valueField){
17319 value.push(i[_this.valueField]);
17326 this.value = value.join(',');
17328 if(this.hiddenField){
17329 this.hiddenField.dom.value = this.value;
17332 this.store.fireEvent("datachanged", this.store);
17337 clearItem : function()
17339 if(!this.multiple){
17345 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17353 if(this.tickable && !Roo.isTouch){
17354 this.view.refresh();
17358 inputEl: function ()
17360 if(Roo.isIOS && this.useNativeIOS){
17361 return this.el.select('select.roo-ios-select', true).first();
17364 if(Roo.isTouch && this.mobileTouchView){
17365 return this.el.select('input.form-control',true).first();
17369 return this.searchField;
17372 return this.el.select('input.form-control',true).first();
17375 onTickableFooterButtonClick : function(e, btn, el)
17377 e.preventDefault();
17379 this.lastItem = Roo.apply([], this.item);
17381 if(btn && btn.name == 'cancel'){
17382 this.tickItems = Roo.apply([], this.item);
17391 Roo.each(this.tickItems, function(o){
17399 validate : function()
17401 if(this.getVisibilityEl().hasClass('hidden')){
17405 var v = this.getRawValue();
17408 v = this.getValue();
17411 if(this.disabled || this.allowBlank || v.length){
17416 this.markInvalid();
17420 tickableInputEl : function()
17422 if(!this.tickable || !this.editable){
17423 return this.inputEl();
17426 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17430 getAutoCreateTouchView : function()
17435 cls: 'form-group' //input-group
17441 type : this.inputType,
17442 cls : 'form-control x-combo-noedit',
17443 autocomplete: 'new-password',
17444 placeholder : this.placeholder || '',
17449 input.name = this.name;
17453 input.cls += ' input-' + this.size;
17456 if (this.disabled) {
17457 input.disabled = true;
17461 cls : 'roo-combobox-wrap',
17468 inputblock.cls += ' input-group';
17470 inputblock.cn.unshift({
17472 cls : 'input-group-addon input-group-prepend input-group-text',
17477 if(this.removable && !this.multiple){
17478 inputblock.cls += ' roo-removable';
17480 inputblock.cn.push({
17483 cls : 'roo-combo-removable-btn close'
17487 if(this.hasFeedback && !this.allowBlank){
17489 inputblock.cls += ' has-feedback';
17491 inputblock.cn.push({
17493 cls: 'glyphicon form-control-feedback'
17500 inputblock.cls += (this.before) ? '' : ' input-group';
17502 inputblock.cn.push({
17504 cls : 'input-group-addon input-group-append input-group-text',
17510 var ibwrap = inputblock;
17515 cls: 'roo-select2-choices',
17519 cls: 'roo-select2-search-field',
17532 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17537 cls: 'form-hidden-field'
17543 if(!this.multiple && this.showToggleBtn){
17549 if (this.caret != false) {
17552 cls: 'fa fa-' + this.caret
17559 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17561 Roo.bootstrap.version == 3 ? caret : '',
17564 cls: 'combobox-clear',
17578 combobox.cls += ' roo-select2-container-multi';
17581 var align = this.labelAlign || this.parentLabelAlign();
17583 if (align ==='left' && this.fieldLabel.length) {
17588 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17589 tooltip : 'This field is required'
17593 cls : 'control-label col-form-label',
17594 html : this.fieldLabel
17598 cls : 'roo-combobox-wrap ',
17605 var labelCfg = cfg.cn[1];
17606 var contentCfg = cfg.cn[2];
17609 if(this.indicatorpos == 'right'){
17614 cls : 'control-label col-form-label',
17618 html : this.fieldLabel
17622 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17623 tooltip : 'This field is required'
17628 cls : "roo-combobox-wrap ",
17636 labelCfg = cfg.cn[0];
17637 contentCfg = cfg.cn[1];
17642 if(this.labelWidth > 12){
17643 labelCfg.style = "width: " + this.labelWidth + 'px';
17646 if(this.labelWidth < 13 && this.labelmd == 0){
17647 this.labelmd = this.labelWidth;
17650 if(this.labellg > 0){
17651 labelCfg.cls += ' col-lg-' + this.labellg;
17652 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17655 if(this.labelmd > 0){
17656 labelCfg.cls += ' col-md-' + this.labelmd;
17657 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17660 if(this.labelsm > 0){
17661 labelCfg.cls += ' col-sm-' + this.labelsm;
17662 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17665 if(this.labelxs > 0){
17666 labelCfg.cls += ' col-xs-' + this.labelxs;
17667 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17671 } else if ( this.fieldLabel.length) {
17675 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17676 tooltip : 'This field is required'
17680 cls : 'control-label',
17681 html : this.fieldLabel
17692 if(this.indicatorpos == 'right'){
17696 cls : 'control-label',
17697 html : this.fieldLabel,
17701 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17702 tooltip : 'This field is required'
17719 var settings = this;
17721 ['xs','sm','md','lg'].map(function(size){
17722 if (settings[size]) {
17723 cfg.cls += ' col-' + size + '-' + settings[size];
17730 initTouchView : function()
17732 this.renderTouchView();
17734 this.touchViewEl.on('scroll', function(){
17735 this.el.dom.scrollTop = 0;
17738 this.originalValue = this.getValue();
17740 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17742 this.inputEl().on("click", this.showTouchView, this);
17743 if (this.triggerEl) {
17744 this.triggerEl.on("click", this.showTouchView, this);
17748 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17749 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17751 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17753 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17754 this.store.on('load', this.onTouchViewLoad, this);
17755 this.store.on('loadexception', this.onTouchViewLoadException, this);
17757 if(this.hiddenName){
17759 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17761 this.hiddenField.dom.value =
17762 this.hiddenValue !== undefined ? this.hiddenValue :
17763 this.value !== undefined ? this.value : '';
17765 this.el.dom.removeAttribute('name');
17766 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17770 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17771 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17774 if(this.removable && !this.multiple){
17775 var close = this.closeTriggerEl();
17777 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17778 close.on('click', this.removeBtnClick, this, close);
17782 * fix the bug in Safari iOS8
17784 this.inputEl().on("focus", function(e){
17785 document.activeElement.blur();
17788 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17795 renderTouchView : function()
17797 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17798 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17800 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17801 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17803 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17804 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17805 this.touchViewBodyEl.setStyle('overflow', 'auto');
17807 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17808 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17810 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17811 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17815 showTouchView : function()
17821 this.touchViewHeaderEl.hide();
17823 if(this.modalTitle.length){
17824 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17825 this.touchViewHeaderEl.show();
17828 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17829 this.touchViewEl.show();
17831 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17833 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17834 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17836 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17838 if(this.modalTitle.length){
17839 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17842 this.touchViewBodyEl.setHeight(bodyHeight);
17846 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17848 this.touchViewEl.addClass(['in','show']);
17851 if(this._touchViewMask){
17852 Roo.get(document.body).addClass("x-body-masked");
17853 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17854 this._touchViewMask.setStyle('z-index', 10000);
17855 this._touchViewMask.addClass('show');
17858 this.doTouchViewQuery();
17862 hideTouchView : function()
17864 this.touchViewEl.removeClass(['in','show']);
17868 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17870 this.touchViewEl.setStyle('display', 'none');
17873 if(this._touchViewMask){
17874 this._touchViewMask.removeClass('show');
17875 Roo.get(document.body).removeClass("x-body-masked");
17879 setTouchViewValue : function()
17886 Roo.each(this.tickItems, function(o){
17891 this.hideTouchView();
17894 doTouchViewQuery : function()
17903 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17907 if(!this.alwaysQuery || this.mode == 'local'){
17908 this.onTouchViewLoad();
17915 onTouchViewBeforeLoad : function(combo,opts)
17921 onTouchViewLoad : function()
17923 if(this.store.getCount() < 1){
17924 this.onTouchViewEmptyResults();
17928 this.clearTouchView();
17930 var rawValue = this.getRawValue();
17932 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17934 this.tickItems = [];
17936 this.store.data.each(function(d, rowIndex){
17937 var row = this.touchViewListGroup.createChild(template);
17939 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17940 row.addClass(d.data.cls);
17943 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17946 html : d.data[this.displayField]
17949 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17950 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17953 row.removeClass('selected');
17954 if(!this.multiple && this.valueField &&
17955 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17958 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17959 row.addClass('selected');
17962 if(this.multiple && this.valueField &&
17963 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17967 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17968 this.tickItems.push(d.data);
17971 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17975 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17977 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17979 if(this.modalTitle.length){
17980 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17983 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17985 if(this.mobile_restrict_height && listHeight < bodyHeight){
17986 this.touchViewBodyEl.setHeight(listHeight);
17991 if(firstChecked && listHeight > bodyHeight){
17992 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17997 onTouchViewLoadException : function()
17999 this.hideTouchView();
18002 onTouchViewEmptyResults : function()
18004 this.clearTouchView();
18006 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18008 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18012 clearTouchView : function()
18014 this.touchViewListGroup.dom.innerHTML = '';
18017 onTouchViewClick : function(e, el, o)
18019 e.preventDefault();
18022 var rowIndex = o.rowIndex;
18024 var r = this.store.getAt(rowIndex);
18026 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18028 if(!this.multiple){
18029 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18030 c.dom.removeAttribute('checked');
18033 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18035 this.setFromData(r.data);
18037 var close = this.closeTriggerEl();
18043 this.hideTouchView();
18045 this.fireEvent('select', this, r, rowIndex);
18050 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18051 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18052 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18056 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18057 this.addItem(r.data);
18058 this.tickItems.push(r.data);
18062 getAutoCreateNativeIOS : function()
18065 cls: 'form-group' //input-group,
18070 cls : 'roo-ios-select'
18074 combobox.name = this.name;
18077 if (this.disabled) {
18078 combobox.disabled = true;
18081 var settings = this;
18083 ['xs','sm','md','lg'].map(function(size){
18084 if (settings[size]) {
18085 cfg.cls += ' col-' + size + '-' + settings[size];
18095 initIOSView : function()
18097 this.store.on('load', this.onIOSViewLoad, this);
18102 onIOSViewLoad : function()
18104 if(this.store.getCount() < 1){
18108 this.clearIOSView();
18110 if(this.allowBlank) {
18112 var default_text = '-- SELECT --';
18114 if(this.placeholder.length){
18115 default_text = this.placeholder;
18118 if(this.emptyTitle.length){
18119 default_text += ' - ' + this.emptyTitle + ' -';
18122 var opt = this.inputEl().createChild({
18125 html : default_text
18129 o[this.valueField] = 0;
18130 o[this.displayField] = default_text;
18132 this.ios_options.push({
18139 this.store.data.each(function(d, rowIndex){
18143 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18144 html = d.data[this.displayField];
18149 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18150 value = d.data[this.valueField];
18159 if(this.value == d.data[this.valueField]){
18160 option['selected'] = true;
18163 var opt = this.inputEl().createChild(option);
18165 this.ios_options.push({
18172 this.inputEl().on('change', function(){
18173 this.fireEvent('select', this);
18178 clearIOSView: function()
18180 this.inputEl().dom.innerHTML = '';
18182 this.ios_options = [];
18185 setIOSValue: function(v)
18189 if(!this.ios_options){
18193 Roo.each(this.ios_options, function(opts){
18195 opts.el.dom.removeAttribute('selected');
18197 if(opts.data[this.valueField] != v){
18201 opts.el.dom.setAttribute('selected', true);
18207 * @cfg {Boolean} grow
18211 * @cfg {Number} growMin
18215 * @cfg {Number} growMax
18224 Roo.apply(Roo.bootstrap.ComboBox, {
18228 cls: 'modal-header',
18250 cls: 'list-group-item',
18254 cls: 'roo-combobox-list-group-item-value'
18258 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18272 listItemCheckbox : {
18274 cls: 'list-group-item',
18278 cls: 'roo-combobox-list-group-item-value'
18282 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18298 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18303 cls: 'modal-footer',
18311 cls: 'col-xs-6 text-left',
18314 cls: 'btn btn-danger roo-touch-view-cancel',
18320 cls: 'col-xs-6 text-right',
18323 cls: 'btn btn-success roo-touch-view-ok',
18334 Roo.apply(Roo.bootstrap.ComboBox, {
18336 touchViewTemplate : {
18338 cls: 'modal fade roo-combobox-touch-view',
18342 cls: 'modal-dialog',
18343 style : 'position:fixed', // we have to fix position....
18347 cls: 'modal-content',
18349 Roo.bootstrap.ComboBox.header,
18350 Roo.bootstrap.ComboBox.body,
18351 Roo.bootstrap.ComboBox.footer
18360 * Ext JS Library 1.1.1
18361 * Copyright(c) 2006-2007, Ext JS, LLC.
18363 * Originally Released Under LGPL - original licence link has changed is not relivant.
18366 * <script type="text/javascript">
18371 * @extends Roo.util.Observable
18372 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18373 * This class also supports single and multi selection modes. <br>
18374 * Create a data model bound view:
18376 var store = new Roo.data.Store(...);
18378 var view = new Roo.View({
18380 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18382 singleSelect: true,
18383 selectedClass: "ydataview-selected",
18387 // listen for node click?
18388 view.on("click", function(vw, index, node, e){
18389 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18393 dataModel.load("foobar.xml");
18395 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18397 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18398 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18400 * Note: old style constructor is still suported (container, template, config)
18403 * Create a new View
18404 * @param {Object} config The config object
18407 Roo.View = function(config, depreciated_tpl, depreciated_config){
18409 this.parent = false;
18411 if (typeof(depreciated_tpl) == 'undefined') {
18412 // new way.. - universal constructor.
18413 Roo.apply(this, config);
18414 this.el = Roo.get(this.el);
18417 this.el = Roo.get(config);
18418 this.tpl = depreciated_tpl;
18419 Roo.apply(this, depreciated_config);
18421 this.wrapEl = this.el.wrap().wrap();
18422 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18425 if(typeof(this.tpl) == "string"){
18426 this.tpl = new Roo.Template(this.tpl);
18428 // support xtype ctors..
18429 this.tpl = new Roo.factory(this.tpl, Roo);
18433 this.tpl.compile();
18438 * @event beforeclick
18439 * Fires before a click is processed. Returns false to cancel the default action.
18440 * @param {Roo.View} this
18441 * @param {Number} index The index of the target node
18442 * @param {HTMLElement} node The target node
18443 * @param {Roo.EventObject} e The raw event object
18445 "beforeclick" : true,
18448 * Fires when a template node is clicked.
18449 * @param {Roo.View} this
18450 * @param {Number} index The index of the target node
18451 * @param {HTMLElement} node The target node
18452 * @param {Roo.EventObject} e The raw event object
18457 * Fires when a template node is double clicked.
18458 * @param {Roo.View} this
18459 * @param {Number} index The index of the target node
18460 * @param {HTMLElement} node The target node
18461 * @param {Roo.EventObject} e The raw event object
18465 * @event contextmenu
18466 * Fires when a template node is right clicked.
18467 * @param {Roo.View} this
18468 * @param {Number} index The index of the target node
18469 * @param {HTMLElement} node The target node
18470 * @param {Roo.EventObject} e The raw event object
18472 "contextmenu" : true,
18474 * @event selectionchange
18475 * Fires when the selected nodes change.
18476 * @param {Roo.View} this
18477 * @param {Array} selections Array of the selected nodes
18479 "selectionchange" : true,
18482 * @event beforeselect
18483 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18484 * @param {Roo.View} this
18485 * @param {HTMLElement} node The node to be selected
18486 * @param {Array} selections Array of currently selected nodes
18488 "beforeselect" : true,
18490 * @event preparedata
18491 * Fires on every row to render, to allow you to change the data.
18492 * @param {Roo.View} this
18493 * @param {Object} data to be rendered (change this)
18495 "preparedata" : true
18503 "click": this.onClick,
18504 "dblclick": this.onDblClick,
18505 "contextmenu": this.onContextMenu,
18509 this.selections = [];
18511 this.cmp = new Roo.CompositeElementLite([]);
18513 this.store = Roo.factory(this.store, Roo.data);
18514 this.setStore(this.store, true);
18517 if ( this.footer && this.footer.xtype) {
18519 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18521 this.footer.dataSource = this.store;
18522 this.footer.container = fctr;
18523 this.footer = Roo.factory(this.footer, Roo);
18524 fctr.insertFirst(this.el);
18526 // this is a bit insane - as the paging toolbar seems to detach the el..
18527 // dom.parentNode.parentNode.parentNode
18528 // they get detached?
18532 Roo.View.superclass.constructor.call(this);
18537 Roo.extend(Roo.View, Roo.util.Observable, {
18540 * @cfg {Roo.data.Store} store Data store to load data from.
18545 * @cfg {String|Roo.Element} el The container element.
18550 * @cfg {String|Roo.Template} tpl The template used by this View
18554 * @cfg {String} dataName the named area of the template to use as the data area
18555 * Works with domtemplates roo-name="name"
18559 * @cfg {String} selectedClass The css class to add to selected nodes
18561 selectedClass : "x-view-selected",
18563 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18568 * @cfg {String} text to display on mask (default Loading)
18572 * @cfg {Boolean} multiSelect Allow multiple selection
18574 multiSelect : false,
18576 * @cfg {Boolean} singleSelect Allow single selection
18578 singleSelect: false,
18581 * @cfg {Boolean} toggleSelect - selecting
18583 toggleSelect : false,
18586 * @cfg {Boolean} tickable - selecting
18591 * Returns the element this view is bound to.
18592 * @return {Roo.Element}
18594 getEl : function(){
18595 return this.wrapEl;
18601 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18603 refresh : function(){
18604 //Roo.log('refresh');
18607 // if we are using something like 'domtemplate', then
18608 // the what gets used is:
18609 // t.applySubtemplate(NAME, data, wrapping data..)
18610 // the outer template then get' applied with
18611 // the store 'extra data'
18612 // and the body get's added to the
18613 // roo-name="data" node?
18614 // <span class='roo-tpl-{name}'></span> ?????
18618 this.clearSelections();
18619 this.el.update("");
18621 var records = this.store.getRange();
18622 if(records.length < 1) {
18624 // is this valid?? = should it render a template??
18626 this.el.update(this.emptyText);
18630 if (this.dataName) {
18631 this.el.update(t.apply(this.store.meta)); //????
18632 el = this.el.child('.roo-tpl-' + this.dataName);
18635 for(var i = 0, len = records.length; i < len; i++){
18636 var data = this.prepareData(records[i].data, i, records[i]);
18637 this.fireEvent("preparedata", this, data, i, records[i]);
18639 var d = Roo.apply({}, data);
18642 Roo.apply(d, {'roo-id' : Roo.id()});
18646 Roo.each(this.parent.item, function(item){
18647 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18650 Roo.apply(d, {'roo-data-checked' : 'checked'});
18654 html[html.length] = Roo.util.Format.trim(
18656 t.applySubtemplate(this.dataName, d, this.store.meta) :
18663 el.update(html.join(""));
18664 this.nodes = el.dom.childNodes;
18665 this.updateIndexes(0);
18670 * Function to override to reformat the data that is sent to
18671 * the template for each node.
18672 * DEPRICATED - use the preparedata event handler.
18673 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18674 * a JSON object for an UpdateManager bound view).
18676 prepareData : function(data, index, record)
18678 this.fireEvent("preparedata", this, data, index, record);
18682 onUpdate : function(ds, record){
18683 // Roo.log('on update');
18684 this.clearSelections();
18685 var index = this.store.indexOf(record);
18686 var n = this.nodes[index];
18687 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18688 n.parentNode.removeChild(n);
18689 this.updateIndexes(index, index);
18695 onAdd : function(ds, records, index)
18697 //Roo.log(['on Add', ds, records, index] );
18698 this.clearSelections();
18699 if(this.nodes.length == 0){
18703 var n = this.nodes[index];
18704 for(var i = 0, len = records.length; i < len; i++){
18705 var d = this.prepareData(records[i].data, i, records[i]);
18707 this.tpl.insertBefore(n, d);
18710 this.tpl.append(this.el, d);
18713 this.updateIndexes(index);
18716 onRemove : function(ds, record, index){
18717 // Roo.log('onRemove');
18718 this.clearSelections();
18719 var el = this.dataName ?
18720 this.el.child('.roo-tpl-' + this.dataName) :
18723 el.dom.removeChild(this.nodes[index]);
18724 this.updateIndexes(index);
18728 * Refresh an individual node.
18729 * @param {Number} index
18731 refreshNode : function(index){
18732 this.onUpdate(this.store, this.store.getAt(index));
18735 updateIndexes : function(startIndex, endIndex){
18736 var ns = this.nodes;
18737 startIndex = startIndex || 0;
18738 endIndex = endIndex || ns.length - 1;
18739 for(var i = startIndex; i <= endIndex; i++){
18740 ns[i].nodeIndex = i;
18745 * Changes the data store this view uses and refresh the view.
18746 * @param {Store} store
18748 setStore : function(store, initial){
18749 if(!initial && this.store){
18750 this.store.un("datachanged", this.refresh);
18751 this.store.un("add", this.onAdd);
18752 this.store.un("remove", this.onRemove);
18753 this.store.un("update", this.onUpdate);
18754 this.store.un("clear", this.refresh);
18755 this.store.un("beforeload", this.onBeforeLoad);
18756 this.store.un("load", this.onLoad);
18757 this.store.un("loadexception", this.onLoad);
18761 store.on("datachanged", this.refresh, this);
18762 store.on("add", this.onAdd, this);
18763 store.on("remove", this.onRemove, this);
18764 store.on("update", this.onUpdate, this);
18765 store.on("clear", this.refresh, this);
18766 store.on("beforeload", this.onBeforeLoad, this);
18767 store.on("load", this.onLoad, this);
18768 store.on("loadexception", this.onLoad, this);
18776 * onbeforeLoad - masks the loading area.
18779 onBeforeLoad : function(store,opts)
18781 //Roo.log('onBeforeLoad');
18783 this.el.update("");
18785 this.el.mask(this.mask ? this.mask : "Loading" );
18787 onLoad : function ()
18794 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18795 * @param {HTMLElement} node
18796 * @return {HTMLElement} The template node
18798 findItemFromChild : function(node){
18799 var el = this.dataName ?
18800 this.el.child('.roo-tpl-' + this.dataName,true) :
18803 if(!node || node.parentNode == el){
18806 var p = node.parentNode;
18807 while(p && p != el){
18808 if(p.parentNode == el){
18817 onClick : function(e){
18818 var item = this.findItemFromChild(e.getTarget());
18820 var index = this.indexOf(item);
18821 if(this.onItemClick(item, index, e) !== false){
18822 this.fireEvent("click", this, index, item, e);
18825 this.clearSelections();
18830 onContextMenu : function(e){
18831 var item = this.findItemFromChild(e.getTarget());
18833 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18838 onDblClick : function(e){
18839 var item = this.findItemFromChild(e.getTarget());
18841 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18845 onItemClick : function(item, index, e)
18847 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18850 if (this.toggleSelect) {
18851 var m = this.isSelected(item) ? 'unselect' : 'select';
18854 _t[m](item, true, false);
18857 if(this.multiSelect || this.singleSelect){
18858 if(this.multiSelect && e.shiftKey && this.lastSelection){
18859 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18861 this.select(item, this.multiSelect && e.ctrlKey);
18862 this.lastSelection = item;
18865 if(!this.tickable){
18866 e.preventDefault();
18874 * Get the number of selected nodes.
18877 getSelectionCount : function(){
18878 return this.selections.length;
18882 * Get the currently selected nodes.
18883 * @return {Array} An array of HTMLElements
18885 getSelectedNodes : function(){
18886 return this.selections;
18890 * Get the indexes of the selected nodes.
18893 getSelectedIndexes : function(){
18894 var indexes = [], s = this.selections;
18895 for(var i = 0, len = s.length; i < len; i++){
18896 indexes.push(s[i].nodeIndex);
18902 * Clear all selections
18903 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18905 clearSelections : function(suppressEvent){
18906 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18907 this.cmp.elements = this.selections;
18908 this.cmp.removeClass(this.selectedClass);
18909 this.selections = [];
18910 if(!suppressEvent){
18911 this.fireEvent("selectionchange", this, this.selections);
18917 * Returns true if the passed node is selected
18918 * @param {HTMLElement/Number} node The node or node index
18919 * @return {Boolean}
18921 isSelected : function(node){
18922 var s = this.selections;
18926 node = this.getNode(node);
18927 return s.indexOf(node) !== -1;
18932 * @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
18933 * @param {Boolean} keepExisting (optional) true to keep existing selections
18934 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18936 select : function(nodeInfo, keepExisting, suppressEvent){
18937 if(nodeInfo instanceof Array){
18939 this.clearSelections(true);
18941 for(var i = 0, len = nodeInfo.length; i < len; i++){
18942 this.select(nodeInfo[i], true, true);
18946 var node = this.getNode(nodeInfo);
18947 if(!node || this.isSelected(node)){
18948 return; // already selected.
18951 this.clearSelections(true);
18954 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18955 Roo.fly(node).addClass(this.selectedClass);
18956 this.selections.push(node);
18957 if(!suppressEvent){
18958 this.fireEvent("selectionchange", this, this.selections);
18966 * @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
18967 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18968 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18970 unselect : function(nodeInfo, keepExisting, suppressEvent)
18972 if(nodeInfo instanceof Array){
18973 Roo.each(this.selections, function(s) {
18974 this.unselect(s, nodeInfo);
18978 var node = this.getNode(nodeInfo);
18979 if(!node || !this.isSelected(node)){
18980 //Roo.log("not selected");
18981 return; // not selected.
18985 Roo.each(this.selections, function(s) {
18987 Roo.fly(node).removeClass(this.selectedClass);
18994 this.selections= ns;
18995 this.fireEvent("selectionchange", this, this.selections);
18999 * Gets a template node.
19000 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19001 * @return {HTMLElement} The node or null if it wasn't found
19003 getNode : function(nodeInfo){
19004 if(typeof nodeInfo == "string"){
19005 return document.getElementById(nodeInfo);
19006 }else if(typeof nodeInfo == "number"){
19007 return this.nodes[nodeInfo];
19013 * Gets a range template nodes.
19014 * @param {Number} startIndex
19015 * @param {Number} endIndex
19016 * @return {Array} An array of nodes
19018 getNodes : function(start, end){
19019 var ns = this.nodes;
19020 start = start || 0;
19021 end = typeof end == "undefined" ? ns.length - 1 : end;
19024 for(var i = start; i <= end; i++){
19028 for(var i = start; i >= end; i--){
19036 * Finds the index of the passed node
19037 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19038 * @return {Number} The index of the node or -1
19040 indexOf : function(node){
19041 node = this.getNode(node);
19042 if(typeof node.nodeIndex == "number"){
19043 return node.nodeIndex;
19045 var ns = this.nodes;
19046 for(var i = 0, len = ns.length; i < len; i++){
19057 * based on jquery fullcalendar
19061 Roo.bootstrap = Roo.bootstrap || {};
19063 * @class Roo.bootstrap.Calendar
19064 * @extends Roo.bootstrap.Component
19065 * Bootstrap Calendar class
19066 * @cfg {Boolean} loadMask (true|false) default false
19067 * @cfg {Object} header generate the user specific header of the calendar, default false
19070 * Create a new Container
19071 * @param {Object} config The config object
19076 Roo.bootstrap.Calendar = function(config){
19077 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19081 * Fires when a date is selected
19082 * @param {DatePicker} this
19083 * @param {Date} date The selected date
19087 * @event monthchange
19088 * Fires when the displayed month changes
19089 * @param {DatePicker} this
19090 * @param {Date} date The selected month
19092 'monthchange': true,
19094 * @event evententer
19095 * Fires when mouse over an event
19096 * @param {Calendar} this
19097 * @param {event} Event
19099 'evententer': true,
19101 * @event eventleave
19102 * Fires when the mouse leaves an
19103 * @param {Calendar} this
19106 'eventleave': true,
19108 * @event eventclick
19109 * Fires when the mouse click an
19110 * @param {Calendar} this
19119 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19122 * @cfg {Number} startDay
19123 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19131 getAutoCreate : function(){
19134 var fc_button = function(name, corner, style, content ) {
19135 return Roo.apply({},{
19137 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19139 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19142 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19153 style : 'width:100%',
19160 cls : 'fc-header-left',
19162 fc_button('prev', 'left', 'arrow', '‹' ),
19163 fc_button('next', 'right', 'arrow', '›' ),
19164 { tag: 'span', cls: 'fc-header-space' },
19165 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19173 cls : 'fc-header-center',
19177 cls: 'fc-header-title',
19180 html : 'month / year'
19188 cls : 'fc-header-right',
19190 /* fc_button('month', 'left', '', 'month' ),
19191 fc_button('week', '', '', 'week' ),
19192 fc_button('day', 'right', '', 'day' )
19204 header = this.header;
19207 var cal_heads = function() {
19209 // fixme - handle this.
19211 for (var i =0; i < Date.dayNames.length; i++) {
19212 var d = Date.dayNames[i];
19215 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19216 html : d.substring(0,3)
19220 ret[0].cls += ' fc-first';
19221 ret[6].cls += ' fc-last';
19224 var cal_cell = function(n) {
19227 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19232 cls: 'fc-day-number',
19236 cls: 'fc-day-content',
19240 style: 'position: relative;' // height: 17px;
19252 var cal_rows = function() {
19255 for (var r = 0; r < 6; r++) {
19262 for (var i =0; i < Date.dayNames.length; i++) {
19263 var d = Date.dayNames[i];
19264 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19267 row.cn[0].cls+=' fc-first';
19268 row.cn[0].cn[0].style = 'min-height:90px';
19269 row.cn[6].cls+=' fc-last';
19273 ret[0].cls += ' fc-first';
19274 ret[4].cls += ' fc-prev-last';
19275 ret[5].cls += ' fc-last';
19282 cls: 'fc-border-separate',
19283 style : 'width:100%',
19291 cls : 'fc-first fc-last',
19309 cls : 'fc-content',
19310 style : "position: relative;",
19313 cls : 'fc-view fc-view-month fc-grid',
19314 style : 'position: relative',
19315 unselectable : 'on',
19318 cls : 'fc-event-container',
19319 style : 'position:absolute;z-index:8;top:0;left:0;'
19337 initEvents : function()
19340 throw "can not find store for calendar";
19346 style: "text-align:center",
19350 style: "background-color:white;width:50%;margin:250 auto",
19354 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19365 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19367 var size = this.el.select('.fc-content', true).first().getSize();
19368 this.maskEl.setSize(size.width, size.height);
19369 this.maskEl.enableDisplayMode("block");
19370 if(!this.loadMask){
19371 this.maskEl.hide();
19374 this.store = Roo.factory(this.store, Roo.data);
19375 this.store.on('load', this.onLoad, this);
19376 this.store.on('beforeload', this.onBeforeLoad, this);
19380 this.cells = this.el.select('.fc-day',true);
19381 //Roo.log(this.cells);
19382 this.textNodes = this.el.query('.fc-day-number');
19383 this.cells.addClassOnOver('fc-state-hover');
19385 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19386 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19387 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19388 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19390 this.on('monthchange', this.onMonthChange, this);
19392 this.update(new Date().clearTime());
19395 resize : function() {
19396 var sz = this.el.getSize();
19398 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19399 this.el.select('.fc-day-content div',true).setHeight(34);
19404 showPrevMonth : function(e){
19405 this.update(this.activeDate.add("mo", -1));
19407 showToday : function(e){
19408 this.update(new Date().clearTime());
19411 showNextMonth : function(e){
19412 this.update(this.activeDate.add("mo", 1));
19416 showPrevYear : function(){
19417 this.update(this.activeDate.add("y", -1));
19421 showNextYear : function(){
19422 this.update(this.activeDate.add("y", 1));
19427 update : function(date)
19429 var vd = this.activeDate;
19430 this.activeDate = date;
19431 // if(vd && this.el){
19432 // var t = date.getTime();
19433 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19434 // Roo.log('using add remove');
19436 // this.fireEvent('monthchange', this, date);
19438 // this.cells.removeClass("fc-state-highlight");
19439 // this.cells.each(function(c){
19440 // if(c.dateValue == t){
19441 // c.addClass("fc-state-highlight");
19442 // setTimeout(function(){
19443 // try{c.dom.firstChild.focus();}catch(e){}
19453 var days = date.getDaysInMonth();
19455 var firstOfMonth = date.getFirstDateOfMonth();
19456 var startingPos = firstOfMonth.getDay()-this.startDay;
19458 if(startingPos < this.startDay){
19462 var pm = date.add(Date.MONTH, -1);
19463 var prevStart = pm.getDaysInMonth()-startingPos;
19465 this.cells = this.el.select('.fc-day',true);
19466 this.textNodes = this.el.query('.fc-day-number');
19467 this.cells.addClassOnOver('fc-state-hover');
19469 var cells = this.cells.elements;
19470 var textEls = this.textNodes;
19472 Roo.each(cells, function(cell){
19473 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19476 days += startingPos;
19478 // convert everything to numbers so it's fast
19479 var day = 86400000;
19480 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19483 //Roo.log(prevStart);
19485 var today = new Date().clearTime().getTime();
19486 var sel = date.clearTime().getTime();
19487 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19488 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19489 var ddMatch = this.disabledDatesRE;
19490 var ddText = this.disabledDatesText;
19491 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19492 var ddaysText = this.disabledDaysText;
19493 var format = this.format;
19495 var setCellClass = function(cal, cell){
19499 //Roo.log('set Cell Class');
19501 var t = d.getTime();
19505 cell.dateValue = t;
19507 cell.className += " fc-today";
19508 cell.className += " fc-state-highlight";
19509 cell.title = cal.todayText;
19512 // disable highlight in other month..
19513 //cell.className += " fc-state-highlight";
19518 cell.className = " fc-state-disabled";
19519 cell.title = cal.minText;
19523 cell.className = " fc-state-disabled";
19524 cell.title = cal.maxText;
19528 if(ddays.indexOf(d.getDay()) != -1){
19529 cell.title = ddaysText;
19530 cell.className = " fc-state-disabled";
19533 if(ddMatch && format){
19534 var fvalue = d.dateFormat(format);
19535 if(ddMatch.test(fvalue)){
19536 cell.title = ddText.replace("%0", fvalue);
19537 cell.className = " fc-state-disabled";
19541 if (!cell.initialClassName) {
19542 cell.initialClassName = cell.dom.className;
19545 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19550 for(; i < startingPos; i++) {
19551 textEls[i].innerHTML = (++prevStart);
19552 d.setDate(d.getDate()+1);
19554 cells[i].className = "fc-past fc-other-month";
19555 setCellClass(this, cells[i]);
19560 for(; i < days; i++){
19561 intDay = i - startingPos + 1;
19562 textEls[i].innerHTML = (intDay);
19563 d.setDate(d.getDate()+1);
19565 cells[i].className = ''; // "x-date-active";
19566 setCellClass(this, cells[i]);
19570 for(; i < 42; i++) {
19571 textEls[i].innerHTML = (++extraDays);
19572 d.setDate(d.getDate()+1);
19574 cells[i].className = "fc-future fc-other-month";
19575 setCellClass(this, cells[i]);
19578 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19580 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19582 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19583 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19585 if(totalRows != 6){
19586 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19587 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19590 this.fireEvent('monthchange', this, date);
19594 if(!this.internalRender){
19595 var main = this.el.dom.firstChild;
19596 var w = main.offsetWidth;
19597 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19598 Roo.fly(main).setWidth(w);
19599 this.internalRender = true;
19600 // opera does not respect the auto grow header center column
19601 // then, after it gets a width opera refuses to recalculate
19602 // without a second pass
19603 if(Roo.isOpera && !this.secondPass){
19604 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19605 this.secondPass = true;
19606 this.update.defer(10, this, [date]);
19613 findCell : function(dt) {
19614 dt = dt.clearTime().getTime();
19616 this.cells.each(function(c){
19617 //Roo.log("check " +c.dateValue + '?=' + dt);
19618 if(c.dateValue == dt){
19628 findCells : function(ev) {
19629 var s = ev.start.clone().clearTime().getTime();
19631 var e= ev.end.clone().clearTime().getTime();
19634 this.cells.each(function(c){
19635 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19637 if(c.dateValue > e){
19640 if(c.dateValue < s){
19649 // findBestRow: function(cells)
19653 // for (var i =0 ; i < cells.length;i++) {
19654 // ret = Math.max(cells[i].rows || 0,ret);
19661 addItem : function(ev)
19663 // look for vertical location slot in
19664 var cells = this.findCells(ev);
19666 // ev.row = this.findBestRow(cells);
19668 // work out the location.
19672 for(var i =0; i < cells.length; i++) {
19674 cells[i].row = cells[0].row;
19677 cells[i].row = cells[i].row + 1;
19687 if (crow.start.getY() == cells[i].getY()) {
19689 crow.end = cells[i];
19706 cells[0].events.push(ev);
19708 this.calevents.push(ev);
19711 clearEvents: function() {
19713 if(!this.calevents){
19717 Roo.each(this.cells.elements, function(c){
19723 Roo.each(this.calevents, function(e) {
19724 Roo.each(e.els, function(el) {
19725 el.un('mouseenter' ,this.onEventEnter, this);
19726 el.un('mouseleave' ,this.onEventLeave, this);
19731 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19737 renderEvents: function()
19741 this.cells.each(function(c) {
19750 if(c.row != c.events.length){
19751 r = 4 - (4 - (c.row - c.events.length));
19754 c.events = ev.slice(0, r);
19755 c.more = ev.slice(r);
19757 if(c.more.length && c.more.length == 1){
19758 c.events.push(c.more.pop());
19761 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19765 this.cells.each(function(c) {
19767 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19770 for (var e = 0; e < c.events.length; e++){
19771 var ev = c.events[e];
19772 var rows = ev.rows;
19774 for(var i = 0; i < rows.length; i++) {
19776 // how many rows should it span..
19779 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19780 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19782 unselectable : "on",
19785 cls: 'fc-event-inner',
19789 // cls: 'fc-event-time',
19790 // html : cells.length > 1 ? '' : ev.time
19794 cls: 'fc-event-title',
19795 html : String.format('{0}', ev.title)
19802 cls: 'ui-resizable-handle ui-resizable-e',
19803 html : '  '
19810 cfg.cls += ' fc-event-start';
19812 if ((i+1) == rows.length) {
19813 cfg.cls += ' fc-event-end';
19816 var ctr = _this.el.select('.fc-event-container',true).first();
19817 var cg = ctr.createChild(cfg);
19819 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19820 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19822 var r = (c.more.length) ? 1 : 0;
19823 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19824 cg.setWidth(ebox.right - sbox.x -2);
19826 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19827 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19828 cg.on('click', _this.onEventClick, _this, ev);
19839 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19840 style : 'position: absolute',
19841 unselectable : "on",
19844 cls: 'fc-event-inner',
19848 cls: 'fc-event-title',
19856 cls: 'ui-resizable-handle ui-resizable-e',
19857 html : '  '
19863 var ctr = _this.el.select('.fc-event-container',true).first();
19864 var cg = ctr.createChild(cfg);
19866 var sbox = c.select('.fc-day-content',true).first().getBox();
19867 var ebox = c.select('.fc-day-content',true).first().getBox();
19869 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19870 cg.setWidth(ebox.right - sbox.x -2);
19872 cg.on('click', _this.onMoreEventClick, _this, c.more);
19882 onEventEnter: function (e, el,event,d) {
19883 this.fireEvent('evententer', this, el, event);
19886 onEventLeave: function (e, el,event,d) {
19887 this.fireEvent('eventleave', this, el, event);
19890 onEventClick: function (e, el,event,d) {
19891 this.fireEvent('eventclick', this, el, event);
19894 onMonthChange: function () {
19898 onMoreEventClick: function(e, el, more)
19902 this.calpopover.placement = 'right';
19903 this.calpopover.setTitle('More');
19905 this.calpopover.setContent('');
19907 var ctr = this.calpopover.el.select('.popover-content', true).first();
19909 Roo.each(more, function(m){
19911 cls : 'fc-event-hori fc-event-draggable',
19914 var cg = ctr.createChild(cfg);
19916 cg.on('click', _this.onEventClick, _this, m);
19919 this.calpopover.show(el);
19924 onLoad: function ()
19926 this.calevents = [];
19929 if(this.store.getCount() > 0){
19930 this.store.data.each(function(d){
19933 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19934 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19935 time : d.data.start_time,
19936 title : d.data.title,
19937 description : d.data.description,
19938 venue : d.data.venue
19943 this.renderEvents();
19945 if(this.calevents.length && this.loadMask){
19946 this.maskEl.hide();
19950 onBeforeLoad: function()
19952 this.clearEvents();
19954 this.maskEl.show();
19968 * @class Roo.bootstrap.Popover
19969 * @extends Roo.bootstrap.Component
19970 * Bootstrap Popover class
19971 * @cfg {String} html contents of the popover (or false to use children..)
19972 * @cfg {String} title of popover (or false to hide)
19973 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19974 * @cfg {String} trigger click || hover (or false to trigger manually)
19975 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19976 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19977 * - if false and it has a 'parent' then it will be automatically added to that element
19978 * - if string - Roo.get will be called
19979 * @cfg {Number} delay - delay before showing
19982 * Create a new Popover
19983 * @param {Object} config The config object
19986 Roo.bootstrap.Popover = function(config){
19987 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19993 * After the popover show
19995 * @param {Roo.bootstrap.Popover} this
20000 * After the popover hide
20002 * @param {Roo.bootstrap.Popover} this
20008 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20013 placement : 'right',
20014 trigger : 'hover', // hover
20020 can_build_overlaid : false,
20022 maskEl : false, // the mask element
20025 alignEl : false, // when show is called with an element - this get's stored.
20027 getChildContainer : function()
20029 return this.contentEl;
20032 getPopoverHeader : function()
20034 this.title = true; // flag not to hide it..
20035 this.headerEl.addClass('p-0');
20036 return this.headerEl
20040 getAutoCreate : function(){
20043 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20044 style: 'display:block',
20050 cls : 'popover-inner ',
20054 cls: 'popover-title popover-header',
20055 html : this.title === false ? '' : this.title
20058 cls : 'popover-content popover-body ' + (this.cls || ''),
20059 html : this.html || ''
20070 * @param {string} the title
20072 setTitle: function(str)
20076 this.headerEl.dom.innerHTML = str;
20081 * @param {string} the body content
20083 setContent: function(str)
20086 if (this.contentEl) {
20087 this.contentEl.dom.innerHTML = str;
20091 // as it get's added to the bottom of the page.
20092 onRender : function(ct, position)
20094 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20099 var cfg = Roo.apply({}, this.getAutoCreate());
20103 cfg.cls += ' ' + this.cls;
20106 cfg.style = this.style;
20108 //Roo.log("adding to ");
20109 this.el = Roo.get(document.body).createChild(cfg, position);
20110 // Roo.log(this.el);
20113 this.contentEl = this.el.select('.popover-content',true).first();
20114 this.headerEl = this.el.select('.popover-title',true).first();
20117 if(typeof(this.items) != 'undefined'){
20118 var items = this.items;
20121 for(var i =0;i < items.length;i++) {
20122 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20126 this.items = nitems;
20128 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20129 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20136 resizeMask : function()
20138 this.maskEl.setSize(
20139 Roo.lib.Dom.getViewWidth(true),
20140 Roo.lib.Dom.getViewHeight(true)
20144 initEvents : function()
20148 Roo.bootstrap.Popover.register(this);
20151 this.arrowEl = this.el.select('.arrow',true).first();
20152 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20153 this.el.enableDisplayMode('block');
20157 if (this.over === false && !this.parent()) {
20160 if (this.triggers === false) {
20165 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20166 var triggers = this.trigger ? this.trigger.split(' ') : [];
20167 Roo.each(triggers, function(trigger) {
20169 if (trigger == 'click') {
20170 on_el.on('click', this.toggle, this);
20171 } else if (trigger != 'manual') {
20172 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20173 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20175 on_el.on(eventIn ,this.enter, this);
20176 on_el.on(eventOut, this.leave, this);
20186 toggle : function () {
20187 this.hoverState == 'in' ? this.leave() : this.enter();
20190 enter : function () {
20192 clearTimeout(this.timeout);
20194 this.hoverState = 'in';
20196 if (!this.delay || !this.delay.show) {
20201 this.timeout = setTimeout(function () {
20202 if (_t.hoverState == 'in') {
20205 }, this.delay.show)
20208 leave : function() {
20209 clearTimeout(this.timeout);
20211 this.hoverState = 'out';
20213 if (!this.delay || !this.delay.hide) {
20218 this.timeout = setTimeout(function () {
20219 if (_t.hoverState == 'out') {
20222 }, this.delay.hide)
20226 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20227 * @param {string} (left|right|top|bottom) position
20229 show : function (on_el, placement)
20231 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20232 on_el = on_el || false; // default to false
20235 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20236 on_el = this.parent().el;
20237 } else if (this.over) {
20238 on_el = Roo.get(this.over);
20243 this.alignEl = Roo.get( on_el );
20246 this.render(document.body);
20252 if (this.title === false) {
20253 this.headerEl.hide();
20258 this.el.dom.style.display = 'block';
20261 if (this.alignEl) {
20262 this.updatePosition(this.placement, true);
20265 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20266 var es = this.el.getSize();
20267 var x = Roo.lib.Dom.getViewWidth()/2;
20268 var y = Roo.lib.Dom.getViewHeight()/2;
20269 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20274 //var arrow = this.el.select('.arrow',true).first();
20275 //arrow.set(align[2],
20277 this.el.addClass('in');
20281 this.hoverState = 'in';
20284 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20285 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20286 this.maskEl.dom.style.display = 'block';
20287 this.maskEl.addClass('show');
20289 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20291 this.fireEvent('show', this);
20295 * fire this manually after loading a grid in the table for example
20296 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20297 * @param {Boolean} try and move it if we cant get right position.
20299 updatePosition : function(placement, try_move)
20301 // allow for calling with no parameters
20302 placement = placement ? placement : this.placement;
20303 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20305 this.el.removeClass([
20306 'fade','top','bottom', 'left', 'right','in',
20307 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20309 this.el.addClass(placement + ' bs-popover-' + placement);
20311 if (!this.alignEl ) {
20315 switch (placement) {
20317 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20318 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20319 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20320 //normal display... or moved up/down.
20321 this.el.setXY(offset);
20322 var xy = this.alignEl.getAnchorXY('tr', false);
20324 this.arrowEl.setXY(xy);
20327 // continue through...
20328 return this.updatePosition('left', false);
20332 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20333 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20334 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20335 //normal display... or moved up/down.
20336 this.el.setXY(offset);
20337 var xy = this.alignEl.getAnchorXY('tl', false);
20338 xy[0]-=10;xy[1]+=5; // << fix me
20339 this.arrowEl.setXY(xy);
20343 return this.updatePosition('right', false);
20346 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20347 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20348 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20349 //normal display... or moved up/down.
20350 this.el.setXY(offset);
20351 var xy = this.alignEl.getAnchorXY('t', false);
20352 xy[1]-=10; // << fix me
20353 this.arrowEl.setXY(xy);
20357 return this.updatePosition('bottom', false);
20360 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20361 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20362 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20363 //normal display... or moved up/down.
20364 this.el.setXY(offset);
20365 var xy = this.alignEl.getAnchorXY('b', false);
20366 xy[1]+=2; // << fix me
20367 this.arrowEl.setXY(xy);
20371 return this.updatePosition('top', false);
20382 this.el.setXY([0,0]);
20383 this.el.removeClass('in');
20385 this.hoverState = null;
20386 this.maskEl.hide(); // always..
20387 this.fireEvent('hide', this);
20393 Roo.apply(Roo.bootstrap.Popover, {
20396 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20397 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20398 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20399 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20404 clickHander : false,
20407 onMouseDown : function(e)
20409 if (!e.getTarget(".roo-popover")) {
20417 register : function(popup)
20419 if (!Roo.bootstrap.Popover.clickHandler) {
20420 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20422 // hide other popups.
20424 this.popups.push(popup);
20426 hideAll : function()
20428 this.popups.forEach(function(p) {
20436 * Card header - holder for the card header elements.
20441 * @class Roo.bootstrap.PopoverNav
20442 * @extends Roo.bootstrap.NavGroup
20443 * Bootstrap Popover header navigation class
20445 * Create a new Popover Header Navigation
20446 * @param {Object} config The config object
20449 Roo.bootstrap.PopoverNav = function(config){
20450 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20453 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20456 container_method : 'getPopoverHeader'
20474 * @class Roo.bootstrap.Progress
20475 * @extends Roo.bootstrap.Component
20476 * Bootstrap Progress class
20477 * @cfg {Boolean} striped striped of the progress bar
20478 * @cfg {Boolean} active animated of the progress bar
20482 * Create a new Progress
20483 * @param {Object} config The config object
20486 Roo.bootstrap.Progress = function(config){
20487 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20490 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20495 getAutoCreate : function(){
20503 cfg.cls += ' progress-striped';
20507 cfg.cls += ' active';
20526 * @class Roo.bootstrap.ProgressBar
20527 * @extends Roo.bootstrap.Component
20528 * Bootstrap ProgressBar class
20529 * @cfg {Number} aria_valuenow aria-value now
20530 * @cfg {Number} aria_valuemin aria-value min
20531 * @cfg {Number} aria_valuemax aria-value max
20532 * @cfg {String} label label for the progress bar
20533 * @cfg {String} panel (success | info | warning | danger )
20534 * @cfg {String} role role of the progress bar
20535 * @cfg {String} sr_only text
20539 * Create a new ProgressBar
20540 * @param {Object} config The config object
20543 Roo.bootstrap.ProgressBar = function(config){
20544 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20547 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20551 aria_valuemax : 100,
20557 getAutoCreate : function()
20562 cls: 'progress-bar',
20563 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20575 cfg.role = this.role;
20578 if(this.aria_valuenow){
20579 cfg['aria-valuenow'] = this.aria_valuenow;
20582 if(this.aria_valuemin){
20583 cfg['aria-valuemin'] = this.aria_valuemin;
20586 if(this.aria_valuemax){
20587 cfg['aria-valuemax'] = this.aria_valuemax;
20590 if(this.label && !this.sr_only){
20591 cfg.html = this.label;
20595 cfg.cls += ' progress-bar-' + this.panel;
20601 update : function(aria_valuenow)
20603 this.aria_valuenow = aria_valuenow;
20605 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20620 * @class Roo.bootstrap.TabGroup
20621 * @extends Roo.bootstrap.Column
20622 * Bootstrap Column class
20623 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20624 * @cfg {Boolean} carousel true to make the group behave like a carousel
20625 * @cfg {Boolean} bullets show bullets for the panels
20626 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20627 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20628 * @cfg {Boolean} showarrow (true|false) show arrow default true
20631 * Create a new TabGroup
20632 * @param {Object} config The config object
20635 Roo.bootstrap.TabGroup = function(config){
20636 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20638 this.navId = Roo.id();
20641 Roo.bootstrap.TabGroup.register(this);
20645 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20648 transition : false,
20653 slideOnTouch : false,
20656 getAutoCreate : function()
20658 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20660 cfg.cls += ' tab-content';
20662 if (this.carousel) {
20663 cfg.cls += ' carousel slide';
20666 cls : 'carousel-inner',
20670 if(this.bullets && !Roo.isTouch){
20673 cls : 'carousel-bullets',
20677 if(this.bullets_cls){
20678 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20685 cfg.cn[0].cn.push(bullets);
20688 if(this.showarrow){
20689 cfg.cn[0].cn.push({
20691 class : 'carousel-arrow',
20695 class : 'carousel-prev',
20699 class : 'fa fa-chevron-left'
20705 class : 'carousel-next',
20709 class : 'fa fa-chevron-right'
20722 initEvents: function()
20724 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20725 // this.el.on("touchstart", this.onTouchStart, this);
20728 if(this.autoslide){
20731 this.slideFn = window.setInterval(function() {
20732 _this.showPanelNext();
20736 if(this.showarrow){
20737 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20738 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20744 // onTouchStart : function(e, el, o)
20746 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20750 // this.showPanelNext();
20754 getChildContainer : function()
20756 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20760 * register a Navigation item
20761 * @param {Roo.bootstrap.NavItem} the navitem to add
20763 register : function(item)
20765 this.tabs.push( item);
20766 item.navId = this.navId; // not really needed..
20771 getActivePanel : function()
20774 Roo.each(this.tabs, function(t) {
20784 getPanelByName : function(n)
20787 Roo.each(this.tabs, function(t) {
20788 if (t.tabId == n) {
20796 indexOfPanel : function(p)
20799 Roo.each(this.tabs, function(t,i) {
20800 if (t.tabId == p.tabId) {
20809 * show a specific panel
20810 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20811 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20813 showPanel : function (pan)
20815 if(this.transition || typeof(pan) == 'undefined'){
20816 Roo.log("waiting for the transitionend");
20820 if (typeof(pan) == 'number') {
20821 pan = this.tabs[pan];
20824 if (typeof(pan) == 'string') {
20825 pan = this.getPanelByName(pan);
20828 var cur = this.getActivePanel();
20831 Roo.log('pan or acitve pan is undefined');
20835 if (pan.tabId == this.getActivePanel().tabId) {
20839 if (false === cur.fireEvent('beforedeactivate')) {
20843 if(this.bullets > 0 && !Roo.isTouch){
20844 this.setActiveBullet(this.indexOfPanel(pan));
20847 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20849 //class="carousel-item carousel-item-next carousel-item-left"
20851 this.transition = true;
20852 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20853 var lr = dir == 'next' ? 'left' : 'right';
20854 pan.el.addClass(dir); // or prev
20855 pan.el.addClass('carousel-item-' + dir); // or prev
20856 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20857 cur.el.addClass(lr); // or right
20858 pan.el.addClass(lr);
20859 cur.el.addClass('carousel-item-' +lr); // or right
20860 pan.el.addClass('carousel-item-' +lr);
20864 cur.el.on('transitionend', function() {
20865 Roo.log("trans end?");
20867 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20868 pan.setActive(true);
20870 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20871 cur.setActive(false);
20873 _this.transition = false;
20875 }, this, { single: true } );
20880 cur.setActive(false);
20881 pan.setActive(true);
20886 showPanelNext : function()
20888 var i = this.indexOfPanel(this.getActivePanel());
20890 if (i >= this.tabs.length - 1 && !this.autoslide) {
20894 if (i >= this.tabs.length - 1 && this.autoslide) {
20898 this.showPanel(this.tabs[i+1]);
20901 showPanelPrev : function()
20903 var i = this.indexOfPanel(this.getActivePanel());
20905 if (i < 1 && !this.autoslide) {
20909 if (i < 1 && this.autoslide) {
20910 i = this.tabs.length;
20913 this.showPanel(this.tabs[i-1]);
20917 addBullet: function()
20919 if(!this.bullets || Roo.isTouch){
20922 var ctr = this.el.select('.carousel-bullets',true).first();
20923 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20924 var bullet = ctr.createChild({
20925 cls : 'bullet bullet-' + i
20926 },ctr.dom.lastChild);
20931 bullet.on('click', (function(e, el, o, ii, t){
20933 e.preventDefault();
20935 this.showPanel(ii);
20937 if(this.autoslide && this.slideFn){
20938 clearInterval(this.slideFn);
20939 this.slideFn = window.setInterval(function() {
20940 _this.showPanelNext();
20944 }).createDelegate(this, [i, bullet], true));
20949 setActiveBullet : function(i)
20955 Roo.each(this.el.select('.bullet', true).elements, function(el){
20956 el.removeClass('selected');
20959 var bullet = this.el.select('.bullet-' + i, true).first();
20965 bullet.addClass('selected');
20976 Roo.apply(Roo.bootstrap.TabGroup, {
20980 * register a Navigation Group
20981 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20983 register : function(navgrp)
20985 this.groups[navgrp.navId] = navgrp;
20989 * fetch a Navigation Group based on the navigation ID
20990 * if one does not exist , it will get created.
20991 * @param {string} the navgroup to add
20992 * @returns {Roo.bootstrap.NavGroup} the navgroup
20994 get: function(navId) {
20995 if (typeof(this.groups[navId]) == 'undefined') {
20996 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20998 return this.groups[navId] ;
21013 * @class Roo.bootstrap.TabPanel
21014 * @extends Roo.bootstrap.Component
21015 * Bootstrap TabPanel class
21016 * @cfg {Boolean} active panel active
21017 * @cfg {String} html panel content
21018 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21019 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21020 * @cfg {String} href click to link..
21021 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21025 * Create a new TabPanel
21026 * @param {Object} config The config object
21029 Roo.bootstrap.TabPanel = function(config){
21030 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21034 * Fires when the active status changes
21035 * @param {Roo.bootstrap.TabPanel} this
21036 * @param {Boolean} state the new state
21041 * @event beforedeactivate
21042 * Fires before a tab is de-activated - can be used to do validation on a form.
21043 * @param {Roo.bootstrap.TabPanel} this
21044 * @return {Boolean} false if there is an error
21047 'beforedeactivate': true
21050 this.tabId = this.tabId || Roo.id();
21054 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21061 touchSlide : false,
21062 getAutoCreate : function(){
21067 // item is needed for carousel - not sure if it has any effect otherwise
21068 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21069 html: this.html || ''
21073 cfg.cls += ' active';
21077 cfg.tabId = this.tabId;
21085 initEvents: function()
21087 var p = this.parent();
21089 this.navId = this.navId || p.navId;
21091 if (typeof(this.navId) != 'undefined') {
21092 // not really needed.. but just in case.. parent should be a NavGroup.
21093 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21097 var i = tg.tabs.length - 1;
21099 if(this.active && tg.bullets > 0 && i < tg.bullets){
21100 tg.setActiveBullet(i);
21104 this.el.on('click', this.onClick, this);
21106 if(Roo.isTouch && this.touchSlide){
21107 this.el.on("touchstart", this.onTouchStart, this);
21108 this.el.on("touchmove", this.onTouchMove, this);
21109 this.el.on("touchend", this.onTouchEnd, this);
21114 onRender : function(ct, position)
21116 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21119 setActive : function(state)
21121 Roo.log("panel - set active " + this.tabId + "=" + state);
21123 this.active = state;
21125 this.el.removeClass('active');
21127 } else if (!this.el.hasClass('active')) {
21128 this.el.addClass('active');
21131 this.fireEvent('changed', this, state);
21134 onClick : function(e)
21136 e.preventDefault();
21138 if(!this.href.length){
21142 window.location.href = this.href;
21151 onTouchStart : function(e)
21153 this.swiping = false;
21155 this.startX = e.browserEvent.touches[0].clientX;
21156 this.startY = e.browserEvent.touches[0].clientY;
21159 onTouchMove : function(e)
21161 this.swiping = true;
21163 this.endX = e.browserEvent.touches[0].clientX;
21164 this.endY = e.browserEvent.touches[0].clientY;
21167 onTouchEnd : function(e)
21174 var tabGroup = this.parent();
21176 if(this.endX > this.startX){ // swiping right
21177 tabGroup.showPanelPrev();
21181 if(this.startX > this.endX){ // swiping left
21182 tabGroup.showPanelNext();
21201 * @class Roo.bootstrap.DateField
21202 * @extends Roo.bootstrap.Input
21203 * Bootstrap DateField class
21204 * @cfg {Number} weekStart default 0
21205 * @cfg {String} viewMode default empty, (months|years)
21206 * @cfg {String} minViewMode default empty, (months|years)
21207 * @cfg {Number} startDate default -Infinity
21208 * @cfg {Number} endDate default Infinity
21209 * @cfg {Boolean} todayHighlight default false
21210 * @cfg {Boolean} todayBtn default false
21211 * @cfg {Boolean} calendarWeeks default false
21212 * @cfg {Object} daysOfWeekDisabled default empty
21213 * @cfg {Boolean} singleMode default false (true | false)
21215 * @cfg {Boolean} keyboardNavigation default true
21216 * @cfg {String} language default en
21219 * Create a new DateField
21220 * @param {Object} config The config object
21223 Roo.bootstrap.DateField = function(config){
21224 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21228 * Fires when this field show.
21229 * @param {Roo.bootstrap.DateField} this
21230 * @param {Mixed} date The date value
21235 * Fires when this field hide.
21236 * @param {Roo.bootstrap.DateField} this
21237 * @param {Mixed} date The date value
21242 * Fires when select a date.
21243 * @param {Roo.bootstrap.DateField} this
21244 * @param {Mixed} date The date value
21248 * @event beforeselect
21249 * Fires when before select a date.
21250 * @param {Roo.bootstrap.DateField} this
21251 * @param {Mixed} date The date value
21253 beforeselect : true
21257 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21260 * @cfg {String} format
21261 * The default date format string which can be overriden for localization support. The format must be
21262 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21266 * @cfg {String} altFormats
21267 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21268 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21270 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21278 todayHighlight : false,
21284 keyboardNavigation: true,
21286 calendarWeeks: false,
21288 startDate: -Infinity,
21292 daysOfWeekDisabled: [],
21296 singleMode : false,
21298 UTCDate: function()
21300 return new Date(Date.UTC.apply(Date, arguments));
21303 UTCToday: function()
21305 var today = new Date();
21306 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21309 getDate: function() {
21310 var d = this.getUTCDate();
21311 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21314 getUTCDate: function() {
21318 setDate: function(d) {
21319 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21322 setUTCDate: function(d) {
21324 this.setValue(this.formatDate(this.date));
21327 onRender: function(ct, position)
21330 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21332 this.language = this.language || 'en';
21333 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21334 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21336 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21337 this.format = this.format || 'm/d/y';
21338 this.isInline = false;
21339 this.isInput = true;
21340 this.component = this.el.select('.add-on', true).first() || false;
21341 this.component = (this.component && this.component.length === 0) ? false : this.component;
21342 this.hasInput = this.component && this.inputEl().length;
21344 if (typeof(this.minViewMode === 'string')) {
21345 switch (this.minViewMode) {
21347 this.minViewMode = 1;
21350 this.minViewMode = 2;
21353 this.minViewMode = 0;
21358 if (typeof(this.viewMode === 'string')) {
21359 switch (this.viewMode) {
21372 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21374 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21376 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21378 this.picker().on('mousedown', this.onMousedown, this);
21379 this.picker().on('click', this.onClick, this);
21381 this.picker().addClass('datepicker-dropdown');
21383 this.startViewMode = this.viewMode;
21385 if(this.singleMode){
21386 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21387 v.setVisibilityMode(Roo.Element.DISPLAY);
21391 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21392 v.setStyle('width', '189px');
21396 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21397 if(!this.calendarWeeks){
21402 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21403 v.attr('colspan', function(i, val){
21404 return parseInt(val) + 1;
21409 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21411 this.setStartDate(this.startDate);
21412 this.setEndDate(this.endDate);
21414 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21421 if(this.isInline) {
21426 picker : function()
21428 return this.pickerEl;
21429 // return this.el.select('.datepicker', true).first();
21432 fillDow: function()
21434 var dowCnt = this.weekStart;
21443 if(this.calendarWeeks){
21451 while (dowCnt < this.weekStart + 7) {
21455 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21459 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21462 fillMonths: function()
21465 var months = this.picker().select('>.datepicker-months td', true).first();
21467 months.dom.innerHTML = '';
21473 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21476 months.createChild(month);
21483 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;
21485 if (this.date < this.startDate) {
21486 this.viewDate = new Date(this.startDate);
21487 } else if (this.date > this.endDate) {
21488 this.viewDate = new Date(this.endDate);
21490 this.viewDate = new Date(this.date);
21498 var d = new Date(this.viewDate),
21499 year = d.getUTCFullYear(),
21500 month = d.getUTCMonth(),
21501 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21502 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21503 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21504 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21505 currentDate = this.date && this.date.valueOf(),
21506 today = this.UTCToday();
21508 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21510 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21512 // this.picker.select('>tfoot th.today').
21513 // .text(dates[this.language].today)
21514 // .toggle(this.todayBtn !== false);
21516 this.updateNavArrows();
21519 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21521 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21523 prevMonth.setUTCDate(day);
21525 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21527 var nextMonth = new Date(prevMonth);
21529 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21531 nextMonth = nextMonth.valueOf();
21533 var fillMonths = false;
21535 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21537 while(prevMonth.valueOf() <= nextMonth) {
21540 if (prevMonth.getUTCDay() === this.weekStart) {
21542 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21550 if(this.calendarWeeks){
21551 // ISO 8601: First week contains first thursday.
21552 // ISO also states week starts on Monday, but we can be more abstract here.
21554 // Start of current week: based on weekstart/current date
21555 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21556 // Thursday of this week
21557 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21558 // First Thursday of year, year from thursday
21559 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21560 // Calendar week: ms between thursdays, div ms per day, div 7 days
21561 calWeek = (th - yth) / 864e5 / 7 + 1;
21563 fillMonths.cn.push({
21571 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21573 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21576 if (this.todayHighlight &&
21577 prevMonth.getUTCFullYear() == today.getFullYear() &&
21578 prevMonth.getUTCMonth() == today.getMonth() &&
21579 prevMonth.getUTCDate() == today.getDate()) {
21580 clsName += ' today';
21583 if (currentDate && prevMonth.valueOf() === currentDate) {
21584 clsName += ' active';
21587 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21588 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21589 clsName += ' disabled';
21592 fillMonths.cn.push({
21594 cls: 'day ' + clsName,
21595 html: prevMonth.getDate()
21598 prevMonth.setDate(prevMonth.getDate()+1);
21601 var currentYear = this.date && this.date.getUTCFullYear();
21602 var currentMonth = this.date && this.date.getUTCMonth();
21604 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21606 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21607 v.removeClass('active');
21609 if(currentYear === year && k === currentMonth){
21610 v.addClass('active');
21613 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21614 v.addClass('disabled');
21620 year = parseInt(year/10, 10) * 10;
21622 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21624 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21627 for (var i = -1; i < 11; i++) {
21628 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21630 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21638 showMode: function(dir)
21641 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21644 Roo.each(this.picker().select('>div',true).elements, function(v){
21645 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21648 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21653 if(this.isInline) {
21657 this.picker().removeClass(['bottom', 'top']);
21659 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21661 * place to the top of element!
21665 this.picker().addClass('top');
21666 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21671 this.picker().addClass('bottom');
21673 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21676 parseDate : function(value)
21678 if(!value || value instanceof Date){
21681 var v = Date.parseDate(value, this.format);
21682 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21683 v = Date.parseDate(value, 'Y-m-d');
21685 if(!v && this.altFormats){
21686 if(!this.altFormatsArray){
21687 this.altFormatsArray = this.altFormats.split("|");
21689 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21690 v = Date.parseDate(value, this.altFormatsArray[i]);
21696 formatDate : function(date, fmt)
21698 return (!date || !(date instanceof Date)) ?
21699 date : date.dateFormat(fmt || this.format);
21702 onFocus : function()
21704 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21708 onBlur : function()
21710 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21712 var d = this.inputEl().getValue();
21719 showPopup : function()
21721 this.picker().show();
21725 this.fireEvent('showpopup', this, this.date);
21728 hidePopup : function()
21730 if(this.isInline) {
21733 this.picker().hide();
21734 this.viewMode = this.startViewMode;
21737 this.fireEvent('hidepopup', this, this.date);
21741 onMousedown: function(e)
21743 e.stopPropagation();
21744 e.preventDefault();
21749 Roo.bootstrap.DateField.superclass.keyup.call(this);
21753 setValue: function(v)
21755 if(this.fireEvent('beforeselect', this, v) !== false){
21756 var d = new Date(this.parseDate(v) ).clearTime();
21758 if(isNaN(d.getTime())){
21759 this.date = this.viewDate = '';
21760 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21764 v = this.formatDate(d);
21766 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21768 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21772 this.fireEvent('select', this, this.date);
21776 getValue: function()
21778 return this.formatDate(this.date);
21781 fireKey: function(e)
21783 if (!this.picker().isVisible()){
21784 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21790 var dateChanged = false,
21792 newDate, newViewDate;
21797 e.preventDefault();
21801 if (!this.keyboardNavigation) {
21804 dir = e.keyCode == 37 ? -1 : 1;
21807 newDate = this.moveYear(this.date, dir);
21808 newViewDate = this.moveYear(this.viewDate, dir);
21809 } else if (e.shiftKey){
21810 newDate = this.moveMonth(this.date, dir);
21811 newViewDate = this.moveMonth(this.viewDate, dir);
21813 newDate = new Date(this.date);
21814 newDate.setUTCDate(this.date.getUTCDate() + dir);
21815 newViewDate = new Date(this.viewDate);
21816 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21818 if (this.dateWithinRange(newDate)){
21819 this.date = newDate;
21820 this.viewDate = newViewDate;
21821 this.setValue(this.formatDate(this.date));
21823 e.preventDefault();
21824 dateChanged = true;
21829 if (!this.keyboardNavigation) {
21832 dir = e.keyCode == 38 ? -1 : 1;
21834 newDate = this.moveYear(this.date, dir);
21835 newViewDate = this.moveYear(this.viewDate, dir);
21836 } else if (e.shiftKey){
21837 newDate = this.moveMonth(this.date, dir);
21838 newViewDate = this.moveMonth(this.viewDate, dir);
21840 newDate = new Date(this.date);
21841 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21842 newViewDate = new Date(this.viewDate);
21843 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21845 if (this.dateWithinRange(newDate)){
21846 this.date = newDate;
21847 this.viewDate = newViewDate;
21848 this.setValue(this.formatDate(this.date));
21850 e.preventDefault();
21851 dateChanged = true;
21855 this.setValue(this.formatDate(this.date));
21857 e.preventDefault();
21860 this.setValue(this.formatDate(this.date));
21874 onClick: function(e)
21876 e.stopPropagation();
21877 e.preventDefault();
21879 var target = e.getTarget();
21881 if(target.nodeName.toLowerCase() === 'i'){
21882 target = Roo.get(target).dom.parentNode;
21885 var nodeName = target.nodeName;
21886 var className = target.className;
21887 var html = target.innerHTML;
21888 //Roo.log(nodeName);
21890 switch(nodeName.toLowerCase()) {
21892 switch(className) {
21898 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21899 switch(this.viewMode){
21901 this.viewDate = this.moveMonth(this.viewDate, dir);
21905 this.viewDate = this.moveYear(this.viewDate, dir);
21911 var date = new Date();
21912 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21914 this.setValue(this.formatDate(this.date));
21921 if (className.indexOf('disabled') < 0) {
21922 this.viewDate.setUTCDate(1);
21923 if (className.indexOf('month') > -1) {
21924 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21926 var year = parseInt(html, 10) || 0;
21927 this.viewDate.setUTCFullYear(year);
21931 if(this.singleMode){
21932 this.setValue(this.formatDate(this.viewDate));
21943 //Roo.log(className);
21944 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21945 var day = parseInt(html, 10) || 1;
21946 var year = (this.viewDate || new Date()).getUTCFullYear(),
21947 month = (this.viewDate || new Date()).getUTCMonth();
21949 if (className.indexOf('old') > -1) {
21956 } else if (className.indexOf('new') > -1) {
21964 //Roo.log([year,month,day]);
21965 this.date = this.UTCDate(year, month, day,0,0,0,0);
21966 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21968 //Roo.log(this.formatDate(this.date));
21969 this.setValue(this.formatDate(this.date));
21976 setStartDate: function(startDate)
21978 this.startDate = startDate || -Infinity;
21979 if (this.startDate !== -Infinity) {
21980 this.startDate = this.parseDate(this.startDate);
21983 this.updateNavArrows();
21986 setEndDate: function(endDate)
21988 this.endDate = endDate || Infinity;
21989 if (this.endDate !== Infinity) {
21990 this.endDate = this.parseDate(this.endDate);
21993 this.updateNavArrows();
21996 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21998 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21999 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22000 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22002 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22003 return parseInt(d, 10);
22006 this.updateNavArrows();
22009 updateNavArrows: function()
22011 if(this.singleMode){
22015 var d = new Date(this.viewDate),
22016 year = d.getUTCFullYear(),
22017 month = d.getUTCMonth();
22019 Roo.each(this.picker().select('.prev', true).elements, function(v){
22021 switch (this.viewMode) {
22024 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22030 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22037 Roo.each(this.picker().select('.next', true).elements, function(v){
22039 switch (this.viewMode) {
22042 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22048 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22056 moveMonth: function(date, dir)
22061 var new_date = new Date(date.valueOf()),
22062 day = new_date.getUTCDate(),
22063 month = new_date.getUTCMonth(),
22064 mag = Math.abs(dir),
22066 dir = dir > 0 ? 1 : -1;
22069 // If going back one month, make sure month is not current month
22070 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22072 return new_date.getUTCMonth() == month;
22074 // If going forward one month, make sure month is as expected
22075 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22077 return new_date.getUTCMonth() != new_month;
22079 new_month = month + dir;
22080 new_date.setUTCMonth(new_month);
22081 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22082 if (new_month < 0 || new_month > 11) {
22083 new_month = (new_month + 12) % 12;
22086 // For magnitudes >1, move one month at a time...
22087 for (var i=0; i<mag; i++) {
22088 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22089 new_date = this.moveMonth(new_date, dir);
22091 // ...then reset the day, keeping it in the new month
22092 new_month = new_date.getUTCMonth();
22093 new_date.setUTCDate(day);
22095 return new_month != new_date.getUTCMonth();
22098 // Common date-resetting loop -- if date is beyond end of month, make it
22101 new_date.setUTCDate(--day);
22102 new_date.setUTCMonth(new_month);
22107 moveYear: function(date, dir)
22109 return this.moveMonth(date, dir*12);
22112 dateWithinRange: function(date)
22114 return date >= this.startDate && date <= this.endDate;
22120 this.picker().remove();
22123 validateValue : function(value)
22125 if(this.getVisibilityEl().hasClass('hidden')){
22129 if(value.length < 1) {
22130 if(this.allowBlank){
22136 if(value.length < this.minLength){
22139 if(value.length > this.maxLength){
22143 var vt = Roo.form.VTypes;
22144 if(!vt[this.vtype](value, this)){
22148 if(typeof this.validator == "function"){
22149 var msg = this.validator(value);
22155 if(this.regex && !this.regex.test(value)){
22159 if(typeof(this.parseDate(value)) == 'undefined'){
22163 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22167 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22177 this.date = this.viewDate = '';
22179 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22184 Roo.apply(Roo.bootstrap.DateField, {
22195 html: '<i class="fa fa-arrow-left"/>'
22205 html: '<i class="fa fa-arrow-right"/>'
22247 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22248 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22249 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22250 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22251 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22264 navFnc: 'FullYear',
22269 navFnc: 'FullYear',
22274 Roo.apply(Roo.bootstrap.DateField, {
22278 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22282 cls: 'datepicker-days',
22286 cls: 'table-condensed',
22288 Roo.bootstrap.DateField.head,
22292 Roo.bootstrap.DateField.footer
22299 cls: 'datepicker-months',
22303 cls: 'table-condensed',
22305 Roo.bootstrap.DateField.head,
22306 Roo.bootstrap.DateField.content,
22307 Roo.bootstrap.DateField.footer
22314 cls: 'datepicker-years',
22318 cls: 'table-condensed',
22320 Roo.bootstrap.DateField.head,
22321 Roo.bootstrap.DateField.content,
22322 Roo.bootstrap.DateField.footer
22341 * @class Roo.bootstrap.TimeField
22342 * @extends Roo.bootstrap.Input
22343 * Bootstrap DateField class
22347 * Create a new TimeField
22348 * @param {Object} config The config object
22351 Roo.bootstrap.TimeField = function(config){
22352 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22356 * Fires when this field show.
22357 * @param {Roo.bootstrap.DateField} thisthis
22358 * @param {Mixed} date The date value
22363 * Fires when this field hide.
22364 * @param {Roo.bootstrap.DateField} this
22365 * @param {Mixed} date The date value
22370 * Fires when select a date.
22371 * @param {Roo.bootstrap.DateField} this
22372 * @param {Mixed} date The date value
22378 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22381 * @cfg {String} format
22382 * The default time format string which can be overriden for localization support. The format must be
22383 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22387 getAutoCreate : function()
22389 this.after = '<i class="fa far fa-clock"></i>';
22390 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22394 onRender: function(ct, position)
22397 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22399 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22401 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22403 this.pop = this.picker().select('>.datepicker-time',true).first();
22404 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22406 this.picker().on('mousedown', this.onMousedown, this);
22407 this.picker().on('click', this.onClick, this);
22409 this.picker().addClass('datepicker-dropdown');
22414 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22415 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22416 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22417 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22418 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22419 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22423 fireKey: function(e){
22424 if (!this.picker().isVisible()){
22425 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22431 e.preventDefault();
22439 this.onTogglePeriod();
22442 this.onIncrementMinutes();
22445 this.onDecrementMinutes();
22454 onClick: function(e) {
22455 e.stopPropagation();
22456 e.preventDefault();
22459 picker : function()
22461 return this.pickerEl;
22464 fillTime: function()
22466 var time = this.pop.select('tbody', true).first();
22468 time.dom.innerHTML = '';
22483 cls: 'hours-up fa fas fa-chevron-up'
22503 cls: 'minutes-up fa fas fa-chevron-up'
22524 cls: 'timepicker-hour',
22539 cls: 'timepicker-minute',
22554 cls: 'btn btn-primary period',
22576 cls: 'hours-down fa fas fa-chevron-down'
22596 cls: 'minutes-down fa fas fa-chevron-down'
22614 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22621 var hours = this.time.getHours();
22622 var minutes = this.time.getMinutes();
22635 hours = hours - 12;
22639 hours = '0' + hours;
22643 minutes = '0' + minutes;
22646 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22647 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22648 this.pop.select('button', true).first().dom.innerHTML = period;
22654 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22656 var cls = ['bottom'];
22658 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22665 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22669 //this.picker().setXY(20000,20000);
22670 this.picker().addClass(cls.join('-'));
22674 Roo.each(cls, function(c){
22679 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22680 //_this.picker().setTop(_this.inputEl().getHeight());
22684 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22686 //_this.picker().setTop(0 - _this.picker().getHeight());
22691 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22695 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22703 onFocus : function()
22705 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22709 onBlur : function()
22711 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22717 this.picker().show();
22722 this.fireEvent('show', this, this.date);
22727 this.picker().hide();
22730 this.fireEvent('hide', this, this.date);
22733 setTime : function()
22736 this.setValue(this.time.format(this.format));
22738 this.fireEvent('select', this, this.date);
22743 onMousedown: function(e){
22744 e.stopPropagation();
22745 e.preventDefault();
22748 onIncrementHours: function()
22750 Roo.log('onIncrementHours');
22751 this.time = this.time.add(Date.HOUR, 1);
22756 onDecrementHours: function()
22758 Roo.log('onDecrementHours');
22759 this.time = this.time.add(Date.HOUR, -1);
22763 onIncrementMinutes: function()
22765 Roo.log('onIncrementMinutes');
22766 this.time = this.time.add(Date.MINUTE, 1);
22770 onDecrementMinutes: function()
22772 Roo.log('onDecrementMinutes');
22773 this.time = this.time.add(Date.MINUTE, -1);
22777 onTogglePeriod: function()
22779 Roo.log('onTogglePeriod');
22780 this.time = this.time.add(Date.HOUR, 12);
22788 Roo.apply(Roo.bootstrap.TimeField, {
22792 cls: 'datepicker dropdown-menu',
22796 cls: 'datepicker-time',
22800 cls: 'table-condensed',
22829 cls: 'btn btn-info ok',
22857 * @class Roo.bootstrap.MonthField
22858 * @extends Roo.bootstrap.Input
22859 * Bootstrap MonthField class
22861 * @cfg {String} language default en
22864 * Create a new MonthField
22865 * @param {Object} config The config object
22868 Roo.bootstrap.MonthField = function(config){
22869 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22874 * Fires when this field show.
22875 * @param {Roo.bootstrap.MonthField} this
22876 * @param {Mixed} date The date value
22881 * Fires when this field hide.
22882 * @param {Roo.bootstrap.MonthField} this
22883 * @param {Mixed} date The date value
22888 * Fires when select a date.
22889 * @param {Roo.bootstrap.MonthField} this
22890 * @param {String} oldvalue The old value
22891 * @param {String} newvalue The new value
22897 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22899 onRender: function(ct, position)
22902 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22904 this.language = this.language || 'en';
22905 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22906 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22908 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22909 this.isInline = false;
22910 this.isInput = true;
22911 this.component = this.el.select('.add-on', true).first() || false;
22912 this.component = (this.component && this.component.length === 0) ? false : this.component;
22913 this.hasInput = this.component && this.inputEL().length;
22915 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22917 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22919 this.picker().on('mousedown', this.onMousedown, this);
22920 this.picker().on('click', this.onClick, this);
22922 this.picker().addClass('datepicker-dropdown');
22924 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22925 v.setStyle('width', '189px');
22932 if(this.isInline) {
22938 setValue: function(v, suppressEvent)
22940 var o = this.getValue();
22942 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22946 if(suppressEvent !== true){
22947 this.fireEvent('select', this, o, v);
22952 getValue: function()
22957 onClick: function(e)
22959 e.stopPropagation();
22960 e.preventDefault();
22962 var target = e.getTarget();
22964 if(target.nodeName.toLowerCase() === 'i'){
22965 target = Roo.get(target).dom.parentNode;
22968 var nodeName = target.nodeName;
22969 var className = target.className;
22970 var html = target.innerHTML;
22972 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22976 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22978 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22984 picker : function()
22986 return this.pickerEl;
22989 fillMonths: function()
22992 var months = this.picker().select('>.datepicker-months td', true).first();
22994 months.dom.innerHTML = '';
23000 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23003 months.createChild(month);
23012 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23013 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23016 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23017 e.removeClass('active');
23019 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23020 e.addClass('active');
23027 if(this.isInline) {
23031 this.picker().removeClass(['bottom', 'top']);
23033 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23035 * place to the top of element!
23039 this.picker().addClass('top');
23040 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23045 this.picker().addClass('bottom');
23047 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23050 onFocus : function()
23052 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23056 onBlur : function()
23058 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23060 var d = this.inputEl().getValue();
23069 this.picker().show();
23070 this.picker().select('>.datepicker-months', true).first().show();
23074 this.fireEvent('show', this, this.date);
23079 if(this.isInline) {
23082 this.picker().hide();
23083 this.fireEvent('hide', this, this.date);
23087 onMousedown: function(e)
23089 e.stopPropagation();
23090 e.preventDefault();
23095 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23099 fireKey: function(e)
23101 if (!this.picker().isVisible()){
23102 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23113 e.preventDefault();
23117 dir = e.keyCode == 37 ? -1 : 1;
23119 this.vIndex = this.vIndex + dir;
23121 if(this.vIndex < 0){
23125 if(this.vIndex > 11){
23129 if(isNaN(this.vIndex)){
23133 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23139 dir = e.keyCode == 38 ? -1 : 1;
23141 this.vIndex = this.vIndex + dir * 4;
23143 if(this.vIndex < 0){
23147 if(this.vIndex > 11){
23151 if(isNaN(this.vIndex)){
23155 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23160 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23161 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23165 e.preventDefault();
23168 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23169 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23185 this.picker().remove();
23190 Roo.apply(Roo.bootstrap.MonthField, {
23209 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23210 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23215 Roo.apply(Roo.bootstrap.MonthField, {
23219 cls: 'datepicker dropdown-menu roo-dynamic',
23223 cls: 'datepicker-months',
23227 cls: 'table-condensed',
23229 Roo.bootstrap.DateField.content
23249 * @class Roo.bootstrap.CheckBox
23250 * @extends Roo.bootstrap.Input
23251 * Bootstrap CheckBox class
23253 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23254 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23255 * @cfg {String} boxLabel The text that appears beside the checkbox
23256 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23257 * @cfg {Boolean} checked initnal the element
23258 * @cfg {Boolean} inline inline the element (default false)
23259 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23260 * @cfg {String} tooltip label tooltip
23263 * Create a new CheckBox
23264 * @param {Object} config The config object
23267 Roo.bootstrap.CheckBox = function(config){
23268 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23273 * Fires when the element is checked or unchecked.
23274 * @param {Roo.bootstrap.CheckBox} this This input
23275 * @param {Boolean} checked The new checked value
23280 * Fires when the element is click.
23281 * @param {Roo.bootstrap.CheckBox} this This input
23288 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23290 inputType: 'checkbox',
23299 // checkbox success does not make any sense really..
23304 getAutoCreate : function()
23306 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23312 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23315 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23321 type : this.inputType,
23322 value : this.inputValue,
23323 cls : 'roo-' + this.inputType, //'form-box',
23324 placeholder : this.placeholder || ''
23328 if(this.inputType != 'radio'){
23332 cls : 'roo-hidden-value',
23333 value : this.checked ? this.inputValue : this.valueOff
23338 if (this.weight) { // Validity check?
23339 cfg.cls += " " + this.inputType + "-" + this.weight;
23342 if (this.disabled) {
23343 input.disabled=true;
23347 input.checked = this.checked;
23352 input.name = this.name;
23354 if(this.inputType != 'radio'){
23355 hidden.name = this.name;
23356 input.name = '_hidden_' + this.name;
23361 input.cls += ' input-' + this.size;
23366 ['xs','sm','md','lg'].map(function(size){
23367 if (settings[size]) {
23368 cfg.cls += ' col-' + size + '-' + settings[size];
23372 var inputblock = input;
23374 if (this.before || this.after) {
23377 cls : 'input-group',
23382 inputblock.cn.push({
23384 cls : 'input-group-addon',
23389 inputblock.cn.push(input);
23391 if(this.inputType != 'radio'){
23392 inputblock.cn.push(hidden);
23396 inputblock.cn.push({
23398 cls : 'input-group-addon',
23404 var boxLabelCfg = false;
23410 //'for': id, // box label is handled by onclick - so no for...
23412 html: this.boxLabel
23415 boxLabelCfg.tooltip = this.tooltip;
23421 if (align ==='left' && this.fieldLabel.length) {
23422 // Roo.log("left and has label");
23427 cls : 'control-label',
23428 html : this.fieldLabel
23439 cfg.cn[1].cn.push(boxLabelCfg);
23442 if(this.labelWidth > 12){
23443 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23446 if(this.labelWidth < 13 && this.labelmd == 0){
23447 this.labelmd = this.labelWidth;
23450 if(this.labellg > 0){
23451 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23452 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23455 if(this.labelmd > 0){
23456 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23457 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23460 if(this.labelsm > 0){
23461 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23462 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23465 if(this.labelxs > 0){
23466 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23467 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23470 } else if ( this.fieldLabel.length) {
23471 // Roo.log(" label");
23475 tag: this.boxLabel ? 'span' : 'label',
23477 cls: 'control-label box-input-label',
23478 //cls : 'input-group-addon',
23479 html : this.fieldLabel
23486 cfg.cn.push(boxLabelCfg);
23491 // Roo.log(" no label && no align");
23492 cfg.cn = [ inputblock ] ;
23494 cfg.cn.push(boxLabelCfg);
23502 if(this.inputType != 'radio'){
23503 cfg.cn.push(hidden);
23511 * return the real input element.
23513 inputEl: function ()
23515 return this.el.select('input.roo-' + this.inputType,true).first();
23517 hiddenEl: function ()
23519 return this.el.select('input.roo-hidden-value',true).first();
23522 labelEl: function()
23524 return this.el.select('label.control-label',true).first();
23526 /* depricated... */
23530 return this.labelEl();
23533 boxLabelEl: function()
23535 return this.el.select('label.box-label',true).first();
23538 initEvents : function()
23540 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23542 this.inputEl().on('click', this.onClick, this);
23544 if (this.boxLabel) {
23545 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23548 this.startValue = this.getValue();
23551 Roo.bootstrap.CheckBox.register(this);
23555 onClick : function(e)
23557 if(this.fireEvent('click', this, e) !== false){
23558 this.setChecked(!this.checked);
23563 setChecked : function(state,suppressEvent)
23565 this.startValue = this.getValue();
23567 if(this.inputType == 'radio'){
23569 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23570 e.dom.checked = false;
23573 this.inputEl().dom.checked = true;
23575 this.inputEl().dom.value = this.inputValue;
23577 if(suppressEvent !== true){
23578 this.fireEvent('check', this, true);
23586 this.checked = state;
23588 this.inputEl().dom.checked = state;
23591 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23593 if(suppressEvent !== true){
23594 this.fireEvent('check', this, state);
23600 getValue : function()
23602 if(this.inputType == 'radio'){
23603 return this.getGroupValue();
23606 return this.hiddenEl().dom.value;
23610 getGroupValue : function()
23612 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23616 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23619 setValue : function(v,suppressEvent)
23621 if(this.inputType == 'radio'){
23622 this.setGroupValue(v, suppressEvent);
23626 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23631 setGroupValue : function(v, suppressEvent)
23633 this.startValue = this.getValue();
23635 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23636 e.dom.checked = false;
23638 if(e.dom.value == v){
23639 e.dom.checked = true;
23643 if(suppressEvent !== true){
23644 this.fireEvent('check', this, true);
23652 validate : function()
23654 if(this.getVisibilityEl().hasClass('hidden')){
23660 (this.inputType == 'radio' && this.validateRadio()) ||
23661 (this.inputType == 'checkbox' && this.validateCheckbox())
23667 this.markInvalid();
23671 validateRadio : function()
23673 if(this.getVisibilityEl().hasClass('hidden')){
23677 if(this.allowBlank){
23683 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23684 if(!e.dom.checked){
23696 validateCheckbox : function()
23699 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23700 //return (this.getValue() == this.inputValue) ? true : false;
23703 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23711 for(var i in group){
23712 if(group[i].el.isVisible(true)){
23720 for(var i in group){
23725 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23732 * Mark this field as valid
23734 markValid : function()
23738 this.fireEvent('valid', this);
23740 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23743 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23750 if(this.inputType == 'radio'){
23751 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23752 var fg = e.findParent('.form-group', false, true);
23753 if (Roo.bootstrap.version == 3) {
23754 fg.removeClass([_this.invalidClass, _this.validClass]);
23755 fg.addClass(_this.validClass);
23757 fg.removeClass(['is-valid', 'is-invalid']);
23758 fg.addClass('is-valid');
23766 var fg = this.el.findParent('.form-group', false, true);
23767 if (Roo.bootstrap.version == 3) {
23768 fg.removeClass([this.invalidClass, this.validClass]);
23769 fg.addClass(this.validClass);
23771 fg.removeClass(['is-valid', 'is-invalid']);
23772 fg.addClass('is-valid');
23777 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23783 for(var i in group){
23784 var fg = group[i].el.findParent('.form-group', false, true);
23785 if (Roo.bootstrap.version == 3) {
23786 fg.removeClass([this.invalidClass, this.validClass]);
23787 fg.addClass(this.validClass);
23789 fg.removeClass(['is-valid', 'is-invalid']);
23790 fg.addClass('is-valid');
23796 * Mark this field as invalid
23797 * @param {String} msg The validation message
23799 markInvalid : function(msg)
23801 if(this.allowBlank){
23807 this.fireEvent('invalid', this, msg);
23809 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23812 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23816 label.markInvalid();
23819 if(this.inputType == 'radio'){
23821 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23822 var fg = e.findParent('.form-group', false, true);
23823 if (Roo.bootstrap.version == 3) {
23824 fg.removeClass([_this.invalidClass, _this.validClass]);
23825 fg.addClass(_this.invalidClass);
23827 fg.removeClass(['is-invalid', 'is-valid']);
23828 fg.addClass('is-invalid');
23836 var fg = this.el.findParent('.form-group', false, true);
23837 if (Roo.bootstrap.version == 3) {
23838 fg.removeClass([_this.invalidClass, _this.validClass]);
23839 fg.addClass(_this.invalidClass);
23841 fg.removeClass(['is-invalid', 'is-valid']);
23842 fg.addClass('is-invalid');
23847 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23853 for(var i in group){
23854 var fg = group[i].el.findParent('.form-group', false, true);
23855 if (Roo.bootstrap.version == 3) {
23856 fg.removeClass([_this.invalidClass, _this.validClass]);
23857 fg.addClass(_this.invalidClass);
23859 fg.removeClass(['is-invalid', 'is-valid']);
23860 fg.addClass('is-invalid');
23866 clearInvalid : function()
23868 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23870 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23872 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23874 if (label && label.iconEl) {
23875 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23876 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23880 disable : function()
23882 if(this.inputType != 'radio'){
23883 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23890 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23891 _this.getActionEl().addClass(this.disabledClass);
23892 e.dom.disabled = true;
23896 this.disabled = true;
23897 this.fireEvent("disable", this);
23901 enable : function()
23903 if(this.inputType != 'radio'){
23904 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23911 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23912 _this.getActionEl().removeClass(this.disabledClass);
23913 e.dom.disabled = false;
23917 this.disabled = false;
23918 this.fireEvent("enable", this);
23922 setBoxLabel : function(v)
23927 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23933 Roo.apply(Roo.bootstrap.CheckBox, {
23938 * register a CheckBox Group
23939 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23941 register : function(checkbox)
23943 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23944 this.groups[checkbox.groupId] = {};
23947 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23951 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23955 * fetch a CheckBox Group based on the group ID
23956 * @param {string} the group ID
23957 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23959 get: function(groupId) {
23960 if (typeof(this.groups[groupId]) == 'undefined') {
23964 return this.groups[groupId] ;
23977 * @class Roo.bootstrap.Radio
23978 * @extends Roo.bootstrap.Component
23979 * Bootstrap Radio class
23980 * @cfg {String} boxLabel - the label associated
23981 * @cfg {String} value - the value of radio
23984 * Create a new Radio
23985 * @param {Object} config The config object
23987 Roo.bootstrap.Radio = function(config){
23988 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23992 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23998 getAutoCreate : function()
24002 cls : 'form-group radio',
24007 html : this.boxLabel
24015 initEvents : function()
24017 this.parent().register(this);
24019 this.el.on('click', this.onClick, this);
24023 onClick : function(e)
24025 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24026 this.setChecked(true);
24030 setChecked : function(state, suppressEvent)
24032 this.parent().setValue(this.value, suppressEvent);
24036 setBoxLabel : function(v)
24041 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24056 * @class Roo.bootstrap.SecurePass
24057 * @extends Roo.bootstrap.Input
24058 * Bootstrap SecurePass class
24062 * Create a new SecurePass
24063 * @param {Object} config The config object
24066 Roo.bootstrap.SecurePass = function (config) {
24067 // these go here, so the translation tool can replace them..
24069 PwdEmpty: "Please type a password, and then retype it to confirm.",
24070 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24071 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24072 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24073 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24074 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24075 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24076 TooWeak: "Your password is Too Weak."
24078 this.meterLabel = "Password strength:";
24079 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24080 this.meterClass = [
24081 "roo-password-meter-tooweak",
24082 "roo-password-meter-weak",
24083 "roo-password-meter-medium",
24084 "roo-password-meter-strong",
24085 "roo-password-meter-grey"
24090 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24093 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24095 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24097 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24098 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24099 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24100 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24101 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24102 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24103 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24113 * @cfg {String/Object} Label for the strength meter (defaults to
24114 * 'Password strength:')
24119 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24120 * ['Weak', 'Medium', 'Strong'])
24123 pwdStrengths: false,
24136 initEvents: function ()
24138 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24140 if (this.el.is('input[type=password]') && Roo.isSafari) {
24141 this.el.on('keydown', this.SafariOnKeyDown, this);
24144 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24147 onRender: function (ct, position)
24149 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24150 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24151 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24153 this.trigger.createChild({
24158 cls: 'roo-password-meter-grey col-xs-12',
24161 //width: this.meterWidth + 'px'
24165 cls: 'roo-password-meter-text'
24171 if (this.hideTrigger) {
24172 this.trigger.setDisplayed(false);
24174 this.setSize(this.width || '', this.height || '');
24177 onDestroy: function ()
24179 if (this.trigger) {
24180 this.trigger.removeAllListeners();
24181 this.trigger.remove();
24184 this.wrap.remove();
24186 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24189 checkStrength: function ()
24191 var pwd = this.inputEl().getValue();
24192 if (pwd == this._lastPwd) {
24197 if (this.ClientSideStrongPassword(pwd)) {
24199 } else if (this.ClientSideMediumPassword(pwd)) {
24201 } else if (this.ClientSideWeakPassword(pwd)) {
24207 Roo.log('strength1: ' + strength);
24209 //var pm = this.trigger.child('div/div/div').dom;
24210 var pm = this.trigger.child('div/div');
24211 pm.removeClass(this.meterClass);
24212 pm.addClass(this.meterClass[strength]);
24215 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24217 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24219 this._lastPwd = pwd;
24223 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24225 this._lastPwd = '';
24227 var pm = this.trigger.child('div/div');
24228 pm.removeClass(this.meterClass);
24229 pm.addClass('roo-password-meter-grey');
24232 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24235 this.inputEl().dom.type='password';
24238 validateValue: function (value)
24240 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24243 if (value.length == 0) {
24244 if (this.allowBlank) {
24245 this.clearInvalid();
24249 this.markInvalid(this.errors.PwdEmpty);
24250 this.errorMsg = this.errors.PwdEmpty;
24258 if (!value.match(/[\x21-\x7e]+/)) {
24259 this.markInvalid(this.errors.PwdBadChar);
24260 this.errorMsg = this.errors.PwdBadChar;
24263 if (value.length < 6) {
24264 this.markInvalid(this.errors.PwdShort);
24265 this.errorMsg = this.errors.PwdShort;
24268 if (value.length > 16) {
24269 this.markInvalid(this.errors.PwdLong);
24270 this.errorMsg = this.errors.PwdLong;
24274 if (this.ClientSideStrongPassword(value)) {
24276 } else if (this.ClientSideMediumPassword(value)) {
24278 } else if (this.ClientSideWeakPassword(value)) {
24285 if (strength < 2) {
24286 //this.markInvalid(this.errors.TooWeak);
24287 this.errorMsg = this.errors.TooWeak;
24292 console.log('strength2: ' + strength);
24294 //var pm = this.trigger.child('div/div/div').dom;
24296 var pm = this.trigger.child('div/div');
24297 pm.removeClass(this.meterClass);
24298 pm.addClass(this.meterClass[strength]);
24300 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24302 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24304 this.errorMsg = '';
24308 CharacterSetChecks: function (type)
24311 this.fResult = false;
24314 isctype: function (character, type)
24317 case this.kCapitalLetter:
24318 if (character >= 'A' && character <= 'Z') {
24323 case this.kSmallLetter:
24324 if (character >= 'a' && character <= 'z') {
24330 if (character >= '0' && character <= '9') {
24335 case this.kPunctuation:
24336 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24347 IsLongEnough: function (pwd, size)
24349 return !(pwd == null || isNaN(size) || pwd.length < size);
24352 SpansEnoughCharacterSets: function (word, nb)
24354 if (!this.IsLongEnough(word, nb))
24359 var characterSetChecks = new Array(
24360 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24361 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24364 for (var index = 0; index < word.length; ++index) {
24365 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24366 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24367 characterSetChecks[nCharSet].fResult = true;
24374 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24375 if (characterSetChecks[nCharSet].fResult) {
24380 if (nCharSets < nb) {
24386 ClientSideStrongPassword: function (pwd)
24388 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24391 ClientSideMediumPassword: function (pwd)
24393 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24396 ClientSideWeakPassword: function (pwd)
24398 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24401 })//<script type="text/javascript">
24404 * Based Ext JS Library 1.1.1
24405 * Copyright(c) 2006-2007, Ext JS, LLC.
24411 * @class Roo.HtmlEditorCore
24412 * @extends Roo.Component
24413 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24415 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24418 Roo.HtmlEditorCore = function(config){
24421 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24426 * @event initialize
24427 * Fires when the editor is fully initialized (including the iframe)
24428 * @param {Roo.HtmlEditorCore} this
24433 * Fires when the editor is first receives the focus. Any insertion must wait
24434 * until after this event.
24435 * @param {Roo.HtmlEditorCore} this
24439 * @event beforesync
24440 * Fires before the textarea is updated with content from the editor iframe. Return false
24441 * to cancel the sync.
24442 * @param {Roo.HtmlEditorCore} this
24443 * @param {String} html
24447 * @event beforepush
24448 * Fires before the iframe editor is updated with content from the textarea. Return false
24449 * to cancel the push.
24450 * @param {Roo.HtmlEditorCore} this
24451 * @param {String} html
24456 * Fires when the textarea is updated with content from the editor iframe.
24457 * @param {Roo.HtmlEditorCore} this
24458 * @param {String} html
24463 * Fires when the iframe editor is updated with content from the textarea.
24464 * @param {Roo.HtmlEditorCore} this
24465 * @param {String} html
24470 * @event editorevent
24471 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24472 * @param {Roo.HtmlEditorCore} this
24478 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24480 // defaults : white / black...
24481 this.applyBlacklists();
24488 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24492 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24498 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24503 * @cfg {Number} height (in pixels)
24507 * @cfg {Number} width (in pixels)
24512 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24515 stylesheets: false,
24520 // private properties
24521 validationEvent : false,
24523 initialized : false,
24525 sourceEditMode : false,
24526 onFocus : Roo.emptyFn,
24528 hideMode:'offsets',
24532 // blacklist + whitelisted elements..
24539 * Protected method that will not generally be called directly. It
24540 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24541 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24543 getDocMarkup : function(){
24547 // inherit styels from page...??
24548 if (this.stylesheets === false) {
24550 Roo.get(document.head).select('style').each(function(node) {
24551 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24554 Roo.get(document.head).select('link').each(function(node) {
24555 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24558 } else if (!this.stylesheets.length) {
24560 st = '<style type="text/css">' +
24561 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24564 for (var i in this.stylesheets) {
24565 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24570 st += '<style type="text/css">' +
24571 'IMG { cursor: pointer } ' +
24574 var cls = 'roo-htmleditor-body';
24576 if(this.bodyCls.length){
24577 cls += ' ' + this.bodyCls;
24580 return '<html><head>' + st +
24581 //<style type="text/css">' +
24582 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24584 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24588 onRender : function(ct, position)
24591 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24592 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24595 this.el.dom.style.border = '0 none';
24596 this.el.dom.setAttribute('tabIndex', -1);
24597 this.el.addClass('x-hidden hide');
24601 if(Roo.isIE){ // fix IE 1px bogus margin
24602 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24606 this.frameId = Roo.id();
24610 var iframe = this.owner.wrap.createChild({
24612 cls: 'form-control', // bootstrap..
24614 name: this.frameId,
24615 frameBorder : 'no',
24616 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24621 this.iframe = iframe.dom;
24623 this.assignDocWin();
24625 this.doc.designMode = 'on';
24628 this.doc.write(this.getDocMarkup());
24632 var task = { // must defer to wait for browser to be ready
24634 //console.log("run task?" + this.doc.readyState);
24635 this.assignDocWin();
24636 if(this.doc.body || this.doc.readyState == 'complete'){
24638 this.doc.designMode="on";
24642 Roo.TaskMgr.stop(task);
24643 this.initEditor.defer(10, this);
24650 Roo.TaskMgr.start(task);
24655 onResize : function(w, h)
24657 Roo.log('resize: ' +w + ',' + h );
24658 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24662 if(typeof w == 'number'){
24664 this.iframe.style.width = w + 'px';
24666 if(typeof h == 'number'){
24668 this.iframe.style.height = h + 'px';
24670 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24677 * Toggles the editor between standard and source edit mode.
24678 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24680 toggleSourceEdit : function(sourceEditMode){
24682 this.sourceEditMode = sourceEditMode === true;
24684 if(this.sourceEditMode){
24686 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24689 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24690 //this.iframe.className = '';
24693 //this.setSize(this.owner.wrap.getSize());
24694 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24701 * Protected method that will not generally be called directly. If you need/want
24702 * custom HTML cleanup, this is the method you should override.
24703 * @param {String} html The HTML to be cleaned
24704 * return {String} The cleaned HTML
24706 cleanHtml : function(html){
24707 html = String(html);
24708 if(html.length > 5){
24709 if(Roo.isSafari){ // strip safari nonsense
24710 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24713 if(html == ' '){
24720 * HTML Editor -> Textarea
24721 * Protected method that will not generally be called directly. Syncs the contents
24722 * of the editor iframe with the textarea.
24724 syncValue : function(){
24725 if(this.initialized){
24726 var bd = (this.doc.body || this.doc.documentElement);
24727 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24728 var html = bd.innerHTML;
24730 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24731 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24733 html = '<div style="'+m[0]+'">' + html + '</div>';
24736 html = this.cleanHtml(html);
24737 // fix up the special chars.. normaly like back quotes in word...
24738 // however we do not want to do this with chinese..
24739 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24741 var cc = match.charCodeAt();
24743 // Get the character value, handling surrogate pairs
24744 if (match.length == 2) {
24745 // It's a surrogate pair, calculate the Unicode code point
24746 var high = match.charCodeAt(0) - 0xD800;
24747 var low = match.charCodeAt(1) - 0xDC00;
24748 cc = (high * 0x400) + low + 0x10000;
24750 (cc >= 0x4E00 && cc < 0xA000 ) ||
24751 (cc >= 0x3400 && cc < 0x4E00 ) ||
24752 (cc >= 0xf900 && cc < 0xfb00 )
24757 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24758 return "&#" + cc + ";";
24765 if(this.owner.fireEvent('beforesync', this, html) !== false){
24766 this.el.dom.value = html;
24767 this.owner.fireEvent('sync', this, html);
24773 * Protected method that will not generally be called directly. Pushes the value of the textarea
24774 * into the iframe editor.
24776 pushValue : function(){
24777 if(this.initialized){
24778 var v = this.el.dom.value.trim();
24780 // if(v.length < 1){
24784 if(this.owner.fireEvent('beforepush', this, v) !== false){
24785 var d = (this.doc.body || this.doc.documentElement);
24787 this.cleanUpPaste();
24788 this.el.dom.value = d.innerHTML;
24789 this.owner.fireEvent('push', this, v);
24795 deferFocus : function(){
24796 this.focus.defer(10, this);
24800 focus : function(){
24801 if(this.win && !this.sourceEditMode){
24808 assignDocWin: function()
24810 var iframe = this.iframe;
24813 this.doc = iframe.contentWindow.document;
24814 this.win = iframe.contentWindow;
24816 // if (!Roo.get(this.frameId)) {
24819 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24820 // this.win = Roo.get(this.frameId).dom.contentWindow;
24822 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24826 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24827 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24832 initEditor : function(){
24833 //console.log("INIT EDITOR");
24834 this.assignDocWin();
24838 this.doc.designMode="on";
24840 this.doc.write(this.getDocMarkup());
24843 var dbody = (this.doc.body || this.doc.documentElement);
24844 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24845 // this copies styles from the containing element into thsi one..
24846 // not sure why we need all of this..
24847 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24849 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24850 //ss['background-attachment'] = 'fixed'; // w3c
24851 dbody.bgProperties = 'fixed'; // ie
24852 //Roo.DomHelper.applyStyles(dbody, ss);
24853 Roo.EventManager.on(this.doc, {
24854 //'mousedown': this.onEditorEvent,
24855 'mouseup': this.onEditorEvent,
24856 'dblclick': this.onEditorEvent,
24857 'click': this.onEditorEvent,
24858 'keyup': this.onEditorEvent,
24863 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24865 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24866 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24868 this.initialized = true;
24870 this.owner.fireEvent('initialize', this);
24875 onDestroy : function(){
24881 //for (var i =0; i < this.toolbars.length;i++) {
24882 // // fixme - ask toolbars for heights?
24883 // this.toolbars[i].onDestroy();
24886 //this.wrap.dom.innerHTML = '';
24887 //this.wrap.remove();
24892 onFirstFocus : function(){
24894 this.assignDocWin();
24897 this.activated = true;
24900 if(Roo.isGecko){ // prevent silly gecko errors
24902 var s = this.win.getSelection();
24903 if(!s.focusNode || s.focusNode.nodeType != 3){
24904 var r = s.getRangeAt(0);
24905 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24910 this.execCmd('useCSS', true);
24911 this.execCmd('styleWithCSS', false);
24914 this.owner.fireEvent('activate', this);
24918 adjustFont: function(btn){
24919 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24920 //if(Roo.isSafari){ // safari
24923 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24924 if(Roo.isSafari){ // safari
24925 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24926 v = (v < 10) ? 10 : v;
24927 v = (v > 48) ? 48 : v;
24928 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24933 v = Math.max(1, v+adjust);
24935 this.execCmd('FontSize', v );
24938 onEditorEvent : function(e)
24940 this.owner.fireEvent('editorevent', this, e);
24941 // this.updateToolbar();
24942 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24945 insertTag : function(tg)
24947 // could be a bit smarter... -> wrap the current selected tRoo..
24948 if (tg.toLowerCase() == 'span' ||
24949 tg.toLowerCase() == 'code' ||
24950 tg.toLowerCase() == 'sup' ||
24951 tg.toLowerCase() == 'sub'
24954 range = this.createRange(this.getSelection());
24955 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24956 wrappingNode.appendChild(range.extractContents());
24957 range.insertNode(wrappingNode);
24964 this.execCmd("formatblock", tg);
24968 insertText : function(txt)
24972 var range = this.createRange();
24973 range.deleteContents();
24974 //alert(Sender.getAttribute('label'));
24976 range.insertNode(this.doc.createTextNode(txt));
24982 * Executes a Midas editor command on the editor document and performs necessary focus and
24983 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24984 * @param {String} cmd The Midas command
24985 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24987 relayCmd : function(cmd, value){
24989 this.execCmd(cmd, value);
24990 this.owner.fireEvent('editorevent', this);
24991 //this.updateToolbar();
24992 this.owner.deferFocus();
24996 * Executes a Midas editor command directly on the editor document.
24997 * For visual commands, you should use {@link #relayCmd} instead.
24998 * <b>This should only be called after the editor is initialized.</b>
24999 * @param {String} cmd The Midas command
25000 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25002 execCmd : function(cmd, value){
25003 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25010 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25012 * @param {String} text | dom node..
25014 insertAtCursor : function(text)
25017 if(!this.activated){
25023 var r = this.doc.selection.createRange();
25034 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25038 // from jquery ui (MIT licenced)
25040 var win = this.win;
25042 if (win.getSelection && win.getSelection().getRangeAt) {
25043 range = win.getSelection().getRangeAt(0);
25044 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25045 range.insertNode(node);
25046 } else if (win.document.selection && win.document.selection.createRange) {
25047 // no firefox support
25048 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25049 win.document.selection.createRange().pasteHTML(txt);
25051 // no firefox support
25052 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25053 this.execCmd('InsertHTML', txt);
25062 mozKeyPress : function(e){
25064 var c = e.getCharCode(), cmd;
25067 c = String.fromCharCode(c).toLowerCase();
25081 this.cleanUpPaste.defer(100, this);
25089 e.preventDefault();
25097 fixKeys : function(){ // load time branching for fastest keydown performance
25099 return function(e){
25100 var k = e.getKey(), r;
25103 r = this.doc.selection.createRange();
25106 r.pasteHTML('    ');
25113 r = this.doc.selection.createRange();
25115 var target = r.parentElement();
25116 if(!target || target.tagName.toLowerCase() != 'li'){
25118 r.pasteHTML('<br />');
25124 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25125 this.cleanUpPaste.defer(100, this);
25131 }else if(Roo.isOpera){
25132 return function(e){
25133 var k = e.getKey();
25137 this.execCmd('InsertHTML','    ');
25140 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25141 this.cleanUpPaste.defer(100, this);
25146 }else if(Roo.isSafari){
25147 return function(e){
25148 var k = e.getKey();
25152 this.execCmd('InsertText','\t');
25156 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25157 this.cleanUpPaste.defer(100, this);
25165 getAllAncestors: function()
25167 var p = this.getSelectedNode();
25170 a.push(p); // push blank onto stack..
25171 p = this.getParentElement();
25175 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25179 a.push(this.doc.body);
25183 lastSelNode : false,
25186 getSelection : function()
25188 this.assignDocWin();
25189 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25192 getSelectedNode: function()
25194 // this may only work on Gecko!!!
25196 // should we cache this!!!!
25201 var range = this.createRange(this.getSelection()).cloneRange();
25204 var parent = range.parentElement();
25206 var testRange = range.duplicate();
25207 testRange.moveToElementText(parent);
25208 if (testRange.inRange(range)) {
25211 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25214 parent = parent.parentElement;
25219 // is ancestor a text element.
25220 var ac = range.commonAncestorContainer;
25221 if (ac.nodeType == 3) {
25222 ac = ac.parentNode;
25225 var ar = ac.childNodes;
25228 var other_nodes = [];
25229 var has_other_nodes = false;
25230 for (var i=0;i<ar.length;i++) {
25231 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25234 // fullly contained node.
25236 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25241 // probably selected..
25242 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25243 other_nodes.push(ar[i]);
25247 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25252 has_other_nodes = true;
25254 if (!nodes.length && other_nodes.length) {
25255 nodes= other_nodes;
25257 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25263 createRange: function(sel)
25265 // this has strange effects when using with
25266 // top toolbar - not sure if it's a great idea.
25267 //this.editor.contentWindow.focus();
25268 if (typeof sel != "undefined") {
25270 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25272 return this.doc.createRange();
25275 return this.doc.createRange();
25278 getParentElement: function()
25281 this.assignDocWin();
25282 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25284 var range = this.createRange(sel);
25287 var p = range.commonAncestorContainer;
25288 while (p.nodeType == 3) { // text node
25299 * Range intersection.. the hard stuff...
25303 * [ -- selected range --- ]
25307 * if end is before start or hits it. fail.
25308 * if start is after end or hits it fail.
25310 * if either hits (but other is outside. - then it's not
25316 // @see http://www.thismuchiknow.co.uk/?p=64.
25317 rangeIntersectsNode : function(range, node)
25319 var nodeRange = node.ownerDocument.createRange();
25321 nodeRange.selectNode(node);
25323 nodeRange.selectNodeContents(node);
25326 var rangeStartRange = range.cloneRange();
25327 rangeStartRange.collapse(true);
25329 var rangeEndRange = range.cloneRange();
25330 rangeEndRange.collapse(false);
25332 var nodeStartRange = nodeRange.cloneRange();
25333 nodeStartRange.collapse(true);
25335 var nodeEndRange = nodeRange.cloneRange();
25336 nodeEndRange.collapse(false);
25338 return rangeStartRange.compareBoundaryPoints(
25339 Range.START_TO_START, nodeEndRange) == -1 &&
25340 rangeEndRange.compareBoundaryPoints(
25341 Range.START_TO_START, nodeStartRange) == 1;
25345 rangeCompareNode : function(range, node)
25347 var nodeRange = node.ownerDocument.createRange();
25349 nodeRange.selectNode(node);
25351 nodeRange.selectNodeContents(node);
25355 range.collapse(true);
25357 nodeRange.collapse(true);
25359 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25360 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25362 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25364 var nodeIsBefore = ss == 1;
25365 var nodeIsAfter = ee == -1;
25367 if (nodeIsBefore && nodeIsAfter) {
25370 if (!nodeIsBefore && nodeIsAfter) {
25371 return 1; //right trailed.
25374 if (nodeIsBefore && !nodeIsAfter) {
25375 return 2; // left trailed.
25381 // private? - in a new class?
25382 cleanUpPaste : function()
25384 // cleans up the whole document..
25385 Roo.log('cleanuppaste');
25387 this.cleanUpChildren(this.doc.body);
25388 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25389 if (clean != this.doc.body.innerHTML) {
25390 this.doc.body.innerHTML = clean;
25395 cleanWordChars : function(input) {// change the chars to hex code
25396 var he = Roo.HtmlEditorCore;
25398 var output = input;
25399 Roo.each(he.swapCodes, function(sw) {
25400 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25402 output = output.replace(swapper, sw[1]);
25409 cleanUpChildren : function (n)
25411 if (!n.childNodes.length) {
25414 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25415 this.cleanUpChild(n.childNodes[i]);
25422 cleanUpChild : function (node)
25425 //console.log(node);
25426 if (node.nodeName == "#text") {
25427 // clean up silly Windows -- stuff?
25430 if (node.nodeName == "#comment") {
25431 node.parentNode.removeChild(node);
25432 // clean up silly Windows -- stuff?
25435 var lcname = node.tagName.toLowerCase();
25436 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25437 // whitelist of tags..
25439 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25441 node.parentNode.removeChild(node);
25446 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25448 // spans with no attributes - just remove them..
25449 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25450 remove_keep_children = true;
25453 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25454 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25456 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25457 // remove_keep_children = true;
25460 if (remove_keep_children) {
25461 this.cleanUpChildren(node);
25462 // inserts everything just before this node...
25463 while (node.childNodes.length) {
25464 var cn = node.childNodes[0];
25465 node.removeChild(cn);
25466 node.parentNode.insertBefore(cn, node);
25468 node.parentNode.removeChild(node);
25472 if (!node.attributes || !node.attributes.length) {
25477 this.cleanUpChildren(node);
25481 function cleanAttr(n,v)
25484 if (v.match(/^\./) || v.match(/^\//)) {
25487 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25490 if (v.match(/^#/)) {
25493 if (v.match(/^\{/)) { // allow template editing.
25496 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25497 node.removeAttribute(n);
25501 var cwhite = this.cwhite;
25502 var cblack = this.cblack;
25504 function cleanStyle(n,v)
25506 if (v.match(/expression/)) { //XSS?? should we even bother..
25507 node.removeAttribute(n);
25511 var parts = v.split(/;/);
25514 Roo.each(parts, function(p) {
25515 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25519 var l = p.split(':').shift().replace(/\s+/g,'');
25520 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25522 if ( cwhite.length && cblack.indexOf(l) > -1) {
25523 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25524 //node.removeAttribute(n);
25528 // only allow 'c whitelisted system attributes'
25529 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25530 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25531 //node.removeAttribute(n);
25541 if (clean.length) {
25542 node.setAttribute(n, clean.join(';'));
25544 node.removeAttribute(n);
25550 for (var i = node.attributes.length-1; i > -1 ; i--) {
25551 var a = node.attributes[i];
25554 if (a.name.toLowerCase().substr(0,2)=='on') {
25555 node.removeAttribute(a.name);
25558 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25559 node.removeAttribute(a.name);
25562 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25563 cleanAttr(a.name,a.value); // fixme..
25566 if (a.name == 'style') {
25567 cleanStyle(a.name,a.value);
25570 /// clean up MS crap..
25571 // tecnically this should be a list of valid class'es..
25574 if (a.name == 'class') {
25575 if (a.value.match(/^Mso/)) {
25576 node.removeAttribute('class');
25579 if (a.value.match(/^body$/)) {
25580 node.removeAttribute('class');
25591 this.cleanUpChildren(node);
25597 * Clean up MS wordisms...
25599 cleanWord : function(node)
25602 this.cleanWord(this.doc.body);
25607 node.nodeName == 'SPAN' &&
25608 !node.hasAttributes() &&
25609 node.childNodes.length == 1 &&
25610 node.firstChild.nodeName == "#text"
25612 var textNode = node.firstChild;
25613 node.removeChild(textNode);
25614 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25615 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25617 node.parentNode.insertBefore(textNode, node);
25618 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25619 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25621 node.parentNode.removeChild(node);
25624 if (node.nodeName == "#text") {
25625 // clean up silly Windows -- stuff?
25628 if (node.nodeName == "#comment") {
25629 node.parentNode.removeChild(node);
25630 // clean up silly Windows -- stuff?
25634 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25635 node.parentNode.removeChild(node);
25638 //Roo.log(node.tagName);
25639 // remove - but keep children..
25640 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25641 //Roo.log('-- removed');
25642 while (node.childNodes.length) {
25643 var cn = node.childNodes[0];
25644 node.removeChild(cn);
25645 node.parentNode.insertBefore(cn, node);
25646 // move node to parent - and clean it..
25647 this.cleanWord(cn);
25649 node.parentNode.removeChild(node);
25650 /// no need to iterate chidlren = it's got none..
25651 //this.iterateChildren(node, this.cleanWord);
25655 if (node.className.length) {
25657 var cn = node.className.split(/\W+/);
25659 Roo.each(cn, function(cls) {
25660 if (cls.match(/Mso[a-zA-Z]+/)) {
25665 node.className = cna.length ? cna.join(' ') : '';
25667 node.removeAttribute("class");
25671 if (node.hasAttribute("lang")) {
25672 node.removeAttribute("lang");
25675 if (node.hasAttribute("style")) {
25677 var styles = node.getAttribute("style").split(";");
25679 Roo.each(styles, function(s) {
25680 if (!s.match(/:/)) {
25683 var kv = s.split(":");
25684 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25687 // what ever is left... we allow.
25690 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25691 if (!nstyle.length) {
25692 node.removeAttribute('style');
25695 this.iterateChildren(node, this.cleanWord);
25701 * iterateChildren of a Node, calling fn each time, using this as the scole..
25702 * @param {DomNode} node node to iterate children of.
25703 * @param {Function} fn method of this class to call on each item.
25705 iterateChildren : function(node, fn)
25707 if (!node.childNodes.length) {
25710 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25711 fn.call(this, node.childNodes[i])
25717 * cleanTableWidths.
25719 * Quite often pasting from word etc.. results in tables with column and widths.
25720 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25723 cleanTableWidths : function(node)
25728 this.cleanTableWidths(this.doc.body);
25733 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25736 Roo.log(node.tagName);
25737 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25738 this.iterateChildren(node, this.cleanTableWidths);
25741 if (node.hasAttribute('width')) {
25742 node.removeAttribute('width');
25746 if (node.hasAttribute("style")) {
25749 var styles = node.getAttribute("style").split(";");
25751 Roo.each(styles, function(s) {
25752 if (!s.match(/:/)) {
25755 var kv = s.split(":");
25756 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25759 // what ever is left... we allow.
25762 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25763 if (!nstyle.length) {
25764 node.removeAttribute('style');
25768 this.iterateChildren(node, this.cleanTableWidths);
25776 domToHTML : function(currentElement, depth, nopadtext) {
25778 depth = depth || 0;
25779 nopadtext = nopadtext || false;
25781 if (!currentElement) {
25782 return this.domToHTML(this.doc.body);
25785 //Roo.log(currentElement);
25787 var allText = false;
25788 var nodeName = currentElement.nodeName;
25789 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25791 if (nodeName == '#text') {
25793 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25798 if (nodeName != 'BODY') {
25801 // Prints the node tagName, such as <A>, <IMG>, etc
25804 for(i = 0; i < currentElement.attributes.length;i++) {
25806 var aname = currentElement.attributes.item(i).name;
25807 if (!currentElement.attributes.item(i).value.length) {
25810 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25813 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25822 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25825 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25830 // Traverse the tree
25832 var currentElementChild = currentElement.childNodes.item(i);
25833 var allText = true;
25834 var innerHTML = '';
25836 while (currentElementChild) {
25837 // Formatting code (indent the tree so it looks nice on the screen)
25838 var nopad = nopadtext;
25839 if (lastnode == 'SPAN') {
25843 if (currentElementChild.nodeName == '#text') {
25844 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25845 toadd = nopadtext ? toadd : toadd.trim();
25846 if (!nopad && toadd.length > 80) {
25847 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25849 innerHTML += toadd;
25852 currentElementChild = currentElement.childNodes.item(i);
25858 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25860 // Recursively traverse the tree structure of the child node
25861 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25862 lastnode = currentElementChild.nodeName;
25864 currentElementChild=currentElement.childNodes.item(i);
25870 // The remaining code is mostly for formatting the tree
25871 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25876 ret+= "</"+tagName+">";
25882 applyBlacklists : function()
25884 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25885 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25889 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25890 if (b.indexOf(tag) > -1) {
25893 this.white.push(tag);
25897 Roo.each(w, function(tag) {
25898 if (b.indexOf(tag) > -1) {
25901 if (this.white.indexOf(tag) > -1) {
25904 this.white.push(tag);
25909 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25910 if (w.indexOf(tag) > -1) {
25913 this.black.push(tag);
25917 Roo.each(b, function(tag) {
25918 if (w.indexOf(tag) > -1) {
25921 if (this.black.indexOf(tag) > -1) {
25924 this.black.push(tag);
25929 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25930 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25934 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25935 if (b.indexOf(tag) > -1) {
25938 this.cwhite.push(tag);
25942 Roo.each(w, function(tag) {
25943 if (b.indexOf(tag) > -1) {
25946 if (this.cwhite.indexOf(tag) > -1) {
25949 this.cwhite.push(tag);
25954 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25955 if (w.indexOf(tag) > -1) {
25958 this.cblack.push(tag);
25962 Roo.each(b, function(tag) {
25963 if (w.indexOf(tag) > -1) {
25966 if (this.cblack.indexOf(tag) > -1) {
25969 this.cblack.push(tag);
25974 setStylesheets : function(stylesheets)
25976 if(typeof(stylesheets) == 'string'){
25977 Roo.get(this.iframe.contentDocument.head).createChild({
25979 rel : 'stylesheet',
25988 Roo.each(stylesheets, function(s) {
25993 Roo.get(_this.iframe.contentDocument.head).createChild({
25995 rel : 'stylesheet',
26004 removeStylesheets : function()
26008 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26013 setStyle : function(style)
26015 Roo.get(this.iframe.contentDocument.head).createChild({
26024 // hide stuff that is not compatible
26038 * @event specialkey
26042 * @cfg {String} fieldClass @hide
26045 * @cfg {String} focusClass @hide
26048 * @cfg {String} autoCreate @hide
26051 * @cfg {String} inputType @hide
26054 * @cfg {String} invalidClass @hide
26057 * @cfg {String} invalidText @hide
26060 * @cfg {String} msgFx @hide
26063 * @cfg {String} validateOnBlur @hide
26067 Roo.HtmlEditorCore.white = [
26068 'area', 'br', 'img', 'input', 'hr', 'wbr',
26070 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26071 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26072 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26073 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26074 'table', 'ul', 'xmp',
26076 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26079 'dir', 'menu', 'ol', 'ul', 'dl',
26085 Roo.HtmlEditorCore.black = [
26086 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26088 'base', 'basefont', 'bgsound', 'blink', 'body',
26089 'frame', 'frameset', 'head', 'html', 'ilayer',
26090 'iframe', 'layer', 'link', 'meta', 'object',
26091 'script', 'style' ,'title', 'xml' // clean later..
26093 Roo.HtmlEditorCore.clean = [
26094 'script', 'style', 'title', 'xml'
26096 Roo.HtmlEditorCore.remove = [
26101 Roo.HtmlEditorCore.ablack = [
26105 Roo.HtmlEditorCore.aclean = [
26106 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26110 Roo.HtmlEditorCore.pwhite= [
26111 'http', 'https', 'mailto'
26114 // white listed style attributes.
26115 Roo.HtmlEditorCore.cwhite= [
26116 // 'text-align', /// default is to allow most things..
26122 // black listed style attributes.
26123 Roo.HtmlEditorCore.cblack= [
26124 // 'font-size' -- this can be set by the project
26128 Roo.HtmlEditorCore.swapCodes =[
26129 [ 8211, "–" ],
26130 [ 8212, "—" ],
26147 * @class Roo.bootstrap.HtmlEditor
26148 * @extends Roo.bootstrap.TextArea
26149 * Bootstrap HtmlEditor class
26152 * Create a new HtmlEditor
26153 * @param {Object} config The config object
26156 Roo.bootstrap.HtmlEditor = function(config){
26157 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26158 if (!this.toolbars) {
26159 this.toolbars = [];
26162 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26165 * @event initialize
26166 * Fires when the editor is fully initialized (including the iframe)
26167 * @param {HtmlEditor} this
26172 * Fires when the editor is first receives the focus. Any insertion must wait
26173 * until after this event.
26174 * @param {HtmlEditor} this
26178 * @event beforesync
26179 * Fires before the textarea is updated with content from the editor iframe. Return false
26180 * to cancel the sync.
26181 * @param {HtmlEditor} this
26182 * @param {String} html
26186 * @event beforepush
26187 * Fires before the iframe editor is updated with content from the textarea. Return false
26188 * to cancel the push.
26189 * @param {HtmlEditor} this
26190 * @param {String} html
26195 * Fires when the textarea is updated with content from the editor iframe.
26196 * @param {HtmlEditor} this
26197 * @param {String} html
26202 * Fires when the iframe editor is updated with content from the textarea.
26203 * @param {HtmlEditor} this
26204 * @param {String} html
26208 * @event editmodechange
26209 * Fires when the editor switches edit modes
26210 * @param {HtmlEditor} this
26211 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26213 editmodechange: true,
26215 * @event editorevent
26216 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26217 * @param {HtmlEditor} this
26221 * @event firstfocus
26222 * Fires when on first focus - needed by toolbars..
26223 * @param {HtmlEditor} this
26228 * Auto save the htmlEditor value as a file into Events
26229 * @param {HtmlEditor} this
26233 * @event savedpreview
26234 * preview the saved version of htmlEditor
26235 * @param {HtmlEditor} this
26242 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26246 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26251 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26256 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26261 * @cfg {Number} height (in pixels)
26265 * @cfg {Number} width (in pixels)
26270 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26273 stylesheets: false,
26278 // private properties
26279 validationEvent : false,
26281 initialized : false,
26284 onFocus : Roo.emptyFn,
26286 hideMode:'offsets',
26288 tbContainer : false,
26292 toolbarContainer :function() {
26293 return this.wrap.select('.x-html-editor-tb',true).first();
26297 * Protected method that will not generally be called directly. It
26298 * is called when the editor creates its toolbar. Override this method if you need to
26299 * add custom toolbar buttons.
26300 * @param {HtmlEditor} editor
26302 createToolbar : function(){
26303 Roo.log('renewing');
26304 Roo.log("create toolbars");
26306 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26307 this.toolbars[0].render(this.toolbarContainer());
26311 // if (!editor.toolbars || !editor.toolbars.length) {
26312 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26315 // for (var i =0 ; i < editor.toolbars.length;i++) {
26316 // editor.toolbars[i] = Roo.factory(
26317 // typeof(editor.toolbars[i]) == 'string' ?
26318 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26319 // Roo.bootstrap.HtmlEditor);
26320 // editor.toolbars[i].init(editor);
26326 onRender : function(ct, position)
26328 // Roo.log("Call onRender: " + this.xtype);
26330 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26332 this.wrap = this.inputEl().wrap({
26333 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26336 this.editorcore.onRender(ct, position);
26338 if (this.resizable) {
26339 this.resizeEl = new Roo.Resizable(this.wrap, {
26343 minHeight : this.height,
26344 height: this.height,
26345 handles : this.resizable,
26348 resize : function(r, w, h) {
26349 _t.onResize(w,h); // -something
26355 this.createToolbar(this);
26358 if(!this.width && this.resizable){
26359 this.setSize(this.wrap.getSize());
26361 if (this.resizeEl) {
26362 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26363 // should trigger onReize..
26369 onResize : function(w, h)
26371 Roo.log('resize: ' +w + ',' + h );
26372 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26376 if(this.inputEl() ){
26377 if(typeof w == 'number'){
26378 var aw = w - this.wrap.getFrameWidth('lr');
26379 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26382 if(typeof h == 'number'){
26383 var tbh = -11; // fixme it needs to tool bar size!
26384 for (var i =0; i < this.toolbars.length;i++) {
26385 // fixme - ask toolbars for heights?
26386 tbh += this.toolbars[i].el.getHeight();
26387 //if (this.toolbars[i].footer) {
26388 // tbh += this.toolbars[i].footer.el.getHeight();
26396 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26397 ah -= 5; // knock a few pixes off for look..
26398 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26402 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26403 this.editorcore.onResize(ew,eh);
26408 * Toggles the editor between standard and source edit mode.
26409 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26411 toggleSourceEdit : function(sourceEditMode)
26413 this.editorcore.toggleSourceEdit(sourceEditMode);
26415 if(this.editorcore.sourceEditMode){
26416 Roo.log('editor - showing textarea');
26419 // Roo.log(this.syncValue());
26421 this.inputEl().removeClass(['hide', 'x-hidden']);
26422 this.inputEl().dom.removeAttribute('tabIndex');
26423 this.inputEl().focus();
26425 Roo.log('editor - hiding textarea');
26427 // Roo.log(this.pushValue());
26430 this.inputEl().addClass(['hide', 'x-hidden']);
26431 this.inputEl().dom.setAttribute('tabIndex', -1);
26432 //this.deferFocus();
26435 if(this.resizable){
26436 this.setSize(this.wrap.getSize());
26439 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26442 // private (for BoxComponent)
26443 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26445 // private (for BoxComponent)
26446 getResizeEl : function(){
26450 // private (for BoxComponent)
26451 getPositionEl : function(){
26456 initEvents : function(){
26457 this.originalValue = this.getValue();
26461 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26464 // markInvalid : Roo.emptyFn,
26466 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26469 // clearInvalid : Roo.emptyFn,
26471 setValue : function(v){
26472 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26473 this.editorcore.pushValue();
26478 deferFocus : function(){
26479 this.focus.defer(10, this);
26483 focus : function(){
26484 this.editorcore.focus();
26490 onDestroy : function(){
26496 for (var i =0; i < this.toolbars.length;i++) {
26497 // fixme - ask toolbars for heights?
26498 this.toolbars[i].onDestroy();
26501 this.wrap.dom.innerHTML = '';
26502 this.wrap.remove();
26507 onFirstFocus : function(){
26508 //Roo.log("onFirstFocus");
26509 this.editorcore.onFirstFocus();
26510 for (var i =0; i < this.toolbars.length;i++) {
26511 this.toolbars[i].onFirstFocus();
26517 syncValue : function()
26519 this.editorcore.syncValue();
26522 pushValue : function()
26524 this.editorcore.pushValue();
26528 // hide stuff that is not compatible
26542 * @event specialkey
26546 * @cfg {String} fieldClass @hide
26549 * @cfg {String} focusClass @hide
26552 * @cfg {String} autoCreate @hide
26555 * @cfg {String} inputType @hide
26559 * @cfg {String} invalidText @hide
26562 * @cfg {String} msgFx @hide
26565 * @cfg {String} validateOnBlur @hide
26574 Roo.namespace('Roo.bootstrap.htmleditor');
26576 * @class Roo.bootstrap.HtmlEditorToolbar1
26582 new Roo.bootstrap.HtmlEditor({
26585 new Roo.bootstrap.HtmlEditorToolbar1({
26586 disable : { fonts: 1 , format: 1, ..., ... , ...],
26592 * @cfg {Object} disable List of elements to disable..
26593 * @cfg {Array} btns List of additional buttons.
26597 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26600 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26603 Roo.apply(this, config);
26605 // default disabled, based on 'good practice'..
26606 this.disable = this.disable || {};
26607 Roo.applyIf(this.disable, {
26610 specialElements : true
26612 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26614 this.editor = config.editor;
26615 this.editorcore = config.editor.editorcore;
26617 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26619 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26620 // dont call parent... till later.
26622 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26627 editorcore : false,
26632 "h1","h2","h3","h4","h5","h6",
26634 "abbr", "acronym", "address", "cite", "samp", "var",
26638 onRender : function(ct, position)
26640 // Roo.log("Call onRender: " + this.xtype);
26642 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26644 this.el.dom.style.marginBottom = '0';
26646 var editorcore = this.editorcore;
26647 var editor= this.editor;
26650 var btn = function(id,cmd , toggle, handler, html){
26652 var event = toggle ? 'toggle' : 'click';
26657 xns: Roo.bootstrap,
26661 enableToggle:toggle !== false,
26663 pressed : toggle ? false : null,
26666 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26667 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26673 // var cb_box = function...
26678 xns: Roo.bootstrap,
26683 xns: Roo.bootstrap,
26687 Roo.each(this.formats, function(f) {
26688 style.menu.items.push({
26690 xns: Roo.bootstrap,
26691 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26696 editorcore.insertTag(this.tagname);
26703 children.push(style);
26705 btn('bold',false,true);
26706 btn('italic',false,true);
26707 btn('align-left', 'justifyleft',true);
26708 btn('align-center', 'justifycenter',true);
26709 btn('align-right' , 'justifyright',true);
26710 btn('link', false, false, function(btn) {
26711 //Roo.log("create link?");
26712 var url = prompt(this.createLinkText, this.defaultLinkValue);
26713 if(url && url != 'http:/'+'/'){
26714 this.editorcore.relayCmd('createlink', url);
26717 btn('list','insertunorderedlist',true);
26718 btn('pencil', false,true, function(btn){
26720 this.toggleSourceEdit(btn.pressed);
26723 if (this.editor.btns.length > 0) {
26724 for (var i = 0; i<this.editor.btns.length; i++) {
26725 children.push(this.editor.btns[i]);
26733 xns: Roo.bootstrap,
26738 xns: Roo.bootstrap,
26743 cog.menu.items.push({
26745 xns: Roo.bootstrap,
26746 html : Clean styles,
26751 editorcore.insertTag(this.tagname);
26760 this.xtype = 'NavSimplebar';
26762 for(var i=0;i< children.length;i++) {
26764 this.buttons.add(this.addxtypeChild(children[i]));
26768 editor.on('editorevent', this.updateToolbar, this);
26770 onBtnClick : function(id)
26772 this.editorcore.relayCmd(id);
26773 this.editorcore.focus();
26777 * Protected method that will not generally be called directly. It triggers
26778 * a toolbar update by reading the markup state of the current selection in the editor.
26780 updateToolbar: function(){
26782 if(!this.editorcore.activated){
26783 this.editor.onFirstFocus(); // is this neeed?
26787 var btns = this.buttons;
26788 var doc = this.editorcore.doc;
26789 btns.get('bold').setActive(doc.queryCommandState('bold'));
26790 btns.get('italic').setActive(doc.queryCommandState('italic'));
26791 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26793 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26794 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26795 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26797 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26798 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26801 var ans = this.editorcore.getAllAncestors();
26802 if (this.formatCombo) {
26805 var store = this.formatCombo.store;
26806 this.formatCombo.setValue("");
26807 for (var i =0; i < ans.length;i++) {
26808 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26810 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26818 // hides menus... - so this cant be on a menu...
26819 Roo.bootstrap.MenuMgr.hideAll();
26821 Roo.bootstrap.MenuMgr.hideAll();
26822 //this.editorsyncValue();
26824 onFirstFocus: function() {
26825 this.buttons.each(function(item){
26829 toggleSourceEdit : function(sourceEditMode){
26832 if(sourceEditMode){
26833 Roo.log("disabling buttons");
26834 this.buttons.each( function(item){
26835 if(item.cmd != 'pencil'){
26841 Roo.log("enabling buttons");
26842 if(this.editorcore.initialized){
26843 this.buttons.each( function(item){
26849 Roo.log("calling toggole on editor");
26850 // tell the editor that it's been pressed..
26851 this.editor.toggleSourceEdit(sourceEditMode);
26865 * @class Roo.bootstrap.Markdown
26866 * @extends Roo.bootstrap.TextArea
26867 * Bootstrap Showdown editable area
26868 * @cfg {string} content
26871 * Create a new Showdown
26874 Roo.bootstrap.Markdown = function(config){
26875 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26879 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26883 initEvents : function()
26886 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26887 this.markdownEl = this.el.createChild({
26888 cls : 'roo-markdown-area'
26890 this.inputEl().addClass('d-none');
26891 if (this.getValue() == '') {
26892 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26895 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26897 this.markdownEl.on('click', this.toggleTextEdit, this);
26898 this.on('blur', this.toggleTextEdit, this);
26899 this.on('specialkey', this.resizeTextArea, this);
26902 toggleTextEdit : function()
26904 var sh = this.markdownEl.getHeight();
26905 this.inputEl().addClass('d-none');
26906 this.markdownEl.addClass('d-none');
26907 if (!this.editing) {
26909 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26910 this.inputEl().removeClass('d-none');
26911 this.inputEl().focus();
26912 this.editing = true;
26915 // show showdown...
26916 this.updateMarkdown();
26917 this.markdownEl.removeClass('d-none');
26918 this.editing = false;
26921 updateMarkdown : function()
26923 if (this.getValue() == '') {
26924 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26928 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26931 resizeTextArea: function () {
26934 Roo.log([sh, this.getValue().split("\n").length * 30]);
26935 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26937 setValue : function(val)
26939 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26940 if (!this.editing) {
26941 this.updateMarkdown();
26947 if (!this.editing) {
26948 this.toggleTextEdit();
26956 * @class Roo.bootstrap.Table.AbstractSelectionModel
26957 * @extends Roo.util.Observable
26958 * Abstract base class for grid SelectionModels. It provides the interface that should be
26959 * implemented by descendant classes. This class should not be directly instantiated.
26962 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26963 this.locked = false;
26964 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26968 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26969 /** @ignore Called by the grid automatically. Do not call directly. */
26970 init : function(grid){
26976 * Locks the selections.
26979 this.locked = true;
26983 * Unlocks the selections.
26985 unlock : function(){
26986 this.locked = false;
26990 * Returns true if the selections are locked.
26991 * @return {Boolean}
26993 isLocked : function(){
26994 return this.locked;
26998 initEvents : function ()
27004 * @extends Roo.bootstrap.Table.AbstractSelectionModel
27005 * @class Roo.bootstrap.Table.RowSelectionModel
27006 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27007 * It supports multiple selections and keyboard selection/navigation.
27009 * @param {Object} config
27012 Roo.bootstrap.Table.RowSelectionModel = function(config){
27013 Roo.apply(this, config);
27014 this.selections = new Roo.util.MixedCollection(false, function(o){
27019 this.lastActive = false;
27023 * @event selectionchange
27024 * Fires when the selection changes
27025 * @param {SelectionModel} this
27027 "selectionchange" : true,
27029 * @event afterselectionchange
27030 * Fires after the selection changes (eg. by key press or clicking)
27031 * @param {SelectionModel} this
27033 "afterselectionchange" : true,
27035 * @event beforerowselect
27036 * Fires when a row is selected being selected, return false to cancel.
27037 * @param {SelectionModel} this
27038 * @param {Number} rowIndex The selected index
27039 * @param {Boolean} keepExisting False if other selections will be cleared
27041 "beforerowselect" : true,
27044 * Fires when a row is selected.
27045 * @param {SelectionModel} this
27046 * @param {Number} rowIndex The selected index
27047 * @param {Roo.data.Record} r The record
27049 "rowselect" : true,
27051 * @event rowdeselect
27052 * Fires when a row is deselected.
27053 * @param {SelectionModel} this
27054 * @param {Number} rowIndex The selected index
27056 "rowdeselect" : true
27058 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27059 this.locked = false;
27062 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27064 * @cfg {Boolean} singleSelect
27065 * True to allow selection of only one row at a time (defaults to false)
27067 singleSelect : false,
27070 initEvents : function()
27073 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27074 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27075 //}else{ // allow click to work like normal
27076 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27078 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27079 this.grid.on("rowclick", this.handleMouseDown, this);
27081 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27082 "up" : function(e){
27084 this.selectPrevious(e.shiftKey);
27085 }else if(this.last !== false && this.lastActive !== false){
27086 var last = this.last;
27087 this.selectRange(this.last, this.lastActive-1);
27088 this.grid.getView().focusRow(this.lastActive);
27089 if(last !== false){
27093 this.selectFirstRow();
27095 this.fireEvent("afterselectionchange", this);
27097 "down" : function(e){
27099 this.selectNext(e.shiftKey);
27100 }else if(this.last !== false && this.lastActive !== false){
27101 var last = this.last;
27102 this.selectRange(this.last, this.lastActive+1);
27103 this.grid.getView().focusRow(this.lastActive);
27104 if(last !== false){
27108 this.selectFirstRow();
27110 this.fireEvent("afterselectionchange", this);
27114 this.grid.store.on('load', function(){
27115 this.selections.clear();
27118 var view = this.grid.view;
27119 view.on("refresh", this.onRefresh, this);
27120 view.on("rowupdated", this.onRowUpdated, this);
27121 view.on("rowremoved", this.onRemove, this);
27126 onRefresh : function()
27128 var ds = this.grid.store, i, v = this.grid.view;
27129 var s = this.selections;
27130 s.each(function(r){
27131 if((i = ds.indexOfId(r.id)) != -1){
27140 onRemove : function(v, index, r){
27141 this.selections.remove(r);
27145 onRowUpdated : function(v, index, r){
27146 if(this.isSelected(r)){
27147 v.onRowSelect(index);
27153 * @param {Array} records The records to select
27154 * @param {Boolean} keepExisting (optional) True to keep existing selections
27156 selectRecords : function(records, keepExisting)
27159 this.clearSelections();
27161 var ds = this.grid.store;
27162 for(var i = 0, len = records.length; i < len; i++){
27163 this.selectRow(ds.indexOf(records[i]), true);
27168 * Gets the number of selected rows.
27171 getCount : function(){
27172 return this.selections.length;
27176 * Selects the first row in the grid.
27178 selectFirstRow : function(){
27183 * Select the last row.
27184 * @param {Boolean} keepExisting (optional) True to keep existing selections
27186 selectLastRow : function(keepExisting){
27187 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27188 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27192 * Selects the row immediately following the last selected row.
27193 * @param {Boolean} keepExisting (optional) True to keep existing selections
27195 selectNext : function(keepExisting)
27197 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27198 this.selectRow(this.last+1, keepExisting);
27199 this.grid.getView().focusRow(this.last);
27204 * Selects the row that precedes the last selected row.
27205 * @param {Boolean} keepExisting (optional) True to keep existing selections
27207 selectPrevious : function(keepExisting){
27209 this.selectRow(this.last-1, keepExisting);
27210 this.grid.getView().focusRow(this.last);
27215 * Returns the selected records
27216 * @return {Array} Array of selected records
27218 getSelections : function(){
27219 return [].concat(this.selections.items);
27223 * Returns the first selected record.
27226 getSelected : function(){
27227 return this.selections.itemAt(0);
27232 * Clears all selections.
27234 clearSelections : function(fast)
27240 var ds = this.grid.store;
27241 var s = this.selections;
27242 s.each(function(r){
27243 this.deselectRow(ds.indexOfId(r.id));
27247 this.selections.clear();
27254 * Selects all rows.
27256 selectAll : function(){
27260 this.selections.clear();
27261 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27262 this.selectRow(i, true);
27267 * Returns True if there is a selection.
27268 * @return {Boolean}
27270 hasSelection : function(){
27271 return this.selections.length > 0;
27275 * Returns True if the specified row is selected.
27276 * @param {Number/Record} record The record or index of the record to check
27277 * @return {Boolean}
27279 isSelected : function(index){
27280 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27281 return (r && this.selections.key(r.id) ? true : false);
27285 * Returns True if the specified record id is selected.
27286 * @param {String} id The id of record to check
27287 * @return {Boolean}
27289 isIdSelected : function(id){
27290 return (this.selections.key(id) ? true : false);
27295 handleMouseDBClick : function(e, t){
27299 handleMouseDown : function(e, t)
27301 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27302 if(this.isLocked() || rowIndex < 0 ){
27305 if(e.shiftKey && this.last !== false){
27306 var last = this.last;
27307 this.selectRange(last, rowIndex, e.ctrlKey);
27308 this.last = last; // reset the last
27312 var isSelected = this.isSelected(rowIndex);
27313 //Roo.log("select row:" + rowIndex);
27315 this.deselectRow(rowIndex);
27317 this.selectRow(rowIndex, true);
27321 if(e.button !== 0 && isSelected){
27322 alert('rowIndex 2: ' + rowIndex);
27323 view.focusRow(rowIndex);
27324 }else if(e.ctrlKey && isSelected){
27325 this.deselectRow(rowIndex);
27326 }else if(!isSelected){
27327 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27328 view.focusRow(rowIndex);
27332 this.fireEvent("afterselectionchange", this);
27335 handleDragableRowClick : function(grid, rowIndex, e)
27337 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27338 this.selectRow(rowIndex, false);
27339 grid.view.focusRow(rowIndex);
27340 this.fireEvent("afterselectionchange", this);
27345 * Selects multiple rows.
27346 * @param {Array} rows Array of the indexes of the row to select
27347 * @param {Boolean} keepExisting (optional) True to keep existing selections
27349 selectRows : function(rows, keepExisting){
27351 this.clearSelections();
27353 for(var i = 0, len = rows.length; i < len; i++){
27354 this.selectRow(rows[i], true);
27359 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27360 * @param {Number} startRow The index of the first row in the range
27361 * @param {Number} endRow The index of the last row in the range
27362 * @param {Boolean} keepExisting (optional) True to retain existing selections
27364 selectRange : function(startRow, endRow, keepExisting){
27369 this.clearSelections();
27371 if(startRow <= endRow){
27372 for(var i = startRow; i <= endRow; i++){
27373 this.selectRow(i, true);
27376 for(var i = startRow; i >= endRow; i--){
27377 this.selectRow(i, true);
27383 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27384 * @param {Number} startRow The index of the first row in the range
27385 * @param {Number} endRow The index of the last row in the range
27387 deselectRange : function(startRow, endRow, preventViewNotify){
27391 for(var i = startRow; i <= endRow; i++){
27392 this.deselectRow(i, preventViewNotify);
27398 * @param {Number} row The index of the row to select
27399 * @param {Boolean} keepExisting (optional) True to keep existing selections
27401 selectRow : function(index, keepExisting, preventViewNotify)
27403 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27406 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27407 if(!keepExisting || this.singleSelect){
27408 this.clearSelections();
27411 var r = this.grid.store.getAt(index);
27412 //console.log('selectRow - record id :' + r.id);
27414 this.selections.add(r);
27415 this.last = this.lastActive = index;
27416 if(!preventViewNotify){
27417 var proxy = new Roo.Element(
27418 this.grid.getRowDom(index)
27420 proxy.addClass('bg-info info');
27422 this.fireEvent("rowselect", this, index, r);
27423 this.fireEvent("selectionchange", this);
27429 * @param {Number} row The index of the row to deselect
27431 deselectRow : function(index, preventViewNotify)
27436 if(this.last == index){
27439 if(this.lastActive == index){
27440 this.lastActive = false;
27443 var r = this.grid.store.getAt(index);
27448 this.selections.remove(r);
27449 //.console.log('deselectRow - record id :' + r.id);
27450 if(!preventViewNotify){
27452 var proxy = new Roo.Element(
27453 this.grid.getRowDom(index)
27455 proxy.removeClass('bg-info info');
27457 this.fireEvent("rowdeselect", this, index);
27458 this.fireEvent("selectionchange", this);
27462 restoreLast : function(){
27464 this.last = this._last;
27469 acceptsNav : function(row, col, cm){
27470 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27474 onEditorKey : function(field, e){
27475 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27480 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27482 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27484 }else if(k == e.ENTER && !e.ctrlKey){
27488 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27490 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27492 }else if(k == e.ESC){
27496 g.startEditing(newCell[0], newCell[1]);
27502 * Ext JS Library 1.1.1
27503 * Copyright(c) 2006-2007, Ext JS, LLC.
27505 * Originally Released Under LGPL - original licence link has changed is not relivant.
27508 * <script type="text/javascript">
27512 * @class Roo.bootstrap.PagingToolbar
27513 * @extends Roo.bootstrap.NavSimplebar
27514 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27516 * Create a new PagingToolbar
27517 * @param {Object} config The config object
27518 * @param {Roo.data.Store} store
27520 Roo.bootstrap.PagingToolbar = function(config)
27522 // old args format still supported... - xtype is prefered..
27523 // created from xtype...
27525 this.ds = config.dataSource;
27527 if (config.store && !this.ds) {
27528 this.store= Roo.factory(config.store, Roo.data);
27529 this.ds = this.store;
27530 this.ds.xmodule = this.xmodule || false;
27533 this.toolbarItems = [];
27534 if (config.items) {
27535 this.toolbarItems = config.items;
27538 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27543 this.bind(this.ds);
27546 if (Roo.bootstrap.version == 4) {
27547 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27549 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27554 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27556 * @cfg {Roo.data.Store} dataSource
27557 * The underlying data store providing the paged data
27560 * @cfg {String/HTMLElement/Element} container
27561 * container The id or element that will contain the toolbar
27564 * @cfg {Boolean} displayInfo
27565 * True to display the displayMsg (defaults to false)
27568 * @cfg {Number} pageSize
27569 * The number of records to display per page (defaults to 20)
27573 * @cfg {String} displayMsg
27574 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27576 displayMsg : 'Displaying {0} - {1} of {2}',
27578 * @cfg {String} emptyMsg
27579 * The message to display when no records are found (defaults to "No data to display")
27581 emptyMsg : 'No data to display',
27583 * Customizable piece of the default paging text (defaults to "Page")
27586 beforePageText : "Page",
27588 * Customizable piece of the default paging text (defaults to "of %0")
27591 afterPageText : "of {0}",
27593 * Customizable piece of the default paging text (defaults to "First Page")
27596 firstText : "First Page",
27598 * Customizable piece of the default paging text (defaults to "Previous Page")
27601 prevText : "Previous Page",
27603 * Customizable piece of the default paging text (defaults to "Next Page")
27606 nextText : "Next Page",
27608 * Customizable piece of the default paging text (defaults to "Last Page")
27611 lastText : "Last Page",
27613 * Customizable piece of the default paging text (defaults to "Refresh")
27616 refreshText : "Refresh",
27620 onRender : function(ct, position)
27622 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27623 this.navgroup.parentId = this.id;
27624 this.navgroup.onRender(this.el, null);
27625 // add the buttons to the navgroup
27627 if(this.displayInfo){
27628 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27629 this.displayEl = this.el.select('.x-paging-info', true).first();
27630 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27631 // this.displayEl = navel.el.select('span',true).first();
27637 Roo.each(_this.buttons, function(e){ // this might need to use render????
27638 Roo.factory(e).render(_this.el);
27642 Roo.each(_this.toolbarItems, function(e) {
27643 _this.navgroup.addItem(e);
27647 this.first = this.navgroup.addItem({
27648 tooltip: this.firstText,
27649 cls: "prev btn-outline-secondary",
27650 html : ' <i class="fa fa-step-backward"></i>',
27652 preventDefault: true,
27653 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27656 this.prev = this.navgroup.addItem({
27657 tooltip: this.prevText,
27658 cls: "prev btn-outline-secondary",
27659 html : ' <i class="fa fa-backward"></i>',
27661 preventDefault: true,
27662 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27664 //this.addSeparator();
27667 var field = this.navgroup.addItem( {
27669 cls : 'x-paging-position btn-outline-secondary',
27671 html : this.beforePageText +
27672 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27673 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27676 this.field = field.el.select('input', true).first();
27677 this.field.on("keydown", this.onPagingKeydown, this);
27678 this.field.on("focus", function(){this.dom.select();});
27681 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27682 //this.field.setHeight(18);
27683 //this.addSeparator();
27684 this.next = this.navgroup.addItem({
27685 tooltip: this.nextText,
27686 cls: "next btn-outline-secondary",
27687 html : ' <i class="fa fa-forward"></i>',
27689 preventDefault: true,
27690 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27692 this.last = this.navgroup.addItem({
27693 tooltip: this.lastText,
27694 html : ' <i class="fa fa-step-forward"></i>',
27695 cls: "next btn-outline-secondary",
27697 preventDefault: true,
27698 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27700 //this.addSeparator();
27701 this.loading = this.navgroup.addItem({
27702 tooltip: this.refreshText,
27703 cls: "btn-outline-secondary",
27704 html : ' <i class="fa fa-refresh"></i>',
27705 preventDefault: true,
27706 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27712 updateInfo : function(){
27713 if(this.displayEl){
27714 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27715 var msg = count == 0 ?
27719 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27721 this.displayEl.update(msg);
27726 onLoad : function(ds, r, o)
27728 this.cursor = o.params && o.params.start ? o.params.start : 0;
27730 var d = this.getPageData(),
27735 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27736 this.field.dom.value = ap;
27737 this.first.setDisabled(ap == 1);
27738 this.prev.setDisabled(ap == 1);
27739 this.next.setDisabled(ap == ps);
27740 this.last.setDisabled(ap == ps);
27741 this.loading.enable();
27746 getPageData : function(){
27747 var total = this.ds.getTotalCount();
27750 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27751 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27756 onLoadError : function(){
27757 this.loading.enable();
27761 onPagingKeydown : function(e){
27762 var k = e.getKey();
27763 var d = this.getPageData();
27765 var v = this.field.dom.value, pageNum;
27766 if(!v || isNaN(pageNum = parseInt(v, 10))){
27767 this.field.dom.value = d.activePage;
27770 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27771 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27774 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))
27776 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27777 this.field.dom.value = pageNum;
27778 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27781 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27783 var v = this.field.dom.value, pageNum;
27784 var increment = (e.shiftKey) ? 10 : 1;
27785 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27788 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27789 this.field.dom.value = d.activePage;
27792 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27794 this.field.dom.value = parseInt(v, 10) + increment;
27795 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27796 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27803 beforeLoad : function(){
27805 this.loading.disable();
27810 onClick : function(which){
27819 ds.load({params:{start: 0, limit: this.pageSize}});
27822 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27825 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27828 var total = ds.getTotalCount();
27829 var extra = total % this.pageSize;
27830 var lastStart = extra ? (total - extra) : total-this.pageSize;
27831 ds.load({params:{start: lastStart, limit: this.pageSize}});
27834 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27840 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27841 * @param {Roo.data.Store} store The data store to unbind
27843 unbind : function(ds){
27844 ds.un("beforeload", this.beforeLoad, this);
27845 ds.un("load", this.onLoad, this);
27846 ds.un("loadexception", this.onLoadError, this);
27847 ds.un("remove", this.updateInfo, this);
27848 ds.un("add", this.updateInfo, this);
27849 this.ds = undefined;
27853 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27854 * @param {Roo.data.Store} store The data store to bind
27856 bind : function(ds){
27857 ds.on("beforeload", this.beforeLoad, this);
27858 ds.on("load", this.onLoad, this);
27859 ds.on("loadexception", this.onLoadError, this);
27860 ds.on("remove", this.updateInfo, this);
27861 ds.on("add", this.updateInfo, this);
27872 * @class Roo.bootstrap.MessageBar
27873 * @extends Roo.bootstrap.Component
27874 * Bootstrap MessageBar class
27875 * @cfg {String} html contents of the MessageBar
27876 * @cfg {String} weight (info | success | warning | danger) default info
27877 * @cfg {String} beforeClass insert the bar before the given class
27878 * @cfg {Boolean} closable (true | false) default false
27879 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27882 * Create a new Element
27883 * @param {Object} config The config object
27886 Roo.bootstrap.MessageBar = function(config){
27887 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27890 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27896 beforeClass: 'bootstrap-sticky-wrap',
27898 getAutoCreate : function(){
27902 cls: 'alert alert-dismissable alert-' + this.weight,
27907 html: this.html || ''
27913 cfg.cls += ' alert-messages-fixed';
27927 onRender : function(ct, position)
27929 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27932 var cfg = Roo.apply({}, this.getAutoCreate());
27936 cfg.cls += ' ' + this.cls;
27939 cfg.style = this.style;
27941 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27943 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27946 this.el.select('>button.close').on('click', this.hide, this);
27952 if (!this.rendered) {
27958 this.fireEvent('show', this);
27964 if (!this.rendered) {
27970 this.fireEvent('hide', this);
27973 update : function()
27975 // var e = this.el.dom.firstChild;
27977 // if(this.closable){
27978 // e = e.nextSibling;
27981 // e.data = this.html || '';
27983 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27999 * @class Roo.bootstrap.Graph
28000 * @extends Roo.bootstrap.Component
28001 * Bootstrap Graph class
28005 @cfg {String} graphtype bar | vbar | pie
28006 @cfg {number} g_x coodinator | centre x (pie)
28007 @cfg {number} g_y coodinator | centre y (pie)
28008 @cfg {number} g_r radius (pie)
28009 @cfg {number} g_height height of the chart (respected by all elements in the set)
28010 @cfg {number} g_width width of the chart (respected by all elements in the set)
28011 @cfg {Object} title The title of the chart
28014 -opts (object) options for the chart
28016 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28017 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28019 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.
28020 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28022 o stretch (boolean)
28024 -opts (object) options for the pie
28027 o startAngle (number)
28028 o endAngle (number)
28032 * Create a new Input
28033 * @param {Object} config The config object
28036 Roo.bootstrap.Graph = function(config){
28037 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28043 * The img click event for the img.
28044 * @param {Roo.EventObject} e
28050 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28061 //g_colors: this.colors,
28068 getAutoCreate : function(){
28079 onRender : function(ct,position){
28082 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28084 if (typeof(Raphael) == 'undefined') {
28085 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28089 this.raphael = Raphael(this.el.dom);
28091 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28092 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28093 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28094 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28096 r.text(160, 10, "Single Series Chart").attr(txtattr);
28097 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28098 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28099 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28101 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28102 r.barchart(330, 10, 300, 220, data1);
28103 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28104 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28107 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28108 // r.barchart(30, 30, 560, 250, xdata, {
28109 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28110 // axis : "0 0 1 1",
28111 // axisxlabels : xdata
28112 // //yvalues : cols,
28115 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28117 // this.load(null,xdata,{
28118 // axis : "0 0 1 1",
28119 // axisxlabels : xdata
28124 load : function(graphtype,xdata,opts)
28126 this.raphael.clear();
28128 graphtype = this.graphtype;
28133 var r = this.raphael,
28134 fin = function () {
28135 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28137 fout = function () {
28138 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28140 pfin = function() {
28141 this.sector.stop();
28142 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28145 this.label[0].stop();
28146 this.label[0].attr({ r: 7.5 });
28147 this.label[1].attr({ "font-weight": 800 });
28150 pfout = function() {
28151 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28154 this.label[0].animate({ r: 5 }, 500, "bounce");
28155 this.label[1].attr({ "font-weight": 400 });
28161 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28164 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28167 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28168 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28170 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28177 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28182 setTitle: function(o)
28187 initEvents: function() {
28190 this.el.on('click', this.onClick, this);
28194 onClick : function(e)
28196 Roo.log('img onclick');
28197 this.fireEvent('click', this, e);
28209 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28212 * @class Roo.bootstrap.dash.NumberBox
28213 * @extends Roo.bootstrap.Component
28214 * Bootstrap NumberBox class
28215 * @cfg {String} headline Box headline
28216 * @cfg {String} content Box content
28217 * @cfg {String} icon Box icon
28218 * @cfg {String} footer Footer text
28219 * @cfg {String} fhref Footer href
28222 * Create a new NumberBox
28223 * @param {Object} config The config object
28227 Roo.bootstrap.dash.NumberBox = function(config){
28228 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28232 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28241 getAutoCreate : function(){
28245 cls : 'small-box ',
28253 cls : 'roo-headline',
28254 html : this.headline
28258 cls : 'roo-content',
28259 html : this.content
28273 cls : 'ion ' + this.icon
28282 cls : 'small-box-footer',
28283 href : this.fhref || '#',
28287 cfg.cn.push(footer);
28294 onRender : function(ct,position){
28295 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28302 setHeadline: function (value)
28304 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28307 setFooter: function (value, href)
28309 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28312 this.el.select('a.small-box-footer',true).first().attr('href', href);
28317 setContent: function (value)
28319 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28322 initEvents: function()
28336 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28339 * @class Roo.bootstrap.dash.TabBox
28340 * @extends Roo.bootstrap.Component
28341 * Bootstrap TabBox class
28342 * @cfg {String} title Title of the TabBox
28343 * @cfg {String} icon Icon of the TabBox
28344 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28345 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28348 * Create a new TabBox
28349 * @param {Object} config The config object
28353 Roo.bootstrap.dash.TabBox = function(config){
28354 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28359 * When a pane is added
28360 * @param {Roo.bootstrap.dash.TabPane} pane
28364 * @event activatepane
28365 * When a pane is activated
28366 * @param {Roo.bootstrap.dash.TabPane} pane
28368 "activatepane" : true
28376 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28381 tabScrollable : false,
28383 getChildContainer : function()
28385 return this.el.select('.tab-content', true).first();
28388 getAutoCreate : function(){
28392 cls: 'pull-left header',
28400 cls: 'fa ' + this.icon
28406 cls: 'nav nav-tabs pull-right',
28412 if(this.tabScrollable){
28419 cls: 'nav nav-tabs pull-right',
28430 cls: 'nav-tabs-custom',
28435 cls: 'tab-content no-padding',
28443 initEvents : function()
28445 //Roo.log('add add pane handler');
28446 this.on('addpane', this.onAddPane, this);
28449 * Updates the box title
28450 * @param {String} html to set the title to.
28452 setTitle : function(value)
28454 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28456 onAddPane : function(pane)
28458 this.panes.push(pane);
28459 //Roo.log('addpane');
28461 // tabs are rendere left to right..
28462 if(!this.showtabs){
28466 var ctr = this.el.select('.nav-tabs', true).first();
28469 var existing = ctr.select('.nav-tab',true);
28470 var qty = existing.getCount();;
28473 var tab = ctr.createChild({
28475 cls : 'nav-tab' + (qty ? '' : ' active'),
28483 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28486 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28488 pane.el.addClass('active');
28493 onTabClick : function(ev,un,ob,pane)
28495 //Roo.log('tab - prev default');
28496 ev.preventDefault();
28499 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28500 pane.tab.addClass('active');
28501 //Roo.log(pane.title);
28502 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28503 // technically we should have a deactivate event.. but maybe add later.
28504 // and it should not de-activate the selected tab...
28505 this.fireEvent('activatepane', pane);
28506 pane.el.addClass('active');
28507 pane.fireEvent('activate');
28512 getActivePane : function()
28515 Roo.each(this.panes, function(p) {
28516 if(p.el.hasClass('active')){
28537 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28539 * @class Roo.bootstrap.TabPane
28540 * @extends Roo.bootstrap.Component
28541 * Bootstrap TabPane class
28542 * @cfg {Boolean} active (false | true) Default false
28543 * @cfg {String} title title of panel
28547 * Create a new TabPane
28548 * @param {Object} config The config object
28551 Roo.bootstrap.dash.TabPane = function(config){
28552 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28558 * When a pane is activated
28559 * @param {Roo.bootstrap.dash.TabPane} pane
28566 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28571 // the tabBox that this is attached to.
28574 getAutoCreate : function()
28582 cfg.cls += ' active';
28587 initEvents : function()
28589 //Roo.log('trigger add pane handler');
28590 this.parent().fireEvent('addpane', this)
28594 * Updates the tab title
28595 * @param {String} html to set the title to.
28597 setTitle: function(str)
28603 this.tab.select('a', true).first().dom.innerHTML = str;
28620 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28623 * @class Roo.bootstrap.menu.Menu
28624 * @extends Roo.bootstrap.Component
28625 * Bootstrap Menu class - container for Menu
28626 * @cfg {String} html Text of the menu
28627 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28628 * @cfg {String} icon Font awesome icon
28629 * @cfg {String} pos Menu align to (top | bottom) default bottom
28633 * Create a new Menu
28634 * @param {Object} config The config object
28638 Roo.bootstrap.menu.Menu = function(config){
28639 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28643 * @event beforeshow
28644 * Fires before this menu is displayed
28645 * @param {Roo.bootstrap.menu.Menu} this
28649 * @event beforehide
28650 * Fires before this menu is hidden
28651 * @param {Roo.bootstrap.menu.Menu} this
28656 * Fires after this menu is displayed
28657 * @param {Roo.bootstrap.menu.Menu} this
28662 * Fires after this menu is hidden
28663 * @param {Roo.bootstrap.menu.Menu} this
28668 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28669 * @param {Roo.bootstrap.menu.Menu} this
28670 * @param {Roo.EventObject} e
28677 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28681 weight : 'default',
28686 getChildContainer : function() {
28687 if(this.isSubMenu){
28691 return this.el.select('ul.dropdown-menu', true).first();
28694 getAutoCreate : function()
28699 cls : 'roo-menu-text',
28707 cls : 'fa ' + this.icon
28718 cls : 'dropdown-button btn btn-' + this.weight,
28723 cls : 'dropdown-toggle btn btn-' + this.weight,
28733 cls : 'dropdown-menu'
28739 if(this.pos == 'top'){
28740 cfg.cls += ' dropup';
28743 if(this.isSubMenu){
28746 cls : 'dropdown-menu'
28753 onRender : function(ct, position)
28755 this.isSubMenu = ct.hasClass('dropdown-submenu');
28757 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28760 initEvents : function()
28762 if(this.isSubMenu){
28766 this.hidden = true;
28768 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28769 this.triggerEl.on('click', this.onTriggerPress, this);
28771 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28772 this.buttonEl.on('click', this.onClick, this);
28778 if(this.isSubMenu){
28782 return this.el.select('ul.dropdown-menu', true).first();
28785 onClick : function(e)
28787 this.fireEvent("click", this, e);
28790 onTriggerPress : function(e)
28792 if (this.isVisible()) {
28799 isVisible : function(){
28800 return !this.hidden;
28805 this.fireEvent("beforeshow", this);
28807 this.hidden = false;
28808 this.el.addClass('open');
28810 Roo.get(document).on("mouseup", this.onMouseUp, this);
28812 this.fireEvent("show", this);
28819 this.fireEvent("beforehide", this);
28821 this.hidden = true;
28822 this.el.removeClass('open');
28824 Roo.get(document).un("mouseup", this.onMouseUp);
28826 this.fireEvent("hide", this);
28829 onMouseUp : function()
28843 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28846 * @class Roo.bootstrap.menu.Item
28847 * @extends Roo.bootstrap.Component
28848 * Bootstrap MenuItem class
28849 * @cfg {Boolean} submenu (true | false) default false
28850 * @cfg {String} html text of the item
28851 * @cfg {String} href the link
28852 * @cfg {Boolean} disable (true | false) default false
28853 * @cfg {Boolean} preventDefault (true | false) default true
28854 * @cfg {String} icon Font awesome icon
28855 * @cfg {String} pos Submenu align to (left | right) default right
28859 * Create a new Item
28860 * @param {Object} config The config object
28864 Roo.bootstrap.menu.Item = function(config){
28865 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28869 * Fires when the mouse is hovering over this menu
28870 * @param {Roo.bootstrap.menu.Item} this
28871 * @param {Roo.EventObject} e
28876 * Fires when the mouse exits this menu
28877 * @param {Roo.bootstrap.menu.Item} this
28878 * @param {Roo.EventObject} e
28884 * The raw click event for the entire grid.
28885 * @param {Roo.EventObject} e
28891 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28896 preventDefault: true,
28901 getAutoCreate : function()
28906 cls : 'roo-menu-item-text',
28914 cls : 'fa ' + this.icon
28923 href : this.href || '#',
28930 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28934 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28936 if(this.pos == 'left'){
28937 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28944 initEvents : function()
28946 this.el.on('mouseover', this.onMouseOver, this);
28947 this.el.on('mouseout', this.onMouseOut, this);
28949 this.el.select('a', true).first().on('click', this.onClick, this);
28953 onClick : function(e)
28955 if(this.preventDefault){
28956 e.preventDefault();
28959 this.fireEvent("click", this, e);
28962 onMouseOver : function(e)
28964 if(this.submenu && this.pos == 'left'){
28965 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28968 this.fireEvent("mouseover", this, e);
28971 onMouseOut : function(e)
28973 this.fireEvent("mouseout", this, e);
28985 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28988 * @class Roo.bootstrap.menu.Separator
28989 * @extends Roo.bootstrap.Component
28990 * Bootstrap Separator class
28993 * Create a new Separator
28994 * @param {Object} config The config object
28998 Roo.bootstrap.menu.Separator = function(config){
28999 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29002 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29004 getAutoCreate : function(){
29007 cls: 'dropdown-divider divider'
29025 * @class Roo.bootstrap.Tooltip
29026 * Bootstrap Tooltip class
29027 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29028 * to determine which dom element triggers the tooltip.
29030 * It needs to add support for additional attributes like tooltip-position
29033 * Create a new Toolti
29034 * @param {Object} config The config object
29037 Roo.bootstrap.Tooltip = function(config){
29038 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29040 this.alignment = Roo.bootstrap.Tooltip.alignment;
29042 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29043 this.alignment = config.alignment;
29048 Roo.apply(Roo.bootstrap.Tooltip, {
29050 * @function init initialize tooltip monitoring.
29054 currentTip : false,
29055 currentRegion : false,
29061 Roo.get(document).on('mouseover', this.enter ,this);
29062 Roo.get(document).on('mouseout', this.leave, this);
29065 this.currentTip = new Roo.bootstrap.Tooltip();
29068 enter : function(ev)
29070 var dom = ev.getTarget();
29072 //Roo.log(['enter',dom]);
29073 var el = Roo.fly(dom);
29074 if (this.currentEl) {
29076 //Roo.log(this.currentEl);
29077 //Roo.log(this.currentEl.contains(dom));
29078 if (this.currentEl == el) {
29081 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29087 if (this.currentTip.el) {
29088 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29092 if(!el || el.dom == document){
29098 if (!el.attr('tooltip')) {
29099 pel = el.findParent("[tooltip]");
29101 bindEl = Roo.get(pel);
29107 // you can not look for children, as if el is the body.. then everythign is the child..
29108 if (!pel && !el.attr('tooltip')) { //
29109 if (!el.select("[tooltip]").elements.length) {
29112 // is the mouse over this child...?
29113 bindEl = el.select("[tooltip]").first();
29114 var xy = ev.getXY();
29115 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29116 //Roo.log("not in region.");
29119 //Roo.log("child element over..");
29122 this.currentEl = el;
29123 this.currentTip.bind(bindEl);
29124 this.currentRegion = Roo.lib.Region.getRegion(dom);
29125 this.currentTip.enter();
29128 leave : function(ev)
29130 var dom = ev.getTarget();
29131 //Roo.log(['leave',dom]);
29132 if (!this.currentEl) {
29137 if (dom != this.currentEl.dom) {
29140 var xy = ev.getXY();
29141 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29144 // only activate leave if mouse cursor is outside... bounding box..
29149 if (this.currentTip) {
29150 this.currentTip.leave();
29152 //Roo.log('clear currentEl');
29153 this.currentEl = false;
29158 'left' : ['r-l', [-2,0], 'right'],
29159 'right' : ['l-r', [2,0], 'left'],
29160 'bottom' : ['t-b', [0,2], 'top'],
29161 'top' : [ 'b-t', [0,-2], 'bottom']
29167 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29172 delay : null, // can be { show : 300 , hide: 500}
29176 hoverState : null, //???
29178 placement : 'bottom',
29182 getAutoCreate : function(){
29189 cls : 'tooltip-arrow arrow'
29192 cls : 'tooltip-inner'
29199 bind : function(el)
29204 initEvents : function()
29206 this.arrowEl = this.el.select('.arrow', true).first();
29207 this.innerEl = this.el.select('.tooltip-inner', true).first();
29210 enter : function () {
29212 if (this.timeout != null) {
29213 clearTimeout(this.timeout);
29216 this.hoverState = 'in';
29217 //Roo.log("enter - show");
29218 if (!this.delay || !this.delay.show) {
29223 this.timeout = setTimeout(function () {
29224 if (_t.hoverState == 'in') {
29227 }, this.delay.show);
29231 clearTimeout(this.timeout);
29233 this.hoverState = 'out';
29234 if (!this.delay || !this.delay.hide) {
29240 this.timeout = setTimeout(function () {
29241 //Roo.log("leave - timeout");
29243 if (_t.hoverState == 'out') {
29245 Roo.bootstrap.Tooltip.currentEl = false;
29250 show : function (msg)
29253 this.render(document.body);
29256 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29258 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29260 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29262 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29263 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29265 var placement = typeof this.placement == 'function' ?
29266 this.placement.call(this, this.el, on_el) :
29269 var autoToken = /\s?auto?\s?/i;
29270 var autoPlace = autoToken.test(placement);
29272 placement = placement.replace(autoToken, '') || 'top';
29276 //this.el.setXY([0,0]);
29278 //this.el.dom.style.display='block';
29280 //this.el.appendTo(on_el);
29282 var p = this.getPosition();
29283 var box = this.el.getBox();
29289 var align = this.alignment[placement];
29291 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29293 if(placement == 'top' || placement == 'bottom'){
29295 placement = 'right';
29298 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29299 placement = 'left';
29302 var scroll = Roo.select('body', true).first().getScroll();
29304 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29308 align = this.alignment[placement];
29310 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29314 var elems = document.getElementsByTagName('div');
29315 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29316 for (var i = 0; i < elems.length; i++) {
29317 var zindex = Number.parseInt(
29318 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29321 if (zindex > highest) {
29328 this.el.dom.style.zIndex = highest;
29330 this.el.alignTo(this.bindEl, align[0],align[1]);
29331 //var arrow = this.el.select('.arrow',true).first();
29332 //arrow.set(align[2],
29334 this.el.addClass(placement);
29335 this.el.addClass("bs-tooltip-"+ placement);
29337 this.el.addClass('in fade show');
29339 this.hoverState = null;
29341 if (this.el.hasClass('fade')) {
29356 //this.el.setXY([0,0]);
29357 this.el.removeClass(['show', 'in']);
29373 * @class Roo.bootstrap.LocationPicker
29374 * @extends Roo.bootstrap.Component
29375 * Bootstrap LocationPicker class
29376 * @cfg {Number} latitude Position when init default 0
29377 * @cfg {Number} longitude Position when init default 0
29378 * @cfg {Number} zoom default 15
29379 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29380 * @cfg {Boolean} mapTypeControl default false
29381 * @cfg {Boolean} disableDoubleClickZoom default false
29382 * @cfg {Boolean} scrollwheel default true
29383 * @cfg {Boolean} streetViewControl default false
29384 * @cfg {Number} radius default 0
29385 * @cfg {String} locationName
29386 * @cfg {Boolean} draggable default true
29387 * @cfg {Boolean} enableAutocomplete default false
29388 * @cfg {Boolean} enableReverseGeocode default true
29389 * @cfg {String} markerTitle
29392 * Create a new LocationPicker
29393 * @param {Object} config The config object
29397 Roo.bootstrap.LocationPicker = function(config){
29399 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29404 * Fires when the picker initialized.
29405 * @param {Roo.bootstrap.LocationPicker} this
29406 * @param {Google Location} location
29410 * @event positionchanged
29411 * Fires when the picker position changed.
29412 * @param {Roo.bootstrap.LocationPicker} this
29413 * @param {Google Location} location
29415 positionchanged : true,
29418 * Fires when the map resize.
29419 * @param {Roo.bootstrap.LocationPicker} this
29424 * Fires when the map show.
29425 * @param {Roo.bootstrap.LocationPicker} this
29430 * Fires when the map hide.
29431 * @param {Roo.bootstrap.LocationPicker} this
29436 * Fires when click the map.
29437 * @param {Roo.bootstrap.LocationPicker} this
29438 * @param {Map event} e
29442 * @event mapRightClick
29443 * Fires when right click the map.
29444 * @param {Roo.bootstrap.LocationPicker} this
29445 * @param {Map event} e
29447 mapRightClick : true,
29449 * @event markerClick
29450 * Fires when click the marker.
29451 * @param {Roo.bootstrap.LocationPicker} this
29452 * @param {Map event} e
29454 markerClick : true,
29456 * @event markerRightClick
29457 * Fires when right click the marker.
29458 * @param {Roo.bootstrap.LocationPicker} this
29459 * @param {Map event} e
29461 markerRightClick : true,
29463 * @event OverlayViewDraw
29464 * Fires when OverlayView Draw
29465 * @param {Roo.bootstrap.LocationPicker} this
29467 OverlayViewDraw : true,
29469 * @event OverlayViewOnAdd
29470 * Fires when OverlayView Draw
29471 * @param {Roo.bootstrap.LocationPicker} this
29473 OverlayViewOnAdd : true,
29475 * @event OverlayViewOnRemove
29476 * Fires when OverlayView Draw
29477 * @param {Roo.bootstrap.LocationPicker} this
29479 OverlayViewOnRemove : true,
29481 * @event OverlayViewShow
29482 * Fires when OverlayView Draw
29483 * @param {Roo.bootstrap.LocationPicker} this
29484 * @param {Pixel} cpx
29486 OverlayViewShow : true,
29488 * @event OverlayViewHide
29489 * Fires when OverlayView Draw
29490 * @param {Roo.bootstrap.LocationPicker} this
29492 OverlayViewHide : true,
29494 * @event loadexception
29495 * Fires when load google lib failed.
29496 * @param {Roo.bootstrap.LocationPicker} this
29498 loadexception : true
29503 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29505 gMapContext: false,
29511 mapTypeControl: false,
29512 disableDoubleClickZoom: false,
29514 streetViewControl: false,
29518 enableAutocomplete: false,
29519 enableReverseGeocode: true,
29522 getAutoCreate: function()
29527 cls: 'roo-location-picker'
29533 initEvents: function(ct, position)
29535 if(!this.el.getWidth() || this.isApplied()){
29539 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29544 initial: function()
29546 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29547 this.fireEvent('loadexception', this);
29551 if(!this.mapTypeId){
29552 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29555 this.gMapContext = this.GMapContext();
29557 this.initOverlayView();
29559 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29563 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29564 _this.setPosition(_this.gMapContext.marker.position);
29567 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29568 _this.fireEvent('mapClick', this, event);
29572 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29573 _this.fireEvent('mapRightClick', this, event);
29577 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29578 _this.fireEvent('markerClick', this, event);
29582 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29583 _this.fireEvent('markerRightClick', this, event);
29587 this.setPosition(this.gMapContext.location);
29589 this.fireEvent('initial', this, this.gMapContext.location);
29592 initOverlayView: function()
29596 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29600 _this.fireEvent('OverlayViewDraw', _this);
29605 _this.fireEvent('OverlayViewOnAdd', _this);
29608 onRemove: function()
29610 _this.fireEvent('OverlayViewOnRemove', _this);
29613 show: function(cpx)
29615 _this.fireEvent('OverlayViewShow', _this, cpx);
29620 _this.fireEvent('OverlayViewHide', _this);
29626 fromLatLngToContainerPixel: function(event)
29628 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29631 isApplied: function()
29633 return this.getGmapContext() == false ? false : true;
29636 getGmapContext: function()
29638 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29641 GMapContext: function()
29643 var position = new google.maps.LatLng(this.latitude, this.longitude);
29645 var _map = new google.maps.Map(this.el.dom, {
29648 mapTypeId: this.mapTypeId,
29649 mapTypeControl: this.mapTypeControl,
29650 disableDoubleClickZoom: this.disableDoubleClickZoom,
29651 scrollwheel: this.scrollwheel,
29652 streetViewControl: this.streetViewControl,
29653 locationName: this.locationName,
29654 draggable: this.draggable,
29655 enableAutocomplete: this.enableAutocomplete,
29656 enableReverseGeocode: this.enableReverseGeocode
29659 var _marker = new google.maps.Marker({
29660 position: position,
29662 title: this.markerTitle,
29663 draggable: this.draggable
29670 location: position,
29671 radius: this.radius,
29672 locationName: this.locationName,
29673 addressComponents: {
29674 formatted_address: null,
29675 addressLine1: null,
29676 addressLine2: null,
29678 streetNumber: null,
29682 stateOrProvince: null
29685 domContainer: this.el.dom,
29686 geodecoder: new google.maps.Geocoder()
29690 drawCircle: function(center, radius, options)
29692 if (this.gMapContext.circle != null) {
29693 this.gMapContext.circle.setMap(null);
29697 options = Roo.apply({}, options, {
29698 strokeColor: "#0000FF",
29699 strokeOpacity: .35,
29701 fillColor: "#0000FF",
29705 options.map = this.gMapContext.map;
29706 options.radius = radius;
29707 options.center = center;
29708 this.gMapContext.circle = new google.maps.Circle(options);
29709 return this.gMapContext.circle;
29715 setPosition: function(location)
29717 this.gMapContext.location = location;
29718 this.gMapContext.marker.setPosition(location);
29719 this.gMapContext.map.panTo(location);
29720 this.drawCircle(location, this.gMapContext.radius, {});
29724 if (this.gMapContext.settings.enableReverseGeocode) {
29725 this.gMapContext.geodecoder.geocode({
29726 latLng: this.gMapContext.location
29727 }, function(results, status) {
29729 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29730 _this.gMapContext.locationName = results[0].formatted_address;
29731 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29733 _this.fireEvent('positionchanged', this, location);
29740 this.fireEvent('positionchanged', this, location);
29745 google.maps.event.trigger(this.gMapContext.map, "resize");
29747 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29749 this.fireEvent('resize', this);
29752 setPositionByLatLng: function(latitude, longitude)
29754 this.setPosition(new google.maps.LatLng(latitude, longitude));
29757 getCurrentPosition: function()
29760 latitude: this.gMapContext.location.lat(),
29761 longitude: this.gMapContext.location.lng()
29765 getAddressName: function()
29767 return this.gMapContext.locationName;
29770 getAddressComponents: function()
29772 return this.gMapContext.addressComponents;
29775 address_component_from_google_geocode: function(address_components)
29779 for (var i = 0; i < address_components.length; i++) {
29780 var component = address_components[i];
29781 if (component.types.indexOf("postal_code") >= 0) {
29782 result.postalCode = component.short_name;
29783 } else if (component.types.indexOf("street_number") >= 0) {
29784 result.streetNumber = component.short_name;
29785 } else if (component.types.indexOf("route") >= 0) {
29786 result.streetName = component.short_name;
29787 } else if (component.types.indexOf("neighborhood") >= 0) {
29788 result.city = component.short_name;
29789 } else if (component.types.indexOf("locality") >= 0) {
29790 result.city = component.short_name;
29791 } else if (component.types.indexOf("sublocality") >= 0) {
29792 result.district = component.short_name;
29793 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29794 result.stateOrProvince = component.short_name;
29795 } else if (component.types.indexOf("country") >= 0) {
29796 result.country = component.short_name;
29800 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29801 result.addressLine2 = "";
29805 setZoomLevel: function(zoom)
29807 this.gMapContext.map.setZoom(zoom);
29820 this.fireEvent('show', this);
29831 this.fireEvent('hide', this);
29836 Roo.apply(Roo.bootstrap.LocationPicker, {
29838 OverlayView : function(map, options)
29840 options = options || {};
29847 * @class Roo.bootstrap.Alert
29848 * @extends Roo.bootstrap.Component
29849 * Bootstrap Alert class - shows an alert area box
29851 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29852 Enter a valid email address
29855 * @cfg {String} title The title of alert
29856 * @cfg {String} html The content of alert
29857 * @cfg {String} weight ( success | info | warning | danger )
29858 * @cfg {String} fa font-awesomeicon
29859 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29860 * @cfg {Boolean} close true to show a x closer
29864 * Create a new alert
29865 * @param {Object} config The config object
29869 Roo.bootstrap.Alert = function(config){
29870 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29874 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29880 faicon: false, // BC
29884 getAutoCreate : function()
29896 style : this.close ? '' : 'display:none'
29900 cls : 'roo-alert-icon'
29905 cls : 'roo-alert-title',
29910 cls : 'roo-alert-text',
29917 cfg.cn[0].cls += ' fa ' + this.faicon;
29920 cfg.cn[0].cls += ' fa ' + this.fa;
29924 cfg.cls += ' alert-' + this.weight;
29930 initEvents: function()
29932 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29933 this.titleEl = this.el.select('.roo-alert-title',true).first();
29934 this.iconEl = this.el.select('.roo-alert-icon',true).first();
29935 if (this.seconds > 0) {
29936 this.hide.defer(this.seconds, this);
29940 setTitle : function(str)
29942 this.titleEl.dom.innerHTML = str;
29945 setText : function(str)
29947 this.titleEl.dom.innerHTML = str;
29950 setWeight : function(weight)
29953 this.el.removeClass('alert-' + this.weight);
29956 this.weight = weight;
29958 this.el.addClass('alert-' + this.weight);
29961 setIcon : function(icon)
29964 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29967 this.faicon = icon;
29969 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
29990 * @class Roo.bootstrap.UploadCropbox
29991 * @extends Roo.bootstrap.Component
29992 * Bootstrap UploadCropbox class
29993 * @cfg {String} emptyText show when image has been loaded
29994 * @cfg {String} rotateNotify show when image too small to rotate
29995 * @cfg {Number} errorTimeout default 3000
29996 * @cfg {Number} minWidth default 300
29997 * @cfg {Number} minHeight default 300
29998 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29999 * @cfg {Boolean} isDocument (true|false) default false
30000 * @cfg {String} url action url
30001 * @cfg {String} paramName default 'imageUpload'
30002 * @cfg {String} method default POST
30003 * @cfg {Boolean} loadMask (true|false) default true
30004 * @cfg {Boolean} loadingText default 'Loading...'
30007 * Create a new UploadCropbox
30008 * @param {Object} config The config object
30011 Roo.bootstrap.UploadCropbox = function(config){
30012 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30016 * @event beforeselectfile
30017 * Fire before select file
30018 * @param {Roo.bootstrap.UploadCropbox} this
30020 "beforeselectfile" : true,
30023 * Fire after initEvent
30024 * @param {Roo.bootstrap.UploadCropbox} this
30029 * Fire after initEvent
30030 * @param {Roo.bootstrap.UploadCropbox} this
30031 * @param {String} data
30036 * Fire when preparing the file data
30037 * @param {Roo.bootstrap.UploadCropbox} this
30038 * @param {Object} file
30043 * Fire when get exception
30044 * @param {Roo.bootstrap.UploadCropbox} this
30045 * @param {XMLHttpRequest} xhr
30047 "exception" : true,
30049 * @event beforeloadcanvas
30050 * Fire before load the canvas
30051 * @param {Roo.bootstrap.UploadCropbox} this
30052 * @param {String} src
30054 "beforeloadcanvas" : true,
30057 * Fire when trash image
30058 * @param {Roo.bootstrap.UploadCropbox} this
30063 * Fire when download the image
30064 * @param {Roo.bootstrap.UploadCropbox} this
30068 * @event footerbuttonclick
30069 * Fire when footerbuttonclick
30070 * @param {Roo.bootstrap.UploadCropbox} this
30071 * @param {String} type
30073 "footerbuttonclick" : true,
30077 * @param {Roo.bootstrap.UploadCropbox} this
30082 * Fire when rotate the image
30083 * @param {Roo.bootstrap.UploadCropbox} this
30084 * @param {String} pos
30089 * Fire when inspect the file
30090 * @param {Roo.bootstrap.UploadCropbox} this
30091 * @param {Object} file
30096 * Fire when xhr upload the file
30097 * @param {Roo.bootstrap.UploadCropbox} this
30098 * @param {Object} data
30103 * Fire when arrange the file data
30104 * @param {Roo.bootstrap.UploadCropbox} this
30105 * @param {Object} formData
30110 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30113 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30115 emptyText : 'Click to upload image',
30116 rotateNotify : 'Image is too small to rotate',
30117 errorTimeout : 3000,
30131 cropType : 'image/jpeg',
30133 canvasLoaded : false,
30134 isDocument : false,
30136 paramName : 'imageUpload',
30138 loadingText : 'Loading...',
30141 getAutoCreate : function()
30145 cls : 'roo-upload-cropbox',
30149 cls : 'roo-upload-cropbox-selector',
30154 cls : 'roo-upload-cropbox-body',
30155 style : 'cursor:pointer',
30159 cls : 'roo-upload-cropbox-preview'
30163 cls : 'roo-upload-cropbox-thumb'
30167 cls : 'roo-upload-cropbox-empty-notify',
30168 html : this.emptyText
30172 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30173 html : this.rotateNotify
30179 cls : 'roo-upload-cropbox-footer',
30182 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30192 onRender : function(ct, position)
30194 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30196 if (this.buttons.length) {
30198 Roo.each(this.buttons, function(bb) {
30200 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30202 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30208 this.maskEl = this.el;
30212 initEvents : function()
30214 this.urlAPI = (window.createObjectURL && window) ||
30215 (window.URL && URL.revokeObjectURL && URL) ||
30216 (window.webkitURL && webkitURL);
30218 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30219 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30221 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30222 this.selectorEl.hide();
30224 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30225 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30227 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30228 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30229 this.thumbEl.hide();
30231 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30232 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30234 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30235 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30236 this.errorEl.hide();
30238 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30239 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30240 this.footerEl.hide();
30242 this.setThumbBoxSize();
30248 this.fireEvent('initial', this);
30255 window.addEventListener("resize", function() { _this.resize(); } );
30257 this.bodyEl.on('click', this.beforeSelectFile, this);
30260 this.bodyEl.on('touchstart', this.onTouchStart, this);
30261 this.bodyEl.on('touchmove', this.onTouchMove, this);
30262 this.bodyEl.on('touchend', this.onTouchEnd, this);
30266 this.bodyEl.on('mousedown', this.onMouseDown, this);
30267 this.bodyEl.on('mousemove', this.onMouseMove, this);
30268 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30269 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30270 Roo.get(document).on('mouseup', this.onMouseUp, this);
30273 this.selectorEl.on('change', this.onFileSelected, this);
30279 this.baseScale = 1;
30281 this.baseRotate = 1;
30282 this.dragable = false;
30283 this.pinching = false;
30286 this.cropData = false;
30287 this.notifyEl.dom.innerHTML = this.emptyText;
30289 this.selectorEl.dom.value = '';
30293 resize : function()
30295 if(this.fireEvent('resize', this) != false){
30296 this.setThumbBoxPosition();
30297 this.setCanvasPosition();
30301 onFooterButtonClick : function(e, el, o, type)
30304 case 'rotate-left' :
30305 this.onRotateLeft(e);
30307 case 'rotate-right' :
30308 this.onRotateRight(e);
30311 this.beforeSelectFile(e);
30326 this.fireEvent('footerbuttonclick', this, type);
30329 beforeSelectFile : function(e)
30331 e.preventDefault();
30333 if(this.fireEvent('beforeselectfile', this) != false){
30334 this.selectorEl.dom.click();
30338 onFileSelected : function(e)
30340 e.preventDefault();
30342 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30346 var file = this.selectorEl.dom.files[0];
30348 if(this.fireEvent('inspect', this, file) != false){
30349 this.prepare(file);
30354 trash : function(e)
30356 this.fireEvent('trash', this);
30359 download : function(e)
30361 this.fireEvent('download', this);
30364 loadCanvas : function(src)
30366 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30370 this.imageEl = document.createElement('img');
30374 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30376 this.imageEl.src = src;
30380 onLoadCanvas : function()
30382 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30383 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30385 this.bodyEl.un('click', this.beforeSelectFile, this);
30387 this.notifyEl.hide();
30388 this.thumbEl.show();
30389 this.footerEl.show();
30391 this.baseRotateLevel();
30393 if(this.isDocument){
30394 this.setThumbBoxSize();
30397 this.setThumbBoxPosition();
30399 this.baseScaleLevel();
30405 this.canvasLoaded = true;
30408 this.maskEl.unmask();
30413 setCanvasPosition : function()
30415 if(!this.canvasEl){
30419 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30420 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30422 this.previewEl.setLeft(pw);
30423 this.previewEl.setTop(ph);
30427 onMouseDown : function(e)
30431 this.dragable = true;
30432 this.pinching = false;
30434 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30435 this.dragable = false;
30439 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30440 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30444 onMouseMove : function(e)
30448 if(!this.canvasLoaded){
30452 if (!this.dragable){
30456 var minX = Math.ceil(this.thumbEl.getLeft(true));
30457 var minY = Math.ceil(this.thumbEl.getTop(true));
30459 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30460 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30462 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30463 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30465 x = x - this.mouseX;
30466 y = y - this.mouseY;
30468 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30469 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30471 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30472 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30474 this.previewEl.setLeft(bgX);
30475 this.previewEl.setTop(bgY);
30477 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30478 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30481 onMouseUp : function(e)
30485 this.dragable = false;
30488 onMouseWheel : function(e)
30492 this.startScale = this.scale;
30494 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30496 if(!this.zoomable()){
30497 this.scale = this.startScale;
30506 zoomable : function()
30508 var minScale = this.thumbEl.getWidth() / this.minWidth;
30510 if(this.minWidth < this.minHeight){
30511 minScale = this.thumbEl.getHeight() / this.minHeight;
30514 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30515 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30519 (this.rotate == 0 || this.rotate == 180) &&
30521 width > this.imageEl.OriginWidth ||
30522 height > this.imageEl.OriginHeight ||
30523 (width < this.minWidth && height < this.minHeight)
30531 (this.rotate == 90 || this.rotate == 270) &&
30533 width > this.imageEl.OriginWidth ||
30534 height > this.imageEl.OriginHeight ||
30535 (width < this.minHeight && height < this.minWidth)
30542 !this.isDocument &&
30543 (this.rotate == 0 || this.rotate == 180) &&
30545 width < this.minWidth ||
30546 width > this.imageEl.OriginWidth ||
30547 height < this.minHeight ||
30548 height > this.imageEl.OriginHeight
30555 !this.isDocument &&
30556 (this.rotate == 90 || this.rotate == 270) &&
30558 width < this.minHeight ||
30559 width > this.imageEl.OriginWidth ||
30560 height < this.minWidth ||
30561 height > this.imageEl.OriginHeight
30571 onRotateLeft : function(e)
30573 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30575 var minScale = this.thumbEl.getWidth() / this.minWidth;
30577 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30578 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30580 this.startScale = this.scale;
30582 while (this.getScaleLevel() < minScale){
30584 this.scale = this.scale + 1;
30586 if(!this.zoomable()){
30591 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30592 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30597 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30604 this.scale = this.startScale;
30606 this.onRotateFail();
30611 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30613 if(this.isDocument){
30614 this.setThumbBoxSize();
30615 this.setThumbBoxPosition();
30616 this.setCanvasPosition();
30621 this.fireEvent('rotate', this, 'left');
30625 onRotateRight : function(e)
30627 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30629 var minScale = this.thumbEl.getWidth() / this.minWidth;
30631 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30632 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30634 this.startScale = this.scale;
30636 while (this.getScaleLevel() < minScale){
30638 this.scale = this.scale + 1;
30640 if(!this.zoomable()){
30645 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30646 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30651 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30658 this.scale = this.startScale;
30660 this.onRotateFail();
30665 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30667 if(this.isDocument){
30668 this.setThumbBoxSize();
30669 this.setThumbBoxPosition();
30670 this.setCanvasPosition();
30675 this.fireEvent('rotate', this, 'right');
30678 onRotateFail : function()
30680 this.errorEl.show(true);
30684 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30689 this.previewEl.dom.innerHTML = '';
30691 var canvasEl = document.createElement("canvas");
30693 var contextEl = canvasEl.getContext("2d");
30695 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30696 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30697 var center = this.imageEl.OriginWidth / 2;
30699 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30700 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30701 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30702 center = this.imageEl.OriginHeight / 2;
30705 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30707 contextEl.translate(center, center);
30708 contextEl.rotate(this.rotate * Math.PI / 180);
30710 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30712 this.canvasEl = document.createElement("canvas");
30714 this.contextEl = this.canvasEl.getContext("2d");
30716 switch (this.rotate) {
30719 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30720 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30722 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30727 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30728 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30730 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30731 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);
30735 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30740 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30741 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30743 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30744 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);
30748 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);
30753 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30754 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30756 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30757 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30761 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);
30768 this.previewEl.appendChild(this.canvasEl);
30770 this.setCanvasPosition();
30775 if(!this.canvasLoaded){
30779 var imageCanvas = document.createElement("canvas");
30781 var imageContext = imageCanvas.getContext("2d");
30783 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30784 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30786 var center = imageCanvas.width / 2;
30788 imageContext.translate(center, center);
30790 imageContext.rotate(this.rotate * Math.PI / 180);
30792 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30794 var canvas = document.createElement("canvas");
30796 var context = canvas.getContext("2d");
30798 canvas.width = this.minWidth;
30799 canvas.height = this.minHeight;
30801 switch (this.rotate) {
30804 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30805 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30807 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30808 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30810 var targetWidth = this.minWidth - 2 * x;
30811 var targetHeight = this.minHeight - 2 * y;
30815 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30816 scale = targetWidth / width;
30819 if(x > 0 && y == 0){
30820 scale = targetHeight / height;
30823 if(x > 0 && y > 0){
30824 scale = targetWidth / width;
30826 if(width < height){
30827 scale = targetHeight / height;
30831 context.scale(scale, scale);
30833 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30834 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30836 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30837 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30839 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30844 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30845 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30847 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30848 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30850 var targetWidth = this.minWidth - 2 * x;
30851 var targetHeight = this.minHeight - 2 * y;
30855 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30856 scale = targetWidth / width;
30859 if(x > 0 && y == 0){
30860 scale = targetHeight / height;
30863 if(x > 0 && y > 0){
30864 scale = targetWidth / width;
30866 if(width < height){
30867 scale = targetHeight / height;
30871 context.scale(scale, scale);
30873 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30874 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30876 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30877 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30879 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30881 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30886 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30887 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30889 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30890 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30892 var targetWidth = this.minWidth - 2 * x;
30893 var targetHeight = this.minHeight - 2 * y;
30897 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30898 scale = targetWidth / width;
30901 if(x > 0 && y == 0){
30902 scale = targetHeight / height;
30905 if(x > 0 && y > 0){
30906 scale = targetWidth / width;
30908 if(width < height){
30909 scale = targetHeight / height;
30913 context.scale(scale, scale);
30915 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30916 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30918 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30919 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30921 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30922 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30924 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30929 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30930 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30932 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30933 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30935 var targetWidth = this.minWidth - 2 * x;
30936 var targetHeight = this.minHeight - 2 * y;
30940 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30941 scale = targetWidth / width;
30944 if(x > 0 && y == 0){
30945 scale = targetHeight / height;
30948 if(x > 0 && y > 0){
30949 scale = targetWidth / width;
30951 if(width < height){
30952 scale = targetHeight / height;
30956 context.scale(scale, scale);
30958 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30959 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30961 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30962 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30964 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30966 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30973 this.cropData = canvas.toDataURL(this.cropType);
30975 if(this.fireEvent('crop', this, this.cropData) !== false){
30976 this.process(this.file, this.cropData);
30983 setThumbBoxSize : function()
30987 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30988 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30989 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30991 this.minWidth = width;
30992 this.minHeight = height;
30994 if(this.rotate == 90 || this.rotate == 270){
30995 this.minWidth = height;
30996 this.minHeight = width;
31001 width = Math.ceil(this.minWidth * height / this.minHeight);
31003 if(this.minWidth > this.minHeight){
31005 height = Math.ceil(this.minHeight * width / this.minWidth);
31008 this.thumbEl.setStyle({
31009 width : width + 'px',
31010 height : height + 'px'
31017 setThumbBoxPosition : function()
31019 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31020 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31022 this.thumbEl.setLeft(x);
31023 this.thumbEl.setTop(y);
31027 baseRotateLevel : function()
31029 this.baseRotate = 1;
31032 typeof(this.exif) != 'undefined' &&
31033 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31034 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31036 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31039 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31043 baseScaleLevel : function()
31047 if(this.isDocument){
31049 if(this.baseRotate == 6 || this.baseRotate == 8){
31051 height = this.thumbEl.getHeight();
31052 this.baseScale = height / this.imageEl.OriginWidth;
31054 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31055 width = this.thumbEl.getWidth();
31056 this.baseScale = width / this.imageEl.OriginHeight;
31062 height = this.thumbEl.getHeight();
31063 this.baseScale = height / this.imageEl.OriginHeight;
31065 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31066 width = this.thumbEl.getWidth();
31067 this.baseScale = width / this.imageEl.OriginWidth;
31073 if(this.baseRotate == 6 || this.baseRotate == 8){
31075 width = this.thumbEl.getHeight();
31076 this.baseScale = width / this.imageEl.OriginHeight;
31078 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31079 height = this.thumbEl.getWidth();
31080 this.baseScale = height / this.imageEl.OriginHeight;
31083 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31084 height = this.thumbEl.getWidth();
31085 this.baseScale = height / this.imageEl.OriginHeight;
31087 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31088 width = this.thumbEl.getHeight();
31089 this.baseScale = width / this.imageEl.OriginWidth;
31096 width = this.thumbEl.getWidth();
31097 this.baseScale = width / this.imageEl.OriginWidth;
31099 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31100 height = this.thumbEl.getHeight();
31101 this.baseScale = height / this.imageEl.OriginHeight;
31104 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31106 height = this.thumbEl.getHeight();
31107 this.baseScale = height / this.imageEl.OriginHeight;
31109 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31110 width = this.thumbEl.getWidth();
31111 this.baseScale = width / this.imageEl.OriginWidth;
31119 getScaleLevel : function()
31121 return this.baseScale * Math.pow(1.1, this.scale);
31124 onTouchStart : function(e)
31126 if(!this.canvasLoaded){
31127 this.beforeSelectFile(e);
31131 var touches = e.browserEvent.touches;
31137 if(touches.length == 1){
31138 this.onMouseDown(e);
31142 if(touches.length != 2){
31148 for(var i = 0, finger; finger = touches[i]; i++){
31149 coords.push(finger.pageX, finger.pageY);
31152 var x = Math.pow(coords[0] - coords[2], 2);
31153 var y = Math.pow(coords[1] - coords[3], 2);
31155 this.startDistance = Math.sqrt(x + y);
31157 this.startScale = this.scale;
31159 this.pinching = true;
31160 this.dragable = false;
31164 onTouchMove : function(e)
31166 if(!this.pinching && !this.dragable){
31170 var touches = e.browserEvent.touches;
31177 this.onMouseMove(e);
31183 for(var i = 0, finger; finger = touches[i]; i++){
31184 coords.push(finger.pageX, finger.pageY);
31187 var x = Math.pow(coords[0] - coords[2], 2);
31188 var y = Math.pow(coords[1] - coords[3], 2);
31190 this.endDistance = Math.sqrt(x + y);
31192 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31194 if(!this.zoomable()){
31195 this.scale = this.startScale;
31203 onTouchEnd : function(e)
31205 this.pinching = false;
31206 this.dragable = false;
31210 process : function(file, crop)
31213 this.maskEl.mask(this.loadingText);
31216 this.xhr = new XMLHttpRequest();
31218 file.xhr = this.xhr;
31220 this.xhr.open(this.method, this.url, true);
31223 "Accept": "application/json",
31224 "Cache-Control": "no-cache",
31225 "X-Requested-With": "XMLHttpRequest"
31228 for (var headerName in headers) {
31229 var headerValue = headers[headerName];
31231 this.xhr.setRequestHeader(headerName, headerValue);
31237 this.xhr.onload = function()
31239 _this.xhrOnLoad(_this.xhr);
31242 this.xhr.onerror = function()
31244 _this.xhrOnError(_this.xhr);
31247 var formData = new FormData();
31249 formData.append('returnHTML', 'NO');
31252 formData.append('crop', crop);
31255 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31256 formData.append(this.paramName, file, file.name);
31259 if(typeof(file.filename) != 'undefined'){
31260 formData.append('filename', file.filename);
31263 if(typeof(file.mimetype) != 'undefined'){
31264 formData.append('mimetype', file.mimetype);
31267 if(this.fireEvent('arrange', this, formData) != false){
31268 this.xhr.send(formData);
31272 xhrOnLoad : function(xhr)
31275 this.maskEl.unmask();
31278 if (xhr.readyState !== 4) {
31279 this.fireEvent('exception', this, xhr);
31283 var response = Roo.decode(xhr.responseText);
31285 if(!response.success){
31286 this.fireEvent('exception', this, xhr);
31290 var response = Roo.decode(xhr.responseText);
31292 this.fireEvent('upload', this, response);
31296 xhrOnError : function()
31299 this.maskEl.unmask();
31302 Roo.log('xhr on error');
31304 var response = Roo.decode(xhr.responseText);
31310 prepare : function(file)
31313 this.maskEl.mask(this.loadingText);
31319 if(typeof(file) === 'string'){
31320 this.loadCanvas(file);
31324 if(!file || !this.urlAPI){
31329 this.cropType = file.type;
31333 if(this.fireEvent('prepare', this, this.file) != false){
31335 var reader = new FileReader();
31337 reader.onload = function (e) {
31338 if (e.target.error) {
31339 Roo.log(e.target.error);
31343 var buffer = e.target.result,
31344 dataView = new DataView(buffer),
31346 maxOffset = dataView.byteLength - 4,
31350 if (dataView.getUint16(0) === 0xffd8) {
31351 while (offset < maxOffset) {
31352 markerBytes = dataView.getUint16(offset);
31354 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31355 markerLength = dataView.getUint16(offset + 2) + 2;
31356 if (offset + markerLength > dataView.byteLength) {
31357 Roo.log('Invalid meta data: Invalid segment size.');
31361 if(markerBytes == 0xffe1){
31362 _this.parseExifData(
31369 offset += markerLength;
31379 var url = _this.urlAPI.createObjectURL(_this.file);
31381 _this.loadCanvas(url);
31386 reader.readAsArrayBuffer(this.file);
31392 parseExifData : function(dataView, offset, length)
31394 var tiffOffset = offset + 10,
31398 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31399 // No Exif data, might be XMP data instead
31403 // Check for the ASCII code for "Exif" (0x45786966):
31404 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31405 // No Exif data, might be XMP data instead
31408 if (tiffOffset + 8 > dataView.byteLength) {
31409 Roo.log('Invalid Exif data: Invalid segment size.');
31412 // Check for the two null bytes:
31413 if (dataView.getUint16(offset + 8) !== 0x0000) {
31414 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31417 // Check the byte alignment:
31418 switch (dataView.getUint16(tiffOffset)) {
31420 littleEndian = true;
31423 littleEndian = false;
31426 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31429 // Check for the TIFF tag marker (0x002A):
31430 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31431 Roo.log('Invalid Exif data: Missing TIFF marker.');
31434 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31435 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31437 this.parseExifTags(
31440 tiffOffset + dirOffset,
31445 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31450 if (dirOffset + 6 > dataView.byteLength) {
31451 Roo.log('Invalid Exif data: Invalid directory offset.');
31454 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31455 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31456 if (dirEndOffset + 4 > dataView.byteLength) {
31457 Roo.log('Invalid Exif data: Invalid directory size.');
31460 for (i = 0; i < tagsNumber; i += 1) {
31464 dirOffset + 2 + 12 * i, // tag offset
31468 // Return the offset to the next directory:
31469 return dataView.getUint32(dirEndOffset, littleEndian);
31472 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31474 var tag = dataView.getUint16(offset, littleEndian);
31476 this.exif[tag] = this.getExifValue(
31480 dataView.getUint16(offset + 2, littleEndian), // tag type
31481 dataView.getUint32(offset + 4, littleEndian), // tag length
31486 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31488 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31497 Roo.log('Invalid Exif data: Invalid tag type.');
31501 tagSize = tagType.size * length;
31502 // Determine if the value is contained in the dataOffset bytes,
31503 // or if the value at the dataOffset is a pointer to the actual data:
31504 dataOffset = tagSize > 4 ?
31505 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31506 if (dataOffset + tagSize > dataView.byteLength) {
31507 Roo.log('Invalid Exif data: Invalid data offset.');
31510 if (length === 1) {
31511 return tagType.getValue(dataView, dataOffset, littleEndian);
31514 for (i = 0; i < length; i += 1) {
31515 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31518 if (tagType.ascii) {
31520 // Concatenate the chars:
31521 for (i = 0; i < values.length; i += 1) {
31523 // Ignore the terminating NULL byte(s):
31524 if (c === '\u0000') {
31536 Roo.apply(Roo.bootstrap.UploadCropbox, {
31538 'Orientation': 0x0112
31542 1: 0, //'top-left',
31544 3: 180, //'bottom-right',
31545 // 4: 'bottom-left',
31547 6: 90, //'right-top',
31548 // 7: 'right-bottom',
31549 8: 270 //'left-bottom'
31553 // byte, 8-bit unsigned int:
31555 getValue: function (dataView, dataOffset) {
31556 return dataView.getUint8(dataOffset);
31560 // ascii, 8-bit byte:
31562 getValue: function (dataView, dataOffset) {
31563 return String.fromCharCode(dataView.getUint8(dataOffset));
31568 // short, 16 bit int:
31570 getValue: function (dataView, dataOffset, littleEndian) {
31571 return dataView.getUint16(dataOffset, littleEndian);
31575 // long, 32 bit int:
31577 getValue: function (dataView, dataOffset, littleEndian) {
31578 return dataView.getUint32(dataOffset, littleEndian);
31582 // rational = two long values, first is numerator, second is denominator:
31584 getValue: function (dataView, dataOffset, littleEndian) {
31585 return dataView.getUint32(dataOffset, littleEndian) /
31586 dataView.getUint32(dataOffset + 4, littleEndian);
31590 // slong, 32 bit signed int:
31592 getValue: function (dataView, dataOffset, littleEndian) {
31593 return dataView.getInt32(dataOffset, littleEndian);
31597 // srational, two slongs, first is numerator, second is denominator:
31599 getValue: function (dataView, dataOffset, littleEndian) {
31600 return dataView.getInt32(dataOffset, littleEndian) /
31601 dataView.getInt32(dataOffset + 4, littleEndian);
31611 cls : 'btn-group roo-upload-cropbox-rotate-left',
31612 action : 'rotate-left',
31616 cls : 'btn btn-default',
31617 html : '<i class="fa fa-undo"></i>'
31623 cls : 'btn-group roo-upload-cropbox-picture',
31624 action : 'picture',
31628 cls : 'btn btn-default',
31629 html : '<i class="fa fa-picture-o"></i>'
31635 cls : 'btn-group roo-upload-cropbox-rotate-right',
31636 action : 'rotate-right',
31640 cls : 'btn btn-default',
31641 html : '<i class="fa fa-repeat"></i>'
31649 cls : 'btn-group roo-upload-cropbox-rotate-left',
31650 action : 'rotate-left',
31654 cls : 'btn btn-default',
31655 html : '<i class="fa fa-undo"></i>'
31661 cls : 'btn-group roo-upload-cropbox-download',
31662 action : 'download',
31666 cls : 'btn btn-default',
31667 html : '<i class="fa fa-download"></i>'
31673 cls : 'btn-group roo-upload-cropbox-crop',
31678 cls : 'btn btn-default',
31679 html : '<i class="fa fa-crop"></i>'
31685 cls : 'btn-group roo-upload-cropbox-trash',
31690 cls : 'btn btn-default',
31691 html : '<i class="fa fa-trash"></i>'
31697 cls : 'btn-group roo-upload-cropbox-rotate-right',
31698 action : 'rotate-right',
31702 cls : 'btn btn-default',
31703 html : '<i class="fa fa-repeat"></i>'
31711 cls : 'btn-group roo-upload-cropbox-rotate-left',
31712 action : 'rotate-left',
31716 cls : 'btn btn-default',
31717 html : '<i class="fa fa-undo"></i>'
31723 cls : 'btn-group roo-upload-cropbox-rotate-right',
31724 action : 'rotate-right',
31728 cls : 'btn btn-default',
31729 html : '<i class="fa fa-repeat"></i>'
31742 * @class Roo.bootstrap.DocumentManager
31743 * @extends Roo.bootstrap.Component
31744 * Bootstrap DocumentManager class
31745 * @cfg {String} paramName default 'imageUpload'
31746 * @cfg {String} toolTipName default 'filename'
31747 * @cfg {String} method default POST
31748 * @cfg {String} url action url
31749 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31750 * @cfg {Boolean} multiple multiple upload default true
31751 * @cfg {Number} thumbSize default 300
31752 * @cfg {String} fieldLabel
31753 * @cfg {Number} labelWidth default 4
31754 * @cfg {String} labelAlign (left|top) default left
31755 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31756 * @cfg {Number} labellg set the width of label (1-12)
31757 * @cfg {Number} labelmd set the width of label (1-12)
31758 * @cfg {Number} labelsm set the width of label (1-12)
31759 * @cfg {Number} labelxs set the width of label (1-12)
31762 * Create a new DocumentManager
31763 * @param {Object} config The config object
31766 Roo.bootstrap.DocumentManager = function(config){
31767 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31770 this.delegates = [];
31775 * Fire when initial the DocumentManager
31776 * @param {Roo.bootstrap.DocumentManager} this
31781 * inspect selected file
31782 * @param {Roo.bootstrap.DocumentManager} this
31783 * @param {File} file
31788 * Fire when xhr load exception
31789 * @param {Roo.bootstrap.DocumentManager} this
31790 * @param {XMLHttpRequest} xhr
31792 "exception" : true,
31794 * @event afterupload
31795 * Fire when xhr load exception
31796 * @param {Roo.bootstrap.DocumentManager} this
31797 * @param {XMLHttpRequest} xhr
31799 "afterupload" : true,
31802 * prepare the form data
31803 * @param {Roo.bootstrap.DocumentManager} this
31804 * @param {Object} formData
31809 * Fire when remove the file
31810 * @param {Roo.bootstrap.DocumentManager} this
31811 * @param {Object} file
31816 * Fire after refresh the file
31817 * @param {Roo.bootstrap.DocumentManager} this
31822 * Fire after click the image
31823 * @param {Roo.bootstrap.DocumentManager} this
31824 * @param {Object} file
31829 * Fire when upload a image and editable set to true
31830 * @param {Roo.bootstrap.DocumentManager} this
31831 * @param {Object} file
31835 * @event beforeselectfile
31836 * Fire before select file
31837 * @param {Roo.bootstrap.DocumentManager} this
31839 "beforeselectfile" : true,
31842 * Fire before process file
31843 * @param {Roo.bootstrap.DocumentManager} this
31844 * @param {Object} file
31848 * @event previewrendered
31849 * Fire when preview rendered
31850 * @param {Roo.bootstrap.DocumentManager} this
31851 * @param {Object} file
31853 "previewrendered" : true,
31856 "previewResize" : true
31861 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31870 paramName : 'imageUpload',
31871 toolTipName : 'filename',
31874 labelAlign : 'left',
31884 getAutoCreate : function()
31886 var managerWidget = {
31888 cls : 'roo-document-manager',
31892 cls : 'roo-document-manager-selector',
31897 cls : 'roo-document-manager-uploader',
31901 cls : 'roo-document-manager-upload-btn',
31902 html : '<i class="fa fa-plus"></i>'
31913 cls : 'column col-md-12',
31918 if(this.fieldLabel.length){
31923 cls : 'column col-md-12',
31924 html : this.fieldLabel
31928 cls : 'column col-md-12',
31933 if(this.labelAlign == 'left'){
31938 html : this.fieldLabel
31947 if(this.labelWidth > 12){
31948 content[0].style = "width: " + this.labelWidth + 'px';
31951 if(this.labelWidth < 13 && this.labelmd == 0){
31952 this.labelmd = this.labelWidth;
31955 if(this.labellg > 0){
31956 content[0].cls += ' col-lg-' + this.labellg;
31957 content[1].cls += ' col-lg-' + (12 - this.labellg);
31960 if(this.labelmd > 0){
31961 content[0].cls += ' col-md-' + this.labelmd;
31962 content[1].cls += ' col-md-' + (12 - this.labelmd);
31965 if(this.labelsm > 0){
31966 content[0].cls += ' col-sm-' + this.labelsm;
31967 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31970 if(this.labelxs > 0){
31971 content[0].cls += ' col-xs-' + this.labelxs;
31972 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31980 cls : 'row clearfix',
31988 initEvents : function()
31990 this.managerEl = this.el.select('.roo-document-manager', true).first();
31991 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31993 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31994 this.selectorEl.hide();
31997 this.selectorEl.attr('multiple', 'multiple');
32000 this.selectorEl.on('change', this.onFileSelected, this);
32002 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32003 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32005 this.uploader.on('click', this.onUploaderClick, this);
32007 this.renderProgressDialog();
32011 window.addEventListener("resize", function() { _this.refresh(); } );
32013 this.fireEvent('initial', this);
32016 renderProgressDialog : function()
32020 this.progressDialog = new Roo.bootstrap.Modal({
32021 cls : 'roo-document-manager-progress-dialog',
32022 allow_close : false,
32033 btnclick : function() {
32034 _this.uploadCancel();
32040 this.progressDialog.render(Roo.get(document.body));
32042 this.progress = new Roo.bootstrap.Progress({
32043 cls : 'roo-document-manager-progress',
32048 this.progress.render(this.progressDialog.getChildContainer());
32050 this.progressBar = new Roo.bootstrap.ProgressBar({
32051 cls : 'roo-document-manager-progress-bar',
32054 aria_valuemax : 12,
32058 this.progressBar.render(this.progress.getChildContainer());
32061 onUploaderClick : function(e)
32063 e.preventDefault();
32065 if(this.fireEvent('beforeselectfile', this) != false){
32066 this.selectorEl.dom.click();
32071 onFileSelected : function(e)
32073 e.preventDefault();
32075 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32079 Roo.each(this.selectorEl.dom.files, function(file){
32080 if(this.fireEvent('inspect', this, file) != false){
32081 this.files.push(file);
32091 this.selectorEl.dom.value = '';
32093 if(!this.files || !this.files.length){
32097 if(this.boxes > 0 && this.files.length > this.boxes){
32098 this.files = this.files.slice(0, this.boxes);
32101 this.uploader.show();
32103 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32104 this.uploader.hide();
32113 Roo.each(this.files, function(file){
32115 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32116 var f = this.renderPreview(file);
32121 if(file.type.indexOf('image') != -1){
32122 this.delegates.push(
32124 _this.process(file);
32125 }).createDelegate(this)
32133 _this.process(file);
32134 }).createDelegate(this)
32139 this.files = files;
32141 this.delegates = this.delegates.concat(docs);
32143 if(!this.delegates.length){
32148 this.progressBar.aria_valuemax = this.delegates.length;
32155 arrange : function()
32157 if(!this.delegates.length){
32158 this.progressDialog.hide();
32163 var delegate = this.delegates.shift();
32165 this.progressDialog.show();
32167 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32169 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32174 refresh : function()
32176 this.uploader.show();
32178 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32179 this.uploader.hide();
32182 Roo.isTouch ? this.closable(false) : this.closable(true);
32184 this.fireEvent('refresh', this);
32187 onRemove : function(e, el, o)
32189 e.preventDefault();
32191 this.fireEvent('remove', this, o);
32195 remove : function(o)
32199 Roo.each(this.files, function(file){
32200 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32209 this.files = files;
32216 Roo.each(this.files, function(file){
32221 file.target.remove();
32230 onClick : function(e, el, o)
32232 e.preventDefault();
32234 this.fireEvent('click', this, o);
32238 closable : function(closable)
32240 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32242 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32254 xhrOnLoad : function(xhr)
32256 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32260 if (xhr.readyState !== 4) {
32262 this.fireEvent('exception', this, xhr);
32266 var response = Roo.decode(xhr.responseText);
32268 if(!response.success){
32270 this.fireEvent('exception', this, xhr);
32274 var file = this.renderPreview(response.data);
32276 this.files.push(file);
32280 this.fireEvent('afterupload', this, xhr);
32284 xhrOnError : function(xhr)
32286 Roo.log('xhr on error');
32288 var response = Roo.decode(xhr.responseText);
32295 process : function(file)
32297 if(this.fireEvent('process', this, file) !== false){
32298 if(this.editable && file.type.indexOf('image') != -1){
32299 this.fireEvent('edit', this, file);
32303 this.uploadStart(file, false);
32310 uploadStart : function(file, crop)
32312 this.xhr = new XMLHttpRequest();
32314 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32319 file.xhr = this.xhr;
32321 this.managerEl.createChild({
32323 cls : 'roo-document-manager-loading',
32327 tooltip : file.name,
32328 cls : 'roo-document-manager-thumb',
32329 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32335 this.xhr.open(this.method, this.url, true);
32338 "Accept": "application/json",
32339 "Cache-Control": "no-cache",
32340 "X-Requested-With": "XMLHttpRequest"
32343 for (var headerName in headers) {
32344 var headerValue = headers[headerName];
32346 this.xhr.setRequestHeader(headerName, headerValue);
32352 this.xhr.onload = function()
32354 _this.xhrOnLoad(_this.xhr);
32357 this.xhr.onerror = function()
32359 _this.xhrOnError(_this.xhr);
32362 var formData = new FormData();
32364 formData.append('returnHTML', 'NO');
32367 formData.append('crop', crop);
32370 formData.append(this.paramName, file, file.name);
32377 if(this.fireEvent('prepare', this, formData, options) != false){
32379 if(options.manually){
32383 this.xhr.send(formData);
32387 this.uploadCancel();
32390 uploadCancel : function()
32396 this.delegates = [];
32398 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32405 renderPreview : function(file)
32407 if(typeof(file.target) != 'undefined' && file.target){
32411 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32413 var previewEl = this.managerEl.createChild({
32415 cls : 'roo-document-manager-preview',
32419 tooltip : file[this.toolTipName],
32420 cls : 'roo-document-manager-thumb',
32421 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32426 html : '<i class="fa fa-times-circle"></i>'
32431 var close = previewEl.select('button.close', true).first();
32433 close.on('click', this.onRemove, this, file);
32435 file.target = previewEl;
32437 var image = previewEl.select('img', true).first();
32441 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32443 image.on('click', this.onClick, this, file);
32445 this.fireEvent('previewrendered', this, file);
32451 onPreviewLoad : function(file, image)
32453 if(typeof(file.target) == 'undefined' || !file.target){
32457 var width = image.dom.naturalWidth || image.dom.width;
32458 var height = image.dom.naturalHeight || image.dom.height;
32460 if(!this.previewResize) {
32464 if(width > height){
32465 file.target.addClass('wide');
32469 file.target.addClass('tall');
32474 uploadFromSource : function(file, crop)
32476 this.xhr = new XMLHttpRequest();
32478 this.managerEl.createChild({
32480 cls : 'roo-document-manager-loading',
32484 tooltip : file.name,
32485 cls : 'roo-document-manager-thumb',
32486 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32492 this.xhr.open(this.method, this.url, true);
32495 "Accept": "application/json",
32496 "Cache-Control": "no-cache",
32497 "X-Requested-With": "XMLHttpRequest"
32500 for (var headerName in headers) {
32501 var headerValue = headers[headerName];
32503 this.xhr.setRequestHeader(headerName, headerValue);
32509 this.xhr.onload = function()
32511 _this.xhrOnLoad(_this.xhr);
32514 this.xhr.onerror = function()
32516 _this.xhrOnError(_this.xhr);
32519 var formData = new FormData();
32521 formData.append('returnHTML', 'NO');
32523 formData.append('crop', crop);
32525 if(typeof(file.filename) != 'undefined'){
32526 formData.append('filename', file.filename);
32529 if(typeof(file.mimetype) != 'undefined'){
32530 formData.append('mimetype', file.mimetype);
32535 if(this.fireEvent('prepare', this, formData) != false){
32536 this.xhr.send(formData);
32546 * @class Roo.bootstrap.DocumentViewer
32547 * @extends Roo.bootstrap.Component
32548 * Bootstrap DocumentViewer class
32549 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32550 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32553 * Create a new DocumentViewer
32554 * @param {Object} config The config object
32557 Roo.bootstrap.DocumentViewer = function(config){
32558 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32563 * Fire after initEvent
32564 * @param {Roo.bootstrap.DocumentViewer} this
32570 * @param {Roo.bootstrap.DocumentViewer} this
32575 * Fire after download button
32576 * @param {Roo.bootstrap.DocumentViewer} this
32581 * Fire after trash button
32582 * @param {Roo.bootstrap.DocumentViewer} this
32589 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32591 showDownload : true,
32595 getAutoCreate : function()
32599 cls : 'roo-document-viewer',
32603 cls : 'roo-document-viewer-body',
32607 cls : 'roo-document-viewer-thumb',
32611 cls : 'roo-document-viewer-image'
32619 cls : 'roo-document-viewer-footer',
32622 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32626 cls : 'btn-group roo-document-viewer-download',
32630 cls : 'btn btn-default',
32631 html : '<i class="fa fa-download"></i>'
32637 cls : 'btn-group roo-document-viewer-trash',
32641 cls : 'btn btn-default',
32642 html : '<i class="fa fa-trash"></i>'
32655 initEvents : function()
32657 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32658 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32660 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32661 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32663 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32664 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32666 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32667 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32669 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32670 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32672 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32673 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32675 this.bodyEl.on('click', this.onClick, this);
32676 this.downloadBtn.on('click', this.onDownload, this);
32677 this.trashBtn.on('click', this.onTrash, this);
32679 this.downloadBtn.hide();
32680 this.trashBtn.hide();
32682 if(this.showDownload){
32683 this.downloadBtn.show();
32686 if(this.showTrash){
32687 this.trashBtn.show();
32690 if(!this.showDownload && !this.showTrash) {
32691 this.footerEl.hide();
32696 initial : function()
32698 this.fireEvent('initial', this);
32702 onClick : function(e)
32704 e.preventDefault();
32706 this.fireEvent('click', this);
32709 onDownload : function(e)
32711 e.preventDefault();
32713 this.fireEvent('download', this);
32716 onTrash : function(e)
32718 e.preventDefault();
32720 this.fireEvent('trash', this);
32732 * @class Roo.bootstrap.NavProgressBar
32733 * @extends Roo.bootstrap.Component
32734 * Bootstrap NavProgressBar class
32737 * Create a new nav progress bar
32738 * @param {Object} config The config object
32741 Roo.bootstrap.NavProgressBar = function(config){
32742 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32744 this.bullets = this.bullets || [];
32746 // Roo.bootstrap.NavProgressBar.register(this);
32750 * Fires when the active item changes
32751 * @param {Roo.bootstrap.NavProgressBar} this
32752 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32753 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32760 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32765 getAutoCreate : function()
32767 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32771 cls : 'roo-navigation-bar-group',
32775 cls : 'roo-navigation-top-bar'
32779 cls : 'roo-navigation-bullets-bar',
32783 cls : 'roo-navigation-bar'
32790 cls : 'roo-navigation-bottom-bar'
32800 initEvents: function()
32805 onRender : function(ct, position)
32807 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32809 if(this.bullets.length){
32810 Roo.each(this.bullets, function(b){
32819 addItem : function(cfg)
32821 var item = new Roo.bootstrap.NavProgressItem(cfg);
32823 item.parentId = this.id;
32824 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32827 var top = new Roo.bootstrap.Element({
32829 cls : 'roo-navigation-bar-text'
32832 var bottom = new Roo.bootstrap.Element({
32834 cls : 'roo-navigation-bar-text'
32837 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32838 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32840 var topText = new Roo.bootstrap.Element({
32842 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32845 var bottomText = new Roo.bootstrap.Element({
32847 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32850 topText.onRender(top.el, null);
32851 bottomText.onRender(bottom.el, null);
32854 item.bottomEl = bottom;
32857 this.barItems.push(item);
32862 getActive : function()
32864 var active = false;
32866 Roo.each(this.barItems, function(v){
32868 if (!v.isActive()) {
32880 setActiveItem : function(item)
32884 Roo.each(this.barItems, function(v){
32885 if (v.rid == item.rid) {
32889 if (v.isActive()) {
32890 v.setActive(false);
32895 item.setActive(true);
32897 this.fireEvent('changed', this, item, prev);
32900 getBarItem: function(rid)
32904 Roo.each(this.barItems, function(e) {
32905 if (e.rid != rid) {
32916 indexOfItem : function(item)
32920 Roo.each(this.barItems, function(v, i){
32922 if (v.rid != item.rid) {
32933 setActiveNext : function()
32935 var i = this.indexOfItem(this.getActive());
32937 if (i > this.barItems.length) {
32941 this.setActiveItem(this.barItems[i+1]);
32944 setActivePrev : function()
32946 var i = this.indexOfItem(this.getActive());
32952 this.setActiveItem(this.barItems[i-1]);
32955 format : function()
32957 if(!this.barItems.length){
32961 var width = 100 / this.barItems.length;
32963 Roo.each(this.barItems, function(i){
32964 i.el.setStyle('width', width + '%');
32965 i.topEl.el.setStyle('width', width + '%');
32966 i.bottomEl.el.setStyle('width', width + '%');
32975 * Nav Progress Item
32980 * @class Roo.bootstrap.NavProgressItem
32981 * @extends Roo.bootstrap.Component
32982 * Bootstrap NavProgressItem class
32983 * @cfg {String} rid the reference id
32984 * @cfg {Boolean} active (true|false) Is item active default false
32985 * @cfg {Boolean} disabled (true|false) Is item active default false
32986 * @cfg {String} html
32987 * @cfg {String} position (top|bottom) text position default bottom
32988 * @cfg {String} icon show icon instead of number
32991 * Create a new NavProgressItem
32992 * @param {Object} config The config object
32994 Roo.bootstrap.NavProgressItem = function(config){
32995 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33000 * The raw click event for the entire grid.
33001 * @param {Roo.bootstrap.NavProgressItem} this
33002 * @param {Roo.EventObject} e
33009 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33015 position : 'bottom',
33018 getAutoCreate : function()
33020 var iconCls = 'roo-navigation-bar-item-icon';
33022 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33026 cls: 'roo-navigation-bar-item',
33036 cfg.cls += ' active';
33039 cfg.cls += ' disabled';
33045 disable : function()
33047 this.setDisabled(true);
33050 enable : function()
33052 this.setDisabled(false);
33055 initEvents: function()
33057 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33059 this.iconEl.on('click', this.onClick, this);
33062 onClick : function(e)
33064 e.preventDefault();
33070 if(this.fireEvent('click', this, e) === false){
33074 this.parent().setActiveItem(this);
33077 isActive: function ()
33079 return this.active;
33082 setActive : function(state)
33084 if(this.active == state){
33088 this.active = state;
33091 this.el.addClass('active');
33095 this.el.removeClass('active');
33100 setDisabled : function(state)
33102 if(this.disabled == state){
33106 this.disabled = state;
33109 this.el.addClass('disabled');
33113 this.el.removeClass('disabled');
33116 tooltipEl : function()
33118 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33131 * @class Roo.bootstrap.FieldLabel
33132 * @extends Roo.bootstrap.Component
33133 * Bootstrap FieldLabel class
33134 * @cfg {String} html contents of the element
33135 * @cfg {String} tag tag of the element default label
33136 * @cfg {String} cls class of the element
33137 * @cfg {String} target label target
33138 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33139 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33140 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33141 * @cfg {String} iconTooltip default "This field is required"
33142 * @cfg {String} indicatorpos (left|right) default left
33145 * Create a new FieldLabel
33146 * @param {Object} config The config object
33149 Roo.bootstrap.FieldLabel = function(config){
33150 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33155 * Fires after the field has been marked as invalid.
33156 * @param {Roo.form.FieldLabel} this
33157 * @param {String} msg The validation message
33162 * Fires after the field has been validated with no errors.
33163 * @param {Roo.form.FieldLabel} this
33169 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33176 invalidClass : 'has-warning',
33177 validClass : 'has-success',
33178 iconTooltip : 'This field is required',
33179 indicatorpos : 'left',
33181 getAutoCreate : function(){
33184 if (!this.allowBlank) {
33190 cls : 'roo-bootstrap-field-label ' + this.cls,
33195 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33196 tooltip : this.iconTooltip
33205 if(this.indicatorpos == 'right'){
33208 cls : 'roo-bootstrap-field-label ' + this.cls,
33217 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33218 tooltip : this.iconTooltip
33227 initEvents: function()
33229 Roo.bootstrap.Element.superclass.initEvents.call(this);
33231 this.indicator = this.indicatorEl();
33233 if(this.indicator){
33234 this.indicator.removeClass('visible');
33235 this.indicator.addClass('invisible');
33238 Roo.bootstrap.FieldLabel.register(this);
33241 indicatorEl : function()
33243 var indicator = this.el.select('i.roo-required-indicator',true).first();
33254 * Mark this field as valid
33256 markValid : function()
33258 if(this.indicator){
33259 this.indicator.removeClass('visible');
33260 this.indicator.addClass('invisible');
33262 if (Roo.bootstrap.version == 3) {
33263 this.el.removeClass(this.invalidClass);
33264 this.el.addClass(this.validClass);
33266 this.el.removeClass('is-invalid');
33267 this.el.addClass('is-valid');
33271 this.fireEvent('valid', this);
33275 * Mark this field as invalid
33276 * @param {String} msg The validation message
33278 markInvalid : function(msg)
33280 if(this.indicator){
33281 this.indicator.removeClass('invisible');
33282 this.indicator.addClass('visible');
33284 if (Roo.bootstrap.version == 3) {
33285 this.el.removeClass(this.validClass);
33286 this.el.addClass(this.invalidClass);
33288 this.el.removeClass('is-valid');
33289 this.el.addClass('is-invalid');
33293 this.fireEvent('invalid', this, msg);
33299 Roo.apply(Roo.bootstrap.FieldLabel, {
33304 * register a FieldLabel Group
33305 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33307 register : function(label)
33309 if(this.groups.hasOwnProperty(label.target)){
33313 this.groups[label.target] = label;
33317 * fetch a FieldLabel Group based on the target
33318 * @param {string} target
33319 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33321 get: function(target) {
33322 if (typeof(this.groups[target]) == 'undefined') {
33326 return this.groups[target] ;
33335 * page DateSplitField.
33341 * @class Roo.bootstrap.DateSplitField
33342 * @extends Roo.bootstrap.Component
33343 * Bootstrap DateSplitField class
33344 * @cfg {string} fieldLabel - the label associated
33345 * @cfg {Number} labelWidth set the width of label (0-12)
33346 * @cfg {String} labelAlign (top|left)
33347 * @cfg {Boolean} dayAllowBlank (true|false) default false
33348 * @cfg {Boolean} monthAllowBlank (true|false) default false
33349 * @cfg {Boolean} yearAllowBlank (true|false) default false
33350 * @cfg {string} dayPlaceholder
33351 * @cfg {string} monthPlaceholder
33352 * @cfg {string} yearPlaceholder
33353 * @cfg {string} dayFormat default 'd'
33354 * @cfg {string} monthFormat default 'm'
33355 * @cfg {string} yearFormat default 'Y'
33356 * @cfg {Number} labellg set the width of label (1-12)
33357 * @cfg {Number} labelmd set the width of label (1-12)
33358 * @cfg {Number} labelsm set the width of label (1-12)
33359 * @cfg {Number} labelxs set the width of label (1-12)
33363 * Create a new DateSplitField
33364 * @param {Object} config The config object
33367 Roo.bootstrap.DateSplitField = function(config){
33368 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33374 * getting the data of years
33375 * @param {Roo.bootstrap.DateSplitField} this
33376 * @param {Object} years
33381 * getting the data of days
33382 * @param {Roo.bootstrap.DateSplitField} this
33383 * @param {Object} days
33388 * Fires after the field has been marked as invalid.
33389 * @param {Roo.form.Field} this
33390 * @param {String} msg The validation message
33395 * Fires after the field has been validated with no errors.
33396 * @param {Roo.form.Field} this
33402 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33405 labelAlign : 'top',
33407 dayAllowBlank : false,
33408 monthAllowBlank : false,
33409 yearAllowBlank : false,
33410 dayPlaceholder : '',
33411 monthPlaceholder : '',
33412 yearPlaceholder : '',
33416 isFormField : true,
33422 getAutoCreate : function()
33426 cls : 'row roo-date-split-field-group',
33431 cls : 'form-hidden-field roo-date-split-field-group-value',
33437 var labelCls = 'col-md-12';
33438 var contentCls = 'col-md-4';
33440 if(this.fieldLabel){
33444 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33448 html : this.fieldLabel
33453 if(this.labelAlign == 'left'){
33455 if(this.labelWidth > 12){
33456 label.style = "width: " + this.labelWidth + 'px';
33459 if(this.labelWidth < 13 && this.labelmd == 0){
33460 this.labelmd = this.labelWidth;
33463 if(this.labellg > 0){
33464 labelCls = ' col-lg-' + this.labellg;
33465 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33468 if(this.labelmd > 0){
33469 labelCls = ' col-md-' + this.labelmd;
33470 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33473 if(this.labelsm > 0){
33474 labelCls = ' col-sm-' + this.labelsm;
33475 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33478 if(this.labelxs > 0){
33479 labelCls = ' col-xs-' + this.labelxs;
33480 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33484 label.cls += ' ' + labelCls;
33486 cfg.cn.push(label);
33489 Roo.each(['day', 'month', 'year'], function(t){
33492 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33499 inputEl: function ()
33501 return this.el.select('.roo-date-split-field-group-value', true).first();
33504 onRender : function(ct, position)
33508 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33510 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33512 this.dayField = new Roo.bootstrap.ComboBox({
33513 allowBlank : this.dayAllowBlank,
33514 alwaysQuery : true,
33515 displayField : 'value',
33518 forceSelection : true,
33520 placeholder : this.dayPlaceholder,
33521 selectOnFocus : true,
33522 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33523 triggerAction : 'all',
33525 valueField : 'value',
33526 store : new Roo.data.SimpleStore({
33527 data : (function() {
33529 _this.fireEvent('days', _this, days);
33532 fields : [ 'value' ]
33535 select : function (_self, record, index)
33537 _this.setValue(_this.getValue());
33542 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33544 this.monthField = new Roo.bootstrap.MonthField({
33545 after : '<i class=\"fa fa-calendar\"></i>',
33546 allowBlank : this.monthAllowBlank,
33547 placeholder : this.monthPlaceholder,
33550 render : function (_self)
33552 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33553 e.preventDefault();
33557 select : function (_self, oldvalue, newvalue)
33559 _this.setValue(_this.getValue());
33564 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33566 this.yearField = new Roo.bootstrap.ComboBox({
33567 allowBlank : this.yearAllowBlank,
33568 alwaysQuery : true,
33569 displayField : 'value',
33572 forceSelection : true,
33574 placeholder : this.yearPlaceholder,
33575 selectOnFocus : true,
33576 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33577 triggerAction : 'all',
33579 valueField : 'value',
33580 store : new Roo.data.SimpleStore({
33581 data : (function() {
33583 _this.fireEvent('years', _this, years);
33586 fields : [ 'value' ]
33589 select : function (_self, record, index)
33591 _this.setValue(_this.getValue());
33596 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33599 setValue : function(v, format)
33601 this.inputEl.dom.value = v;
33603 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33605 var d = Date.parseDate(v, f);
33612 this.setDay(d.format(this.dayFormat));
33613 this.setMonth(d.format(this.monthFormat));
33614 this.setYear(d.format(this.yearFormat));
33621 setDay : function(v)
33623 this.dayField.setValue(v);
33624 this.inputEl.dom.value = this.getValue();
33629 setMonth : function(v)
33631 this.monthField.setValue(v, true);
33632 this.inputEl.dom.value = this.getValue();
33637 setYear : function(v)
33639 this.yearField.setValue(v);
33640 this.inputEl.dom.value = this.getValue();
33645 getDay : function()
33647 return this.dayField.getValue();
33650 getMonth : function()
33652 return this.monthField.getValue();
33655 getYear : function()
33657 return this.yearField.getValue();
33660 getValue : function()
33662 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33664 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33674 this.inputEl.dom.value = '';
33679 validate : function()
33681 var d = this.dayField.validate();
33682 var m = this.monthField.validate();
33683 var y = this.yearField.validate();
33688 (!this.dayAllowBlank && !d) ||
33689 (!this.monthAllowBlank && !m) ||
33690 (!this.yearAllowBlank && !y)
33695 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33704 this.markInvalid();
33709 markValid : function()
33712 var label = this.el.select('label', true).first();
33713 var icon = this.el.select('i.fa-star', true).first();
33719 this.fireEvent('valid', this);
33723 * Mark this field as invalid
33724 * @param {String} msg The validation message
33726 markInvalid : function(msg)
33729 var label = this.el.select('label', true).first();
33730 var icon = this.el.select('i.fa-star', true).first();
33732 if(label && !icon){
33733 this.el.select('.roo-date-split-field-label', true).createChild({
33735 cls : 'text-danger fa fa-lg fa-star',
33736 tooltip : 'This field is required',
33737 style : 'margin-right:5px;'
33741 this.fireEvent('invalid', this, msg);
33744 clearInvalid : function()
33746 var label = this.el.select('label', true).first();
33747 var icon = this.el.select('i.fa-star', true).first();
33753 this.fireEvent('valid', this);
33756 getName: function()
33766 * http://masonry.desandro.com
33768 * The idea is to render all the bricks based on vertical width...
33770 * The original code extends 'outlayer' - we might need to use that....
33776 * @class Roo.bootstrap.LayoutMasonry
33777 * @extends Roo.bootstrap.Component
33778 * Bootstrap Layout Masonry class
33781 * Create a new Element
33782 * @param {Object} config The config object
33785 Roo.bootstrap.LayoutMasonry = function(config){
33787 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33791 Roo.bootstrap.LayoutMasonry.register(this);
33797 * Fire after layout the items
33798 * @param {Roo.bootstrap.LayoutMasonry} this
33799 * @param {Roo.EventObject} e
33806 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33809 * @cfg {Boolean} isLayoutInstant = no animation?
33811 isLayoutInstant : false, // needed?
33814 * @cfg {Number} boxWidth width of the columns
33819 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33824 * @cfg {Number} padWidth padding below box..
33829 * @cfg {Number} gutter gutter width..
33834 * @cfg {Number} maxCols maximum number of columns
33840 * @cfg {Boolean} isAutoInitial defalut true
33842 isAutoInitial : true,
33847 * @cfg {Boolean} isHorizontal defalut false
33849 isHorizontal : false,
33851 currentSize : null,
33857 bricks: null, //CompositeElement
33861 _isLayoutInited : false,
33863 // isAlternative : false, // only use for vertical layout...
33866 * @cfg {Number} alternativePadWidth padding below box..
33868 alternativePadWidth : 50,
33870 selectedBrick : [],
33872 getAutoCreate : function(){
33874 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33878 cls: 'blog-masonary-wrapper ' + this.cls,
33880 cls : 'mas-boxes masonary'
33887 getChildContainer: function( )
33889 if (this.boxesEl) {
33890 return this.boxesEl;
33893 this.boxesEl = this.el.select('.mas-boxes').first();
33895 return this.boxesEl;
33899 initEvents : function()
33903 if(this.isAutoInitial){
33904 Roo.log('hook children rendered');
33905 this.on('childrenrendered', function() {
33906 Roo.log('children rendered');
33912 initial : function()
33914 this.selectedBrick = [];
33916 this.currentSize = this.el.getBox(true);
33918 Roo.EventManager.onWindowResize(this.resize, this);
33920 if(!this.isAutoInitial){
33928 //this.layout.defer(500,this);
33932 resize : function()
33934 var cs = this.el.getBox(true);
33937 this.currentSize.width == cs.width &&
33938 this.currentSize.x == cs.x &&
33939 this.currentSize.height == cs.height &&
33940 this.currentSize.y == cs.y
33942 Roo.log("no change in with or X or Y");
33946 this.currentSize = cs;
33952 layout : function()
33954 this._resetLayout();
33956 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33958 this.layoutItems( isInstant );
33960 this._isLayoutInited = true;
33962 this.fireEvent('layout', this);
33966 _resetLayout : function()
33968 if(this.isHorizontal){
33969 this.horizontalMeasureColumns();
33973 this.verticalMeasureColumns();
33977 verticalMeasureColumns : function()
33979 this.getContainerWidth();
33981 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33982 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33986 var boxWidth = this.boxWidth + this.padWidth;
33988 if(this.containerWidth < this.boxWidth){
33989 boxWidth = this.containerWidth
33992 var containerWidth = this.containerWidth;
33994 var cols = Math.floor(containerWidth / boxWidth);
33996 this.cols = Math.max( cols, 1 );
33998 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34000 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34002 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34004 this.colWidth = boxWidth + avail - this.padWidth;
34006 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34007 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34010 horizontalMeasureColumns : function()
34012 this.getContainerWidth();
34014 var boxWidth = this.boxWidth;
34016 if(this.containerWidth < boxWidth){
34017 boxWidth = this.containerWidth;
34020 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34022 this.el.setHeight(boxWidth);
34026 getContainerWidth : function()
34028 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34031 layoutItems : function( isInstant )
34033 Roo.log(this.bricks);
34035 var items = Roo.apply([], this.bricks);
34037 if(this.isHorizontal){
34038 this._horizontalLayoutItems( items , isInstant );
34042 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34043 // this._verticalAlternativeLayoutItems( items , isInstant );
34047 this._verticalLayoutItems( items , isInstant );
34051 _verticalLayoutItems : function ( items , isInstant)
34053 if ( !items || !items.length ) {
34058 ['xs', 'xs', 'xs', 'tall'],
34059 ['xs', 'xs', 'tall'],
34060 ['xs', 'xs', 'sm'],
34061 ['xs', 'xs', 'xs'],
34067 ['sm', 'xs', 'xs'],
34071 ['tall', 'xs', 'xs', 'xs'],
34072 ['tall', 'xs', 'xs'],
34084 Roo.each(items, function(item, k){
34086 switch (item.size) {
34087 // these layouts take up a full box,
34098 boxes.push([item]);
34121 var filterPattern = function(box, length)
34129 var pattern = box.slice(0, length);
34133 Roo.each(pattern, function(i){
34134 format.push(i.size);
34137 Roo.each(standard, function(s){
34139 if(String(s) != String(format)){
34148 if(!match && length == 1){
34153 filterPattern(box, length - 1);
34157 queue.push(pattern);
34159 box = box.slice(length, box.length);
34161 filterPattern(box, 4);
34167 Roo.each(boxes, function(box, k){
34173 if(box.length == 1){
34178 filterPattern(box, 4);
34182 this._processVerticalLayoutQueue( queue, isInstant );
34186 // _verticalAlternativeLayoutItems : function( items , isInstant )
34188 // if ( !items || !items.length ) {
34192 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34196 _horizontalLayoutItems : function ( items , isInstant)
34198 if ( !items || !items.length || items.length < 3) {
34204 var eItems = items.slice(0, 3);
34206 items = items.slice(3, items.length);
34209 ['xs', 'xs', 'xs', 'wide'],
34210 ['xs', 'xs', 'wide'],
34211 ['xs', 'xs', 'sm'],
34212 ['xs', 'xs', 'xs'],
34218 ['sm', 'xs', 'xs'],
34222 ['wide', 'xs', 'xs', 'xs'],
34223 ['wide', 'xs', 'xs'],
34236 Roo.each(items, function(item, k){
34238 switch (item.size) {
34249 boxes.push([item]);
34273 var filterPattern = function(box, length)
34281 var pattern = box.slice(0, length);
34285 Roo.each(pattern, function(i){
34286 format.push(i.size);
34289 Roo.each(standard, function(s){
34291 if(String(s) != String(format)){
34300 if(!match && length == 1){
34305 filterPattern(box, length - 1);
34309 queue.push(pattern);
34311 box = box.slice(length, box.length);
34313 filterPattern(box, 4);
34319 Roo.each(boxes, function(box, k){
34325 if(box.length == 1){
34330 filterPattern(box, 4);
34337 var pos = this.el.getBox(true);
34341 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34343 var hit_end = false;
34345 Roo.each(queue, function(box){
34349 Roo.each(box, function(b){
34351 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34361 Roo.each(box, function(b){
34363 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34366 mx = Math.max(mx, b.x);
34370 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34374 Roo.each(box, function(b){
34376 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34390 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34393 /** Sets position of item in DOM
34394 * @param {Element} item
34395 * @param {Number} x - horizontal position
34396 * @param {Number} y - vertical position
34397 * @param {Boolean} isInstant - disables transitions
34399 _processVerticalLayoutQueue : function( queue, isInstant )
34401 var pos = this.el.getBox(true);
34406 for (var i = 0; i < this.cols; i++){
34410 Roo.each(queue, function(box, k){
34412 var col = k % this.cols;
34414 Roo.each(box, function(b,kk){
34416 b.el.position('absolute');
34418 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34419 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34421 if(b.size == 'md-left' || b.size == 'md-right'){
34422 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34423 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34426 b.el.setWidth(width);
34427 b.el.setHeight(height);
34429 b.el.select('iframe',true).setSize(width,height);
34433 for (var i = 0; i < this.cols; i++){
34435 if(maxY[i] < maxY[col]){
34440 col = Math.min(col, i);
34444 x = pos.x + col * (this.colWidth + this.padWidth);
34448 var positions = [];
34450 switch (box.length){
34452 positions = this.getVerticalOneBoxColPositions(x, y, box);
34455 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34458 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34461 positions = this.getVerticalFourBoxColPositions(x, y, box);
34467 Roo.each(box, function(b,kk){
34469 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34471 var sz = b.el.getSize();
34473 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34481 for (var i = 0; i < this.cols; i++){
34482 mY = Math.max(mY, maxY[i]);
34485 this.el.setHeight(mY - pos.y);
34489 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34491 // var pos = this.el.getBox(true);
34494 // var maxX = pos.right;
34496 // var maxHeight = 0;
34498 // Roo.each(items, function(item, k){
34502 // item.el.position('absolute');
34504 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34506 // item.el.setWidth(width);
34508 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34510 // item.el.setHeight(height);
34513 // item.el.setXY([x, y], isInstant ? false : true);
34515 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34518 // y = y + height + this.alternativePadWidth;
34520 // maxHeight = maxHeight + height + this.alternativePadWidth;
34524 // this.el.setHeight(maxHeight);
34528 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34530 var pos = this.el.getBox(true);
34535 var maxX = pos.right;
34537 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34539 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34541 Roo.each(queue, function(box, k){
34543 Roo.each(box, function(b, kk){
34545 b.el.position('absolute');
34547 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34548 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34550 if(b.size == 'md-left' || b.size == 'md-right'){
34551 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34552 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34555 b.el.setWidth(width);
34556 b.el.setHeight(height);
34564 var positions = [];
34566 switch (box.length){
34568 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34571 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34574 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34577 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34583 Roo.each(box, function(b,kk){
34585 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34587 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34595 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34597 Roo.each(eItems, function(b,k){
34599 b.size = (k == 0) ? 'sm' : 'xs';
34600 b.x = (k == 0) ? 2 : 1;
34601 b.y = (k == 0) ? 2 : 1;
34603 b.el.position('absolute');
34605 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34607 b.el.setWidth(width);
34609 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34611 b.el.setHeight(height);
34615 var positions = [];
34618 x : maxX - this.unitWidth * 2 - this.gutter,
34623 x : maxX - this.unitWidth,
34624 y : minY + (this.unitWidth + this.gutter) * 2
34628 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34632 Roo.each(eItems, function(b,k){
34634 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34640 getVerticalOneBoxColPositions : function(x, y, box)
34644 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34646 if(box[0].size == 'md-left'){
34650 if(box[0].size == 'md-right'){
34655 x : x + (this.unitWidth + this.gutter) * rand,
34662 getVerticalTwoBoxColPositions : function(x, y, box)
34666 if(box[0].size == 'xs'){
34670 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34674 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34688 x : x + (this.unitWidth + this.gutter) * 2,
34689 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34696 getVerticalThreeBoxColPositions : function(x, y, box)
34700 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34708 x : x + (this.unitWidth + this.gutter) * 1,
34713 x : x + (this.unitWidth + this.gutter) * 2,
34721 if(box[0].size == 'xs' && box[1].size == 'xs'){
34730 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34734 x : x + (this.unitWidth + this.gutter) * 1,
34748 x : x + (this.unitWidth + this.gutter) * 2,
34753 x : x + (this.unitWidth + this.gutter) * 2,
34754 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34761 getVerticalFourBoxColPositions : function(x, y, box)
34765 if(box[0].size == 'xs'){
34774 y : y + (this.unitHeight + this.gutter) * 1
34779 y : y + (this.unitHeight + this.gutter) * 2
34783 x : x + (this.unitWidth + this.gutter) * 1,
34797 x : x + (this.unitWidth + this.gutter) * 2,
34802 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34803 y : y + (this.unitHeight + this.gutter) * 1
34807 x : x + (this.unitWidth + this.gutter) * 2,
34808 y : y + (this.unitWidth + this.gutter) * 2
34815 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34819 if(box[0].size == 'md-left'){
34821 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34828 if(box[0].size == 'md-right'){
34830 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34831 y : minY + (this.unitWidth + this.gutter) * 1
34837 var rand = Math.floor(Math.random() * (4 - box[0].y));
34840 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34841 y : minY + (this.unitWidth + this.gutter) * rand
34848 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34852 if(box[0].size == 'xs'){
34855 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34860 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34861 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34869 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34874 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34875 y : minY + (this.unitWidth + this.gutter) * 2
34882 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34886 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34889 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34894 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34895 y : minY + (this.unitWidth + this.gutter) * 1
34899 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34900 y : minY + (this.unitWidth + this.gutter) * 2
34907 if(box[0].size == 'xs' && box[1].size == 'xs'){
34910 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34915 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34920 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34921 y : minY + (this.unitWidth + this.gutter) * 1
34929 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34934 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34935 y : minY + (this.unitWidth + this.gutter) * 2
34939 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34940 y : minY + (this.unitWidth + this.gutter) * 2
34947 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34951 if(box[0].size == 'xs'){
34954 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34959 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34964 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),
34969 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34970 y : minY + (this.unitWidth + this.gutter) * 1
34978 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34983 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34984 y : minY + (this.unitWidth + this.gutter) * 2
34988 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34989 y : minY + (this.unitWidth + this.gutter) * 2
34993 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),
34994 y : minY + (this.unitWidth + this.gutter) * 2
35002 * remove a Masonry Brick
35003 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35005 removeBrick : function(brick_id)
35011 for (var i = 0; i<this.bricks.length; i++) {
35012 if (this.bricks[i].id == brick_id) {
35013 this.bricks.splice(i,1);
35014 this.el.dom.removeChild(Roo.get(brick_id).dom);
35021 * adds a Masonry Brick
35022 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35024 addBrick : function(cfg)
35026 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35027 //this.register(cn);
35028 cn.parentId = this.id;
35029 cn.render(this.el);
35034 * register a Masonry Brick
35035 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35038 register : function(brick)
35040 this.bricks.push(brick);
35041 brick.masonryId = this.id;
35045 * clear all the Masonry Brick
35047 clearAll : function()
35050 //this.getChildContainer().dom.innerHTML = "";
35051 this.el.dom.innerHTML = '';
35054 getSelected : function()
35056 if (!this.selectedBrick) {
35060 return this.selectedBrick;
35064 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35068 * register a Masonry Layout
35069 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35072 register : function(layout)
35074 this.groups[layout.id] = layout;
35077 * fetch a Masonry Layout based on the masonry layout ID
35078 * @param {string} the masonry layout to add
35079 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35082 get: function(layout_id) {
35083 if (typeof(this.groups[layout_id]) == 'undefined') {
35086 return this.groups[layout_id] ;
35098 * http://masonry.desandro.com
35100 * The idea is to render all the bricks based on vertical width...
35102 * The original code extends 'outlayer' - we might need to use that....
35108 * @class Roo.bootstrap.LayoutMasonryAuto
35109 * @extends Roo.bootstrap.Component
35110 * Bootstrap Layout Masonry class
35113 * Create a new Element
35114 * @param {Object} config The config object
35117 Roo.bootstrap.LayoutMasonryAuto = function(config){
35118 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35121 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35124 * @cfg {Boolean} isFitWidth - resize the width..
35126 isFitWidth : false, // options..
35128 * @cfg {Boolean} isOriginLeft = left align?
35130 isOriginLeft : true,
35132 * @cfg {Boolean} isOriginTop = top align?
35134 isOriginTop : false,
35136 * @cfg {Boolean} isLayoutInstant = no animation?
35138 isLayoutInstant : false, // needed?
35140 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35142 isResizingContainer : true,
35144 * @cfg {Number} columnWidth width of the columns
35150 * @cfg {Number} maxCols maximum number of columns
35155 * @cfg {Number} padHeight padding below box..
35161 * @cfg {Boolean} isAutoInitial defalut true
35164 isAutoInitial : true,
35170 initialColumnWidth : 0,
35171 currentSize : null,
35173 colYs : null, // array.
35180 bricks: null, //CompositeElement
35181 cols : 0, // array?
35182 // element : null, // wrapped now this.el
35183 _isLayoutInited : null,
35186 getAutoCreate : function(){
35190 cls: 'blog-masonary-wrapper ' + this.cls,
35192 cls : 'mas-boxes masonary'
35199 getChildContainer: function( )
35201 if (this.boxesEl) {
35202 return this.boxesEl;
35205 this.boxesEl = this.el.select('.mas-boxes').first();
35207 return this.boxesEl;
35211 initEvents : function()
35215 if(this.isAutoInitial){
35216 Roo.log('hook children rendered');
35217 this.on('childrenrendered', function() {
35218 Roo.log('children rendered');
35225 initial : function()
35227 this.reloadItems();
35229 this.currentSize = this.el.getBox(true);
35231 /// was window resize... - let's see if this works..
35232 Roo.EventManager.onWindowResize(this.resize, this);
35234 if(!this.isAutoInitial){
35239 this.layout.defer(500,this);
35242 reloadItems: function()
35244 this.bricks = this.el.select('.masonry-brick', true);
35246 this.bricks.each(function(b) {
35247 //Roo.log(b.getSize());
35248 if (!b.attr('originalwidth')) {
35249 b.attr('originalwidth', b.getSize().width);
35254 Roo.log(this.bricks.elements.length);
35257 resize : function()
35260 var cs = this.el.getBox(true);
35262 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35263 Roo.log("no change in with or X");
35266 this.currentSize = cs;
35270 layout : function()
35273 this._resetLayout();
35274 //this._manageStamps();
35276 // don't animate first layout
35277 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35278 this.layoutItems( isInstant );
35280 // flag for initalized
35281 this._isLayoutInited = true;
35284 layoutItems : function( isInstant )
35286 //var items = this._getItemsForLayout( this.items );
35287 // original code supports filtering layout items.. we just ignore it..
35289 this._layoutItems( this.bricks , isInstant );
35291 this._postLayout();
35293 _layoutItems : function ( items , isInstant)
35295 //this.fireEvent( 'layout', this, items );
35298 if ( !items || !items.elements.length ) {
35299 // no items, emit event with empty array
35304 items.each(function(item) {
35305 Roo.log("layout item");
35307 // get x/y object from method
35308 var position = this._getItemLayoutPosition( item );
35310 position.item = item;
35311 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35312 queue.push( position );
35315 this._processLayoutQueue( queue );
35317 /** Sets position of item in DOM
35318 * @param {Element} item
35319 * @param {Number} x - horizontal position
35320 * @param {Number} y - vertical position
35321 * @param {Boolean} isInstant - disables transitions
35323 _processLayoutQueue : function( queue )
35325 for ( var i=0, len = queue.length; i < len; i++ ) {
35326 var obj = queue[i];
35327 obj.item.position('absolute');
35328 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35334 * Any logic you want to do after each layout,
35335 * i.e. size the container
35337 _postLayout : function()
35339 this.resizeContainer();
35342 resizeContainer : function()
35344 if ( !this.isResizingContainer ) {
35347 var size = this._getContainerSize();
35349 this.el.setSize(size.width,size.height);
35350 this.boxesEl.setSize(size.width,size.height);
35356 _resetLayout : function()
35358 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35359 this.colWidth = this.el.getWidth();
35360 //this.gutter = this.el.getWidth();
35362 this.measureColumns();
35368 this.colYs.push( 0 );
35374 measureColumns : function()
35376 this.getContainerWidth();
35377 // if columnWidth is 0, default to outerWidth of first item
35378 if ( !this.columnWidth ) {
35379 var firstItem = this.bricks.first();
35380 Roo.log(firstItem);
35381 this.columnWidth = this.containerWidth;
35382 if (firstItem && firstItem.attr('originalwidth') ) {
35383 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35385 // columnWidth fall back to item of first element
35386 Roo.log("set column width?");
35387 this.initialColumnWidth = this.columnWidth ;
35389 // if first elem has no width, default to size of container
35394 if (this.initialColumnWidth) {
35395 this.columnWidth = this.initialColumnWidth;
35400 // column width is fixed at the top - however if container width get's smaller we should
35403 // this bit calcs how man columns..
35405 var columnWidth = this.columnWidth += this.gutter;
35407 // calculate columns
35408 var containerWidth = this.containerWidth + this.gutter;
35410 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35411 // fix rounding errors, typically with gutters
35412 var excess = columnWidth - containerWidth % columnWidth;
35415 // if overshoot is less than a pixel, round up, otherwise floor it
35416 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35417 cols = Math[ mathMethod ]( cols );
35418 this.cols = Math.max( cols, 1 );
35419 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35421 // padding positioning..
35422 var totalColWidth = this.cols * this.columnWidth;
35423 var padavail = this.containerWidth - totalColWidth;
35424 // so for 2 columns - we need 3 'pads'
35426 var padNeeded = (1+this.cols) * this.padWidth;
35428 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35430 this.columnWidth += padExtra
35431 //this.padWidth = Math.floor(padavail / ( this.cols));
35433 // adjust colum width so that padding is fixed??
35435 // we have 3 columns ... total = width * 3
35436 // we have X left over... that should be used by
35438 //if (this.expandC) {
35446 getContainerWidth : function()
35448 /* // container is parent if fit width
35449 var container = this.isFitWidth ? this.element.parentNode : this.element;
35450 // check that this.size and size are there
35451 // IE8 triggers resize on body size change, so they might not be
35453 var size = getSize( container ); //FIXME
35454 this.containerWidth = size && size.innerWidth; //FIXME
35457 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35461 _getItemLayoutPosition : function( item ) // what is item?
35463 // we resize the item to our columnWidth..
35465 item.setWidth(this.columnWidth);
35466 item.autoBoxAdjust = false;
35468 var sz = item.getSize();
35470 // how many columns does this brick span
35471 var remainder = this.containerWidth % this.columnWidth;
35473 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35474 // round if off by 1 pixel, otherwise use ceil
35475 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35476 colSpan = Math.min( colSpan, this.cols );
35478 // normally this should be '1' as we dont' currently allow multi width columns..
35480 var colGroup = this._getColGroup( colSpan );
35481 // get the minimum Y value from the columns
35482 var minimumY = Math.min.apply( Math, colGroup );
35483 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35485 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35487 // position the brick
35489 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35490 y: this.currentSize.y + minimumY + this.padHeight
35494 // apply setHeight to necessary columns
35495 var setHeight = minimumY + sz.height + this.padHeight;
35496 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35498 var setSpan = this.cols + 1 - colGroup.length;
35499 for ( var i = 0; i < setSpan; i++ ) {
35500 this.colYs[ shortColIndex + i ] = setHeight ;
35507 * @param {Number} colSpan - number of columns the element spans
35508 * @returns {Array} colGroup
35510 _getColGroup : function( colSpan )
35512 if ( colSpan < 2 ) {
35513 // if brick spans only one column, use all the column Ys
35518 // how many different places could this brick fit horizontally
35519 var groupCount = this.cols + 1 - colSpan;
35520 // for each group potential horizontal position
35521 for ( var i = 0; i < groupCount; i++ ) {
35522 // make an array of colY values for that one group
35523 var groupColYs = this.colYs.slice( i, i + colSpan );
35524 // and get the max value of the array
35525 colGroup[i] = Math.max.apply( Math, groupColYs );
35530 _manageStamp : function( stamp )
35532 var stampSize = stamp.getSize();
35533 var offset = stamp.getBox();
35534 // get the columns that this stamp affects
35535 var firstX = this.isOriginLeft ? offset.x : offset.right;
35536 var lastX = firstX + stampSize.width;
35537 var firstCol = Math.floor( firstX / this.columnWidth );
35538 firstCol = Math.max( 0, firstCol );
35540 var lastCol = Math.floor( lastX / this.columnWidth );
35541 // lastCol should not go over if multiple of columnWidth #425
35542 lastCol -= lastX % this.columnWidth ? 0 : 1;
35543 lastCol = Math.min( this.cols - 1, lastCol );
35545 // set colYs to bottom of the stamp
35546 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35549 for ( var i = firstCol; i <= lastCol; i++ ) {
35550 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35555 _getContainerSize : function()
35557 this.maxY = Math.max.apply( Math, this.colYs );
35562 if ( this.isFitWidth ) {
35563 size.width = this._getContainerFitWidth();
35569 _getContainerFitWidth : function()
35571 var unusedCols = 0;
35572 // count unused columns
35575 if ( this.colYs[i] !== 0 ) {
35580 // fit container to columns that have been used
35581 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35584 needsResizeLayout : function()
35586 var previousWidth = this.containerWidth;
35587 this.getContainerWidth();
35588 return previousWidth !== this.containerWidth;
35603 * @class Roo.bootstrap.MasonryBrick
35604 * @extends Roo.bootstrap.Component
35605 * Bootstrap MasonryBrick class
35608 * Create a new MasonryBrick
35609 * @param {Object} config The config object
35612 Roo.bootstrap.MasonryBrick = function(config){
35614 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35616 Roo.bootstrap.MasonryBrick.register(this);
35622 * When a MasonryBrick is clcik
35623 * @param {Roo.bootstrap.MasonryBrick} this
35624 * @param {Roo.EventObject} e
35630 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35633 * @cfg {String} title
35637 * @cfg {String} html
35641 * @cfg {String} bgimage
35645 * @cfg {String} videourl
35649 * @cfg {String} cls
35653 * @cfg {String} href
35657 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35662 * @cfg {String} placetitle (center|bottom)
35667 * @cfg {Boolean} isFitContainer defalut true
35669 isFitContainer : true,
35672 * @cfg {Boolean} preventDefault defalut false
35674 preventDefault : false,
35677 * @cfg {Boolean} inverse defalut false
35679 maskInverse : false,
35681 getAutoCreate : function()
35683 if(!this.isFitContainer){
35684 return this.getSplitAutoCreate();
35687 var cls = 'masonry-brick masonry-brick-full';
35689 if(this.href.length){
35690 cls += ' masonry-brick-link';
35693 if(this.bgimage.length){
35694 cls += ' masonry-brick-image';
35697 if(this.maskInverse){
35698 cls += ' mask-inverse';
35701 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35702 cls += ' enable-mask';
35706 cls += ' masonry-' + this.size + '-brick';
35709 if(this.placetitle.length){
35711 switch (this.placetitle) {
35713 cls += ' masonry-center-title';
35716 cls += ' masonry-bottom-title';
35723 if(!this.html.length && !this.bgimage.length){
35724 cls += ' masonry-center-title';
35727 if(!this.html.length && this.bgimage.length){
35728 cls += ' masonry-bottom-title';
35733 cls += ' ' + this.cls;
35737 tag: (this.href.length) ? 'a' : 'div',
35742 cls: 'masonry-brick-mask'
35746 cls: 'masonry-brick-paragraph',
35752 if(this.href.length){
35753 cfg.href = this.href;
35756 var cn = cfg.cn[1].cn;
35758 if(this.title.length){
35761 cls: 'masonry-brick-title',
35766 if(this.html.length){
35769 cls: 'masonry-brick-text',
35774 if (!this.title.length && !this.html.length) {
35775 cfg.cn[1].cls += ' hide';
35778 if(this.bgimage.length){
35781 cls: 'masonry-brick-image-view',
35786 if(this.videourl.length){
35787 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35788 // youtube support only?
35791 cls: 'masonry-brick-image-view',
35794 allowfullscreen : true
35802 getSplitAutoCreate : function()
35804 var cls = 'masonry-brick masonry-brick-split';
35806 if(this.href.length){
35807 cls += ' masonry-brick-link';
35810 if(this.bgimage.length){
35811 cls += ' masonry-brick-image';
35815 cls += ' masonry-' + this.size + '-brick';
35818 switch (this.placetitle) {
35820 cls += ' masonry-center-title';
35823 cls += ' masonry-bottom-title';
35826 if(!this.bgimage.length){
35827 cls += ' masonry-center-title';
35830 if(this.bgimage.length){
35831 cls += ' masonry-bottom-title';
35837 cls += ' ' + this.cls;
35841 tag: (this.href.length) ? 'a' : 'div',
35846 cls: 'masonry-brick-split-head',
35850 cls: 'masonry-brick-paragraph',
35857 cls: 'masonry-brick-split-body',
35863 if(this.href.length){
35864 cfg.href = this.href;
35867 if(this.title.length){
35868 cfg.cn[0].cn[0].cn.push({
35870 cls: 'masonry-brick-title',
35875 if(this.html.length){
35876 cfg.cn[1].cn.push({
35878 cls: 'masonry-brick-text',
35883 if(this.bgimage.length){
35884 cfg.cn[0].cn.push({
35886 cls: 'masonry-brick-image-view',
35891 if(this.videourl.length){
35892 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35893 // youtube support only?
35894 cfg.cn[0].cn.cn.push({
35896 cls: 'masonry-brick-image-view',
35899 allowfullscreen : true
35906 initEvents: function()
35908 switch (this.size) {
35941 this.el.on('touchstart', this.onTouchStart, this);
35942 this.el.on('touchmove', this.onTouchMove, this);
35943 this.el.on('touchend', this.onTouchEnd, this);
35944 this.el.on('contextmenu', this.onContextMenu, this);
35946 this.el.on('mouseenter' ,this.enter, this);
35947 this.el.on('mouseleave', this.leave, this);
35948 this.el.on('click', this.onClick, this);
35951 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35952 this.parent().bricks.push(this);
35957 onClick: function(e, el)
35959 var time = this.endTimer - this.startTimer;
35960 // Roo.log(e.preventDefault());
35963 e.preventDefault();
35968 if(!this.preventDefault){
35972 e.preventDefault();
35974 if (this.activeClass != '') {
35975 this.selectBrick();
35978 this.fireEvent('click', this, e);
35981 enter: function(e, el)
35983 e.preventDefault();
35985 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35989 if(this.bgimage.length && this.html.length){
35990 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35994 leave: function(e, el)
35996 e.preventDefault();
35998 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36002 if(this.bgimage.length && this.html.length){
36003 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36007 onTouchStart: function(e, el)
36009 // e.preventDefault();
36011 this.touchmoved = false;
36013 if(!this.isFitContainer){
36017 if(!this.bgimage.length || !this.html.length){
36021 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36023 this.timer = new Date().getTime();
36027 onTouchMove: function(e, el)
36029 this.touchmoved = true;
36032 onContextMenu : function(e,el)
36034 e.preventDefault();
36035 e.stopPropagation();
36039 onTouchEnd: function(e, el)
36041 // e.preventDefault();
36043 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36050 if(!this.bgimage.length || !this.html.length){
36052 if(this.href.length){
36053 window.location.href = this.href;
36059 if(!this.isFitContainer){
36063 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36065 window.location.href = this.href;
36068 //selection on single brick only
36069 selectBrick : function() {
36071 if (!this.parentId) {
36075 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36076 var index = m.selectedBrick.indexOf(this.id);
36079 m.selectedBrick.splice(index,1);
36080 this.el.removeClass(this.activeClass);
36084 for(var i = 0; i < m.selectedBrick.length; i++) {
36085 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36086 b.el.removeClass(b.activeClass);
36089 m.selectedBrick = [];
36091 m.selectedBrick.push(this.id);
36092 this.el.addClass(this.activeClass);
36096 isSelected : function(){
36097 return this.el.hasClass(this.activeClass);
36102 Roo.apply(Roo.bootstrap.MasonryBrick, {
36105 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36107 * register a Masonry Brick
36108 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36111 register : function(brick)
36113 //this.groups[brick.id] = brick;
36114 this.groups.add(brick.id, brick);
36117 * fetch a masonry brick based on the masonry brick ID
36118 * @param {string} the masonry brick to add
36119 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36122 get: function(brick_id)
36124 // if (typeof(this.groups[brick_id]) == 'undefined') {
36127 // return this.groups[brick_id] ;
36129 if(this.groups.key(brick_id)) {
36130 return this.groups.key(brick_id);
36148 * @class Roo.bootstrap.Brick
36149 * @extends Roo.bootstrap.Component
36150 * Bootstrap Brick class
36153 * Create a new Brick
36154 * @param {Object} config The config object
36157 Roo.bootstrap.Brick = function(config){
36158 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36164 * When a Brick is click
36165 * @param {Roo.bootstrap.Brick} this
36166 * @param {Roo.EventObject} e
36172 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36175 * @cfg {String} title
36179 * @cfg {String} html
36183 * @cfg {String} bgimage
36187 * @cfg {String} cls
36191 * @cfg {String} href
36195 * @cfg {String} video
36199 * @cfg {Boolean} square
36203 getAutoCreate : function()
36205 var cls = 'roo-brick';
36207 if(this.href.length){
36208 cls += ' roo-brick-link';
36211 if(this.bgimage.length){
36212 cls += ' roo-brick-image';
36215 if(!this.html.length && !this.bgimage.length){
36216 cls += ' roo-brick-center-title';
36219 if(!this.html.length && this.bgimage.length){
36220 cls += ' roo-brick-bottom-title';
36224 cls += ' ' + this.cls;
36228 tag: (this.href.length) ? 'a' : 'div',
36233 cls: 'roo-brick-paragraph',
36239 if(this.href.length){
36240 cfg.href = this.href;
36243 var cn = cfg.cn[0].cn;
36245 if(this.title.length){
36248 cls: 'roo-brick-title',
36253 if(this.html.length){
36256 cls: 'roo-brick-text',
36263 if(this.bgimage.length){
36266 cls: 'roo-brick-image-view',
36274 initEvents: function()
36276 if(this.title.length || this.html.length){
36277 this.el.on('mouseenter' ,this.enter, this);
36278 this.el.on('mouseleave', this.leave, this);
36281 Roo.EventManager.onWindowResize(this.resize, this);
36283 if(this.bgimage.length){
36284 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36285 this.imageEl.on('load', this.onImageLoad, this);
36292 onImageLoad : function()
36297 resize : function()
36299 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36301 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36303 if(this.bgimage.length){
36304 var image = this.el.select('.roo-brick-image-view', true).first();
36306 image.setWidth(paragraph.getWidth());
36309 image.setHeight(paragraph.getWidth());
36312 this.el.setHeight(image.getHeight());
36313 paragraph.setHeight(image.getHeight());
36319 enter: function(e, el)
36321 e.preventDefault();
36323 if(this.bgimage.length){
36324 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36325 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36329 leave: function(e, el)
36331 e.preventDefault();
36333 if(this.bgimage.length){
36334 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36335 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36350 * @class Roo.bootstrap.NumberField
36351 * @extends Roo.bootstrap.Input
36352 * Bootstrap NumberField class
36358 * Create a new NumberField
36359 * @param {Object} config The config object
36362 Roo.bootstrap.NumberField = function(config){
36363 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36366 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36369 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36371 allowDecimals : true,
36373 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36375 decimalSeparator : ".",
36377 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36379 decimalPrecision : 2,
36381 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36383 allowNegative : true,
36386 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36390 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36392 minValue : Number.NEGATIVE_INFINITY,
36394 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36396 maxValue : Number.MAX_VALUE,
36398 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36400 minText : "The minimum value for this field is {0}",
36402 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36404 maxText : "The maximum value for this field is {0}",
36406 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36407 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36409 nanText : "{0} is not a valid number",
36411 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36413 thousandsDelimiter : false,
36415 * @cfg {String} valueAlign alignment of value
36417 valueAlign : "left",
36419 getAutoCreate : function()
36421 var hiddenInput = {
36425 cls: 'hidden-number-input'
36429 hiddenInput.name = this.name;
36434 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36436 this.name = hiddenInput.name;
36438 if(cfg.cn.length > 0) {
36439 cfg.cn.push(hiddenInput);
36446 initEvents : function()
36448 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36450 var allowed = "0123456789";
36452 if(this.allowDecimals){
36453 allowed += this.decimalSeparator;
36456 if(this.allowNegative){
36460 if(this.thousandsDelimiter) {
36464 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36466 var keyPress = function(e){
36468 var k = e.getKey();
36470 var c = e.getCharCode();
36473 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36474 allowed.indexOf(String.fromCharCode(c)) === -1
36480 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36484 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36489 this.el.on("keypress", keyPress, this);
36492 validateValue : function(value)
36495 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36499 var num = this.parseValue(value);
36502 this.markInvalid(String.format(this.nanText, value));
36506 if(num < this.minValue){
36507 this.markInvalid(String.format(this.minText, this.minValue));
36511 if(num > this.maxValue){
36512 this.markInvalid(String.format(this.maxText, this.maxValue));
36519 getValue : function()
36521 var v = this.hiddenEl().getValue();
36523 return this.fixPrecision(this.parseValue(v));
36526 parseValue : function(value)
36528 if(this.thousandsDelimiter) {
36530 r = new RegExp(",", "g");
36531 value = value.replace(r, "");
36534 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36535 return isNaN(value) ? '' : value;
36538 fixPrecision : function(value)
36540 if(this.thousandsDelimiter) {
36542 r = new RegExp(",", "g");
36543 value = value.replace(r, "");
36546 var nan = isNaN(value);
36548 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36549 return nan ? '' : value;
36551 return parseFloat(value).toFixed(this.decimalPrecision);
36554 setValue : function(v)
36556 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36562 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36564 this.inputEl().dom.value = (v == '') ? '' :
36565 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36567 if(!this.allowZero && v === '0') {
36568 this.hiddenEl().dom.value = '';
36569 this.inputEl().dom.value = '';
36576 decimalPrecisionFcn : function(v)
36578 return Math.floor(v);
36581 beforeBlur : function()
36583 var v = this.parseValue(this.getRawValue());
36585 if(v || v === 0 || v === ''){
36590 hiddenEl : function()
36592 return this.el.select('input.hidden-number-input',true).first();
36604 * @class Roo.bootstrap.DocumentSlider
36605 * @extends Roo.bootstrap.Component
36606 * Bootstrap DocumentSlider class
36609 * Create a new DocumentViewer
36610 * @param {Object} config The config object
36613 Roo.bootstrap.DocumentSlider = function(config){
36614 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36621 * Fire after initEvent
36622 * @param {Roo.bootstrap.DocumentSlider} this
36627 * Fire after update
36628 * @param {Roo.bootstrap.DocumentSlider} this
36634 * @param {Roo.bootstrap.DocumentSlider} this
36640 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36646 getAutoCreate : function()
36650 cls : 'roo-document-slider',
36654 cls : 'roo-document-slider-header',
36658 cls : 'roo-document-slider-header-title'
36664 cls : 'roo-document-slider-body',
36668 cls : 'roo-document-slider-prev',
36672 cls : 'fa fa-chevron-left'
36678 cls : 'roo-document-slider-thumb',
36682 cls : 'roo-document-slider-image'
36688 cls : 'roo-document-slider-next',
36692 cls : 'fa fa-chevron-right'
36704 initEvents : function()
36706 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36707 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36709 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36710 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36712 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36713 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36715 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36716 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36718 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36719 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36721 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36722 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36724 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36725 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36727 this.thumbEl.on('click', this.onClick, this);
36729 this.prevIndicator.on('click', this.prev, this);
36731 this.nextIndicator.on('click', this.next, this);
36735 initial : function()
36737 if(this.files.length){
36738 this.indicator = 1;
36742 this.fireEvent('initial', this);
36745 update : function()
36747 this.imageEl.attr('src', this.files[this.indicator - 1]);
36749 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36751 this.prevIndicator.show();
36753 if(this.indicator == 1){
36754 this.prevIndicator.hide();
36757 this.nextIndicator.show();
36759 if(this.indicator == this.files.length){
36760 this.nextIndicator.hide();
36763 this.thumbEl.scrollTo('top');
36765 this.fireEvent('update', this);
36768 onClick : function(e)
36770 e.preventDefault();
36772 this.fireEvent('click', this);
36777 e.preventDefault();
36779 this.indicator = Math.max(1, this.indicator - 1);
36786 e.preventDefault();
36788 this.indicator = Math.min(this.files.length, this.indicator + 1);
36802 * @class Roo.bootstrap.RadioSet
36803 * @extends Roo.bootstrap.Input
36804 * Bootstrap RadioSet class
36805 * @cfg {String} indicatorpos (left|right) default left
36806 * @cfg {Boolean} inline (true|false) inline the element (default true)
36807 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36809 * Create a new RadioSet
36810 * @param {Object} config The config object
36813 Roo.bootstrap.RadioSet = function(config){
36815 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36819 Roo.bootstrap.RadioSet.register(this);
36824 * Fires when the element is checked or unchecked.
36825 * @param {Roo.bootstrap.RadioSet} this This radio
36826 * @param {Roo.bootstrap.Radio} item The checked item
36831 * Fires when the element is click.
36832 * @param {Roo.bootstrap.RadioSet} this This radio set
36833 * @param {Roo.bootstrap.Radio} item The checked item
36834 * @param {Roo.EventObject} e The event object
36841 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36849 indicatorpos : 'left',
36851 getAutoCreate : function()
36855 cls : 'roo-radio-set-label',
36859 html : this.fieldLabel
36863 if (Roo.bootstrap.version == 3) {
36866 if(this.indicatorpos == 'left'){
36869 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36870 tooltip : 'This field is required'
36875 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36876 tooltip : 'This field is required'
36882 cls : 'roo-radio-set-items'
36885 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36887 if (align === 'left' && this.fieldLabel.length) {
36890 cls : "roo-radio-set-right",
36896 if(this.labelWidth > 12){
36897 label.style = "width: " + this.labelWidth + 'px';
36900 if(this.labelWidth < 13 && this.labelmd == 0){
36901 this.labelmd = this.labelWidth;
36904 if(this.labellg > 0){
36905 label.cls += ' col-lg-' + this.labellg;
36906 items.cls += ' col-lg-' + (12 - this.labellg);
36909 if(this.labelmd > 0){
36910 label.cls += ' col-md-' + this.labelmd;
36911 items.cls += ' col-md-' + (12 - this.labelmd);
36914 if(this.labelsm > 0){
36915 label.cls += ' col-sm-' + this.labelsm;
36916 items.cls += ' col-sm-' + (12 - this.labelsm);
36919 if(this.labelxs > 0){
36920 label.cls += ' col-xs-' + this.labelxs;
36921 items.cls += ' col-xs-' + (12 - this.labelxs);
36927 cls : 'roo-radio-set',
36931 cls : 'roo-radio-set-input',
36934 value : this.value ? this.value : ''
36941 if(this.weight.length){
36942 cfg.cls += ' roo-radio-' + this.weight;
36946 cfg.cls += ' roo-radio-set-inline';
36950 ['xs','sm','md','lg'].map(function(size){
36951 if (settings[size]) {
36952 cfg.cls += ' col-' + size + '-' + settings[size];
36960 initEvents : function()
36962 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36963 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36965 if(!this.fieldLabel.length){
36966 this.labelEl.hide();
36969 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36970 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36972 this.indicator = this.indicatorEl();
36974 if(this.indicator){
36975 this.indicator.addClass('invisible');
36978 this.originalValue = this.getValue();
36982 inputEl: function ()
36984 return this.el.select('.roo-radio-set-input', true).first();
36987 getChildContainer : function()
36989 return this.itemsEl;
36992 register : function(item)
36994 this.radioes.push(item);
36998 validate : function()
37000 if(this.getVisibilityEl().hasClass('hidden')){
37006 Roo.each(this.radioes, function(i){
37015 if(this.allowBlank) {
37019 if(this.disabled || valid){
37024 this.markInvalid();
37029 markValid : function()
37031 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37032 this.indicatorEl().removeClass('visible');
37033 this.indicatorEl().addClass('invisible');
37037 if (Roo.bootstrap.version == 3) {
37038 this.el.removeClass([this.invalidClass, this.validClass]);
37039 this.el.addClass(this.validClass);
37041 this.el.removeClass(['is-invalid','is-valid']);
37042 this.el.addClass(['is-valid']);
37044 this.fireEvent('valid', this);
37047 markInvalid : function(msg)
37049 if(this.allowBlank || this.disabled){
37053 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37054 this.indicatorEl().removeClass('invisible');
37055 this.indicatorEl().addClass('visible');
37057 if (Roo.bootstrap.version == 3) {
37058 this.el.removeClass([this.invalidClass, this.validClass]);
37059 this.el.addClass(this.invalidClass);
37061 this.el.removeClass(['is-invalid','is-valid']);
37062 this.el.addClass(['is-invalid']);
37065 this.fireEvent('invalid', this, msg);
37069 setValue : function(v, suppressEvent)
37071 if(this.value === v){
37078 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37081 Roo.each(this.radioes, function(i){
37083 i.el.removeClass('checked');
37086 Roo.each(this.radioes, function(i){
37088 if(i.value === v || i.value.toString() === v.toString()){
37090 i.el.addClass('checked');
37092 if(suppressEvent !== true){
37093 this.fireEvent('check', this, i);
37104 clearInvalid : function(){
37106 if(!this.el || this.preventMark){
37110 this.el.removeClass([this.invalidClass]);
37112 this.fireEvent('valid', this);
37117 Roo.apply(Roo.bootstrap.RadioSet, {
37121 register : function(set)
37123 this.groups[set.name] = set;
37126 get: function(name)
37128 if (typeof(this.groups[name]) == 'undefined') {
37132 return this.groups[name] ;
37138 * Ext JS Library 1.1.1
37139 * Copyright(c) 2006-2007, Ext JS, LLC.
37141 * Originally Released Under LGPL - original licence link has changed is not relivant.
37144 * <script type="text/javascript">
37149 * @class Roo.bootstrap.SplitBar
37150 * @extends Roo.util.Observable
37151 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37155 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37156 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37157 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37158 split.minSize = 100;
37159 split.maxSize = 600;
37160 split.animate = true;
37161 split.on('moved', splitterMoved);
37164 * Create a new SplitBar
37165 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37166 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37167 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37168 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37169 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37170 position of the SplitBar).
37172 Roo.bootstrap.SplitBar = function(cfg){
37177 // dragElement : elm
37178 // resizingElement: el,
37180 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37181 // placement : Roo.bootstrap.SplitBar.LEFT ,
37182 // existingProxy ???
37185 this.el = Roo.get(cfg.dragElement, true);
37186 this.el.dom.unselectable = "on";
37188 this.resizingEl = Roo.get(cfg.resizingElement, true);
37192 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37193 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37196 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37199 * The minimum size of the resizing element. (Defaults to 0)
37205 * The maximum size of the resizing element. (Defaults to 2000)
37208 this.maxSize = 2000;
37211 * Whether to animate the transition to the new size
37214 this.animate = false;
37217 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37220 this.useShim = false;
37225 if(!cfg.existingProxy){
37227 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37229 this.proxy = Roo.get(cfg.existingProxy).dom;
37232 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37235 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37238 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37241 this.dragSpecs = {};
37244 * @private The adapter to use to positon and resize elements
37246 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37247 this.adapter.init(this);
37249 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37251 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37252 this.el.addClass("roo-splitbar-h");
37255 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37256 this.el.addClass("roo-splitbar-v");
37262 * Fires when the splitter is moved (alias for {@link #event-moved})
37263 * @param {Roo.bootstrap.SplitBar} this
37264 * @param {Number} newSize the new width or height
37269 * Fires when the splitter is moved
37270 * @param {Roo.bootstrap.SplitBar} this
37271 * @param {Number} newSize the new width or height
37275 * @event beforeresize
37276 * Fires before the splitter is dragged
37277 * @param {Roo.bootstrap.SplitBar} this
37279 "beforeresize" : true,
37281 "beforeapply" : true
37284 Roo.util.Observable.call(this);
37287 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37288 onStartProxyDrag : function(x, y){
37289 this.fireEvent("beforeresize", this);
37291 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37293 o.enableDisplayMode("block");
37294 // all splitbars share the same overlay
37295 Roo.bootstrap.SplitBar.prototype.overlay = o;
37297 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37298 this.overlay.show();
37299 Roo.get(this.proxy).setDisplayed("block");
37300 var size = this.adapter.getElementSize(this);
37301 this.activeMinSize = this.getMinimumSize();;
37302 this.activeMaxSize = this.getMaximumSize();;
37303 var c1 = size - this.activeMinSize;
37304 var c2 = Math.max(this.activeMaxSize - size, 0);
37305 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37306 this.dd.resetConstraints();
37307 this.dd.setXConstraint(
37308 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37309 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37311 this.dd.setYConstraint(0, 0);
37313 this.dd.resetConstraints();
37314 this.dd.setXConstraint(0, 0);
37315 this.dd.setYConstraint(
37316 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37317 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37320 this.dragSpecs.startSize = size;
37321 this.dragSpecs.startPoint = [x, y];
37322 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37326 * @private Called after the drag operation by the DDProxy
37328 onEndProxyDrag : function(e){
37329 Roo.get(this.proxy).setDisplayed(false);
37330 var endPoint = Roo.lib.Event.getXY(e);
37332 this.overlay.hide();
37335 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37336 newSize = this.dragSpecs.startSize +
37337 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37338 endPoint[0] - this.dragSpecs.startPoint[0] :
37339 this.dragSpecs.startPoint[0] - endPoint[0]
37342 newSize = this.dragSpecs.startSize +
37343 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37344 endPoint[1] - this.dragSpecs.startPoint[1] :
37345 this.dragSpecs.startPoint[1] - endPoint[1]
37348 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37349 if(newSize != this.dragSpecs.startSize){
37350 if(this.fireEvent('beforeapply', this, newSize) !== false){
37351 this.adapter.setElementSize(this, newSize);
37352 this.fireEvent("moved", this, newSize);
37353 this.fireEvent("resize", this, newSize);
37359 * Get the adapter this SplitBar uses
37360 * @return The adapter object
37362 getAdapter : function(){
37363 return this.adapter;
37367 * Set the adapter this SplitBar uses
37368 * @param {Object} adapter A SplitBar adapter object
37370 setAdapter : function(adapter){
37371 this.adapter = adapter;
37372 this.adapter.init(this);
37376 * Gets the minimum size for the resizing element
37377 * @return {Number} The minimum size
37379 getMinimumSize : function(){
37380 return this.minSize;
37384 * Sets the minimum size for the resizing element
37385 * @param {Number} minSize The minimum size
37387 setMinimumSize : function(minSize){
37388 this.minSize = minSize;
37392 * Gets the maximum size for the resizing element
37393 * @return {Number} The maximum size
37395 getMaximumSize : function(){
37396 return this.maxSize;
37400 * Sets the maximum size for the resizing element
37401 * @param {Number} maxSize The maximum size
37403 setMaximumSize : function(maxSize){
37404 this.maxSize = maxSize;
37408 * Sets the initialize size for the resizing element
37409 * @param {Number} size The initial size
37411 setCurrentSize : function(size){
37412 var oldAnimate = this.animate;
37413 this.animate = false;
37414 this.adapter.setElementSize(this, size);
37415 this.animate = oldAnimate;
37419 * Destroy this splitbar.
37420 * @param {Boolean} removeEl True to remove the element
37422 destroy : function(removeEl){
37424 this.shim.remove();
37427 this.proxy.parentNode.removeChild(this.proxy);
37435 * @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.
37437 Roo.bootstrap.SplitBar.createProxy = function(dir){
37438 var proxy = new Roo.Element(document.createElement("div"));
37439 proxy.unselectable();
37440 var cls = 'roo-splitbar-proxy';
37441 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37442 document.body.appendChild(proxy.dom);
37447 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37448 * Default Adapter. It assumes the splitter and resizing element are not positioned
37449 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37451 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37454 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37455 // do nothing for now
37456 init : function(s){
37460 * Called before drag operations to get the current size of the resizing element.
37461 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37463 getElementSize : function(s){
37464 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37465 return s.resizingEl.getWidth();
37467 return s.resizingEl.getHeight();
37472 * Called after drag operations to set the size of the resizing element.
37473 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37474 * @param {Number} newSize The new size to set
37475 * @param {Function} onComplete A function to be invoked when resizing is complete
37477 setElementSize : function(s, newSize, onComplete){
37478 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37480 s.resizingEl.setWidth(newSize);
37482 onComplete(s, newSize);
37485 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37490 s.resizingEl.setHeight(newSize);
37492 onComplete(s, newSize);
37495 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37502 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37503 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37504 * Adapter that moves the splitter element to align with the resized sizing element.
37505 * Used with an absolute positioned SplitBar.
37506 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37507 * document.body, make sure you assign an id to the body element.
37509 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37510 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37511 this.container = Roo.get(container);
37514 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37515 init : function(s){
37516 this.basic.init(s);
37519 getElementSize : function(s){
37520 return this.basic.getElementSize(s);
37523 setElementSize : function(s, newSize, onComplete){
37524 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37527 moveSplitter : function(s){
37528 var yes = Roo.bootstrap.SplitBar;
37529 switch(s.placement){
37531 s.el.setX(s.resizingEl.getRight());
37534 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37537 s.el.setY(s.resizingEl.getBottom());
37540 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37547 * Orientation constant - Create a vertical SplitBar
37551 Roo.bootstrap.SplitBar.VERTICAL = 1;
37554 * Orientation constant - Create a horizontal SplitBar
37558 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37561 * Placement constant - The resizing element is to the left of the splitter element
37565 Roo.bootstrap.SplitBar.LEFT = 1;
37568 * Placement constant - The resizing element is to the right of the splitter element
37572 Roo.bootstrap.SplitBar.RIGHT = 2;
37575 * Placement constant - The resizing element is positioned above the splitter element
37579 Roo.bootstrap.SplitBar.TOP = 3;
37582 * Placement constant - The resizing element is positioned under splitter element
37586 Roo.bootstrap.SplitBar.BOTTOM = 4;
37587 Roo.namespace("Roo.bootstrap.layout");/*
37589 * Ext JS Library 1.1.1
37590 * Copyright(c) 2006-2007, Ext JS, LLC.
37592 * Originally Released Under LGPL - original licence link has changed is not relivant.
37595 * <script type="text/javascript">
37599 * @class Roo.bootstrap.layout.Manager
37600 * @extends Roo.bootstrap.Component
37601 * Base class for layout managers.
37603 Roo.bootstrap.layout.Manager = function(config)
37605 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37611 /** false to disable window resize monitoring @type Boolean */
37612 this.monitorWindowResize = true;
37617 * Fires when a layout is performed.
37618 * @param {Roo.LayoutManager} this
37622 * @event regionresized
37623 * Fires when the user resizes a region.
37624 * @param {Roo.LayoutRegion} region The resized region
37625 * @param {Number} newSize The new size (width for east/west, height for north/south)
37627 "regionresized" : true,
37629 * @event regioncollapsed
37630 * Fires when a region is collapsed.
37631 * @param {Roo.LayoutRegion} region The collapsed region
37633 "regioncollapsed" : true,
37635 * @event regionexpanded
37636 * Fires when a region is expanded.
37637 * @param {Roo.LayoutRegion} region The expanded region
37639 "regionexpanded" : true
37641 this.updating = false;
37644 this.el = Roo.get(config.el);
37650 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37655 monitorWindowResize : true,
37661 onRender : function(ct, position)
37664 this.el = Roo.get(ct);
37667 //this.fireEvent('render',this);
37671 initEvents: function()
37675 // ie scrollbar fix
37676 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37677 document.body.scroll = "no";
37678 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37679 this.el.position('relative');
37681 this.id = this.el.id;
37682 this.el.addClass("roo-layout-container");
37683 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37684 if(this.el.dom != document.body ) {
37685 this.el.on('resize', this.layout,this);
37686 this.el.on('show', this.layout,this);
37692 * Returns true if this layout is currently being updated
37693 * @return {Boolean}
37695 isUpdating : function(){
37696 return this.updating;
37700 * Suspend the LayoutManager from doing auto-layouts while
37701 * making multiple add or remove calls
37703 beginUpdate : function(){
37704 this.updating = true;
37708 * Restore auto-layouts and optionally disable the manager from performing a layout
37709 * @param {Boolean} noLayout true to disable a layout update
37711 endUpdate : function(noLayout){
37712 this.updating = false;
37718 layout: function(){
37722 onRegionResized : function(region, newSize){
37723 this.fireEvent("regionresized", region, newSize);
37727 onRegionCollapsed : function(region){
37728 this.fireEvent("regioncollapsed", region);
37731 onRegionExpanded : function(region){
37732 this.fireEvent("regionexpanded", region);
37736 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37737 * performs box-model adjustments.
37738 * @return {Object} The size as an object {width: (the width), height: (the height)}
37740 getViewSize : function()
37743 if(this.el.dom != document.body){
37744 size = this.el.getSize();
37746 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37748 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37749 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37754 * Returns the Element this layout is bound to.
37755 * @return {Roo.Element}
37757 getEl : function(){
37762 * Returns the specified region.
37763 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37764 * @return {Roo.LayoutRegion}
37766 getRegion : function(target){
37767 return this.regions[target.toLowerCase()];
37770 onWindowResize : function(){
37771 if(this.monitorWindowResize){
37778 * Ext JS Library 1.1.1
37779 * Copyright(c) 2006-2007, Ext JS, LLC.
37781 * Originally Released Under LGPL - original licence link has changed is not relivant.
37784 * <script type="text/javascript">
37787 * @class Roo.bootstrap.layout.Border
37788 * @extends Roo.bootstrap.layout.Manager
37789 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37790 * please see: examples/bootstrap/nested.html<br><br>
37792 <b>The container the layout is rendered into can be either the body element or any other element.
37793 If it is not the body element, the container needs to either be an absolute positioned element,
37794 or you will need to add "position:relative" to the css of the container. You will also need to specify
37795 the container size if it is not the body element.</b>
37798 * Create a new Border
37799 * @param {Object} config Configuration options
37801 Roo.bootstrap.layout.Border = function(config){
37802 config = config || {};
37803 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37807 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37808 if(config[region]){
37809 config[region].region = region;
37810 this.addRegion(config[region]);
37816 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37818 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37820 parent : false, // this might point to a 'nest' or a ???
37823 * Creates and adds a new region if it doesn't already exist.
37824 * @param {String} target The target region key (north, south, east, west or center).
37825 * @param {Object} config The regions config object
37826 * @return {BorderLayoutRegion} The new region
37828 addRegion : function(config)
37830 if(!this.regions[config.region]){
37831 var r = this.factory(config);
37832 this.bindRegion(r);
37834 return this.regions[config.region];
37838 bindRegion : function(r){
37839 this.regions[r.config.region] = r;
37841 r.on("visibilitychange", this.layout, this);
37842 r.on("paneladded", this.layout, this);
37843 r.on("panelremoved", this.layout, this);
37844 r.on("invalidated", this.layout, this);
37845 r.on("resized", this.onRegionResized, this);
37846 r.on("collapsed", this.onRegionCollapsed, this);
37847 r.on("expanded", this.onRegionExpanded, this);
37851 * Performs a layout update.
37853 layout : function()
37855 if(this.updating) {
37859 // render all the rebions if they have not been done alreayd?
37860 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37861 if(this.regions[region] && !this.regions[region].bodyEl){
37862 this.regions[region].onRender(this.el)
37866 var size = this.getViewSize();
37867 var w = size.width;
37868 var h = size.height;
37873 //var x = 0, y = 0;
37875 var rs = this.regions;
37876 var north = rs["north"];
37877 var south = rs["south"];
37878 var west = rs["west"];
37879 var east = rs["east"];
37880 var center = rs["center"];
37881 //if(this.hideOnLayout){ // not supported anymore
37882 //c.el.setStyle("display", "none");
37884 if(north && north.isVisible()){
37885 var b = north.getBox();
37886 var m = north.getMargins();
37887 b.width = w - (m.left+m.right);
37890 centerY = b.height + b.y + m.bottom;
37891 centerH -= centerY;
37892 north.updateBox(this.safeBox(b));
37894 if(south && south.isVisible()){
37895 var b = south.getBox();
37896 var m = south.getMargins();
37897 b.width = w - (m.left+m.right);
37899 var totalHeight = (b.height + m.top + m.bottom);
37900 b.y = h - totalHeight + m.top;
37901 centerH -= totalHeight;
37902 south.updateBox(this.safeBox(b));
37904 if(west && west.isVisible()){
37905 var b = west.getBox();
37906 var m = west.getMargins();
37907 b.height = centerH - (m.top+m.bottom);
37909 b.y = centerY + m.top;
37910 var totalWidth = (b.width + m.left + m.right);
37911 centerX += totalWidth;
37912 centerW -= totalWidth;
37913 west.updateBox(this.safeBox(b));
37915 if(east && east.isVisible()){
37916 var b = east.getBox();
37917 var m = east.getMargins();
37918 b.height = centerH - (m.top+m.bottom);
37919 var totalWidth = (b.width + m.left + m.right);
37920 b.x = w - totalWidth + m.left;
37921 b.y = centerY + m.top;
37922 centerW -= totalWidth;
37923 east.updateBox(this.safeBox(b));
37926 var m = center.getMargins();
37928 x: centerX + m.left,
37929 y: centerY + m.top,
37930 width: centerW - (m.left+m.right),
37931 height: centerH - (m.top+m.bottom)
37933 //if(this.hideOnLayout){
37934 //center.el.setStyle("display", "block");
37936 center.updateBox(this.safeBox(centerBox));
37939 this.fireEvent("layout", this);
37943 safeBox : function(box){
37944 box.width = Math.max(0, box.width);
37945 box.height = Math.max(0, box.height);
37950 * Adds a ContentPanel (or subclass) to this layout.
37951 * @param {String} target The target region key (north, south, east, west or center).
37952 * @param {Roo.ContentPanel} panel The panel to add
37953 * @return {Roo.ContentPanel} The added panel
37955 add : function(target, panel){
37957 target = target.toLowerCase();
37958 return this.regions[target].add(panel);
37962 * Remove a ContentPanel (or subclass) to this layout.
37963 * @param {String} target The target region key (north, south, east, west or center).
37964 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37965 * @return {Roo.ContentPanel} The removed panel
37967 remove : function(target, panel){
37968 target = target.toLowerCase();
37969 return this.regions[target].remove(panel);
37973 * Searches all regions for a panel with the specified id
37974 * @param {String} panelId
37975 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37977 findPanel : function(panelId){
37978 var rs = this.regions;
37979 for(var target in rs){
37980 if(typeof rs[target] != "function"){
37981 var p = rs[target].getPanel(panelId);
37991 * Searches all regions for a panel with the specified id and activates (shows) it.
37992 * @param {String/ContentPanel} panelId The panels id or the panel itself
37993 * @return {Roo.ContentPanel} The shown panel or null
37995 showPanel : function(panelId) {
37996 var rs = this.regions;
37997 for(var target in rs){
37998 var r = rs[target];
37999 if(typeof r != "function"){
38000 if(r.hasPanel(panelId)){
38001 return r.showPanel(panelId);
38009 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38010 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38013 restoreState : function(provider){
38015 provider = Roo.state.Manager;
38017 var sm = new Roo.LayoutStateManager();
38018 sm.init(this, provider);
38024 * Adds a xtype elements to the layout.
38028 xtype : 'ContentPanel',
38035 xtype : 'NestedLayoutPanel',
38041 items : [ ... list of content panels or nested layout panels.. ]
38045 * @param {Object} cfg Xtype definition of item to add.
38047 addxtype : function(cfg)
38049 // basically accepts a pannel...
38050 // can accept a layout region..!?!?
38051 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38054 // theory? children can only be panels??
38056 //if (!cfg.xtype.match(/Panel$/)) {
38061 if (typeof(cfg.region) == 'undefined') {
38062 Roo.log("Failed to add Panel, region was not set");
38066 var region = cfg.region;
38072 xitems = cfg.items;
38077 if ( region == 'center') {
38078 Roo.log("Center: " + cfg.title);
38084 case 'Content': // ContentPanel (el, cfg)
38085 case 'Scroll': // ContentPanel (el, cfg)
38087 cfg.autoCreate = cfg.autoCreate || true;
38088 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38090 // var el = this.el.createChild();
38091 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38094 this.add(region, ret);
38098 case 'TreePanel': // our new panel!
38099 cfg.el = this.el.createChild();
38100 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38101 this.add(region, ret);
38106 // create a new Layout (which is a Border Layout...
38108 var clayout = cfg.layout;
38109 clayout.el = this.el.createChild();
38110 clayout.items = clayout.items || [];
38114 // replace this exitems with the clayout ones..
38115 xitems = clayout.items;
38117 // force background off if it's in center...
38118 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38119 cfg.background = false;
38121 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38124 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38125 //console.log('adding nested layout panel ' + cfg.toSource());
38126 this.add(region, ret);
38127 nb = {}; /// find first...
38132 // needs grid and region
38134 //var el = this.getRegion(region).el.createChild();
38136 *var el = this.el.createChild();
38137 // create the grid first...
38138 cfg.grid.container = el;
38139 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38142 if (region == 'center' && this.active ) {
38143 cfg.background = false;
38146 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38148 this.add(region, ret);
38150 if (cfg.background) {
38151 // render grid on panel activation (if panel background)
38152 ret.on('activate', function(gp) {
38153 if (!gp.grid.rendered) {
38154 // gp.grid.render(el);
38158 // cfg.grid.render(el);
38164 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38165 // it was the old xcomponent building that caused this before.
38166 // espeically if border is the top element in the tree.
38176 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38178 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38179 this.add(region, ret);
38183 throw "Can not add '" + cfg.xtype + "' to Border";
38189 this.beginUpdate();
38193 Roo.each(xitems, function(i) {
38194 region = nb && i.region ? i.region : false;
38196 var add = ret.addxtype(i);
38199 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38200 if (!i.background) {
38201 abn[region] = nb[region] ;
38208 // make the last non-background panel active..
38209 //if (nb) { Roo.log(abn); }
38212 for(var r in abn) {
38213 region = this.getRegion(r);
38215 // tried using nb[r], but it does not work..
38217 region.showPanel(abn[r]);
38228 factory : function(cfg)
38231 var validRegions = Roo.bootstrap.layout.Border.regions;
38233 var target = cfg.region;
38236 var r = Roo.bootstrap.layout;
38240 return new r.North(cfg);
38242 return new r.South(cfg);
38244 return new r.East(cfg);
38246 return new r.West(cfg);
38248 return new r.Center(cfg);
38250 throw 'Layout region "'+target+'" not supported.';
38257 * Ext JS Library 1.1.1
38258 * Copyright(c) 2006-2007, Ext JS, LLC.
38260 * Originally Released Under LGPL - original licence link has changed is not relivant.
38263 * <script type="text/javascript">
38267 * @class Roo.bootstrap.layout.Basic
38268 * @extends Roo.util.Observable
38269 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38270 * and does not have a titlebar, tabs or any other features. All it does is size and position
38271 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38272 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38273 * @cfg {string} region the region that it inhabits..
38274 * @cfg {bool} skipConfig skip config?
38278 Roo.bootstrap.layout.Basic = function(config){
38280 this.mgr = config.mgr;
38282 this.position = config.region;
38284 var skipConfig = config.skipConfig;
38288 * @scope Roo.BasicLayoutRegion
38292 * @event beforeremove
38293 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38294 * @param {Roo.LayoutRegion} this
38295 * @param {Roo.ContentPanel} panel The panel
38296 * @param {Object} e The cancel event object
38298 "beforeremove" : true,
38300 * @event invalidated
38301 * Fires when the layout for this region is changed.
38302 * @param {Roo.LayoutRegion} this
38304 "invalidated" : true,
38306 * @event visibilitychange
38307 * Fires when this region is shown or hidden
38308 * @param {Roo.LayoutRegion} this
38309 * @param {Boolean} visibility true or false
38311 "visibilitychange" : true,
38313 * @event paneladded
38314 * Fires when a panel is added.
38315 * @param {Roo.LayoutRegion} this
38316 * @param {Roo.ContentPanel} panel The panel
38318 "paneladded" : true,
38320 * @event panelremoved
38321 * Fires when a panel is removed.
38322 * @param {Roo.LayoutRegion} this
38323 * @param {Roo.ContentPanel} panel The panel
38325 "panelremoved" : true,
38327 * @event beforecollapse
38328 * Fires when this region before collapse.
38329 * @param {Roo.LayoutRegion} this
38331 "beforecollapse" : true,
38334 * Fires when this region is collapsed.
38335 * @param {Roo.LayoutRegion} this
38337 "collapsed" : true,
38340 * Fires when this region is expanded.
38341 * @param {Roo.LayoutRegion} this
38346 * Fires when this region is slid into view.
38347 * @param {Roo.LayoutRegion} this
38349 "slideshow" : true,
38352 * Fires when this region slides out of view.
38353 * @param {Roo.LayoutRegion} this
38355 "slidehide" : true,
38357 * @event panelactivated
38358 * Fires when a panel is activated.
38359 * @param {Roo.LayoutRegion} this
38360 * @param {Roo.ContentPanel} panel The activated panel
38362 "panelactivated" : true,
38365 * Fires when the user resizes this region.
38366 * @param {Roo.LayoutRegion} this
38367 * @param {Number} newSize The new size (width for east/west, height for north/south)
38371 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38372 this.panels = new Roo.util.MixedCollection();
38373 this.panels.getKey = this.getPanelId.createDelegate(this);
38375 this.activePanel = null;
38376 // ensure listeners are added...
38378 if (config.listeners || config.events) {
38379 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38380 listeners : config.listeners || {},
38381 events : config.events || {}
38385 if(skipConfig !== true){
38386 this.applyConfig(config);
38390 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38392 getPanelId : function(p){
38396 applyConfig : function(config){
38397 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38398 this.config = config;
38403 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38404 * the width, for horizontal (north, south) the height.
38405 * @param {Number} newSize The new width or height
38407 resizeTo : function(newSize){
38408 var el = this.el ? this.el :
38409 (this.activePanel ? this.activePanel.getEl() : null);
38411 switch(this.position){
38414 el.setWidth(newSize);
38415 this.fireEvent("resized", this, newSize);
38419 el.setHeight(newSize);
38420 this.fireEvent("resized", this, newSize);
38426 getBox : function(){
38427 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38430 getMargins : function(){
38431 return this.margins;
38434 updateBox : function(box){
38436 var el = this.activePanel.getEl();
38437 el.dom.style.left = box.x + "px";
38438 el.dom.style.top = box.y + "px";
38439 this.activePanel.setSize(box.width, box.height);
38443 * Returns the container element for this region.
38444 * @return {Roo.Element}
38446 getEl : function(){
38447 return this.activePanel;
38451 * Returns true if this region is currently visible.
38452 * @return {Boolean}
38454 isVisible : function(){
38455 return this.activePanel ? true : false;
38458 setActivePanel : function(panel){
38459 panel = this.getPanel(panel);
38460 if(this.activePanel && this.activePanel != panel){
38461 this.activePanel.setActiveState(false);
38462 this.activePanel.getEl().setLeftTop(-10000,-10000);
38464 this.activePanel = panel;
38465 panel.setActiveState(true);
38467 panel.setSize(this.box.width, this.box.height);
38469 this.fireEvent("panelactivated", this, panel);
38470 this.fireEvent("invalidated");
38474 * Show the specified panel.
38475 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38476 * @return {Roo.ContentPanel} The shown panel or null
38478 showPanel : function(panel){
38479 panel = this.getPanel(panel);
38481 this.setActivePanel(panel);
38487 * Get the active panel for this region.
38488 * @return {Roo.ContentPanel} The active panel or null
38490 getActivePanel : function(){
38491 return this.activePanel;
38495 * Add the passed ContentPanel(s)
38496 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38497 * @return {Roo.ContentPanel} The panel added (if only one was added)
38499 add : function(panel){
38500 if(arguments.length > 1){
38501 for(var i = 0, len = arguments.length; i < len; i++) {
38502 this.add(arguments[i]);
38506 if(this.hasPanel(panel)){
38507 this.showPanel(panel);
38510 var el = panel.getEl();
38511 if(el.dom.parentNode != this.mgr.el.dom){
38512 this.mgr.el.dom.appendChild(el.dom);
38514 if(panel.setRegion){
38515 panel.setRegion(this);
38517 this.panels.add(panel);
38518 el.setStyle("position", "absolute");
38519 if(!panel.background){
38520 this.setActivePanel(panel);
38521 if(this.config.initialSize && this.panels.getCount()==1){
38522 this.resizeTo(this.config.initialSize);
38525 this.fireEvent("paneladded", this, panel);
38530 * Returns true if the panel is in this region.
38531 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38532 * @return {Boolean}
38534 hasPanel : function(panel){
38535 if(typeof panel == "object"){ // must be panel obj
38536 panel = panel.getId();
38538 return this.getPanel(panel) ? true : false;
38542 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38543 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38544 * @param {Boolean} preservePanel Overrides the config preservePanel option
38545 * @return {Roo.ContentPanel} The panel that was removed
38547 remove : function(panel, preservePanel){
38548 panel = this.getPanel(panel);
38553 this.fireEvent("beforeremove", this, panel, e);
38554 if(e.cancel === true){
38557 var panelId = panel.getId();
38558 this.panels.removeKey(panelId);
38563 * Returns the panel specified or null if it's not in this region.
38564 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38565 * @return {Roo.ContentPanel}
38567 getPanel : function(id){
38568 if(typeof id == "object"){ // must be panel obj
38571 return this.panels.get(id);
38575 * Returns this regions position (north/south/east/west/center).
38578 getPosition: function(){
38579 return this.position;
38583 * Ext JS Library 1.1.1
38584 * Copyright(c) 2006-2007, Ext JS, LLC.
38586 * Originally Released Under LGPL - original licence link has changed is not relivant.
38589 * <script type="text/javascript">
38593 * @class Roo.bootstrap.layout.Region
38594 * @extends Roo.bootstrap.layout.Basic
38595 * This class represents a region in a layout manager.
38597 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38598 * @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})
38599 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38600 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38601 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38602 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38603 * @cfg {String} title The title for the region (overrides panel titles)
38604 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38605 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38606 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38607 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38608 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38609 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38610 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38611 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38612 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38613 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38615 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38616 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38617 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38618 * @cfg {Number} width For East/West panels
38619 * @cfg {Number} height For North/South panels
38620 * @cfg {Boolean} split To show the splitter
38621 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38623 * @cfg {string} cls Extra CSS classes to add to region
38625 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38626 * @cfg {string} region the region that it inhabits..
38629 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38630 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38632 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38633 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38634 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38636 Roo.bootstrap.layout.Region = function(config)
38638 this.applyConfig(config);
38640 var mgr = config.mgr;
38641 var pos = config.region;
38642 config.skipConfig = true;
38643 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38646 this.onRender(mgr.el);
38649 this.visible = true;
38650 this.collapsed = false;
38651 this.unrendered_panels = [];
38654 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38656 position: '', // set by wrapper (eg. north/south etc..)
38657 unrendered_panels : null, // unrendered panels.
38659 tabPosition : false,
38661 mgr: false, // points to 'Border'
38664 createBody : function(){
38665 /** This region's body element
38666 * @type Roo.Element */
38667 this.bodyEl = this.el.createChild({
38669 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38673 onRender: function(ctr, pos)
38675 var dh = Roo.DomHelper;
38676 /** This region's container element
38677 * @type Roo.Element */
38678 this.el = dh.append(ctr.dom, {
38680 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38682 /** This region's title element
38683 * @type Roo.Element */
38685 this.titleEl = dh.append(this.el.dom, {
38687 unselectable: "on",
38688 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38690 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38691 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38695 this.titleEl.enableDisplayMode();
38696 /** This region's title text element
38697 * @type HTMLElement */
38698 this.titleTextEl = this.titleEl.dom.firstChild;
38699 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38701 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38702 this.closeBtn.enableDisplayMode();
38703 this.closeBtn.on("click", this.closeClicked, this);
38704 this.closeBtn.hide();
38706 this.createBody(this.config);
38707 if(this.config.hideWhenEmpty){
38709 this.on("paneladded", this.validateVisibility, this);
38710 this.on("panelremoved", this.validateVisibility, this);
38712 if(this.autoScroll){
38713 this.bodyEl.setStyle("overflow", "auto");
38715 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38717 //if(c.titlebar !== false){
38718 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38719 this.titleEl.hide();
38721 this.titleEl.show();
38722 if(this.config.title){
38723 this.titleTextEl.innerHTML = this.config.title;
38727 if(this.config.collapsed){
38728 this.collapse(true);
38730 if(this.config.hidden){
38734 if (this.unrendered_panels && this.unrendered_panels.length) {
38735 for (var i =0;i< this.unrendered_panels.length; i++) {
38736 this.add(this.unrendered_panels[i]);
38738 this.unrendered_panels = null;
38744 applyConfig : function(c)
38747 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38748 var dh = Roo.DomHelper;
38749 if(c.titlebar !== false){
38750 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38751 this.collapseBtn.on("click", this.collapse, this);
38752 this.collapseBtn.enableDisplayMode();
38754 if(c.showPin === true || this.showPin){
38755 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38756 this.stickBtn.enableDisplayMode();
38757 this.stickBtn.on("click", this.expand, this);
38758 this.stickBtn.hide();
38763 /** This region's collapsed element
38764 * @type Roo.Element */
38767 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38768 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38771 if(c.floatable !== false){
38772 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38773 this.collapsedEl.on("click", this.collapseClick, this);
38776 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38777 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38778 id: "message", unselectable: "on", style:{"float":"left"}});
38779 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38781 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38782 this.expandBtn.on("click", this.expand, this);
38786 if(this.collapseBtn){
38787 this.collapseBtn.setVisible(c.collapsible == true);
38790 this.cmargins = c.cmargins || this.cmargins ||
38791 (this.position == "west" || this.position == "east" ?
38792 {top: 0, left: 2, right:2, bottom: 0} :
38793 {top: 2, left: 0, right:0, bottom: 2});
38795 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38798 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38800 this.autoScroll = c.autoScroll || false;
38805 this.duration = c.duration || .30;
38806 this.slideDuration = c.slideDuration || .45;
38811 * Returns true if this region is currently visible.
38812 * @return {Boolean}
38814 isVisible : function(){
38815 return this.visible;
38819 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38820 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38822 //setCollapsedTitle : function(title){
38823 // title = title || " ";
38824 // if(this.collapsedTitleTextEl){
38825 // this.collapsedTitleTextEl.innerHTML = title;
38829 getBox : function(){
38831 // if(!this.collapsed){
38832 b = this.el.getBox(false, true);
38834 // b = this.collapsedEl.getBox(false, true);
38839 getMargins : function(){
38840 return this.margins;
38841 //return this.collapsed ? this.cmargins : this.margins;
38844 highlight : function(){
38845 this.el.addClass("x-layout-panel-dragover");
38848 unhighlight : function(){
38849 this.el.removeClass("x-layout-panel-dragover");
38852 updateBox : function(box)
38854 if (!this.bodyEl) {
38855 return; // not rendered yet..
38859 if(!this.collapsed){
38860 this.el.dom.style.left = box.x + "px";
38861 this.el.dom.style.top = box.y + "px";
38862 this.updateBody(box.width, box.height);
38864 this.collapsedEl.dom.style.left = box.x + "px";
38865 this.collapsedEl.dom.style.top = box.y + "px";
38866 this.collapsedEl.setSize(box.width, box.height);
38869 this.tabs.autoSizeTabs();
38873 updateBody : function(w, h)
38876 this.el.setWidth(w);
38877 w -= this.el.getBorderWidth("rl");
38878 if(this.config.adjustments){
38879 w += this.config.adjustments[0];
38882 if(h !== null && h > 0){
38883 this.el.setHeight(h);
38884 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38885 h -= this.el.getBorderWidth("tb");
38886 if(this.config.adjustments){
38887 h += this.config.adjustments[1];
38889 this.bodyEl.setHeight(h);
38891 h = this.tabs.syncHeight(h);
38894 if(this.panelSize){
38895 w = w !== null ? w : this.panelSize.width;
38896 h = h !== null ? h : this.panelSize.height;
38898 if(this.activePanel){
38899 var el = this.activePanel.getEl();
38900 w = w !== null ? w : el.getWidth();
38901 h = h !== null ? h : el.getHeight();
38902 this.panelSize = {width: w, height: h};
38903 this.activePanel.setSize(w, h);
38905 if(Roo.isIE && this.tabs){
38906 this.tabs.el.repaint();
38911 * Returns the container element for this region.
38912 * @return {Roo.Element}
38914 getEl : function(){
38919 * Hides this region.
38922 //if(!this.collapsed){
38923 this.el.dom.style.left = "-2000px";
38926 // this.collapsedEl.dom.style.left = "-2000px";
38927 // this.collapsedEl.hide();
38929 this.visible = false;
38930 this.fireEvent("visibilitychange", this, false);
38934 * Shows this region if it was previously hidden.
38937 //if(!this.collapsed){
38940 // this.collapsedEl.show();
38942 this.visible = true;
38943 this.fireEvent("visibilitychange", this, true);
38946 closeClicked : function(){
38947 if(this.activePanel){
38948 this.remove(this.activePanel);
38952 collapseClick : function(e){
38954 e.stopPropagation();
38957 e.stopPropagation();
38963 * Collapses this region.
38964 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38967 collapse : function(skipAnim, skipCheck = false){
38968 if(this.collapsed) {
38972 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38974 this.collapsed = true;
38976 this.split.el.hide();
38978 if(this.config.animate && skipAnim !== true){
38979 this.fireEvent("invalidated", this);
38980 this.animateCollapse();
38982 this.el.setLocation(-20000,-20000);
38984 this.collapsedEl.show();
38985 this.fireEvent("collapsed", this);
38986 this.fireEvent("invalidated", this);
38992 animateCollapse : function(){
38997 * Expands this region if it was previously collapsed.
38998 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38999 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39002 expand : function(e, skipAnim){
39004 e.stopPropagation();
39006 if(!this.collapsed || this.el.hasActiveFx()) {
39010 this.afterSlideIn();
39013 this.collapsed = false;
39014 if(this.config.animate && skipAnim !== true){
39015 this.animateExpand();
39019 this.split.el.show();
39021 this.collapsedEl.setLocation(-2000,-2000);
39022 this.collapsedEl.hide();
39023 this.fireEvent("invalidated", this);
39024 this.fireEvent("expanded", this);
39028 animateExpand : function(){
39032 initTabs : function()
39034 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39036 var ts = new Roo.bootstrap.panel.Tabs({
39037 el: this.bodyEl.dom,
39039 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39040 disableTooltips: this.config.disableTabTips,
39041 toolbar : this.config.toolbar
39044 if(this.config.hideTabs){
39045 ts.stripWrap.setDisplayed(false);
39048 ts.resizeTabs = this.config.resizeTabs === true;
39049 ts.minTabWidth = this.config.minTabWidth || 40;
39050 ts.maxTabWidth = this.config.maxTabWidth || 250;
39051 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39052 ts.monitorResize = false;
39053 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39054 ts.bodyEl.addClass('roo-layout-tabs-body');
39055 this.panels.each(this.initPanelAsTab, this);
39058 initPanelAsTab : function(panel){
39059 var ti = this.tabs.addTab(
39063 this.config.closeOnTab && panel.isClosable(),
39066 if(panel.tabTip !== undefined){
39067 ti.setTooltip(panel.tabTip);
39069 ti.on("activate", function(){
39070 this.setActivePanel(panel);
39073 if(this.config.closeOnTab){
39074 ti.on("beforeclose", function(t, e){
39076 this.remove(panel);
39080 panel.tabItem = ti;
39085 updatePanelTitle : function(panel, title)
39087 if(this.activePanel == panel){
39088 this.updateTitle(title);
39091 var ti = this.tabs.getTab(panel.getEl().id);
39093 if(panel.tabTip !== undefined){
39094 ti.setTooltip(panel.tabTip);
39099 updateTitle : function(title){
39100 if(this.titleTextEl && !this.config.title){
39101 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39105 setActivePanel : function(panel)
39107 panel = this.getPanel(panel);
39108 if(this.activePanel && this.activePanel != panel){
39109 if(this.activePanel.setActiveState(false) === false){
39113 this.activePanel = panel;
39114 panel.setActiveState(true);
39115 if(this.panelSize){
39116 panel.setSize(this.panelSize.width, this.panelSize.height);
39119 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39121 this.updateTitle(panel.getTitle());
39123 this.fireEvent("invalidated", this);
39125 this.fireEvent("panelactivated", this, panel);
39129 * Shows the specified panel.
39130 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39131 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39133 showPanel : function(panel)
39135 panel = this.getPanel(panel);
39138 var tab = this.tabs.getTab(panel.getEl().id);
39139 if(tab.isHidden()){
39140 this.tabs.unhideTab(tab.id);
39144 this.setActivePanel(panel);
39151 * Get the active panel for this region.
39152 * @return {Roo.ContentPanel} The active panel or null
39154 getActivePanel : function(){
39155 return this.activePanel;
39158 validateVisibility : function(){
39159 if(this.panels.getCount() < 1){
39160 this.updateTitle(" ");
39161 this.closeBtn.hide();
39164 if(!this.isVisible()){
39171 * Adds the passed ContentPanel(s) to this region.
39172 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39173 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39175 add : function(panel)
39177 if(arguments.length > 1){
39178 for(var i = 0, len = arguments.length; i < len; i++) {
39179 this.add(arguments[i]);
39184 // if we have not been rendered yet, then we can not really do much of this..
39185 if (!this.bodyEl) {
39186 this.unrendered_panels.push(panel);
39193 if(this.hasPanel(panel)){
39194 this.showPanel(panel);
39197 panel.setRegion(this);
39198 this.panels.add(panel);
39199 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39200 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39201 // and hide them... ???
39202 this.bodyEl.dom.appendChild(panel.getEl().dom);
39203 if(panel.background !== true){
39204 this.setActivePanel(panel);
39206 this.fireEvent("paneladded", this, panel);
39213 this.initPanelAsTab(panel);
39217 if(panel.background !== true){
39218 this.tabs.activate(panel.getEl().id);
39220 this.fireEvent("paneladded", this, panel);
39225 * Hides the tab for the specified panel.
39226 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39228 hidePanel : function(panel){
39229 if(this.tabs && (panel = this.getPanel(panel))){
39230 this.tabs.hideTab(panel.getEl().id);
39235 * Unhides the tab for a previously hidden panel.
39236 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39238 unhidePanel : function(panel){
39239 if(this.tabs && (panel = this.getPanel(panel))){
39240 this.tabs.unhideTab(panel.getEl().id);
39244 clearPanels : function(){
39245 while(this.panels.getCount() > 0){
39246 this.remove(this.panels.first());
39251 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39252 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39253 * @param {Boolean} preservePanel Overrides the config preservePanel option
39254 * @return {Roo.ContentPanel} The panel that was removed
39256 remove : function(panel, preservePanel)
39258 panel = this.getPanel(panel);
39263 this.fireEvent("beforeremove", this, panel, e);
39264 if(e.cancel === true){
39267 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39268 var panelId = panel.getId();
39269 this.panels.removeKey(panelId);
39271 document.body.appendChild(panel.getEl().dom);
39274 this.tabs.removeTab(panel.getEl().id);
39275 }else if (!preservePanel){
39276 this.bodyEl.dom.removeChild(panel.getEl().dom);
39278 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39279 var p = this.panels.first();
39280 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39281 tempEl.appendChild(p.getEl().dom);
39282 this.bodyEl.update("");
39283 this.bodyEl.dom.appendChild(p.getEl().dom);
39285 this.updateTitle(p.getTitle());
39287 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39288 this.setActivePanel(p);
39290 panel.setRegion(null);
39291 if(this.activePanel == panel){
39292 this.activePanel = null;
39294 if(this.config.autoDestroy !== false && preservePanel !== true){
39295 try{panel.destroy();}catch(e){}
39297 this.fireEvent("panelremoved", this, panel);
39302 * Returns the TabPanel component used by this region
39303 * @return {Roo.TabPanel}
39305 getTabs : function(){
39309 createTool : function(parentEl, className){
39310 var btn = Roo.DomHelper.append(parentEl, {
39312 cls: "x-layout-tools-button",
39315 cls: "roo-layout-tools-button-inner " + className,
39319 btn.addClassOnOver("roo-layout-tools-button-over");
39324 * Ext JS Library 1.1.1
39325 * Copyright(c) 2006-2007, Ext JS, LLC.
39327 * Originally Released Under LGPL - original licence link has changed is not relivant.
39330 * <script type="text/javascript">
39336 * @class Roo.SplitLayoutRegion
39337 * @extends Roo.LayoutRegion
39338 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39340 Roo.bootstrap.layout.Split = function(config){
39341 this.cursor = config.cursor;
39342 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39345 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39347 splitTip : "Drag to resize.",
39348 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39349 useSplitTips : false,
39351 applyConfig : function(config){
39352 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39355 onRender : function(ctr,pos) {
39357 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39358 if(!this.config.split){
39363 var splitEl = Roo.DomHelper.append(ctr.dom, {
39365 id: this.el.id + "-split",
39366 cls: "roo-layout-split roo-layout-split-"+this.position,
39369 /** The SplitBar for this region
39370 * @type Roo.SplitBar */
39371 // does not exist yet...
39372 Roo.log([this.position, this.orientation]);
39374 this.split = new Roo.bootstrap.SplitBar({
39375 dragElement : splitEl,
39376 resizingElement: this.el,
39377 orientation : this.orientation
39380 this.split.on("moved", this.onSplitMove, this);
39381 this.split.useShim = this.config.useShim === true;
39382 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39383 if(this.useSplitTips){
39384 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39386 //if(config.collapsible){
39387 // this.split.el.on("dblclick", this.collapse, this);
39390 if(typeof this.config.minSize != "undefined"){
39391 this.split.minSize = this.config.minSize;
39393 if(typeof this.config.maxSize != "undefined"){
39394 this.split.maxSize = this.config.maxSize;
39396 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39397 this.hideSplitter();
39402 getHMaxSize : function(){
39403 var cmax = this.config.maxSize || 10000;
39404 var center = this.mgr.getRegion("center");
39405 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39408 getVMaxSize : function(){
39409 var cmax = this.config.maxSize || 10000;
39410 var center = this.mgr.getRegion("center");
39411 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39414 onSplitMove : function(split, newSize){
39415 this.fireEvent("resized", this, newSize);
39419 * Returns the {@link Roo.SplitBar} for this region.
39420 * @return {Roo.SplitBar}
39422 getSplitBar : function(){
39427 this.hideSplitter();
39428 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39431 hideSplitter : function(){
39433 this.split.el.setLocation(-2000,-2000);
39434 this.split.el.hide();
39440 this.split.el.show();
39442 Roo.bootstrap.layout.Split.superclass.show.call(this);
39445 beforeSlide: function(){
39446 if(Roo.isGecko){// firefox overflow auto bug workaround
39447 this.bodyEl.clip();
39449 this.tabs.bodyEl.clip();
39451 if(this.activePanel){
39452 this.activePanel.getEl().clip();
39454 if(this.activePanel.beforeSlide){
39455 this.activePanel.beforeSlide();
39461 afterSlide : function(){
39462 if(Roo.isGecko){// firefox overflow auto bug workaround
39463 this.bodyEl.unclip();
39465 this.tabs.bodyEl.unclip();
39467 if(this.activePanel){
39468 this.activePanel.getEl().unclip();
39469 if(this.activePanel.afterSlide){
39470 this.activePanel.afterSlide();
39476 initAutoHide : function(){
39477 if(this.autoHide !== false){
39478 if(!this.autoHideHd){
39479 var st = new Roo.util.DelayedTask(this.slideIn, this);
39480 this.autoHideHd = {
39481 "mouseout": function(e){
39482 if(!e.within(this.el, true)){
39486 "mouseover" : function(e){
39492 this.el.on(this.autoHideHd);
39496 clearAutoHide : function(){
39497 if(this.autoHide !== false){
39498 this.el.un("mouseout", this.autoHideHd.mouseout);
39499 this.el.un("mouseover", this.autoHideHd.mouseover);
39503 clearMonitor : function(){
39504 Roo.get(document).un("click", this.slideInIf, this);
39507 // these names are backwards but not changed for compat
39508 slideOut : function(){
39509 if(this.isSlid || this.el.hasActiveFx()){
39512 this.isSlid = true;
39513 if(this.collapseBtn){
39514 this.collapseBtn.hide();
39516 this.closeBtnState = this.closeBtn.getStyle('display');
39517 this.closeBtn.hide();
39519 this.stickBtn.show();
39522 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39523 this.beforeSlide();
39524 this.el.setStyle("z-index", 10001);
39525 this.el.slideIn(this.getSlideAnchor(), {
39526 callback: function(){
39528 this.initAutoHide();
39529 Roo.get(document).on("click", this.slideInIf, this);
39530 this.fireEvent("slideshow", this);
39537 afterSlideIn : function(){
39538 this.clearAutoHide();
39539 this.isSlid = false;
39540 this.clearMonitor();
39541 this.el.setStyle("z-index", "");
39542 if(this.collapseBtn){
39543 this.collapseBtn.show();
39545 this.closeBtn.setStyle('display', this.closeBtnState);
39547 this.stickBtn.hide();
39549 this.fireEvent("slidehide", this);
39552 slideIn : function(cb){
39553 if(!this.isSlid || this.el.hasActiveFx()){
39557 this.isSlid = false;
39558 this.beforeSlide();
39559 this.el.slideOut(this.getSlideAnchor(), {
39560 callback: function(){
39561 this.el.setLeftTop(-10000, -10000);
39563 this.afterSlideIn();
39571 slideInIf : function(e){
39572 if(!e.within(this.el)){
39577 animateCollapse : function(){
39578 this.beforeSlide();
39579 this.el.setStyle("z-index", 20000);
39580 var anchor = this.getSlideAnchor();
39581 this.el.slideOut(anchor, {
39582 callback : function(){
39583 this.el.setStyle("z-index", "");
39584 this.collapsedEl.slideIn(anchor, {duration:.3});
39586 this.el.setLocation(-10000,-10000);
39588 this.fireEvent("collapsed", this);
39595 animateExpand : function(){
39596 this.beforeSlide();
39597 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39598 this.el.setStyle("z-index", 20000);
39599 this.collapsedEl.hide({
39602 this.el.slideIn(this.getSlideAnchor(), {
39603 callback : function(){
39604 this.el.setStyle("z-index", "");
39607 this.split.el.show();
39609 this.fireEvent("invalidated", this);
39610 this.fireEvent("expanded", this);
39638 getAnchor : function(){
39639 return this.anchors[this.position];
39642 getCollapseAnchor : function(){
39643 return this.canchors[this.position];
39646 getSlideAnchor : function(){
39647 return this.sanchors[this.position];
39650 getAlignAdj : function(){
39651 var cm = this.cmargins;
39652 switch(this.position){
39668 getExpandAdj : function(){
39669 var c = this.collapsedEl, cm = this.cmargins;
39670 switch(this.position){
39672 return [-(cm.right+c.getWidth()+cm.left), 0];
39675 return [cm.right+c.getWidth()+cm.left, 0];
39678 return [0, -(cm.top+cm.bottom+c.getHeight())];
39681 return [0, cm.top+cm.bottom+c.getHeight()];
39687 * Ext JS Library 1.1.1
39688 * Copyright(c) 2006-2007, Ext JS, LLC.
39690 * Originally Released Under LGPL - original licence link has changed is not relivant.
39693 * <script type="text/javascript">
39696 * These classes are private internal classes
39698 Roo.bootstrap.layout.Center = function(config){
39699 config.region = "center";
39700 Roo.bootstrap.layout.Region.call(this, config);
39701 this.visible = true;
39702 this.minWidth = config.minWidth || 20;
39703 this.minHeight = config.minHeight || 20;
39706 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39708 // center panel can't be hidden
39712 // center panel can't be hidden
39715 getMinWidth: function(){
39716 return this.minWidth;
39719 getMinHeight: function(){
39720 return this.minHeight;
39734 Roo.bootstrap.layout.North = function(config)
39736 config.region = 'north';
39737 config.cursor = 'n-resize';
39739 Roo.bootstrap.layout.Split.call(this, config);
39743 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39744 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39745 this.split.el.addClass("roo-layout-split-v");
39747 //var size = config.initialSize || config.height;
39748 //if(this.el && typeof size != "undefined"){
39749 // this.el.setHeight(size);
39752 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39754 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39757 onRender : function(ctr, pos)
39759 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39760 var size = this.config.initialSize || this.config.height;
39761 if(this.el && typeof size != "undefined"){
39762 this.el.setHeight(size);
39767 getBox : function(){
39768 if(this.collapsed){
39769 return this.collapsedEl.getBox();
39771 var box = this.el.getBox();
39773 box.height += this.split.el.getHeight();
39778 updateBox : function(box){
39779 if(this.split && !this.collapsed){
39780 box.height -= this.split.el.getHeight();
39781 this.split.el.setLeft(box.x);
39782 this.split.el.setTop(box.y+box.height);
39783 this.split.el.setWidth(box.width);
39785 if(this.collapsed){
39786 this.updateBody(box.width, null);
39788 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39796 Roo.bootstrap.layout.South = function(config){
39797 config.region = 'south';
39798 config.cursor = 's-resize';
39799 Roo.bootstrap.layout.Split.call(this, config);
39801 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39802 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39803 this.split.el.addClass("roo-layout-split-v");
39808 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39809 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39811 onRender : function(ctr, pos)
39813 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39814 var size = this.config.initialSize || this.config.height;
39815 if(this.el && typeof size != "undefined"){
39816 this.el.setHeight(size);
39821 getBox : function(){
39822 if(this.collapsed){
39823 return this.collapsedEl.getBox();
39825 var box = this.el.getBox();
39827 var sh = this.split.el.getHeight();
39834 updateBox : function(box){
39835 if(this.split && !this.collapsed){
39836 var sh = this.split.el.getHeight();
39839 this.split.el.setLeft(box.x);
39840 this.split.el.setTop(box.y-sh);
39841 this.split.el.setWidth(box.width);
39843 if(this.collapsed){
39844 this.updateBody(box.width, null);
39846 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39850 Roo.bootstrap.layout.East = function(config){
39851 config.region = "east";
39852 config.cursor = "e-resize";
39853 Roo.bootstrap.layout.Split.call(this, config);
39855 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39856 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39857 this.split.el.addClass("roo-layout-split-h");
39861 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39862 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39864 onRender : function(ctr, pos)
39866 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39867 var size = this.config.initialSize || this.config.width;
39868 if(this.el && typeof size != "undefined"){
39869 this.el.setWidth(size);
39874 getBox : function(){
39875 if(this.collapsed){
39876 return this.collapsedEl.getBox();
39878 var box = this.el.getBox();
39880 var sw = this.split.el.getWidth();
39887 updateBox : function(box){
39888 if(this.split && !this.collapsed){
39889 var sw = this.split.el.getWidth();
39891 this.split.el.setLeft(box.x);
39892 this.split.el.setTop(box.y);
39893 this.split.el.setHeight(box.height);
39896 if(this.collapsed){
39897 this.updateBody(null, box.height);
39899 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39903 Roo.bootstrap.layout.West = function(config){
39904 config.region = "west";
39905 config.cursor = "w-resize";
39907 Roo.bootstrap.layout.Split.call(this, config);
39909 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39910 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39911 this.split.el.addClass("roo-layout-split-h");
39915 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39916 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39918 onRender: function(ctr, pos)
39920 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39921 var size = this.config.initialSize || this.config.width;
39922 if(typeof size != "undefined"){
39923 this.el.setWidth(size);
39927 getBox : function(){
39928 if(this.collapsed){
39929 return this.collapsedEl.getBox();
39931 var box = this.el.getBox();
39932 if (box.width == 0) {
39933 box.width = this.config.width; // kludge?
39936 box.width += this.split.el.getWidth();
39941 updateBox : function(box){
39942 if(this.split && !this.collapsed){
39943 var sw = this.split.el.getWidth();
39945 this.split.el.setLeft(box.x+box.width);
39946 this.split.el.setTop(box.y);
39947 this.split.el.setHeight(box.height);
39949 if(this.collapsed){
39950 this.updateBody(null, box.height);
39952 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39954 });Roo.namespace("Roo.bootstrap.panel");/*
39956 * Ext JS Library 1.1.1
39957 * Copyright(c) 2006-2007, Ext JS, LLC.
39959 * Originally Released Under LGPL - original licence link has changed is not relivant.
39962 * <script type="text/javascript">
39965 * @class Roo.ContentPanel
39966 * @extends Roo.util.Observable
39967 * A basic ContentPanel element.
39968 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39969 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39970 * @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
39971 * @cfg {Boolean} closable True if the panel can be closed/removed
39972 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39973 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39974 * @cfg {Toolbar} toolbar A toolbar for this panel
39975 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39976 * @cfg {String} title The title for this panel
39977 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39978 * @cfg {String} url Calls {@link #setUrl} with this value
39979 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39980 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39981 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39982 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39983 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39984 * @cfg {Boolean} badges render the badges
39985 * @cfg {String} cls extra classes to use
39986 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39989 * Create a new ContentPanel.
39990 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39991 * @param {String/Object} config A string to set only the title or a config object
39992 * @param {String} content (optional) Set the HTML content for this panel
39993 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39995 Roo.bootstrap.panel.Content = function( config){
39997 this.tpl = config.tpl || false;
39999 var el = config.el;
40000 var content = config.content;
40002 if(config.autoCreate){ // xtype is available if this is called from factory
40005 this.el = Roo.get(el);
40006 if(!this.el && config && config.autoCreate){
40007 if(typeof config.autoCreate == "object"){
40008 if(!config.autoCreate.id){
40009 config.autoCreate.id = config.id||el;
40011 this.el = Roo.DomHelper.append(document.body,
40012 config.autoCreate, true);
40016 cls: (config.cls || '') +
40017 (config.background ? ' bg-' + config.background : '') +
40018 " roo-layout-inactive-content",
40021 if (config.iframe) {
40025 style : 'border: 0px',
40026 src : 'about:blank'
40032 elcfg.html = config.html;
40036 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40037 if (config.iframe) {
40038 this.iframeEl = this.el.select('iframe',true).first();
40043 this.closable = false;
40044 this.loaded = false;
40045 this.active = false;
40048 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40050 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40052 this.wrapEl = this.el; //this.el.wrap();
40054 if (config.toolbar.items) {
40055 ti = config.toolbar.items ;
40056 delete config.toolbar.items ;
40060 this.toolbar.render(this.wrapEl, 'before');
40061 for(var i =0;i < ti.length;i++) {
40062 // Roo.log(['add child', items[i]]);
40063 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40065 this.toolbar.items = nitems;
40066 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40067 delete config.toolbar;
40071 // xtype created footer. - not sure if will work as we normally have to render first..
40072 if (this.footer && !this.footer.el && this.footer.xtype) {
40073 if (!this.wrapEl) {
40074 this.wrapEl = this.el.wrap();
40077 this.footer.container = this.wrapEl.createChild();
40079 this.footer = Roo.factory(this.footer, Roo);
40084 if(typeof config == "string"){
40085 this.title = config;
40087 Roo.apply(this, config);
40091 this.resizeEl = Roo.get(this.resizeEl, true);
40093 this.resizeEl = this.el;
40095 // handle view.xtype
40103 * Fires when this panel is activated.
40104 * @param {Roo.ContentPanel} this
40108 * @event deactivate
40109 * Fires when this panel is activated.
40110 * @param {Roo.ContentPanel} this
40112 "deactivate" : true,
40116 * Fires when this panel is resized if fitToFrame is true.
40117 * @param {Roo.ContentPanel} this
40118 * @param {Number} width The width after any component adjustments
40119 * @param {Number} height The height after any component adjustments
40125 * Fires when this tab is created
40126 * @param {Roo.ContentPanel} this
40137 if(this.autoScroll && !this.iframe){
40138 this.resizeEl.setStyle("overflow", "auto");
40140 // fix randome scrolling
40141 //this.el.on('scroll', function() {
40142 // Roo.log('fix random scolling');
40143 // this.scrollTo('top',0);
40146 content = content || this.content;
40148 this.setContent(content);
40150 if(config && config.url){
40151 this.setUrl(this.url, this.params, this.loadOnce);
40156 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40158 if (this.view && typeof(this.view.xtype) != 'undefined') {
40159 this.view.el = this.el.appendChild(document.createElement("div"));
40160 this.view = Roo.factory(this.view);
40161 this.view.render && this.view.render(false, '');
40165 this.fireEvent('render', this);
40168 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40178 setRegion : function(region){
40179 this.region = region;
40180 this.setActiveClass(region && !this.background);
40184 setActiveClass: function(state)
40187 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40188 this.el.setStyle('position','relative');
40190 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40191 this.el.setStyle('position', 'absolute');
40196 * Returns the toolbar for this Panel if one was configured.
40197 * @return {Roo.Toolbar}
40199 getToolbar : function(){
40200 return this.toolbar;
40203 setActiveState : function(active)
40205 this.active = active;
40206 this.setActiveClass(active);
40208 if(this.fireEvent("deactivate", this) === false){
40213 this.fireEvent("activate", this);
40217 * Updates this panel's element (not for iframe)
40218 * @param {String} content The new content
40219 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40221 setContent : function(content, loadScripts){
40226 this.el.update(content, loadScripts);
40229 ignoreResize : function(w, h){
40230 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40233 this.lastSize = {width: w, height: h};
40238 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40239 * @return {Roo.UpdateManager} The UpdateManager
40241 getUpdateManager : function(){
40245 return this.el.getUpdateManager();
40248 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40249 * Does not work with IFRAME contents
40250 * @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:
40253 url: "your-url.php",
40254 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40255 callback: yourFunction,
40256 scope: yourObject, //(optional scope)
40259 text: "Loading...",
40265 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40266 * 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.
40267 * @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}
40268 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40269 * @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.
40270 * @return {Roo.ContentPanel} this
40278 var um = this.el.getUpdateManager();
40279 um.update.apply(um, arguments);
40285 * 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.
40286 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40287 * @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)
40288 * @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)
40289 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40291 setUrl : function(url, params, loadOnce){
40293 this.iframeEl.dom.src = url;
40297 if(this.refreshDelegate){
40298 this.removeListener("activate", this.refreshDelegate);
40300 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40301 this.on("activate", this.refreshDelegate);
40302 return this.el.getUpdateManager();
40305 _handleRefresh : function(url, params, loadOnce){
40306 if(!loadOnce || !this.loaded){
40307 var updater = this.el.getUpdateManager();
40308 updater.update(url, params, this._setLoaded.createDelegate(this));
40312 _setLoaded : function(){
40313 this.loaded = true;
40317 * Returns this panel's id
40320 getId : function(){
40325 * Returns this panel's element - used by regiosn to add.
40326 * @return {Roo.Element}
40328 getEl : function(){
40329 return this.wrapEl || this.el;
40334 adjustForComponents : function(width, height)
40336 //Roo.log('adjustForComponents ');
40337 if(this.resizeEl != this.el){
40338 width -= this.el.getFrameWidth('lr');
40339 height -= this.el.getFrameWidth('tb');
40342 var te = this.toolbar.getEl();
40343 te.setWidth(width);
40344 height -= te.getHeight();
40347 var te = this.footer.getEl();
40348 te.setWidth(width);
40349 height -= te.getHeight();
40353 if(this.adjustments){
40354 width += this.adjustments[0];
40355 height += this.adjustments[1];
40357 return {"width": width, "height": height};
40360 setSize : function(width, height){
40361 if(this.fitToFrame && !this.ignoreResize(width, height)){
40362 if(this.fitContainer && this.resizeEl != this.el){
40363 this.el.setSize(width, height);
40365 var size = this.adjustForComponents(width, height);
40367 this.iframeEl.setSize(width,height);
40370 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40371 this.fireEvent('resize', this, size.width, size.height);
40378 * Returns this panel's title
40381 getTitle : function(){
40383 if (typeof(this.title) != 'object') {
40388 for (var k in this.title) {
40389 if (!this.title.hasOwnProperty(k)) {
40393 if (k.indexOf('-') >= 0) {
40394 var s = k.split('-');
40395 for (var i = 0; i<s.length; i++) {
40396 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40399 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40406 * Set this panel's title
40407 * @param {String} title
40409 setTitle : function(title){
40410 this.title = title;
40412 this.region.updatePanelTitle(this, title);
40417 * Returns true is this panel was configured to be closable
40418 * @return {Boolean}
40420 isClosable : function(){
40421 return this.closable;
40424 beforeSlide : function(){
40426 this.resizeEl.clip();
40429 afterSlide : function(){
40431 this.resizeEl.unclip();
40435 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40436 * Will fail silently if the {@link #setUrl} method has not been called.
40437 * This does not activate the panel, just updates its content.
40439 refresh : function(){
40440 if(this.refreshDelegate){
40441 this.loaded = false;
40442 this.refreshDelegate();
40447 * Destroys this panel
40449 destroy : function(){
40450 this.el.removeAllListeners();
40451 var tempEl = document.createElement("span");
40452 tempEl.appendChild(this.el.dom);
40453 tempEl.innerHTML = "";
40459 * form - if the content panel contains a form - this is a reference to it.
40460 * @type {Roo.form.Form}
40464 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40465 * This contains a reference to it.
40471 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40481 * @param {Object} cfg Xtype definition of item to add.
40485 getChildContainer: function () {
40486 return this.getEl();
40491 var ret = new Roo.factory(cfg);
40496 if (cfg.xtype.match(/^Form$/)) {
40499 //if (this.footer) {
40500 // el = this.footer.container.insertSibling(false, 'before');
40502 el = this.el.createChild();
40505 this.form = new Roo.form.Form(cfg);
40508 if ( this.form.allItems.length) {
40509 this.form.render(el.dom);
40513 // should only have one of theses..
40514 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40515 // views.. should not be just added - used named prop 'view''
40517 cfg.el = this.el.appendChild(document.createElement("div"));
40520 var ret = new Roo.factory(cfg);
40522 ret.render && ret.render(false, ''); // render blank..
40532 * @class Roo.bootstrap.panel.Grid
40533 * @extends Roo.bootstrap.panel.Content
40535 * Create a new GridPanel.
40536 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40537 * @param {Object} config A the config object
40543 Roo.bootstrap.panel.Grid = function(config)
40547 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40548 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40550 config.el = this.wrapper;
40551 //this.el = this.wrapper;
40553 if (config.container) {
40554 // ctor'ed from a Border/panel.grid
40557 this.wrapper.setStyle("overflow", "hidden");
40558 this.wrapper.addClass('roo-grid-container');
40563 if(config.toolbar){
40564 var tool_el = this.wrapper.createChild();
40565 this.toolbar = Roo.factory(config.toolbar);
40567 if (config.toolbar.items) {
40568 ti = config.toolbar.items ;
40569 delete config.toolbar.items ;
40573 this.toolbar.render(tool_el);
40574 for(var i =0;i < ti.length;i++) {
40575 // Roo.log(['add child', items[i]]);
40576 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40578 this.toolbar.items = nitems;
40580 delete config.toolbar;
40583 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40584 config.grid.scrollBody = true;;
40585 config.grid.monitorWindowResize = false; // turn off autosizing
40586 config.grid.autoHeight = false;
40587 config.grid.autoWidth = false;
40589 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40591 if (config.background) {
40592 // render grid on panel activation (if panel background)
40593 this.on('activate', function(gp) {
40594 if (!gp.grid.rendered) {
40595 gp.grid.render(this.wrapper);
40596 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40601 this.grid.render(this.wrapper);
40602 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40605 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40606 // ??? needed ??? config.el = this.wrapper;
40611 // xtype created footer. - not sure if will work as we normally have to render first..
40612 if (this.footer && !this.footer.el && this.footer.xtype) {
40614 var ctr = this.grid.getView().getFooterPanel(true);
40615 this.footer.dataSource = this.grid.dataSource;
40616 this.footer = Roo.factory(this.footer, Roo);
40617 this.footer.render(ctr);
40627 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40628 getId : function(){
40629 return this.grid.id;
40633 * Returns the grid for this panel
40634 * @return {Roo.bootstrap.Table}
40636 getGrid : function(){
40640 setSize : function(width, height){
40641 if(!this.ignoreResize(width, height)){
40642 var grid = this.grid;
40643 var size = this.adjustForComponents(width, height);
40644 // tfoot is not a footer?
40647 var gridel = grid.getGridEl();
40648 gridel.setSize(size.width, size.height);
40650 var tbd = grid.getGridEl().select('tbody', true).first();
40651 var thd = grid.getGridEl().select('thead',true).first();
40652 var tbf= grid.getGridEl().select('tfoot', true).first();
40655 size.height -= tbf.getHeight();
40658 size.height -= thd.getHeight();
40661 tbd.setSize(size.width, size.height );
40662 // this is for the account management tab -seems to work there.
40663 var thd = grid.getGridEl().select('thead',true).first();
40665 // tbd.setSize(size.width, size.height - thd.getHeight());
40674 beforeSlide : function(){
40675 this.grid.getView().scroller.clip();
40678 afterSlide : function(){
40679 this.grid.getView().scroller.unclip();
40682 destroy : function(){
40683 this.grid.destroy();
40685 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40690 * @class Roo.bootstrap.panel.Nest
40691 * @extends Roo.bootstrap.panel.Content
40693 * Create a new Panel, that can contain a layout.Border.
40696 * @param {Roo.BorderLayout} layout The layout for this panel
40697 * @param {String/Object} config A string to set only the title or a config object
40699 Roo.bootstrap.panel.Nest = function(config)
40701 // construct with only one argument..
40702 /* FIXME - implement nicer consturctors
40703 if (layout.layout) {
40705 layout = config.layout;
40706 delete config.layout;
40708 if (layout.xtype && !layout.getEl) {
40709 // then layout needs constructing..
40710 layout = Roo.factory(layout, Roo);
40714 config.el = config.layout.getEl();
40716 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40718 config.layout.monitorWindowResize = false; // turn off autosizing
40719 this.layout = config.layout;
40720 this.layout.getEl().addClass("roo-layout-nested-layout");
40721 this.layout.parent = this;
40728 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40730 setSize : function(width, height){
40731 if(!this.ignoreResize(width, height)){
40732 var size = this.adjustForComponents(width, height);
40733 var el = this.layout.getEl();
40734 if (size.height < 1) {
40735 el.setWidth(size.width);
40737 el.setSize(size.width, size.height);
40739 var touch = el.dom.offsetWidth;
40740 this.layout.layout();
40741 // ie requires a double layout on the first pass
40742 if(Roo.isIE && !this.initialized){
40743 this.initialized = true;
40744 this.layout.layout();
40749 // activate all subpanels if not currently active..
40751 setActiveState : function(active){
40752 this.active = active;
40753 this.setActiveClass(active);
40756 this.fireEvent("deactivate", this);
40760 this.fireEvent("activate", this);
40761 // not sure if this should happen before or after..
40762 if (!this.layout) {
40763 return; // should not happen..
40766 for (var r in this.layout.regions) {
40767 reg = this.layout.getRegion(r);
40768 if (reg.getActivePanel()) {
40769 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40770 reg.setActivePanel(reg.getActivePanel());
40773 if (!reg.panels.length) {
40776 reg.showPanel(reg.getPanel(0));
40785 * Returns the nested BorderLayout for this panel
40786 * @return {Roo.BorderLayout}
40788 getLayout : function(){
40789 return this.layout;
40793 * Adds a xtype elements to the layout of the nested panel
40797 xtype : 'ContentPanel',
40804 xtype : 'NestedLayoutPanel',
40810 items : [ ... list of content panels or nested layout panels.. ]
40814 * @param {Object} cfg Xtype definition of item to add.
40816 addxtype : function(cfg) {
40817 return this.layout.addxtype(cfg);
40822 * Ext JS Library 1.1.1
40823 * Copyright(c) 2006-2007, Ext JS, LLC.
40825 * Originally Released Under LGPL - original licence link has changed is not relivant.
40828 * <script type="text/javascript">
40831 * @class Roo.TabPanel
40832 * @extends Roo.util.Observable
40833 * A lightweight tab container.
40837 // basic tabs 1, built from existing content
40838 var tabs = new Roo.TabPanel("tabs1");
40839 tabs.addTab("script", "View Script");
40840 tabs.addTab("markup", "View Markup");
40841 tabs.activate("script");
40843 // more advanced tabs, built from javascript
40844 var jtabs = new Roo.TabPanel("jtabs");
40845 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40847 // set up the UpdateManager
40848 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40849 var updater = tab2.getUpdateManager();
40850 updater.setDefaultUrl("ajax1.htm");
40851 tab2.on('activate', updater.refresh, updater, true);
40853 // Use setUrl for Ajax loading
40854 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40855 tab3.setUrl("ajax2.htm", null, true);
40858 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40861 jtabs.activate("jtabs-1");
40864 * Create a new TabPanel.
40865 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40866 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40868 Roo.bootstrap.panel.Tabs = function(config){
40870 * The container element for this TabPanel.
40871 * @type Roo.Element
40873 this.el = Roo.get(config.el);
40876 if(typeof config == "boolean"){
40877 this.tabPosition = config ? "bottom" : "top";
40879 Roo.apply(this, config);
40883 if(this.tabPosition == "bottom"){
40884 // if tabs are at the bottom = create the body first.
40885 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40886 this.el.addClass("roo-tabs-bottom");
40888 // next create the tabs holders
40890 if (this.tabPosition == "west"){
40892 var reg = this.region; // fake it..
40894 if (!reg.mgr.parent) {
40897 reg = reg.mgr.parent.region;
40899 Roo.log("got nest?");
40901 if (reg.mgr.getRegion('west')) {
40902 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40903 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40904 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40905 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40906 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40914 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40915 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40916 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40917 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40922 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40925 // finally - if tabs are at the top, then create the body last..
40926 if(this.tabPosition != "bottom"){
40927 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40928 * @type Roo.Element
40930 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40931 this.el.addClass("roo-tabs-top");
40935 this.bodyEl.setStyle("position", "relative");
40937 this.active = null;
40938 this.activateDelegate = this.activate.createDelegate(this);
40943 * Fires when the active tab changes
40944 * @param {Roo.TabPanel} this
40945 * @param {Roo.TabPanelItem} activePanel The new active tab
40949 * @event beforetabchange
40950 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40951 * @param {Roo.TabPanel} this
40952 * @param {Object} e Set cancel to true on this object to cancel the tab change
40953 * @param {Roo.TabPanelItem} tab The tab being changed to
40955 "beforetabchange" : true
40958 Roo.EventManager.onWindowResize(this.onResize, this);
40959 this.cpad = this.el.getPadding("lr");
40960 this.hiddenCount = 0;
40963 // toolbar on the tabbar support...
40964 if (this.toolbar) {
40965 alert("no toolbar support yet");
40966 this.toolbar = false;
40968 var tcfg = this.toolbar;
40969 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40970 this.toolbar = new Roo.Toolbar(tcfg);
40971 if (Roo.isSafari) {
40972 var tbl = tcfg.container.child('table', true);
40973 tbl.setAttribute('width', '100%');
40981 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40984 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40986 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40988 tabPosition : "top",
40990 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40992 currentTabWidth : 0,
40994 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40998 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41002 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41004 preferredTabWidth : 175,
41006 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41008 resizeTabs : false,
41010 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41012 monitorResize : true,
41014 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41016 toolbar : false, // set by caller..
41018 region : false, /// set by caller
41020 disableTooltips : true, // not used yet...
41023 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41024 * @param {String} id The id of the div to use <b>or create</b>
41025 * @param {String} text The text for the tab
41026 * @param {String} content (optional) Content to put in the TabPanelItem body
41027 * @param {Boolean} closable (optional) True to create a close icon on the tab
41028 * @return {Roo.TabPanelItem} The created TabPanelItem
41030 addTab : function(id, text, content, closable, tpl)
41032 var item = new Roo.bootstrap.panel.TabItem({
41036 closable : closable,
41039 this.addTabItem(item);
41041 item.setContent(content);
41047 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41048 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41049 * @return {Roo.TabPanelItem}
41051 getTab : function(id){
41052 return this.items[id];
41056 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41057 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41059 hideTab : function(id){
41060 var t = this.items[id];
41063 this.hiddenCount++;
41064 this.autoSizeTabs();
41069 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41070 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41072 unhideTab : function(id){
41073 var t = this.items[id];
41075 t.setHidden(false);
41076 this.hiddenCount--;
41077 this.autoSizeTabs();
41082 * Adds an existing {@link Roo.TabPanelItem}.
41083 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41085 addTabItem : function(item)
41087 this.items[item.id] = item;
41088 this.items.push(item);
41089 this.autoSizeTabs();
41090 // if(this.resizeTabs){
41091 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41092 // this.autoSizeTabs();
41094 // item.autoSize();
41099 * Removes a {@link Roo.TabPanelItem}.
41100 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41102 removeTab : function(id){
41103 var items = this.items;
41104 var tab = items[id];
41105 if(!tab) { return; }
41106 var index = items.indexOf(tab);
41107 if(this.active == tab && items.length > 1){
41108 var newTab = this.getNextAvailable(index);
41113 this.stripEl.dom.removeChild(tab.pnode.dom);
41114 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41115 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41117 items.splice(index, 1);
41118 delete this.items[tab.id];
41119 tab.fireEvent("close", tab);
41120 tab.purgeListeners();
41121 this.autoSizeTabs();
41124 getNextAvailable : function(start){
41125 var items = this.items;
41127 // look for a next tab that will slide over to
41128 // replace the one being removed
41129 while(index < items.length){
41130 var item = items[++index];
41131 if(item && !item.isHidden()){
41135 // if one isn't found select the previous tab (on the left)
41138 var item = items[--index];
41139 if(item && !item.isHidden()){
41147 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41148 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41150 disableTab : function(id){
41151 var tab = this.items[id];
41152 if(tab && this.active != tab){
41158 * Enables a {@link Roo.TabPanelItem} that is disabled.
41159 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41161 enableTab : function(id){
41162 var tab = this.items[id];
41167 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41168 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41169 * @return {Roo.TabPanelItem} The TabPanelItem.
41171 activate : function(id)
41173 //Roo.log('activite:' + id);
41175 var tab = this.items[id];
41179 if(tab == this.active || tab.disabled){
41183 this.fireEvent("beforetabchange", this, e, tab);
41184 if(e.cancel !== true && !tab.disabled){
41186 this.active.hide();
41188 this.active = this.items[id];
41189 this.active.show();
41190 this.fireEvent("tabchange", this, this.active);
41196 * Gets the active {@link Roo.TabPanelItem}.
41197 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41199 getActiveTab : function(){
41200 return this.active;
41204 * Updates the tab body element to fit the height of the container element
41205 * for overflow scrolling
41206 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41208 syncHeight : function(targetHeight){
41209 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41210 var bm = this.bodyEl.getMargins();
41211 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41212 this.bodyEl.setHeight(newHeight);
41216 onResize : function(){
41217 if(this.monitorResize){
41218 this.autoSizeTabs();
41223 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41225 beginUpdate : function(){
41226 this.updating = true;
41230 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41232 endUpdate : function(){
41233 this.updating = false;
41234 this.autoSizeTabs();
41238 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41240 autoSizeTabs : function()
41242 var count = this.items.length;
41243 var vcount = count - this.hiddenCount;
41246 this.stripEl.hide();
41248 this.stripEl.show();
41251 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41256 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41257 var availWidth = Math.floor(w / vcount);
41258 var b = this.stripBody;
41259 if(b.getWidth() > w){
41260 var tabs = this.items;
41261 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41262 if(availWidth < this.minTabWidth){
41263 /*if(!this.sleft){ // incomplete scrolling code
41264 this.createScrollButtons();
41267 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41270 if(this.currentTabWidth < this.preferredTabWidth){
41271 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41277 * Returns the number of tabs in this TabPanel.
41280 getCount : function(){
41281 return this.items.length;
41285 * Resizes all the tabs to the passed width
41286 * @param {Number} The new width
41288 setTabWidth : function(width){
41289 this.currentTabWidth = width;
41290 for(var i = 0, len = this.items.length; i < len; i++) {
41291 if(!this.items[i].isHidden()) {
41292 this.items[i].setWidth(width);
41298 * Destroys this TabPanel
41299 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41301 destroy : function(removeEl){
41302 Roo.EventManager.removeResizeListener(this.onResize, this);
41303 for(var i = 0, len = this.items.length; i < len; i++){
41304 this.items[i].purgeListeners();
41306 if(removeEl === true){
41307 this.el.update("");
41312 createStrip : function(container)
41314 var strip = document.createElement("nav");
41315 strip.className = Roo.bootstrap.version == 4 ?
41316 "navbar-light bg-light" :
41317 "navbar navbar-default"; //"x-tabs-wrap";
41318 container.appendChild(strip);
41322 createStripList : function(strip)
41324 // div wrapper for retard IE
41325 // returns the "tr" element.
41326 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41327 //'<div class="x-tabs-strip-wrap">'+
41328 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41329 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41330 return strip.firstChild; //.firstChild.firstChild.firstChild;
41332 createBody : function(container)
41334 var body = document.createElement("div");
41335 Roo.id(body, "tab-body");
41336 //Roo.fly(body).addClass("x-tabs-body");
41337 Roo.fly(body).addClass("tab-content");
41338 container.appendChild(body);
41341 createItemBody :function(bodyEl, id){
41342 var body = Roo.getDom(id);
41344 body = document.createElement("div");
41347 //Roo.fly(body).addClass("x-tabs-item-body");
41348 Roo.fly(body).addClass("tab-pane");
41349 bodyEl.insertBefore(body, bodyEl.firstChild);
41353 createStripElements : function(stripEl, text, closable, tpl)
41355 var td = document.createElement("li"); // was td..
41356 td.className = 'nav-item';
41358 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41361 stripEl.appendChild(td);
41363 td.className = "x-tabs-closable";
41364 if(!this.closeTpl){
41365 this.closeTpl = new Roo.Template(
41366 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41367 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41368 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41371 var el = this.closeTpl.overwrite(td, {"text": text});
41372 var close = el.getElementsByTagName("div")[0];
41373 var inner = el.getElementsByTagName("em")[0];
41374 return {"el": el, "close": close, "inner": inner};
41377 // not sure what this is..
41378 // if(!this.tabTpl){
41379 //this.tabTpl = new Roo.Template(
41380 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41381 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41383 // this.tabTpl = new Roo.Template(
41384 // '<a href="#">' +
41385 // '<span unselectable="on"' +
41386 // (this.disableTooltips ? '' : ' title="{text}"') +
41387 // ' >{text}</span></a>'
41393 var template = tpl || this.tabTpl || false;
41396 template = new Roo.Template(
41397 Roo.bootstrap.version == 4 ?
41399 '<a class="nav-link" href="#" unselectable="on"' +
41400 (this.disableTooltips ? '' : ' title="{text}"') +
41403 '<a class="nav-link" href="#">' +
41404 '<span unselectable="on"' +
41405 (this.disableTooltips ? '' : ' title="{text}"') +
41406 ' >{text}</span></a>'
41411 switch (typeof(template)) {
41415 template = new Roo.Template(template);
41421 var el = template.overwrite(td, {"text": text});
41423 var inner = el.getElementsByTagName("span")[0];
41425 return {"el": el, "inner": inner};
41433 * @class Roo.TabPanelItem
41434 * @extends Roo.util.Observable
41435 * Represents an individual item (tab plus body) in a TabPanel.
41436 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41437 * @param {String} id The id of this TabPanelItem
41438 * @param {String} text The text for the tab of this TabPanelItem
41439 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41441 Roo.bootstrap.panel.TabItem = function(config){
41443 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41444 * @type Roo.TabPanel
41446 this.tabPanel = config.panel;
41448 * The id for this TabPanelItem
41451 this.id = config.id;
41453 this.disabled = false;
41455 this.text = config.text;
41457 this.loaded = false;
41458 this.closable = config.closable;
41461 * The body element for this TabPanelItem.
41462 * @type Roo.Element
41464 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41465 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41466 this.bodyEl.setStyle("display", "block");
41467 this.bodyEl.setStyle("zoom", "1");
41468 //this.hideAction();
41470 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41472 this.el = Roo.get(els.el);
41473 this.inner = Roo.get(els.inner, true);
41474 this.textEl = Roo.bootstrap.version == 4 ?
41475 this.el : Roo.get(this.el.dom.firstChild, true);
41477 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41478 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41481 // this.el.on("mousedown", this.onTabMouseDown, this);
41482 this.el.on("click", this.onTabClick, this);
41484 if(config.closable){
41485 var c = Roo.get(els.close, true);
41486 c.dom.title = this.closeText;
41487 c.addClassOnOver("close-over");
41488 c.on("click", this.closeClick, this);
41494 * Fires when this tab becomes the active tab.
41495 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41496 * @param {Roo.TabPanelItem} this
41500 * @event beforeclose
41501 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41502 * @param {Roo.TabPanelItem} this
41503 * @param {Object} e Set cancel to true on this object to cancel the close.
41505 "beforeclose": true,
41508 * Fires when this tab is closed.
41509 * @param {Roo.TabPanelItem} this
41513 * @event deactivate
41514 * Fires when this tab is no longer the active tab.
41515 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41516 * @param {Roo.TabPanelItem} this
41518 "deactivate" : true
41520 this.hidden = false;
41522 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41525 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41527 purgeListeners : function(){
41528 Roo.util.Observable.prototype.purgeListeners.call(this);
41529 this.el.removeAllListeners();
41532 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41535 this.status_node.addClass("active");
41538 this.tabPanel.stripWrap.repaint();
41540 this.fireEvent("activate", this.tabPanel, this);
41544 * Returns true if this tab is the active tab.
41545 * @return {Boolean}
41547 isActive : function(){
41548 return this.tabPanel.getActiveTab() == this;
41552 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41555 this.status_node.removeClass("active");
41557 this.fireEvent("deactivate", this.tabPanel, this);
41560 hideAction : function(){
41561 this.bodyEl.hide();
41562 this.bodyEl.setStyle("position", "absolute");
41563 this.bodyEl.setLeft("-20000px");
41564 this.bodyEl.setTop("-20000px");
41567 showAction : function(){
41568 this.bodyEl.setStyle("position", "relative");
41569 this.bodyEl.setTop("");
41570 this.bodyEl.setLeft("");
41571 this.bodyEl.show();
41575 * Set the tooltip for the tab.
41576 * @param {String} tooltip The tab's tooltip
41578 setTooltip : function(text){
41579 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41580 this.textEl.dom.qtip = text;
41581 this.textEl.dom.removeAttribute('title');
41583 this.textEl.dom.title = text;
41587 onTabClick : function(e){
41588 e.preventDefault();
41589 this.tabPanel.activate(this.id);
41592 onTabMouseDown : function(e){
41593 e.preventDefault();
41594 this.tabPanel.activate(this.id);
41597 getWidth : function(){
41598 return this.inner.getWidth();
41601 setWidth : function(width){
41602 var iwidth = width - this.linode.getPadding("lr");
41603 this.inner.setWidth(iwidth);
41604 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41605 this.linode.setWidth(width);
41609 * Show or hide the tab
41610 * @param {Boolean} hidden True to hide or false to show.
41612 setHidden : function(hidden){
41613 this.hidden = hidden;
41614 this.linode.setStyle("display", hidden ? "none" : "");
41618 * Returns true if this tab is "hidden"
41619 * @return {Boolean}
41621 isHidden : function(){
41622 return this.hidden;
41626 * Returns the text for this tab
41629 getText : function(){
41633 autoSize : function(){
41634 //this.el.beginMeasure();
41635 this.textEl.setWidth(1);
41637 * #2804 [new] Tabs in Roojs
41638 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41640 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41641 //this.el.endMeasure();
41645 * Sets the text for the tab (Note: this also sets the tooltip text)
41646 * @param {String} text The tab's text and tooltip
41648 setText : function(text){
41650 this.textEl.update(text);
41651 this.setTooltip(text);
41652 //if(!this.tabPanel.resizeTabs){
41653 // this.autoSize();
41657 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41659 activate : function(){
41660 this.tabPanel.activate(this.id);
41664 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41666 disable : function(){
41667 if(this.tabPanel.active != this){
41668 this.disabled = true;
41669 this.status_node.addClass("disabled");
41674 * Enables this TabPanelItem if it was previously disabled.
41676 enable : function(){
41677 this.disabled = false;
41678 this.status_node.removeClass("disabled");
41682 * Sets the content for this TabPanelItem.
41683 * @param {String} content The content
41684 * @param {Boolean} loadScripts true to look for and load scripts
41686 setContent : function(content, loadScripts){
41687 this.bodyEl.update(content, loadScripts);
41691 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41692 * @return {Roo.UpdateManager} The UpdateManager
41694 getUpdateManager : function(){
41695 return this.bodyEl.getUpdateManager();
41699 * Set a URL to be used to load the content for this TabPanelItem.
41700 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41701 * @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)
41702 * @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)
41703 * @return {Roo.UpdateManager} The UpdateManager
41705 setUrl : function(url, params, loadOnce){
41706 if(this.refreshDelegate){
41707 this.un('activate', this.refreshDelegate);
41709 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41710 this.on("activate", this.refreshDelegate);
41711 return this.bodyEl.getUpdateManager();
41715 _handleRefresh : function(url, params, loadOnce){
41716 if(!loadOnce || !this.loaded){
41717 var updater = this.bodyEl.getUpdateManager();
41718 updater.update(url, params, this._setLoaded.createDelegate(this));
41723 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41724 * Will fail silently if the setUrl method has not been called.
41725 * This does not activate the panel, just updates its content.
41727 refresh : function(){
41728 if(this.refreshDelegate){
41729 this.loaded = false;
41730 this.refreshDelegate();
41735 _setLoaded : function(){
41736 this.loaded = true;
41740 closeClick : function(e){
41743 this.fireEvent("beforeclose", this, o);
41744 if(o.cancel !== true){
41745 this.tabPanel.removeTab(this.id);
41749 * The text displayed in the tooltip for the close icon.
41752 closeText : "Close this tab"
41755 * This script refer to:
41756 * Title: International Telephone Input
41757 * Author: Jack O'Connor
41758 * Code version: v12.1.12
41759 * Availability: https://github.com/jackocnr/intl-tel-input.git
41762 Roo.bootstrap.PhoneInputData = function() {
41765 "Afghanistan (افغانستان)",
41770 "Albania (Shqipëri)",
41775 "Algeria (الجزائر)",
41800 "Antigua and Barbuda",
41810 "Armenia (Հայաստան)",
41826 "Austria (Österreich)",
41831 "Azerbaijan (Azərbaycan)",
41841 "Bahrain (البحرين)",
41846 "Bangladesh (বাংলাদেশ)",
41856 "Belarus (Беларусь)",
41861 "Belgium (België)",
41891 "Bosnia and Herzegovina (Босна и Херцеговина)",
41906 "British Indian Ocean Territory",
41911 "British Virgin Islands",
41921 "Bulgaria (България)",
41931 "Burundi (Uburundi)",
41936 "Cambodia (កម្ពុជា)",
41941 "Cameroon (Cameroun)",
41950 ["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"]
41953 "Cape Verde (Kabu Verdi)",
41958 "Caribbean Netherlands",
41969 "Central African Republic (République centrafricaine)",
41989 "Christmas Island",
41995 "Cocos (Keeling) Islands",
42006 "Comoros (جزر القمر)",
42011 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42016 "Congo (Republic) (Congo-Brazzaville)",
42036 "Croatia (Hrvatska)",
42057 "Czech Republic (Česká republika)",
42062 "Denmark (Danmark)",
42077 "Dominican Republic (República Dominicana)",
42081 ["809", "829", "849"]
42099 "Equatorial Guinea (Guinea Ecuatorial)",
42119 "Falkland Islands (Islas Malvinas)",
42124 "Faroe Islands (Føroyar)",
42145 "French Guiana (Guyane française)",
42150 "French Polynesia (Polynésie française)",
42165 "Georgia (საქართველო)",
42170 "Germany (Deutschland)",
42190 "Greenland (Kalaallit Nunaat)",
42227 "Guinea-Bissau (Guiné Bissau)",
42252 "Hungary (Magyarország)",
42257 "Iceland (Ísland)",
42277 "Iraq (العراق)",
42293 "Israel (ישראל)",
42320 "Jordan (الأردن)",
42325 "Kazakhstan (Казахстан)",
42346 "Kuwait (الكويت)",
42351 "Kyrgyzstan (Кыргызстан)",
42361 "Latvia (Latvija)",
42366 "Lebanon (لبنان)",
42381 "Libya (ليبيا)",
42391 "Lithuania (Lietuva)",
42406 "Macedonia (FYROM) (Македонија)",
42411 "Madagascar (Madagasikara)",
42441 "Marshall Islands",
42451 "Mauritania (موريتانيا)",
42456 "Mauritius (Moris)",
42477 "Moldova (Republica Moldova)",
42487 "Mongolia (Монгол)",
42492 "Montenegro (Crna Gora)",
42502 "Morocco (المغرب)",
42508 "Mozambique (Moçambique)",
42513 "Myanmar (Burma) (မြန်မာ)",
42518 "Namibia (Namibië)",
42533 "Netherlands (Nederland)",
42538 "New Caledonia (Nouvelle-Calédonie)",
42573 "North Korea (조선 민주주의 인민 공화국)",
42578 "Northern Mariana Islands",
42594 "Pakistan (پاکستان)",
42604 "Palestine (فلسطين)",
42614 "Papua New Guinea",
42656 "Réunion (La Réunion)",
42662 "Romania (România)",
42678 "Saint Barthélemy",
42689 "Saint Kitts and Nevis",
42699 "Saint Martin (Saint-Martin (partie française))",
42705 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42710 "Saint Vincent and the Grenadines",
42725 "São Tomé and Príncipe (São Tomé e Príncipe)",
42730 "Saudi Arabia (المملكة العربية السعودية)",
42735 "Senegal (Sénégal)",
42765 "Slovakia (Slovensko)",
42770 "Slovenia (Slovenija)",
42780 "Somalia (Soomaaliya)",
42790 "South Korea (대한민국)",
42795 "South Sudan (جنوب السودان)",
42805 "Sri Lanka (ශ්රී ලංකාව)",
42810 "Sudan (السودان)",
42820 "Svalbard and Jan Mayen",
42831 "Sweden (Sverige)",
42836 "Switzerland (Schweiz)",
42841 "Syria (سوريا)",
42886 "Trinidad and Tobago",
42891 "Tunisia (تونس)",
42896 "Turkey (Türkiye)",
42906 "Turks and Caicos Islands",
42916 "U.S. Virgin Islands",
42926 "Ukraine (Україна)",
42931 "United Arab Emirates (الإمارات العربية المتحدة)",
42953 "Uzbekistan (Oʻzbekiston)",
42963 "Vatican City (Città del Vaticano)",
42974 "Vietnam (Việt Nam)",
42979 "Wallis and Futuna (Wallis-et-Futuna)",
42984 "Western Sahara (الصحراء الغربية)",
42990 "Yemen (اليمن)",
43014 * This script refer to:
43015 * Title: International Telephone Input
43016 * Author: Jack O'Connor
43017 * Code version: v12.1.12
43018 * Availability: https://github.com/jackocnr/intl-tel-input.git
43022 * @class Roo.bootstrap.PhoneInput
43023 * @extends Roo.bootstrap.TriggerField
43024 * An input with International dial-code selection
43026 * @cfg {String} defaultDialCode default '+852'
43027 * @cfg {Array} preferedCountries default []
43030 * Create a new PhoneInput.
43031 * @param {Object} config Configuration options
43034 Roo.bootstrap.PhoneInput = function(config) {
43035 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43038 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43040 listWidth: undefined,
43042 selectedClass: 'active',
43044 invalidClass : "has-warning",
43046 validClass: 'has-success',
43048 allowed: '0123456789',
43053 * @cfg {String} defaultDialCode The default dial code when initializing the input
43055 defaultDialCode: '+852',
43058 * @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
43060 preferedCountries: false,
43062 getAutoCreate : function()
43064 var data = Roo.bootstrap.PhoneInputData();
43065 var align = this.labelAlign || this.parentLabelAlign();
43068 this.allCountries = [];
43069 this.dialCodeMapping = [];
43071 for (var i = 0; i < data.length; i++) {
43073 this.allCountries[i] = {
43077 priority: c[3] || 0,
43078 areaCodes: c[4] || null
43080 this.dialCodeMapping[c[2]] = {
43083 priority: c[3] || 0,
43084 areaCodes: c[4] || null
43096 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43097 maxlength: this.max_length,
43098 cls : 'form-control tel-input',
43099 autocomplete: 'new-password'
43102 var hiddenInput = {
43105 cls: 'hidden-tel-input'
43109 hiddenInput.name = this.name;
43112 if (this.disabled) {
43113 input.disabled = true;
43116 var flag_container = {
43133 cls: this.hasFeedback ? 'has-feedback' : '',
43139 cls: 'dial-code-holder',
43146 cls: 'roo-select2-container input-group',
43153 if (this.fieldLabel.length) {
43156 tooltip: 'This field is required'
43162 cls: 'control-label',
43168 html: this.fieldLabel
43171 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43177 if(this.indicatorpos == 'right') {
43178 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43185 if(align == 'left') {
43193 if(this.labelWidth > 12){
43194 label.style = "width: " + this.labelWidth + 'px';
43196 if(this.labelWidth < 13 && this.labelmd == 0){
43197 this.labelmd = this.labelWidth;
43199 if(this.labellg > 0){
43200 label.cls += ' col-lg-' + this.labellg;
43201 input.cls += ' col-lg-' + (12 - this.labellg);
43203 if(this.labelmd > 0){
43204 label.cls += ' col-md-' + this.labelmd;
43205 container.cls += ' col-md-' + (12 - this.labelmd);
43207 if(this.labelsm > 0){
43208 label.cls += ' col-sm-' + this.labelsm;
43209 container.cls += ' col-sm-' + (12 - this.labelsm);
43211 if(this.labelxs > 0){
43212 label.cls += ' col-xs-' + this.labelxs;
43213 container.cls += ' col-xs-' + (12 - this.labelxs);
43223 var settings = this;
43225 ['xs','sm','md','lg'].map(function(size){
43226 if (settings[size]) {
43227 cfg.cls += ' col-' + size + '-' + settings[size];
43231 this.store = new Roo.data.Store({
43232 proxy : new Roo.data.MemoryProxy({}),
43233 reader : new Roo.data.JsonReader({
43244 'name' : 'dialCode',
43248 'name' : 'priority',
43252 'name' : 'areaCodes',
43259 if(!this.preferedCountries) {
43260 this.preferedCountries = [
43267 var p = this.preferedCountries.reverse();
43270 for (var i = 0; i < p.length; i++) {
43271 for (var j = 0; j < this.allCountries.length; j++) {
43272 if(this.allCountries[j].iso2 == p[i]) {
43273 var t = this.allCountries[j];
43274 this.allCountries.splice(j,1);
43275 this.allCountries.unshift(t);
43281 this.store.proxy.data = {
43283 data: this.allCountries
43289 initEvents : function()
43292 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43294 this.indicator = this.indicatorEl();
43295 this.flag = this.flagEl();
43296 this.dialCodeHolder = this.dialCodeHolderEl();
43298 this.trigger = this.el.select('div.flag-box',true).first();
43299 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43304 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43305 _this.list.setWidth(lw);
43308 this.list.on('mouseover', this.onViewOver, this);
43309 this.list.on('mousemove', this.onViewMove, this);
43310 this.inputEl().on("keyup", this.onKeyUp, this);
43311 this.inputEl().on("keypress", this.onKeyPress, this);
43313 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43315 this.view = new Roo.View(this.list, this.tpl, {
43316 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43319 this.view.on('click', this.onViewClick, this);
43320 this.setValue(this.defaultDialCode);
43323 onTriggerClick : function(e)
43325 Roo.log('trigger click');
43330 if(this.isExpanded()){
43332 this.hasFocus = false;
43334 this.store.load({});
43335 this.hasFocus = true;
43340 isExpanded : function()
43342 return this.list.isVisible();
43345 collapse : function()
43347 if(!this.isExpanded()){
43351 Roo.get(document).un('mousedown', this.collapseIf, this);
43352 Roo.get(document).un('mousewheel', this.collapseIf, this);
43353 this.fireEvent('collapse', this);
43357 expand : function()
43361 if(this.isExpanded() || !this.hasFocus){
43365 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43366 this.list.setWidth(lw);
43369 this.restrictHeight();
43371 Roo.get(document).on('mousedown', this.collapseIf, this);
43372 Roo.get(document).on('mousewheel', this.collapseIf, this);
43374 this.fireEvent('expand', this);
43377 restrictHeight : function()
43379 this.list.alignTo(this.inputEl(), this.listAlign);
43380 this.list.alignTo(this.inputEl(), this.listAlign);
43383 onViewOver : function(e, t)
43385 if(this.inKeyMode){
43388 var item = this.view.findItemFromChild(t);
43391 var index = this.view.indexOf(item);
43392 this.select(index, false);
43397 onViewClick : function(view, doFocus, el, e)
43399 var index = this.view.getSelectedIndexes()[0];
43401 var r = this.store.getAt(index);
43404 this.onSelect(r, index);
43406 if(doFocus !== false && !this.blockFocus){
43407 this.inputEl().focus();
43411 onViewMove : function(e, t)
43413 this.inKeyMode = false;
43416 select : function(index, scrollIntoView)
43418 this.selectedIndex = index;
43419 this.view.select(index);
43420 if(scrollIntoView !== false){
43421 var el = this.view.getNode(index);
43423 this.list.scrollChildIntoView(el, false);
43428 createList : function()
43430 this.list = Roo.get(document.body).createChild({
43432 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43433 style: 'display:none'
43436 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43439 collapseIf : function(e)
43441 var in_combo = e.within(this.el);
43442 var in_list = e.within(this.list);
43443 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43445 if (in_combo || in_list || is_list) {
43451 onSelect : function(record, index)
43453 if(this.fireEvent('beforeselect', this, record, index) !== false){
43455 this.setFlagClass(record.data.iso2);
43456 this.setDialCode(record.data.dialCode);
43457 this.hasFocus = false;
43459 this.fireEvent('select', this, record, index);
43463 flagEl : function()
43465 var flag = this.el.select('div.flag',true).first();
43472 dialCodeHolderEl : function()
43474 var d = this.el.select('input.dial-code-holder',true).first();
43481 setDialCode : function(v)
43483 this.dialCodeHolder.dom.value = '+'+v;
43486 setFlagClass : function(n)
43488 this.flag.dom.className = 'flag '+n;
43491 getValue : function()
43493 var v = this.inputEl().getValue();
43494 if(this.dialCodeHolder) {
43495 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43500 setValue : function(v)
43502 var d = this.getDialCode(v);
43504 //invalid dial code
43505 if(v.length == 0 || !d || d.length == 0) {
43507 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43508 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43514 this.setFlagClass(this.dialCodeMapping[d].iso2);
43515 this.setDialCode(d);
43516 this.inputEl().dom.value = v.replace('+'+d,'');
43517 this.hiddenEl().dom.value = this.getValue();
43522 getDialCode : function(v)
43526 if (v.length == 0) {
43527 return this.dialCodeHolder.dom.value;
43531 if (v.charAt(0) != "+") {
43534 var numericChars = "";
43535 for (var i = 1; i < v.length; i++) {
43536 var c = v.charAt(i);
43539 if (this.dialCodeMapping[numericChars]) {
43540 dialCode = v.substr(1, i);
43542 if (numericChars.length == 4) {
43552 this.setValue(this.defaultDialCode);
43556 hiddenEl : function()
43558 return this.el.select('input.hidden-tel-input',true).first();
43561 // after setting val
43562 onKeyUp : function(e){
43563 this.setValue(this.getValue());
43566 onKeyPress : function(e){
43567 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43574 * @class Roo.bootstrap.MoneyField
43575 * @extends Roo.bootstrap.ComboBox
43576 * Bootstrap MoneyField class
43579 * Create a new MoneyField.
43580 * @param {Object} config Configuration options
43583 Roo.bootstrap.MoneyField = function(config) {
43585 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43589 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43592 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43594 allowDecimals : true,
43596 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43598 decimalSeparator : ".",
43600 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43602 decimalPrecision : 0,
43604 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43606 allowNegative : true,
43608 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43612 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43614 minValue : Number.NEGATIVE_INFINITY,
43616 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43618 maxValue : Number.MAX_VALUE,
43620 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43622 minText : "The minimum value for this field is {0}",
43624 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43626 maxText : "The maximum value for this field is {0}",
43628 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43629 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43631 nanText : "{0} is not a valid number",
43633 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43637 * @cfg {String} defaults currency of the MoneyField
43638 * value should be in lkey
43640 defaultCurrency : false,
43642 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43644 thousandsDelimiter : false,
43646 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43657 getAutoCreate : function()
43659 var align = this.labelAlign || this.parentLabelAlign();
43671 cls : 'form-control roo-money-amount-input',
43672 autocomplete: 'new-password'
43675 var hiddenInput = {
43679 cls: 'hidden-number-input'
43682 if(this.max_length) {
43683 input.maxlength = this.max_length;
43687 hiddenInput.name = this.name;
43690 if (this.disabled) {
43691 input.disabled = true;
43694 var clg = 12 - this.inputlg;
43695 var cmd = 12 - this.inputmd;
43696 var csm = 12 - this.inputsm;
43697 var cxs = 12 - this.inputxs;
43701 cls : 'row roo-money-field',
43705 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43709 cls: 'roo-select2-container input-group',
43713 cls : 'form-control roo-money-currency-input',
43714 autocomplete: 'new-password',
43716 name : this.currencyName
43720 cls : 'input-group-addon',
43734 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43738 cls: this.hasFeedback ? 'has-feedback' : '',
43749 if (this.fieldLabel.length) {
43752 tooltip: 'This field is required'
43758 cls: 'control-label',
43764 html: this.fieldLabel
43767 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43773 if(this.indicatorpos == 'right') {
43774 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43781 if(align == 'left') {
43789 if(this.labelWidth > 12){
43790 label.style = "width: " + this.labelWidth + 'px';
43792 if(this.labelWidth < 13 && this.labelmd == 0){
43793 this.labelmd = this.labelWidth;
43795 if(this.labellg > 0){
43796 label.cls += ' col-lg-' + this.labellg;
43797 input.cls += ' col-lg-' + (12 - this.labellg);
43799 if(this.labelmd > 0){
43800 label.cls += ' col-md-' + this.labelmd;
43801 container.cls += ' col-md-' + (12 - this.labelmd);
43803 if(this.labelsm > 0){
43804 label.cls += ' col-sm-' + this.labelsm;
43805 container.cls += ' col-sm-' + (12 - this.labelsm);
43807 if(this.labelxs > 0){
43808 label.cls += ' col-xs-' + this.labelxs;
43809 container.cls += ' col-xs-' + (12 - this.labelxs);
43820 var settings = this;
43822 ['xs','sm','md','lg'].map(function(size){
43823 if (settings[size]) {
43824 cfg.cls += ' col-' + size + '-' + settings[size];
43831 initEvents : function()
43833 this.indicator = this.indicatorEl();
43835 this.initCurrencyEvent();
43837 this.initNumberEvent();
43840 initCurrencyEvent : function()
43843 throw "can not find store for combo";
43846 this.store = Roo.factory(this.store, Roo.data);
43847 this.store.parent = this;
43851 this.triggerEl = this.el.select('.input-group-addon', true).first();
43853 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43858 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43859 _this.list.setWidth(lw);
43862 this.list.on('mouseover', this.onViewOver, this);
43863 this.list.on('mousemove', this.onViewMove, this);
43864 this.list.on('scroll', this.onViewScroll, this);
43867 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43870 this.view = new Roo.View(this.list, this.tpl, {
43871 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43874 this.view.on('click', this.onViewClick, this);
43876 this.store.on('beforeload', this.onBeforeLoad, this);
43877 this.store.on('load', this.onLoad, this);
43878 this.store.on('loadexception', this.onLoadException, this);
43880 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43881 "up" : function(e){
43882 this.inKeyMode = true;
43886 "down" : function(e){
43887 if(!this.isExpanded()){
43888 this.onTriggerClick();
43890 this.inKeyMode = true;
43895 "enter" : function(e){
43898 if(this.fireEvent("specialkey", this, e)){
43899 this.onViewClick(false);
43905 "esc" : function(e){
43909 "tab" : function(e){
43912 if(this.fireEvent("specialkey", this, e)){
43913 this.onViewClick(false);
43921 doRelay : function(foo, bar, hname){
43922 if(hname == 'down' || this.scope.isExpanded()){
43923 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43931 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43935 initNumberEvent : function(e)
43937 this.inputEl().on("keydown" , this.fireKey, this);
43938 this.inputEl().on("focus", this.onFocus, this);
43939 this.inputEl().on("blur", this.onBlur, this);
43941 this.inputEl().relayEvent('keyup', this);
43943 if(this.indicator){
43944 this.indicator.addClass('invisible');
43947 this.originalValue = this.getValue();
43949 if(this.validationEvent == 'keyup'){
43950 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43951 this.inputEl().on('keyup', this.filterValidation, this);
43953 else if(this.validationEvent !== false){
43954 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43957 if(this.selectOnFocus){
43958 this.on("focus", this.preFocus, this);
43961 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43962 this.inputEl().on("keypress", this.filterKeys, this);
43964 this.inputEl().relayEvent('keypress', this);
43967 var allowed = "0123456789";
43969 if(this.allowDecimals){
43970 allowed += this.decimalSeparator;
43973 if(this.allowNegative){
43977 if(this.thousandsDelimiter) {
43981 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43983 var keyPress = function(e){
43985 var k = e.getKey();
43987 var c = e.getCharCode();
43990 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43991 allowed.indexOf(String.fromCharCode(c)) === -1
43997 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44001 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44006 this.inputEl().on("keypress", keyPress, this);
44010 onTriggerClick : function(e)
44017 this.loadNext = false;
44019 if(this.isExpanded()){
44024 this.hasFocus = true;
44026 if(this.triggerAction == 'all') {
44027 this.doQuery(this.allQuery, true);
44031 this.doQuery(this.getRawValue());
44034 getCurrency : function()
44036 var v = this.currencyEl().getValue();
44041 restrictHeight : function()
44043 this.list.alignTo(this.currencyEl(), this.listAlign);
44044 this.list.alignTo(this.currencyEl(), this.listAlign);
44047 onViewClick : function(view, doFocus, el, e)
44049 var index = this.view.getSelectedIndexes()[0];
44051 var r = this.store.getAt(index);
44054 this.onSelect(r, index);
44058 onSelect : function(record, index){
44060 if(this.fireEvent('beforeselect', this, record, index) !== false){
44062 this.setFromCurrencyData(index > -1 ? record.data : false);
44066 this.fireEvent('select', this, record, index);
44070 setFromCurrencyData : function(o)
44074 this.lastCurrency = o;
44076 if (this.currencyField) {
44077 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44079 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44082 this.lastSelectionText = currency;
44084 //setting default currency
44085 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44086 this.setCurrency(this.defaultCurrency);
44090 this.setCurrency(currency);
44093 setFromData : function(o)
44097 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44099 this.setFromCurrencyData(c);
44104 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44106 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44109 this.setValue(value);
44113 setCurrency : function(v)
44115 this.currencyValue = v;
44118 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44123 setValue : function(v)
44125 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44131 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44133 this.inputEl().dom.value = (v == '') ? '' :
44134 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44136 if(!this.allowZero && v === '0') {
44137 this.hiddenEl().dom.value = '';
44138 this.inputEl().dom.value = '';
44145 getRawValue : function()
44147 var v = this.inputEl().getValue();
44152 getValue : function()
44154 return this.fixPrecision(this.parseValue(this.getRawValue()));
44157 parseValue : function(value)
44159 if(this.thousandsDelimiter) {
44161 r = new RegExp(",", "g");
44162 value = value.replace(r, "");
44165 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44166 return isNaN(value) ? '' : value;
44170 fixPrecision : function(value)
44172 if(this.thousandsDelimiter) {
44174 r = new RegExp(",", "g");
44175 value = value.replace(r, "");
44178 var nan = isNaN(value);
44180 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44181 return nan ? '' : value;
44183 return parseFloat(value).toFixed(this.decimalPrecision);
44186 decimalPrecisionFcn : function(v)
44188 return Math.floor(v);
44191 validateValue : function(value)
44193 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44197 var num = this.parseValue(value);
44200 this.markInvalid(String.format(this.nanText, value));
44204 if(num < this.minValue){
44205 this.markInvalid(String.format(this.minText, this.minValue));
44209 if(num > this.maxValue){
44210 this.markInvalid(String.format(this.maxText, this.maxValue));
44217 validate : function()
44219 if(this.disabled || this.allowBlank){
44224 var currency = this.getCurrency();
44226 if(this.validateValue(this.getRawValue()) && currency.length){
44231 this.markInvalid();
44235 getName: function()
44240 beforeBlur : function()
44246 var v = this.parseValue(this.getRawValue());
44253 onBlur : function()
44257 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44258 //this.el.removeClass(this.focusClass);
44261 this.hasFocus = false;
44263 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44267 var v = this.getValue();
44269 if(String(v) !== String(this.startValue)){
44270 this.fireEvent('change', this, v, this.startValue);
44273 this.fireEvent("blur", this);
44276 inputEl : function()
44278 return this.el.select('.roo-money-amount-input', true).first();
44281 currencyEl : function()
44283 return this.el.select('.roo-money-currency-input', true).first();
44286 hiddenEl : function()
44288 return this.el.select('input.hidden-number-input',true).first();
44292 * @class Roo.bootstrap.BezierSignature
44293 * @extends Roo.bootstrap.Component
44294 * Bootstrap BezierSignature class
44295 * This script refer to:
44296 * Title: Signature Pad
44298 * Availability: https://github.com/szimek/signature_pad
44301 * Create a new BezierSignature
44302 * @param {Object} config The config object
44305 Roo.bootstrap.BezierSignature = function(config){
44306 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44312 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44319 mouse_btn_down: true,
44322 * @cfg {int} canvas height
44324 canvas_height: '200px',
44327 * @cfg {float|function} Radius of a single dot.
44332 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44337 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44342 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44347 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44352 * @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.
44354 bg_color: 'rgba(0, 0, 0, 0)',
44357 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44359 dot_color: 'black',
44362 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44364 velocity_filter_weight: 0.7,
44367 * @cfg {function} Callback when stroke begin.
44372 * @cfg {function} Callback when stroke end.
44376 getAutoCreate : function()
44378 var cls = 'roo-signature column';
44381 cls += ' ' + this.cls;
44391 for(var i = 0; i < col_sizes.length; i++) {
44392 if(this[col_sizes[i]]) {
44393 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44403 cls: 'roo-signature-body',
44407 cls: 'roo-signature-body-canvas',
44408 height: this.canvas_height,
44409 width: this.canvas_width
44416 style: 'display: none'
44424 initEvents: function()
44426 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44428 var canvas = this.canvasEl();
44430 // mouse && touch event swapping...
44431 canvas.dom.style.touchAction = 'none';
44432 canvas.dom.style.msTouchAction = 'none';
44434 this.mouse_btn_down = false;
44435 canvas.on('mousedown', this._handleMouseDown, this);
44436 canvas.on('mousemove', this._handleMouseMove, this);
44437 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44439 if (window.PointerEvent) {
44440 canvas.on('pointerdown', this._handleMouseDown, this);
44441 canvas.on('pointermove', this._handleMouseMove, this);
44442 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44445 if ('ontouchstart' in window) {
44446 canvas.on('touchstart', this._handleTouchStart, this);
44447 canvas.on('touchmove', this._handleTouchMove, this);
44448 canvas.on('touchend', this._handleTouchEnd, this);
44451 Roo.EventManager.onWindowResize(this.resize, this, true);
44453 // file input event
44454 this.fileEl().on('change', this.uploadImage, this);
44461 resize: function(){
44463 var canvas = this.canvasEl().dom;
44464 var ctx = this.canvasElCtx();
44465 var img_data = false;
44467 if(canvas.width > 0) {
44468 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44470 // setting canvas width will clean img data
44473 var style = window.getComputedStyle ?
44474 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44476 var padding_left = parseInt(style.paddingLeft) || 0;
44477 var padding_right = parseInt(style.paddingRight) || 0;
44479 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44482 ctx.putImageData(img_data, 0, 0);
44486 _handleMouseDown: function(e)
44488 if (e.browserEvent.which === 1) {
44489 this.mouse_btn_down = true;
44490 this.strokeBegin(e);
44494 _handleMouseMove: function (e)
44496 if (this.mouse_btn_down) {
44497 this.strokeMoveUpdate(e);
44501 _handleMouseUp: function (e)
44503 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44504 this.mouse_btn_down = false;
44509 _handleTouchStart: function (e) {
44511 e.preventDefault();
44512 if (e.browserEvent.targetTouches.length === 1) {
44513 // var touch = e.browserEvent.changedTouches[0];
44514 // this.strokeBegin(touch);
44516 this.strokeBegin(e); // assume e catching the correct xy...
44520 _handleTouchMove: function (e) {
44521 e.preventDefault();
44522 // var touch = event.targetTouches[0];
44523 // _this._strokeMoveUpdate(touch);
44524 this.strokeMoveUpdate(e);
44527 _handleTouchEnd: function (e) {
44528 var wasCanvasTouched = e.target === this.canvasEl().dom;
44529 if (wasCanvasTouched) {
44530 e.preventDefault();
44531 // var touch = event.changedTouches[0];
44532 // _this._strokeEnd(touch);
44537 reset: function () {
44538 this._lastPoints = [];
44539 this._lastVelocity = 0;
44540 this._lastWidth = (this.min_width + this.max_width) / 2;
44541 this.canvasElCtx().fillStyle = this.dot_color;
44544 strokeMoveUpdate: function(e)
44546 this.strokeUpdate(e);
44548 if (this.throttle) {
44549 this.throttleStroke(this.strokeUpdate, this.throttle);
44552 this.strokeUpdate(e);
44556 strokeBegin: function(e)
44558 var newPointGroup = {
44559 color: this.dot_color,
44563 if (typeof this.onBegin === 'function') {
44567 this.curve_data.push(newPointGroup);
44569 this.strokeUpdate(e);
44572 strokeUpdate: function(e)
44574 var rect = this.canvasEl().dom.getBoundingClientRect();
44575 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44576 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44577 var lastPoints = lastPointGroup.points;
44578 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44579 var isLastPointTooClose = lastPoint
44580 ? point.distanceTo(lastPoint) <= this.min_distance
44582 var color = lastPointGroup.color;
44583 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44584 var curve = this.addPoint(point);
44586 this.drawDot({color: color, point: point});
44589 this.drawCurve({color: color, curve: curve});
44599 strokeEnd: function(e)
44601 this.strokeUpdate(e);
44602 if (typeof this.onEnd === 'function') {
44607 addPoint: function (point) {
44608 var _lastPoints = this._lastPoints;
44609 _lastPoints.push(point);
44610 if (_lastPoints.length > 2) {
44611 if (_lastPoints.length === 3) {
44612 _lastPoints.unshift(_lastPoints[0]);
44614 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44615 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44616 _lastPoints.shift();
44622 calculateCurveWidths: function (startPoint, endPoint) {
44623 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44624 (1 - this.velocity_filter_weight) * this._lastVelocity;
44626 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44629 start: this._lastWidth
44632 this._lastVelocity = velocity;
44633 this._lastWidth = newWidth;
44637 drawDot: function (_a) {
44638 var color = _a.color, point = _a.point;
44639 var ctx = this.canvasElCtx();
44640 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44642 this.drawCurveSegment(point.x, point.y, width);
44644 ctx.fillStyle = color;
44648 drawCurve: function (_a) {
44649 var color = _a.color, curve = _a.curve;
44650 var ctx = this.canvasElCtx();
44651 var widthDelta = curve.endWidth - curve.startWidth;
44652 var drawSteps = Math.floor(curve.length()) * 2;
44654 ctx.fillStyle = color;
44655 for (var i = 0; i < drawSteps; i += 1) {
44656 var t = i / drawSteps;
44662 var x = uuu * curve.startPoint.x;
44663 x += 3 * uu * t * curve.control1.x;
44664 x += 3 * u * tt * curve.control2.x;
44665 x += ttt * curve.endPoint.x;
44666 var y = uuu * curve.startPoint.y;
44667 y += 3 * uu * t * curve.control1.y;
44668 y += 3 * u * tt * curve.control2.y;
44669 y += ttt * curve.endPoint.y;
44670 var width = curve.startWidth + ttt * widthDelta;
44671 this.drawCurveSegment(x, y, width);
44677 drawCurveSegment: function (x, y, width) {
44678 var ctx = this.canvasElCtx();
44680 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44681 this.is_empty = false;
44686 var ctx = this.canvasElCtx();
44687 var canvas = this.canvasEl().dom;
44688 ctx.fillStyle = this.bg_color;
44689 ctx.clearRect(0, 0, canvas.width, canvas.height);
44690 ctx.fillRect(0, 0, canvas.width, canvas.height);
44691 this.curve_data = [];
44693 this.is_empty = true;
44698 return this.el.select('input',true).first();
44701 canvasEl: function()
44703 return this.el.select('canvas',true).first();
44706 canvasElCtx: function()
44708 return this.el.select('canvas',true).first().dom.getContext('2d');
44711 getImage: function(type)
44713 if(this.is_empty) {
44718 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44721 drawFromImage: function(img_src)
44723 var img = new Image();
44725 img.onload = function(){
44726 this.canvasElCtx().drawImage(img, 0, 0);
44731 this.is_empty = false;
44734 selectImage: function()
44736 this.fileEl().dom.click();
44739 uploadImage: function(e)
44741 var reader = new FileReader();
44743 reader.onload = function(e){
44744 var img = new Image();
44745 img.onload = function(){
44747 this.canvasElCtx().drawImage(img, 0, 0);
44749 img.src = e.target.result;
44752 reader.readAsDataURL(e.target.files[0]);
44755 // Bezier Point Constructor
44756 Point: (function () {
44757 function Point(x, y, time) {
44760 this.time = time || Date.now();
44762 Point.prototype.distanceTo = function (start) {
44763 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44765 Point.prototype.equals = function (other) {
44766 return this.x === other.x && this.y === other.y && this.time === other.time;
44768 Point.prototype.velocityFrom = function (start) {
44769 return this.time !== start.time
44770 ? this.distanceTo(start) / (this.time - start.time)
44777 // Bezier Constructor
44778 Bezier: (function () {
44779 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44780 this.startPoint = startPoint;
44781 this.control2 = control2;
44782 this.control1 = control1;
44783 this.endPoint = endPoint;
44784 this.startWidth = startWidth;
44785 this.endWidth = endWidth;
44787 Bezier.fromPoints = function (points, widths, scope) {
44788 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44789 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44790 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44792 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44793 var dx1 = s1.x - s2.x;
44794 var dy1 = s1.y - s2.y;
44795 var dx2 = s2.x - s3.x;
44796 var dy2 = s2.y - s3.y;
44797 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44798 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44799 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44800 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44801 var dxm = m1.x - m2.x;
44802 var dym = m1.y - m2.y;
44803 var k = l2 / (l1 + l2);
44804 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44805 var tx = s2.x - cm.x;
44806 var ty = s2.y - cm.y;
44808 c1: new scope.Point(m1.x + tx, m1.y + ty),
44809 c2: new scope.Point(m2.x + tx, m2.y + ty)
44812 Bezier.prototype.length = function () {
44817 for (var i = 0; i <= steps; i += 1) {
44819 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44820 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44822 var xdiff = cx - px;
44823 var ydiff = cy - py;
44824 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44831 Bezier.prototype.point = function (t, start, c1, c2, end) {
44832 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44833 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44834 + (3.0 * c2 * (1.0 - t) * t * t)
44835 + (end * t * t * t);
44840 throttleStroke: function(fn, wait) {
44841 if (wait === void 0) { wait = 250; }
44843 var timeout = null;
44847 var later = function () {
44848 previous = Date.now();
44850 result = fn.apply(storedContext, storedArgs);
44852 storedContext = null;
44856 return function wrapper() {
44858 for (var _i = 0; _i < arguments.length; _i++) {
44859 args[_i] = arguments[_i];
44861 var now = Date.now();
44862 var remaining = wait - (now - previous);
44863 storedContext = this;
44865 if (remaining <= 0 || remaining > wait) {
44867 clearTimeout(timeout);
44871 result = fn.apply(storedContext, storedArgs);
44873 storedContext = null;
44877 else if (!timeout) {
44878 timeout = window.setTimeout(later, remaining);