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){
3592 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3593 if (this.registerMenu && this.type != 'treeview') {
3594 Roo.bootstrap.MenuMgr.register(this);
3601 * Fires before this menu is displayed (return false to block)
3602 * @param {Roo.menu.Menu} this
3607 * Fires before this menu is hidden (return false to block)
3608 * @param {Roo.menu.Menu} this
3613 * Fires after this menu is displayed
3614 * @param {Roo.menu.Menu} this
3619 * Fires after this menu is hidden
3620 * @param {Roo.menu.Menu} this
3625 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3626 * @param {Roo.menu.Menu} this
3627 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3628 * @param {Roo.EventObject} e
3633 * Fires when the mouse is hovering over this menu
3634 * @param {Roo.menu.Menu} this
3635 * @param {Roo.EventObject} e
3636 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3641 * Fires when the mouse exits this menu
3642 * @param {Roo.menu.Menu} this
3643 * @param {Roo.EventObject} e
3644 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3649 * Fires when a menu item contained in this menu is clicked
3650 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3651 * @param {Roo.EventObject} e
3655 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3658 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3662 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3665 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3667 registerMenu : true,
3669 menuItems :false, // stores the menu items..
3679 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3681 hideTrigger : false,
3686 getChildContainer : function() {
3690 getAutoCreate : function(){
3692 //if (['right'].indexOf(this.align)!==-1) {
3693 // cfg.cn[1].cls += ' pull-right'
3698 cls : 'dropdown-menu shadow' ,
3699 style : 'z-index:1000'
3703 if (this.type === 'submenu') {
3704 cfg.cls = 'submenu active';
3706 if (this.type === 'treeview') {
3707 cfg.cls = 'treeview-menu';
3712 initEvents : function() {
3714 // Roo.log("ADD event");
3715 // Roo.log(this.triggerEl.dom);
3717 this.triggerEl.on('click', this.onTriggerClick, this);
3719 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3721 if (!this.hideTrigger) {
3722 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3723 // dropdown toggle on the 'a' in BS4?
3724 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3726 this.triggerEl.addClass('dropdown-toggle');
3730 this.el.on('touchstart' , this.onTouch, this);
3732 this.el.on('click' , this.onClick, this);
3734 this.el.on("mouseover", this.onMouseOver, this);
3735 this.el.on("mouseout", this.onMouseOut, this);
3739 findTargetItem : function(e)
3741 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3745 //Roo.log(t); Roo.log(t.id);
3747 //Roo.log(this.menuitems);
3748 return this.menuitems.get(t.id);
3750 //return this.items.get(t.menuItemId);
3756 onTouch : function(e)
3758 Roo.log("menu.onTouch");
3759 //e.stopEvent(); this make the user popdown broken
3763 onClick : function(e)
3765 Roo.log("menu.onClick");
3767 var t = this.findTargetItem(e);
3768 if(!t || t.isContainer){
3773 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3774 if(t == this.activeItem && t.shouldDeactivate(e)){
3775 this.activeItem.deactivate();
3776 delete this.activeItem;
3780 this.setActiveItem(t, true);
3788 Roo.log('pass click event');
3792 this.fireEvent("click", this, t, e);
3796 if(!t.href.length || t.href == '#'){
3797 (function() { _this.hide(); }).defer(100);
3802 onMouseOver : function(e){
3803 var t = this.findTargetItem(e);
3806 // if(t.canActivate && !t.disabled){
3807 // this.setActiveItem(t, true);
3811 this.fireEvent("mouseover", this, e, t);
3813 isVisible : function(){
3814 return !this.hidden;
3816 onMouseOut : function(e){
3817 var t = this.findTargetItem(e);
3820 // if(t == this.activeItem && t.shouldDeactivate(e)){
3821 // this.activeItem.deactivate();
3822 // delete this.activeItem;
3825 this.fireEvent("mouseout", this, e, t);
3830 * Displays this menu relative to another element
3831 * @param {String/HTMLElement/Roo.Element} element The element to align to
3832 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3833 * the element (defaults to this.defaultAlign)
3834 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3836 show : function(el, pos, parentMenu)
3838 if (false === this.fireEvent("beforeshow", this)) {
3839 Roo.log("show canceled");
3842 this.parentMenu = parentMenu;
3846 this.el.addClass('show'); // show otherwise we do not know how big we are..
3848 var xy = this.el.getAlignToXY(el, pos);
3850 // bl-tl << left align below
3851 // tl-bl << left align
3853 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3854 // if it goes to far to the right.. -> align left.
3855 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3858 // was left align - go right?
3859 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3862 // goes down the bottom
3863 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3865 var a = this.align.replace('?', '').split('-');
3866 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3870 this.showAt( xy , parentMenu, false);
3873 * Displays this menu at a specific xy position
3874 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3875 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3877 showAt : function(xy, parentMenu, /* private: */_e){
3878 this.parentMenu = parentMenu;
3883 this.fireEvent("beforeshow", this);
3884 //xy = this.el.adjustForConstraints(xy);
3888 this.hideMenuItems();
3889 this.hidden = false;
3890 this.triggerEl.addClass('open');
3891 this.el.addClass('show');
3895 // reassign x when hitting right
3897 // reassign y when hitting bottom
3899 // but the list may align on trigger left or trigger top... should it be a properity?
3901 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3906 this.fireEvent("show", this);
3912 this.doFocus.defer(50, this);
3916 doFocus : function(){
3918 this.focusEl.focus();
3923 * Hides this menu and optionally all parent menus
3924 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3926 hide : function(deep)
3928 if (false === this.fireEvent("beforehide", this)) {
3929 Roo.log("hide canceled");
3932 this.hideMenuItems();
3933 if(this.el && this.isVisible()){
3935 if(this.activeItem){
3936 this.activeItem.deactivate();
3937 this.activeItem = null;
3939 this.triggerEl.removeClass('open');;
3940 this.el.removeClass('show');
3942 this.fireEvent("hide", this);
3944 if(deep === true && this.parentMenu){
3945 this.parentMenu.hide(true);
3949 onTriggerClick : function(e)
3951 Roo.log('trigger click');
3953 var target = e.getTarget();
3955 Roo.log(target.nodeName.toLowerCase());
3957 if(target.nodeName.toLowerCase() === 'i'){
3963 onTriggerPress : function(e)
3965 Roo.log('trigger press');
3966 //Roo.log(e.getTarget());
3967 // Roo.log(this.triggerEl.dom);
3969 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3970 var pel = Roo.get(e.getTarget());
3971 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3972 Roo.log('is treeview or dropdown?');
3976 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3980 if (this.isVisible()) {
3986 this.show(this.triggerEl, this.align, false);
3989 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3996 hideMenuItems : function()
3998 Roo.log("hide Menu Items");
4003 this.el.select('.open',true).each(function(aa) {
4005 aa.removeClass('open');
4009 addxtypeChild : function (tree, cntr) {
4010 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4012 this.menuitems.add(comp);
4024 this.getEl().dom.innerHTML = '';
4025 this.menuitems.clear();
4039 * @class Roo.bootstrap.MenuItem
4040 * @extends Roo.bootstrap.Component
4041 * Bootstrap MenuItem class
4042 * @cfg {String} html the menu label
4043 * @cfg {String} href the link
4044 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4045 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4046 * @cfg {Boolean} active used on sidebars to highlight active itesm
4047 * @cfg {String} fa favicon to show on left of menu item.
4048 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4052 * Create a new MenuItem
4053 * @param {Object} config The config object
4057 Roo.bootstrap.MenuItem = function(config){
4058 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4063 * The raw click event for the entire grid.
4064 * @param {Roo.bootstrap.MenuItem} this
4065 * @param {Roo.EventObject} e
4071 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4075 preventDefault: false,
4076 isContainer : false,
4080 getAutoCreate : function(){
4082 if(this.isContainer){
4085 cls: 'dropdown-menu-item '
4095 cls : 'dropdown-item',
4100 if (this.fa !== false) {
4103 cls : 'fa fa-' + this.fa
4112 cls: 'dropdown-menu-item',
4115 if (this.parent().type == 'treeview') {
4116 cfg.cls = 'treeview-menu';
4119 cfg.cls += ' active';
4124 anc.href = this.href || cfg.cn[0].href ;
4125 ctag.html = this.html || cfg.cn[0].html ;
4129 initEvents: function()
4131 if (this.parent().type == 'treeview') {
4132 this.el.select('a').on('click', this.onClick, this);
4136 this.menu.parentType = this.xtype;
4137 this.menu.triggerEl = this.el;
4138 this.menu = this.addxtype(Roo.apply({}, this.menu));
4142 onClick : function(e)
4144 Roo.log('item on click ');
4146 if(this.preventDefault){
4149 //this.parent().hideMenuItems();
4151 this.fireEvent('click', this, e);
4170 * @class Roo.bootstrap.MenuSeparator
4171 * @extends Roo.bootstrap.Component
4172 * Bootstrap MenuSeparator class
4175 * Create a new MenuItem
4176 * @param {Object} config The config object
4180 Roo.bootstrap.MenuSeparator = function(config){
4181 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4184 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4186 getAutoCreate : function(){
4205 * @class Roo.bootstrap.Modal
4206 * @extends Roo.bootstrap.Component
4207 * Bootstrap Modal class
4208 * @cfg {String} title Title of dialog
4209 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4210 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4211 * @cfg {Boolean} specificTitle default false
4212 * @cfg {Array} buttons Array of buttons or standard button set..
4213 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4214 * @cfg {Boolean} animate default true
4215 * @cfg {Boolean} allow_close default true
4216 * @cfg {Boolean} fitwindow default false
4217 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4218 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4219 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4220 * @cfg {String} size (sm|lg|xl) default empty
4221 * @cfg {Number} max_width set the max width of modal
4222 * @cfg {Boolean} editableTitle can the title be edited
4227 * Create a new Modal Dialog
4228 * @param {Object} config The config object
4231 Roo.bootstrap.Modal = function(config){
4232 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4237 * The raw btnclick event for the button
4238 * @param {Roo.EventObject} e
4243 * Fire when dialog resize
4244 * @param {Roo.bootstrap.Modal} this
4245 * @param {Roo.EventObject} e
4249 * @event titlechanged
4250 * Fire when the editable title has been changed
4251 * @param {Roo.bootstrap.Modal} this
4252 * @param {Roo.EventObject} value
4254 "titlechanged" : true
4257 this.buttons = this.buttons || [];
4260 this.tmpl = Roo.factory(this.tmpl);
4265 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4267 title : 'test dialog',
4277 specificTitle: false,
4279 buttonPosition: 'right',
4301 editableTitle : false,
4303 onRender : function(ct, position)
4305 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4308 var cfg = Roo.apply({}, this.getAutoCreate());
4311 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4313 //if (!cfg.name.length) {
4317 cfg.cls += ' ' + this.cls;
4320 cfg.style = this.style;
4322 this.el = Roo.get(document.body).createChild(cfg, position);
4324 //var type = this.el.dom.type;
4327 if(this.tabIndex !== undefined){
4328 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4331 this.dialogEl = this.el.select('.modal-dialog',true).first();
4332 this.bodyEl = this.el.select('.modal-body',true).first();
4333 this.closeEl = this.el.select('.modal-header .close', true).first();
4334 this.headerEl = this.el.select('.modal-header',true).first();
4335 this.titleEl = this.el.select('.modal-title',true).first();
4336 this.footerEl = this.el.select('.modal-footer',true).first();
4338 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4340 //this.el.addClass("x-dlg-modal");
4342 if (this.buttons.length) {
4343 Roo.each(this.buttons, function(bb) {
4344 var b = Roo.apply({}, bb);
4345 b.xns = b.xns || Roo.bootstrap;
4346 b.xtype = b.xtype || 'Button';
4347 if (typeof(b.listeners) == 'undefined') {
4348 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4351 var btn = Roo.factory(b);
4353 btn.render(this.getButtonContainer());
4357 // render the children.
4360 if(typeof(this.items) != 'undefined'){
4361 var items = this.items;
4364 for(var i =0;i < items.length;i++) {
4365 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4369 this.items = nitems;
4371 // where are these used - they used to be body/close/footer
4375 //this.el.addClass([this.fieldClass, this.cls]);
4379 getAutoCreate : function()
4381 // we will default to modal-body-overflow - might need to remove or make optional later.
4383 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4384 html : this.html || ''
4389 cls : 'modal-title',
4393 if(this.specificTitle){ // WTF is this?
4398 if (this.allow_close && Roo.bootstrap.version == 3) {
4408 if (this.editableTitle) {
4410 cls: 'form-control roo-editable-title d-none',
4416 if (this.allow_close && Roo.bootstrap.version == 4) {
4426 if(this.size.length){
4427 size = 'modal-' + this.size;
4430 var footer = Roo.bootstrap.version == 3 ?
4432 cls : 'modal-footer',
4436 cls: 'btn-' + this.buttonPosition
4441 { // BS4 uses mr-auto on left buttons....
4442 cls : 'modal-footer'
4453 cls: "modal-dialog " + size,
4456 cls : "modal-content",
4459 cls : 'modal-header',
4474 modal.cls += ' fade';
4480 getChildContainer : function() {
4485 getButtonContainer : function() {
4487 return Roo.bootstrap.version == 4 ?
4488 this.el.select('.modal-footer',true).first()
4489 : this.el.select('.modal-footer div',true).first();
4492 initEvents : function()
4494 if (this.allow_close) {
4495 this.closeEl.on('click', this.hide, this);
4497 Roo.EventManager.onWindowResize(this.resize, this, true);
4498 if (this.editableTitle) {
4499 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4500 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4501 this.headerEditEl.on('keyup', function(e) {
4502 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4503 this.toggleHeaderInput(false)
4506 this.headerEditEl.on('blur', function(e) {
4507 this.toggleHeaderInput(false)
4516 this.maskEl.setSize(
4517 Roo.lib.Dom.getViewWidth(true),
4518 Roo.lib.Dom.getViewHeight(true)
4521 if (this.fitwindow) {
4523 this.dialogEl.setStyle( { 'max-width' : '100%' });
4525 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4526 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4531 if(this.max_width !== 0) {
4533 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4536 this.setSize(w, this.height);
4540 if(this.max_height) {
4541 this.setSize(w,Math.min(
4543 Roo.lib.Dom.getViewportHeight(true) - 60
4549 if(!this.fit_content) {
4550 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4554 this.setSize(w, Math.min(
4556 this.headerEl.getHeight() +
4557 this.footerEl.getHeight() +
4558 this.getChildHeight(this.bodyEl.dom.childNodes),
4559 Roo.lib.Dom.getViewportHeight(true) - 60)
4565 setSize : function(w,h)
4576 if (!this.rendered) {
4579 this.toggleHeaderInput(false);
4580 //this.el.setStyle('display', 'block');
4581 this.el.removeClass('hideing');
4582 this.el.dom.style.display='block';
4584 Roo.get(document.body).addClass('modal-open');
4586 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4589 this.el.addClass('show');
4590 this.el.addClass('in');
4593 this.el.addClass('show');
4594 this.el.addClass('in');
4597 // not sure how we can show data in here..
4599 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4602 Roo.get(document.body).addClass("x-body-masked");
4604 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4605 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4606 this.maskEl.dom.style.display = 'block';
4607 this.maskEl.addClass('show');
4612 this.fireEvent('show', this);
4614 // set zindex here - otherwise it appears to be ignored...
4615 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4618 this.items.forEach( function(e) {
4619 e.layout ? e.layout() : false;
4627 if(this.fireEvent("beforehide", this) !== false){
4629 this.maskEl.removeClass('show');
4631 this.maskEl.dom.style.display = '';
4632 Roo.get(document.body).removeClass("x-body-masked");
4633 this.el.removeClass('in');
4634 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4636 if(this.animate){ // why
4637 this.el.addClass('hideing');
4638 this.el.removeClass('show');
4640 if (!this.el.hasClass('hideing')) {
4641 return; // it's been shown again...
4644 this.el.dom.style.display='';
4646 Roo.get(document.body).removeClass('modal-open');
4647 this.el.removeClass('hideing');
4651 this.el.removeClass('show');
4652 this.el.dom.style.display='';
4653 Roo.get(document.body).removeClass('modal-open');
4656 this.fireEvent('hide', this);
4659 isVisible : function()
4662 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4666 addButton : function(str, cb)
4670 var b = Roo.apply({}, { html : str } );
4671 b.xns = b.xns || Roo.bootstrap;
4672 b.xtype = b.xtype || 'Button';
4673 if (typeof(b.listeners) == 'undefined') {
4674 b.listeners = { click : cb.createDelegate(this) };
4677 var btn = Roo.factory(b);
4679 btn.render(this.getButtonContainer());
4685 setDefaultButton : function(btn)
4687 //this.el.select('.modal-footer').()
4690 resizeTo: function(w,h)
4692 this.dialogEl.setWidth(w);
4694 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4696 this.bodyEl.setHeight(h - diff);
4698 this.fireEvent('resize', this);
4701 setContentSize : function(w, h)
4705 onButtonClick: function(btn,e)
4708 this.fireEvent('btnclick', btn.name, e);
4711 * Set the title of the Dialog
4712 * @param {String} str new Title
4714 setTitle: function(str) {
4715 this.titleEl.dom.innerHTML = str;
4719 * Set the body of the Dialog
4720 * @param {String} str new Title
4722 setBody: function(str) {
4723 this.bodyEl.dom.innerHTML = str;
4726 * Set the body of the Dialog using the template
4727 * @param {Obj} data - apply this data to the template and replace the body contents.
4729 applyBody: function(obj)
4732 Roo.log("Error - using apply Body without a template");
4735 this.tmpl.overwrite(this.bodyEl, obj);
4738 getChildHeight : function(child_nodes)
4742 child_nodes.length == 0
4747 var child_height = 0;
4749 for(var i = 0; i < child_nodes.length; i++) {
4752 * for modal with tabs...
4753 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4755 var layout_childs = child_nodes[i].childNodes;
4757 for(var j = 0; j < layout_childs.length; j++) {
4759 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4761 var layout_body_childs = layout_childs[j].childNodes;
4763 for(var k = 0; k < layout_body_childs.length; k++) {
4765 if(layout_body_childs[k].classList.contains('navbar')) {
4766 child_height += layout_body_childs[k].offsetHeight;
4770 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4772 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4774 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4776 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4777 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4792 child_height += child_nodes[i].offsetHeight;
4793 // Roo.log(child_nodes[i].offsetHeight);
4796 return child_height;
4798 toggleHeaderInput : function(is_edit)
4800 if (!this.editableTitle) {
4801 return; // not editable.
4803 if (is_edit && this.is_header_editing) {
4804 return; // already editing..
4808 this.headerEditEl.dom.value = this.title;
4809 this.headerEditEl.removeClass('d-none');
4810 this.headerEditEl.dom.focus();
4811 this.titleEl.addClass('d-none');
4813 this.is_header_editing = true;
4816 // flip back to not editing.
4817 this.title = this.headerEditEl.dom.value;
4818 this.headerEditEl.addClass('d-none');
4819 this.titleEl.removeClass('d-none');
4820 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4821 this.is_header_editing = false;
4822 this.fireEvent('titlechanged', this, this.title);
4831 Roo.apply(Roo.bootstrap.Modal, {
4833 * Button config that displays a single OK button
4842 * Button config that displays Yes and No buttons
4858 * Button config that displays OK and Cancel buttons
4873 * Button config that displays Yes, No and Cancel buttons
4898 * messagebox - can be used as a replace
4902 * @class Roo.MessageBox
4903 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4907 Roo.Msg.alert('Status', 'Changes saved successfully.');
4909 // Prompt for user data:
4910 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4912 // process text value...
4916 // Show a dialog using config options:
4918 title:'Save Changes?',
4919 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4920 buttons: Roo.Msg.YESNOCANCEL,
4927 Roo.bootstrap.MessageBox = function(){
4928 var dlg, opt, mask, waitTimer;
4929 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4930 var buttons, activeTextEl, bwidth;
4934 var handleButton = function(button){
4936 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4940 var handleHide = function(){
4942 dlg.el.removeClass(opt.cls);
4945 // Roo.TaskMgr.stop(waitTimer);
4946 // waitTimer = null;
4951 var updateButtons = function(b){
4954 buttons["ok"].hide();
4955 buttons["cancel"].hide();
4956 buttons["yes"].hide();
4957 buttons["no"].hide();
4958 dlg.footerEl.hide();
4962 dlg.footerEl.show();
4963 for(var k in buttons){
4964 if(typeof buttons[k] != "function"){
4967 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4968 width += buttons[k].el.getWidth()+15;
4978 var handleEsc = function(d, k, e){
4979 if(opt && opt.closable !== false){
4989 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4990 * @return {Roo.BasicDialog} The BasicDialog element
4992 getDialog : function(){
4994 dlg = new Roo.bootstrap.Modal( {
4997 //constraintoviewport:false,
4999 //collapsible : false,
5004 //buttonAlign:"center",
5005 closeClick : function(){
5006 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5009 handleButton("cancel");
5014 dlg.on("hide", handleHide);
5016 //dlg.addKeyListener(27, handleEsc);
5018 this.buttons = buttons;
5019 var bt = this.buttonText;
5020 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5021 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5022 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5023 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5025 bodyEl = dlg.bodyEl.createChild({
5027 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5028 '<textarea class="roo-mb-textarea"></textarea>' +
5029 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5031 msgEl = bodyEl.dom.firstChild;
5032 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5033 textboxEl.enableDisplayMode();
5034 textboxEl.addKeyListener([10,13], function(){
5035 if(dlg.isVisible() && opt && opt.buttons){
5038 }else if(opt.buttons.yes){
5039 handleButton("yes");
5043 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5044 textareaEl.enableDisplayMode();
5045 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5046 progressEl.enableDisplayMode();
5048 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5049 var pf = progressEl.dom.firstChild;
5051 pp = Roo.get(pf.firstChild);
5052 pp.setHeight(pf.offsetHeight);
5060 * Updates the message box body text
5061 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5062 * the XHTML-compliant non-breaking space character '&#160;')
5063 * @return {Roo.MessageBox} This message box
5065 updateText : function(text)
5067 if(!dlg.isVisible() && !opt.width){
5068 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5069 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5071 msgEl.innerHTML = text || ' ';
5073 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5074 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5076 Math.min(opt.width || cw , this.maxWidth),
5077 Math.max(opt.minWidth || this.minWidth, bwidth)
5080 activeTextEl.setWidth(w);
5082 if(dlg.isVisible()){
5083 dlg.fixedcenter = false;
5085 // to big, make it scroll. = But as usual stupid IE does not support
5088 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5089 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5090 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5092 bodyEl.dom.style.height = '';
5093 bodyEl.dom.style.overflowY = '';
5096 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5098 bodyEl.dom.style.overflowX = '';
5101 dlg.setContentSize(w, bodyEl.getHeight());
5102 if(dlg.isVisible()){
5103 dlg.fixedcenter = true;
5109 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5110 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5111 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5112 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5113 * @return {Roo.MessageBox} This message box
5115 updateProgress : function(value, text){
5117 this.updateText(text);
5120 if (pp) { // weird bug on my firefox - for some reason this is not defined
5121 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5122 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5128 * Returns true if the message box is currently displayed
5129 * @return {Boolean} True if the message box is visible, else false
5131 isVisible : function(){
5132 return dlg && dlg.isVisible();
5136 * Hides the message box if it is displayed
5139 if(this.isVisible()){
5145 * Displays a new message box, or reinitializes an existing message box, based on the config options
5146 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5147 * The following config object properties are supported:
5149 Property Type Description
5150 ---------- --------------- ------------------------------------------------------------------------------------
5151 animEl String/Element An id or Element from which the message box should animate as it opens and
5152 closes (defaults to undefined)
5153 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5154 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5155 closable Boolean False to hide the top-right close button (defaults to true). Note that
5156 progress and wait dialogs will ignore this property and always hide the
5157 close button as they can only be closed programmatically.
5158 cls String A custom CSS class to apply to the message box element
5159 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5160 displayed (defaults to 75)
5161 fn Function A callback function to execute after closing the dialog. The arguments to the
5162 function will be btn (the name of the button that was clicked, if applicable,
5163 e.g. "ok"), and text (the value of the active text field, if applicable).
5164 Progress and wait dialogs will ignore this option since they do not respond to
5165 user actions and can only be closed programmatically, so any required function
5166 should be called by the same code after it closes the dialog.
5167 icon String A CSS class that provides a background image to be used as an icon for
5168 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5169 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5170 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5171 modal Boolean False to allow user interaction with the page while the message box is
5172 displayed (defaults to true)
5173 msg String A string that will replace the existing message box body text (defaults
5174 to the XHTML-compliant non-breaking space character ' ')
5175 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5176 progress Boolean True to display a progress bar (defaults to false)
5177 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5178 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5179 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5180 title String The title text
5181 value String The string value to set into the active textbox element if displayed
5182 wait Boolean True to display a progress bar (defaults to false)
5183 width Number The width of the dialog in pixels
5190 msg: 'Please enter your address:',
5192 buttons: Roo.MessageBox.OKCANCEL,
5195 animEl: 'addAddressBtn'
5198 * @param {Object} config Configuration options
5199 * @return {Roo.MessageBox} This message box
5201 show : function(options)
5204 // this causes nightmares if you show one dialog after another
5205 // especially on callbacks..
5207 if(this.isVisible()){
5210 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5211 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5212 Roo.log("New Dialog Message:" + options.msg )
5213 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5214 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5217 var d = this.getDialog();
5219 d.setTitle(opt.title || " ");
5220 d.closeEl.setDisplayed(opt.closable !== false);
5221 activeTextEl = textboxEl;
5222 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5227 textareaEl.setHeight(typeof opt.multiline == "number" ?
5228 opt.multiline : this.defaultTextHeight);
5229 activeTextEl = textareaEl;
5238 progressEl.setDisplayed(opt.progress === true);
5240 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5242 this.updateProgress(0);
5243 activeTextEl.dom.value = opt.value || "";
5245 dlg.setDefaultButton(activeTextEl);
5247 var bs = opt.buttons;
5251 }else if(bs && bs.yes){
5252 db = buttons["yes"];
5254 dlg.setDefaultButton(db);
5256 bwidth = updateButtons(opt.buttons);
5257 this.updateText(opt.msg);
5259 d.el.addClass(opt.cls);
5261 d.proxyDrag = opt.proxyDrag === true;
5262 d.modal = opt.modal !== false;
5263 d.mask = opt.modal !== false ? mask : false;
5265 // force it to the end of the z-index stack so it gets a cursor in FF
5266 document.body.appendChild(dlg.el.dom);
5267 d.animateTarget = null;
5268 d.show(options.animEl);
5274 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5275 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5276 * and closing the message box when the process is complete.
5277 * @param {String} title The title bar text
5278 * @param {String} msg The message box body text
5279 * @return {Roo.MessageBox} This message box
5281 progress : function(title, msg){
5288 minWidth: this.minProgressWidth,
5295 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5296 * If a callback function is passed it will be called after the user clicks the button, and the
5297 * id of the button that was clicked will be passed as the only parameter to the callback
5298 * (could also be the top-right close button).
5299 * @param {String} title The title bar text
5300 * @param {String} msg The message box body text
5301 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5302 * @param {Object} scope (optional) The scope of the callback function
5303 * @return {Roo.MessageBox} This message box
5305 alert : function(title, msg, fn, scope)
5320 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5321 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5322 * You are responsible for closing the message box when the process is complete.
5323 * @param {String} msg The message box body text
5324 * @param {String} title (optional) The title bar text
5325 * @return {Roo.MessageBox} This message box
5327 wait : function(msg, title){
5338 waitTimer = Roo.TaskMgr.start({
5340 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5348 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5349 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5350 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5351 * @param {String} title The title bar text
5352 * @param {String} msg The message box body text
5353 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5354 * @param {Object} scope (optional) The scope of the callback function
5355 * @return {Roo.MessageBox} This message box
5357 confirm : function(title, msg, fn, scope){
5361 buttons: this.YESNO,
5370 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5371 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5372 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5373 * (could also be the top-right close button) and the text that was entered will be passed as the two
5374 * parameters to the callback.
5375 * @param {String} title The title bar text
5376 * @param {String} msg The message box body text
5377 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5378 * @param {Object} scope (optional) The scope of the callback function
5379 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5380 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5381 * @return {Roo.MessageBox} This message box
5383 prompt : function(title, msg, fn, scope, multiline){
5387 buttons: this.OKCANCEL,
5392 multiline: multiline,
5399 * Button config that displays a single OK button
5404 * Button config that displays Yes and No buttons
5407 YESNO : {yes:true, no:true},
5409 * Button config that displays OK and Cancel buttons
5412 OKCANCEL : {ok:true, cancel:true},
5414 * Button config that displays Yes, No and Cancel buttons
5417 YESNOCANCEL : {yes:true, no:true, cancel:true},
5420 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5423 defaultTextHeight : 75,
5425 * The maximum width in pixels of the message box (defaults to 600)
5430 * The minimum width in pixels of the message box (defaults to 100)
5435 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5436 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5439 minProgressWidth : 250,
5441 * An object containing the default button text strings that can be overriden for localized language support.
5442 * Supported properties are: ok, cancel, yes and no.
5443 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5456 * Shorthand for {@link Roo.MessageBox}
5458 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5459 Roo.Msg = Roo.Msg || Roo.MessageBox;
5468 * @class Roo.bootstrap.Navbar
5469 * @extends Roo.bootstrap.Component
5470 * Bootstrap Navbar class
5473 * Create a new Navbar
5474 * @param {Object} config The config object
5478 Roo.bootstrap.Navbar = function(config){
5479 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5483 * @event beforetoggle
5484 * Fire before toggle the menu
5485 * @param {Roo.EventObject} e
5487 "beforetoggle" : true
5491 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5500 getAutoCreate : function(){
5503 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5507 initEvents :function ()
5509 //Roo.log(this.el.select('.navbar-toggle',true));
5510 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5517 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5519 var size = this.el.getSize();
5520 this.maskEl.setSize(size.width, size.height);
5521 this.maskEl.enableDisplayMode("block");
5530 getChildContainer : function()
5532 if (this.el && this.el.select('.collapse').getCount()) {
5533 return this.el.select('.collapse',true).first();
5548 onToggle : function()
5551 if(this.fireEvent('beforetoggle', this) === false){
5554 var ce = this.el.select('.navbar-collapse',true).first();
5556 if (!ce.hasClass('show')) {
5566 * Expand the navbar pulldown
5568 expand : function ()
5571 var ce = this.el.select('.navbar-collapse',true).first();
5572 if (ce.hasClass('collapsing')) {
5575 ce.dom.style.height = '';
5577 ce.addClass('in'); // old...
5578 ce.removeClass('collapse');
5579 ce.addClass('show');
5580 var h = ce.getHeight();
5582 ce.removeClass('show');
5583 // at this point we should be able to see it..
5584 ce.addClass('collapsing');
5586 ce.setHeight(0); // resize it ...
5587 ce.on('transitionend', function() {
5588 //Roo.log('done transition');
5589 ce.removeClass('collapsing');
5590 ce.addClass('show');
5591 ce.removeClass('collapse');
5593 ce.dom.style.height = '';
5594 }, this, { single: true} );
5596 ce.dom.scrollTop = 0;
5599 * Collapse the navbar pulldown
5601 collapse : function()
5603 var ce = this.el.select('.navbar-collapse',true).first();
5605 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5606 // it's collapsed or collapsing..
5609 ce.removeClass('in'); // old...
5610 ce.setHeight(ce.getHeight());
5611 ce.removeClass('show');
5612 ce.addClass('collapsing');
5614 ce.on('transitionend', function() {
5615 ce.dom.style.height = '';
5616 ce.removeClass('collapsing');
5617 ce.addClass('collapse');
5618 }, this, { single: true} );
5638 * @class Roo.bootstrap.NavSimplebar
5639 * @extends Roo.bootstrap.Navbar
5640 * Bootstrap Sidebar class
5642 * @cfg {Boolean} inverse is inverted color
5644 * @cfg {String} type (nav | pills | tabs)
5645 * @cfg {Boolean} arrangement stacked | justified
5646 * @cfg {String} align (left | right) alignment
5648 * @cfg {Boolean} main (true|false) main nav bar? default false
5649 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5651 * @cfg {String} tag (header|footer|nav|div) default is nav
5653 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5657 * Create a new Sidebar
5658 * @param {Object} config The config object
5662 Roo.bootstrap.NavSimplebar = function(config){
5663 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5666 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5682 getAutoCreate : function(){
5686 tag : this.tag || 'div',
5687 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5689 if (['light','white'].indexOf(this.weight) > -1) {
5690 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5692 cfg.cls += ' bg-' + this.weight;
5695 cfg.cls += ' navbar-inverse';
5699 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5701 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5710 cls: 'nav nav-' + this.xtype,
5716 this.type = this.type || 'nav';
5717 if (['tabs','pills'].indexOf(this.type) != -1) {
5718 cfg.cn[0].cls += ' nav-' + this.type
5722 if (this.type!=='nav') {
5723 Roo.log('nav type must be nav/tabs/pills')
5725 cfg.cn[0].cls += ' navbar-nav'
5731 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5732 cfg.cn[0].cls += ' nav-' + this.arrangement;
5736 if (this.align === 'right') {
5737 cfg.cn[0].cls += ' navbar-right';
5762 * navbar-expand-md fixed-top
5766 * @class Roo.bootstrap.NavHeaderbar
5767 * @extends Roo.bootstrap.NavSimplebar
5768 * Bootstrap Sidebar class
5770 * @cfg {String} brand what is brand
5771 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5772 * @cfg {String} brand_href href of the brand
5773 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5774 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5775 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5776 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5779 * Create a new Sidebar
5780 * @param {Object} config The config object
5784 Roo.bootstrap.NavHeaderbar = function(config){
5785 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5789 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5796 desktopCenter : false,
5799 getAutoCreate : function(){
5802 tag: this.nav || 'nav',
5803 cls: 'navbar navbar-expand-md',
5809 if (this.desktopCenter) {
5810 cn.push({cls : 'container', cn : []});
5818 cls: 'navbar-toggle navbar-toggler',
5819 'data-toggle': 'collapse',
5824 html: 'Toggle navigation'
5828 cls: 'icon-bar navbar-toggler-icon'
5841 cn.push( Roo.bootstrap.version == 4 ? btn : {
5843 cls: 'navbar-header',
5852 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5856 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5858 if (['light','white'].indexOf(this.weight) > -1) {
5859 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5861 cfg.cls += ' bg-' + this.weight;
5864 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5865 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5867 // tag can override this..
5869 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5872 if (this.brand !== '') {
5873 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5874 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5876 href: this.brand_href ? this.brand_href : '#',
5877 cls: 'navbar-brand',
5885 cfg.cls += ' main-nav';
5893 getHeaderChildContainer : function()
5895 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5896 return this.el.select('.navbar-header',true).first();
5899 return this.getChildContainer();
5902 getChildContainer : function()
5905 return this.el.select('.roo-navbar-collapse',true).first();
5910 initEvents : function()
5912 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5914 if (this.autohide) {
5919 Roo.get(document).on('scroll',function(e) {
5920 var ns = Roo.get(document).getScroll().top;
5921 var os = prevScroll;
5925 ft.removeClass('slideDown');
5926 ft.addClass('slideUp');
5929 ft.removeClass('slideUp');
5930 ft.addClass('slideDown');
5951 * @class Roo.bootstrap.NavSidebar
5952 * @extends Roo.bootstrap.Navbar
5953 * Bootstrap Sidebar class
5956 * Create a new Sidebar
5957 * @param {Object} config The config object
5961 Roo.bootstrap.NavSidebar = function(config){
5962 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5965 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5967 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5969 getAutoCreate : function(){
5974 cls: 'sidebar sidebar-nav'
5996 * @class Roo.bootstrap.NavGroup
5997 * @extends Roo.bootstrap.Component
5998 * Bootstrap NavGroup class
5999 * @cfg {String} align (left|right)
6000 * @cfg {Boolean} inverse
6001 * @cfg {String} type (nav|pills|tab) default nav
6002 * @cfg {String} navId - reference Id for navbar.
6003 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6006 * Create a new nav group
6007 * @param {Object} config The config object
6010 Roo.bootstrap.NavGroup = function(config){
6011 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6014 Roo.bootstrap.NavGroup.register(this);
6018 * Fires when the active item changes
6019 * @param {Roo.bootstrap.NavGroup} this
6020 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6021 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6028 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6040 getAutoCreate : function()
6042 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6048 if (Roo.bootstrap.version == 4) {
6049 if (['tabs','pills'].indexOf(this.type) != -1) {
6050 cfg.cls += ' nav-' + this.type;
6052 // trying to remove so header bar can right align top?
6053 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6054 // do not use on header bar...
6055 cfg.cls += ' navbar-nav';
6060 if (['tabs','pills'].indexOf(this.type) != -1) {
6061 cfg.cls += ' nav-' + this.type
6063 if (this.type !== 'nav') {
6064 Roo.log('nav type must be nav/tabs/pills')
6066 cfg.cls += ' navbar-nav'
6070 if (this.parent() && this.parent().sidebar) {
6073 cls: 'dashboard-menu sidebar-menu'
6079 if (this.form === true) {
6082 cls: 'navbar-form form-inline'
6084 //nav navbar-right ml-md-auto
6085 if (this.align === 'right') {
6086 cfg.cls += ' navbar-right ml-md-auto';
6088 cfg.cls += ' navbar-left';
6092 if (this.align === 'right') {
6093 cfg.cls += ' navbar-right ml-md-auto';
6095 cfg.cls += ' mr-auto';
6099 cfg.cls += ' navbar-inverse';
6107 * sets the active Navigation item
6108 * @param {Roo.bootstrap.NavItem} the new current navitem
6110 setActiveItem : function(item)
6113 Roo.each(this.navItems, function(v){
6118 v.setActive(false, true);
6125 item.setActive(true, true);
6126 this.fireEvent('changed', this, item, prev);
6131 * gets the active Navigation item
6132 * @return {Roo.bootstrap.NavItem} the current navitem
6134 getActive : function()
6138 Roo.each(this.navItems, function(v){
6149 indexOfNav : function()
6153 Roo.each(this.navItems, function(v,i){
6164 * adds a Navigation item
6165 * @param {Roo.bootstrap.NavItem} the navitem to add
6167 addItem : function(cfg)
6169 if (this.form && Roo.bootstrap.version == 4) {
6172 var cn = new Roo.bootstrap.NavItem(cfg);
6174 cn.parentId = this.id;
6175 cn.onRender(this.el, null);
6179 * register a Navigation item
6180 * @param {Roo.bootstrap.NavItem} the navitem to add
6182 register : function(item)
6184 this.navItems.push( item);
6185 item.navId = this.navId;
6190 * clear all the Navigation item
6193 clearAll : function()
6196 this.el.dom.innerHTML = '';
6199 getNavItem: function(tabId)
6202 Roo.each(this.navItems, function(e) {
6203 if (e.tabId == tabId) {
6213 setActiveNext : function()
6215 var i = this.indexOfNav(this.getActive());
6216 if (i > this.navItems.length) {
6219 this.setActiveItem(this.navItems[i+1]);
6221 setActivePrev : function()
6223 var i = this.indexOfNav(this.getActive());
6227 this.setActiveItem(this.navItems[i-1]);
6229 clearWasActive : function(except) {
6230 Roo.each(this.navItems, function(e) {
6231 if (e.tabId != except.tabId && e.was_active) {
6232 e.was_active = false;
6239 getWasActive : function ()
6242 Roo.each(this.navItems, function(e) {
6257 Roo.apply(Roo.bootstrap.NavGroup, {
6261 * register a Navigation Group
6262 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6264 register : function(navgrp)
6266 this.groups[navgrp.navId] = navgrp;
6270 * fetch a Navigation Group based on the navigation ID
6271 * @param {string} the navgroup to add
6272 * @returns {Roo.bootstrap.NavGroup} the navgroup
6274 get: function(navId) {
6275 if (typeof(this.groups[navId]) == 'undefined') {
6277 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6279 return this.groups[navId] ;
6294 * @class Roo.bootstrap.NavItem
6295 * @extends Roo.bootstrap.Component
6296 * Bootstrap Navbar.NavItem class
6297 * @cfg {String} href link to
6298 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6299 * @cfg {Boolean} button_outline show and outlined button
6300 * @cfg {String} html content of button
6301 * @cfg {String} badge text inside badge
6302 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6303 * @cfg {String} glyphicon DEPRICATED - use fa
6304 * @cfg {String} icon DEPRICATED - use fa
6305 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6306 * @cfg {Boolean} active Is item active
6307 * @cfg {Boolean} disabled Is item disabled
6308 * @cfg {String} linkcls Link Class
6309 * @cfg {Boolean} preventDefault (true | false) default false
6310 * @cfg {String} tabId the tab that this item activates.
6311 * @cfg {String} tagtype (a|span) render as a href or span?
6312 * @cfg {Boolean} animateRef (true|false) link to element default false
6315 * Create a new Navbar Item
6316 * @param {Object} config The config object
6318 Roo.bootstrap.NavItem = function(config){
6319 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6324 * The raw click event for the entire grid.
6325 * @param {Roo.EventObject} e
6330 * Fires when the active item active state changes
6331 * @param {Roo.bootstrap.NavItem} this
6332 * @param {boolean} state the new state
6338 * Fires when scroll to element
6339 * @param {Roo.bootstrap.NavItem} this
6340 * @param {Object} options
6341 * @param {Roo.EventObject} e
6349 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6358 preventDefault : false,
6366 button_outline : false,
6370 getAutoCreate : function(){
6377 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6380 cfg.cls += ' active' ;
6382 if (this.disabled) {
6383 cfg.cls += ' disabled';
6387 if (this.button_weight.length) {
6388 cfg.tag = this.href ? 'a' : 'button';
6389 cfg.html = this.html || '';
6390 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6392 cfg.href = this.href;
6395 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6397 cfg.cls += " nav-html";
6400 // menu .. should add dropdown-menu class - so no need for carat..
6402 if (this.badge !== '') {
6404 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6409 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6413 href : this.href || "#",
6414 html: this.html || '',
6418 if (this.tagtype == 'a') {
6419 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6423 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6424 } else if (this.fa) {
6425 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6426 } else if(this.glyphicon) {
6427 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6429 cfg.cn[0].cls += " nav-html";
6433 cfg.cn[0].html += " <span class='caret'></span>";
6437 if (this.badge !== '') {
6438 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6446 onRender : function(ct, position)
6448 // Roo.log("Call onRender: " + this.xtype);
6449 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6453 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6454 this.navLink = this.el.select('.nav-link',true).first();
6455 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6460 initEvents: function()
6462 if (typeof (this.menu) != 'undefined') {
6463 this.menu.parentType = this.xtype;
6464 this.menu.triggerEl = this.el;
6465 this.menu = this.addxtype(Roo.apply({}, this.menu));
6468 this.el.on('click', this.onClick, this);
6470 //if(this.tagtype == 'span'){
6471 // this.el.select('span',true).on('click', this.onClick, this);
6474 // at this point parent should be available..
6475 this.parent().register(this);
6478 onClick : function(e)
6480 if (e.getTarget('.dropdown-menu-item')) {
6481 // did you click on a menu itemm.... - then don't trigger onclick..
6486 this.preventDefault ||
6489 Roo.log("NavItem - prevent Default?");
6493 if (this.disabled) {
6497 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6498 if (tg && tg.transition) {
6499 Roo.log("waiting for the transitionend");
6505 //Roo.log("fire event clicked");
6506 if(this.fireEvent('click', this, e) === false){
6510 if(this.tagtype == 'span'){
6514 //Roo.log(this.href);
6515 var ael = this.el.select('a',true).first();
6518 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6519 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6520 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6521 return; // ignore... - it's a 'hash' to another page.
6523 Roo.log("NavItem - prevent Default?");
6525 this.scrollToElement(e);
6529 var p = this.parent();
6531 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6532 if (typeof(p.setActiveItem) !== 'undefined') {
6533 p.setActiveItem(this);
6537 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6538 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6539 // remove the collapsed menu expand...
6540 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6544 isActive: function () {
6547 setActive : function(state, fire, is_was_active)
6549 if (this.active && !state && this.navId) {
6550 this.was_active = true;
6551 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6553 nv.clearWasActive(this);
6557 this.active = state;
6560 this.el.removeClass('active');
6561 this.navLink ? this.navLink.removeClass('active') : false;
6562 } else if (!this.el.hasClass('active')) {
6564 this.el.addClass('active');
6565 if (Roo.bootstrap.version == 4 && this.navLink ) {
6566 this.navLink.addClass('active');
6571 this.fireEvent('changed', this, state);
6574 // show a panel if it's registered and related..
6576 if (!this.navId || !this.tabId || !state || is_was_active) {
6580 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6584 var pan = tg.getPanelByName(this.tabId);
6588 // if we can not flip to new panel - go back to old nav highlight..
6589 if (false == tg.showPanel(pan)) {
6590 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6592 var onav = nv.getWasActive();
6594 onav.setActive(true, false, true);
6603 // this should not be here...
6604 setDisabled : function(state)
6606 this.disabled = state;
6608 this.el.removeClass('disabled');
6609 } else if (!this.el.hasClass('disabled')) {
6610 this.el.addClass('disabled');
6616 * Fetch the element to display the tooltip on.
6617 * @return {Roo.Element} defaults to this.el
6619 tooltipEl : function()
6621 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6624 scrollToElement : function(e)
6626 var c = document.body;
6629 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6631 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6632 c = document.documentElement;
6635 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6641 var o = target.calcOffsetsTo(c);
6648 this.fireEvent('scrollto', this, options, e);
6650 Roo.get(c).scrollTo('top', options.value, true);
6655 * Set the HTML (text content) of the item
6656 * @param {string} html content for the nav item
6658 setHtml : function(html)
6661 this.htmlEl.dom.innerHTML = html;
6673 * <span> icon </span>
6674 * <span> text </span>
6675 * <span>badge </span>
6679 * @class Roo.bootstrap.NavSidebarItem
6680 * @extends Roo.bootstrap.NavItem
6681 * Bootstrap Navbar.NavSidebarItem class
6682 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6683 * {Boolean} open is the menu open
6684 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6685 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6686 * {String} buttonSize (sm|md|lg)the extra classes for the button
6687 * {Boolean} showArrow show arrow next to the text (default true)
6689 * Create a new Navbar Button
6690 * @param {Object} config The config object
6692 Roo.bootstrap.NavSidebarItem = function(config){
6693 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6698 * The raw click event for the entire grid.
6699 * @param {Roo.EventObject} e
6704 * Fires when the active item active state changes
6705 * @param {Roo.bootstrap.NavSidebarItem} this
6706 * @param {boolean} state the new state
6714 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6716 badgeWeight : 'default',
6722 buttonWeight : 'default',
6728 getAutoCreate : function(){
6733 href : this.href || '#',
6739 if(this.buttonView){
6742 href : this.href || '#',
6743 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6756 cfg.cls += ' active';
6759 if (this.disabled) {
6760 cfg.cls += ' disabled';
6763 cfg.cls += ' open x-open';
6766 if (this.glyphicon || this.icon) {
6767 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6768 a.cn.push({ tag : 'i', cls : c }) ;
6771 if(!this.buttonView){
6774 html : this.html || ''
6781 if (this.badge !== '') {
6782 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6788 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6791 a.cls += ' dropdown-toggle treeview' ;
6797 initEvents : function()
6799 if (typeof (this.menu) != 'undefined') {
6800 this.menu.parentType = this.xtype;
6801 this.menu.triggerEl = this.el;
6802 this.menu = this.addxtype(Roo.apply({}, this.menu));
6805 this.el.on('click', this.onClick, this);
6807 if(this.badge !== ''){
6808 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6813 onClick : function(e)
6820 if(this.preventDefault){
6824 this.fireEvent('click', this, e);
6827 disable : function()
6829 this.setDisabled(true);
6834 this.setDisabled(false);
6837 setDisabled : function(state)
6839 if(this.disabled == state){
6843 this.disabled = state;
6846 this.el.addClass('disabled');
6850 this.el.removeClass('disabled');
6855 setActive : function(state)
6857 if(this.active == state){
6861 this.active = state;
6864 this.el.addClass('active');
6868 this.el.removeClass('active');
6873 isActive: function ()
6878 setBadge : function(str)
6884 this.badgeEl.dom.innerHTML = str;
6899 Roo.namespace('Roo.bootstrap.breadcrumb');
6903 * @class Roo.bootstrap.breadcrumb.Nav
6904 * @extends Roo.bootstrap.Component
6905 * Bootstrap Breadcrumb Nav Class
6907 * @children Roo.bootstrap.breadcrumb.Item
6910 * Create a new breadcrumb.Nav
6911 * @param {Object} config The config object
6915 Roo.bootstrap.breadcrumb.Nav = function(config){
6916 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6921 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6923 getAutoCreate : function()
6940 initEvents: function()
6942 this.olEl = this.el.select('ol',true).first();
6944 getChildContainer : function()
6960 * @class Roo.bootstrap.breadcrumb.Nav
6961 * @extends Roo.bootstrap.Component
6962 * Bootstrap Breadcrumb Nav Class
6964 * @children Roo.bootstrap.breadcrumb.Component
6965 * @cfg {String} html the content of the link.
6966 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6967 * @cfg {Boolean} active is it active
6971 * Create a new breadcrumb.Nav
6972 * @param {Object} config The config object
6975 Roo.bootstrap.breadcrumb.Item = function(config){
6976 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6981 * The img click event for the img.
6982 * @param {Roo.EventObject} e
6989 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6994 getAutoCreate : function()
6999 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7001 if (this.href !== false) {
7008 cfg.html = this.html;
7014 initEvents: function()
7017 this.el.select('a', true).first().on('click',this.onClick, this)
7021 onClick : function(e)
7024 this.fireEvent('click',this, e);
7037 * @class Roo.bootstrap.Row
7038 * @extends Roo.bootstrap.Component
7039 * Bootstrap Row class (contains columns...)
7043 * @param {Object} config The config object
7046 Roo.bootstrap.Row = function(config){
7047 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7050 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7052 getAutoCreate : function(){
7071 * @class Roo.bootstrap.Pagination
7072 * @extends Roo.bootstrap.Component
7073 * Bootstrap Pagination class
7074 * @cfg {String} size xs | sm | md | lg
7075 * @cfg {Boolean} inverse false | true
7078 * Create a new Pagination
7079 * @param {Object} config The config object
7082 Roo.bootstrap.Pagination = function(config){
7083 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7086 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7092 getAutoCreate : function(){
7098 cfg.cls += ' inverse';
7104 cfg.cls += " " + this.cls;
7122 * @class Roo.bootstrap.PaginationItem
7123 * @extends Roo.bootstrap.Component
7124 * Bootstrap PaginationItem class
7125 * @cfg {String} html text
7126 * @cfg {String} href the link
7127 * @cfg {Boolean} preventDefault (true | false) default true
7128 * @cfg {Boolean} active (true | false) default false
7129 * @cfg {Boolean} disabled default false
7133 * Create a new PaginationItem
7134 * @param {Object} config The config object
7138 Roo.bootstrap.PaginationItem = function(config){
7139 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7144 * The raw click event for the entire grid.
7145 * @param {Roo.EventObject} e
7151 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7155 preventDefault: true,
7160 getAutoCreate : function(){
7166 href : this.href ? this.href : '#',
7167 html : this.html ? this.html : ''
7177 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7181 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7187 initEvents: function() {
7189 this.el.on('click', this.onClick, this);
7192 onClick : function(e)
7194 Roo.log('PaginationItem on click ');
7195 if(this.preventDefault){
7203 this.fireEvent('click', this, e);
7219 * @class Roo.bootstrap.Slider
7220 * @extends Roo.bootstrap.Component
7221 * Bootstrap Slider class
7224 * Create a new Slider
7225 * @param {Object} config The config object
7228 Roo.bootstrap.Slider = function(config){
7229 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7232 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7234 getAutoCreate : function(){
7238 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7242 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7254 * Ext JS Library 1.1.1
7255 * Copyright(c) 2006-2007, Ext JS, LLC.
7257 * Originally Released Under LGPL - original licence link has changed is not relivant.
7260 * <script type="text/javascript">
7265 * @class Roo.grid.ColumnModel
7266 * @extends Roo.util.Observable
7267 * This is the default implementation of a ColumnModel used by the Grid. It defines
7268 * the columns in the grid.
7271 var colModel = new Roo.grid.ColumnModel([
7272 {header: "Ticker", width: 60, sortable: true, locked: true},
7273 {header: "Company Name", width: 150, sortable: true},
7274 {header: "Market Cap.", width: 100, sortable: true},
7275 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7276 {header: "Employees", width: 100, sortable: true, resizable: false}
7281 * The config options listed for this class are options which may appear in each
7282 * individual column definition.
7283 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7285 * @param {Object} config An Array of column config objects. See this class's
7286 * config objects for details.
7288 Roo.grid.ColumnModel = function(config){
7290 * The config passed into the constructor
7292 this.config = config;
7295 // if no id, create one
7296 // if the column does not have a dataIndex mapping,
7297 // map it to the order it is in the config
7298 for(var i = 0, len = config.length; i < len; i++){
7300 if(typeof c.dataIndex == "undefined"){
7303 if(typeof c.renderer == "string"){
7304 c.renderer = Roo.util.Format[c.renderer];
7306 if(typeof c.id == "undefined"){
7309 if(c.editor && c.editor.xtype){
7310 c.editor = Roo.factory(c.editor, Roo.grid);
7312 if(c.editor && c.editor.isFormField){
7313 c.editor = new Roo.grid.GridEditor(c.editor);
7315 this.lookup[c.id] = c;
7319 * The width of columns which have no width specified (defaults to 100)
7322 this.defaultWidth = 100;
7325 * Default sortable of columns which have no sortable specified (defaults to false)
7328 this.defaultSortable = false;
7332 * @event widthchange
7333 * Fires when the width of a column changes.
7334 * @param {ColumnModel} this
7335 * @param {Number} columnIndex The column index
7336 * @param {Number} newWidth The new width
7338 "widthchange": true,
7340 * @event headerchange
7341 * Fires when the text of a header changes.
7342 * @param {ColumnModel} this
7343 * @param {Number} columnIndex The column index
7344 * @param {Number} newText The new header text
7346 "headerchange": true,
7348 * @event hiddenchange
7349 * Fires when a column is hidden or "unhidden".
7350 * @param {ColumnModel} this
7351 * @param {Number} columnIndex The column index
7352 * @param {Boolean} hidden true if hidden, false otherwise
7354 "hiddenchange": true,
7356 * @event columnmoved
7357 * Fires when a column is moved.
7358 * @param {ColumnModel} this
7359 * @param {Number} oldIndex
7360 * @param {Number} newIndex
7362 "columnmoved" : true,
7364 * @event columlockchange
7365 * Fires when a column's locked state is changed
7366 * @param {ColumnModel} this
7367 * @param {Number} colIndex
7368 * @param {Boolean} locked true if locked
7370 "columnlockchange" : true
7372 Roo.grid.ColumnModel.superclass.constructor.call(this);
7374 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7376 * @cfg {String} header The header text to display in the Grid view.
7379 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7380 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7381 * specified, the column's index is used as an index into the Record's data Array.
7384 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7385 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7388 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7389 * Defaults to the value of the {@link #defaultSortable} property.
7390 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7393 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7396 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7399 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7402 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7405 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7406 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7407 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7408 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7411 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7414 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7417 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7420 * @cfg {String} cursor (Optional)
7423 * @cfg {String} tooltip (Optional)
7426 * @cfg {Number} xs (Optional)
7429 * @cfg {Number} sm (Optional)
7432 * @cfg {Number} md (Optional)
7435 * @cfg {Number} lg (Optional)
7438 * Returns the id of the column at the specified index.
7439 * @param {Number} index The column index
7440 * @return {String} the id
7442 getColumnId : function(index){
7443 return this.config[index].id;
7447 * Returns the column for a specified id.
7448 * @param {String} id The column id
7449 * @return {Object} the column
7451 getColumnById : function(id){
7452 return this.lookup[id];
7457 * Returns the column for a specified dataIndex.
7458 * @param {String} dataIndex The column dataIndex
7459 * @return {Object|Boolean} the column or false if not found
7461 getColumnByDataIndex: function(dataIndex){
7462 var index = this.findColumnIndex(dataIndex);
7463 return index > -1 ? this.config[index] : false;
7467 * Returns the index for a specified column id.
7468 * @param {String} id The column id
7469 * @return {Number} the index, or -1 if not found
7471 getIndexById : function(id){
7472 for(var i = 0, len = this.config.length; i < len; i++){
7473 if(this.config[i].id == id){
7481 * Returns the index for a specified column dataIndex.
7482 * @param {String} dataIndex The column dataIndex
7483 * @return {Number} the index, or -1 if not found
7486 findColumnIndex : function(dataIndex){
7487 for(var i = 0, len = this.config.length; i < len; i++){
7488 if(this.config[i].dataIndex == dataIndex){
7496 moveColumn : function(oldIndex, newIndex){
7497 var c = this.config[oldIndex];
7498 this.config.splice(oldIndex, 1);
7499 this.config.splice(newIndex, 0, c);
7500 this.dataMap = null;
7501 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7504 isLocked : function(colIndex){
7505 return this.config[colIndex].locked === true;
7508 setLocked : function(colIndex, value, suppressEvent){
7509 if(this.isLocked(colIndex) == value){
7512 this.config[colIndex].locked = value;
7514 this.fireEvent("columnlockchange", this, colIndex, value);
7518 getTotalLockedWidth : function(){
7520 for(var i = 0; i < this.config.length; i++){
7521 if(this.isLocked(i) && !this.isHidden(i)){
7522 this.totalWidth += this.getColumnWidth(i);
7528 getLockedCount : function(){
7529 for(var i = 0, len = this.config.length; i < len; i++){
7530 if(!this.isLocked(i)){
7535 return this.config.length;
7539 * Returns the number of columns.
7542 getColumnCount : function(visibleOnly){
7543 if(visibleOnly === true){
7545 for(var i = 0, len = this.config.length; i < len; i++){
7546 if(!this.isHidden(i)){
7552 return this.config.length;
7556 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7557 * @param {Function} fn
7558 * @param {Object} scope (optional)
7559 * @return {Array} result
7561 getColumnsBy : function(fn, scope){
7563 for(var i = 0, len = this.config.length; i < len; i++){
7564 var c = this.config[i];
7565 if(fn.call(scope||this, c, i) === true){
7573 * Returns true if the specified column is sortable.
7574 * @param {Number} col The column index
7577 isSortable : function(col){
7578 if(typeof this.config[col].sortable == "undefined"){
7579 return this.defaultSortable;
7581 return this.config[col].sortable;
7585 * Returns the rendering (formatting) function defined for the column.
7586 * @param {Number} col The column index.
7587 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7589 getRenderer : function(col){
7590 if(!this.config[col].renderer){
7591 return Roo.grid.ColumnModel.defaultRenderer;
7593 return this.config[col].renderer;
7597 * Sets the rendering (formatting) function for a column.
7598 * @param {Number} col The column index
7599 * @param {Function} fn The function to use to process the cell's raw data
7600 * to return HTML markup for the grid view. The render function is called with
7601 * the following parameters:<ul>
7602 * <li>Data value.</li>
7603 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7604 * <li>css A CSS style string to apply to the table cell.</li>
7605 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7606 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7607 * <li>Row index</li>
7608 * <li>Column index</li>
7609 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7611 setRenderer : function(col, fn){
7612 this.config[col].renderer = fn;
7616 * Returns the width for the specified column.
7617 * @param {Number} col The column index
7620 getColumnWidth : function(col){
7621 return this.config[col].width * 1 || this.defaultWidth;
7625 * Sets the width for a column.
7626 * @param {Number} col The column index
7627 * @param {Number} width The new width
7629 setColumnWidth : function(col, width, suppressEvent){
7630 this.config[col].width = width;
7631 this.totalWidth = null;
7633 this.fireEvent("widthchange", this, col, width);
7638 * Returns the total width of all columns.
7639 * @param {Boolean} includeHidden True to include hidden column widths
7642 getTotalWidth : function(includeHidden){
7643 if(!this.totalWidth){
7644 this.totalWidth = 0;
7645 for(var i = 0, len = this.config.length; i < len; i++){
7646 if(includeHidden || !this.isHidden(i)){
7647 this.totalWidth += this.getColumnWidth(i);
7651 return this.totalWidth;
7655 * Returns the header for the specified column.
7656 * @param {Number} col The column index
7659 getColumnHeader : function(col){
7660 return this.config[col].header;
7664 * Sets the header for a column.
7665 * @param {Number} col The column index
7666 * @param {String} header The new header
7668 setColumnHeader : function(col, header){
7669 this.config[col].header = header;
7670 this.fireEvent("headerchange", this, col, header);
7674 * Returns the tooltip for the specified column.
7675 * @param {Number} col The column index
7678 getColumnTooltip : function(col){
7679 return this.config[col].tooltip;
7682 * Sets the tooltip for a column.
7683 * @param {Number} col The column index
7684 * @param {String} tooltip The new tooltip
7686 setColumnTooltip : function(col, tooltip){
7687 this.config[col].tooltip = tooltip;
7691 * Returns the dataIndex for the specified column.
7692 * @param {Number} col The column index
7695 getDataIndex : function(col){
7696 return this.config[col].dataIndex;
7700 * Sets the dataIndex for a column.
7701 * @param {Number} col The column index
7702 * @param {Number} dataIndex The new dataIndex
7704 setDataIndex : function(col, dataIndex){
7705 this.config[col].dataIndex = dataIndex;
7711 * Returns true if the cell is editable.
7712 * @param {Number} colIndex The column index
7713 * @param {Number} rowIndex The row index - this is nto actually used..?
7716 isCellEditable : function(colIndex, rowIndex){
7717 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7721 * Returns the editor defined for the cell/column.
7722 * return false or null to disable editing.
7723 * @param {Number} colIndex The column index
7724 * @param {Number} rowIndex The row index
7727 getCellEditor : function(colIndex, rowIndex){
7728 return this.config[colIndex].editor;
7732 * Sets if a column is editable.
7733 * @param {Number} col The column index
7734 * @param {Boolean} editable True if the column is editable
7736 setEditable : function(col, editable){
7737 this.config[col].editable = editable;
7742 * Returns true if the column is hidden.
7743 * @param {Number} colIndex The column index
7746 isHidden : function(colIndex){
7747 return this.config[colIndex].hidden;
7752 * Returns true if the column width cannot be changed
7754 isFixed : function(colIndex){
7755 return this.config[colIndex].fixed;
7759 * Returns true if the column can be resized
7762 isResizable : function(colIndex){
7763 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7766 * Sets if a column is hidden.
7767 * @param {Number} colIndex The column index
7768 * @param {Boolean} hidden True if the column is hidden
7770 setHidden : function(colIndex, hidden){
7771 this.config[colIndex].hidden = hidden;
7772 this.totalWidth = null;
7773 this.fireEvent("hiddenchange", this, colIndex, hidden);
7777 * Sets the editor for a column.
7778 * @param {Number} col The column index
7779 * @param {Object} editor The editor object
7781 setEditor : function(col, editor){
7782 this.config[col].editor = editor;
7786 Roo.grid.ColumnModel.defaultRenderer = function(value)
7788 if(typeof value == "object") {
7791 if(typeof value == "string" && value.length < 1){
7795 return String.format("{0}", value);
7798 // Alias for backwards compatibility
7799 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7802 * Ext JS Library 1.1.1
7803 * Copyright(c) 2006-2007, Ext JS, LLC.
7805 * Originally Released Under LGPL - original licence link has changed is not relivant.
7808 * <script type="text/javascript">
7812 * @class Roo.LoadMask
7813 * A simple utility class for generically masking elements while loading data. If the element being masked has
7814 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7815 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7816 * element's UpdateManager load indicator and will be destroyed after the initial load.
7818 * Create a new LoadMask
7819 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7820 * @param {Object} config The config object
7822 Roo.LoadMask = function(el, config){
7823 this.el = Roo.get(el);
7824 Roo.apply(this, config);
7826 this.store.on('beforeload', this.onBeforeLoad, this);
7827 this.store.on('load', this.onLoad, this);
7828 this.store.on('loadexception', this.onLoadException, this);
7829 this.removeMask = false;
7831 var um = this.el.getUpdateManager();
7832 um.showLoadIndicator = false; // disable the default indicator
7833 um.on('beforeupdate', this.onBeforeLoad, this);
7834 um.on('update', this.onLoad, this);
7835 um.on('failure', this.onLoad, this);
7836 this.removeMask = true;
7840 Roo.LoadMask.prototype = {
7842 * @cfg {Boolean} removeMask
7843 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7844 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7848 * The text to display in a centered loading message box (defaults to 'Loading...')
7852 * @cfg {String} msgCls
7853 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7855 msgCls : 'x-mask-loading',
7858 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7864 * Disables the mask to prevent it from being displayed
7866 disable : function(){
7867 this.disabled = true;
7871 * Enables the mask so that it can be displayed
7873 enable : function(){
7874 this.disabled = false;
7877 onLoadException : function()
7881 if (typeof(arguments[3]) != 'undefined') {
7882 Roo.MessageBox.alert("Error loading",arguments[3]);
7886 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7887 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7894 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7899 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7903 onBeforeLoad : function(){
7905 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7910 destroy : function(){
7912 this.store.un('beforeload', this.onBeforeLoad, this);
7913 this.store.un('load', this.onLoad, this);
7914 this.store.un('loadexception', this.onLoadException, this);
7916 var um = this.el.getUpdateManager();
7917 um.un('beforeupdate', this.onBeforeLoad, this);
7918 um.un('update', this.onLoad, this);
7919 um.un('failure', this.onLoad, this);
7930 * @class Roo.bootstrap.Table
7931 * @extends Roo.bootstrap.Component
7932 * Bootstrap Table class
7933 * @cfg {String} cls table class
7934 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7935 * @cfg {String} bgcolor Specifies the background color for a table
7936 * @cfg {Number} border Specifies whether the table cells should have borders or not
7937 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7938 * @cfg {Number} cellspacing Specifies the space between cells
7939 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7940 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7941 * @cfg {String} sortable Specifies that the table should be sortable
7942 * @cfg {String} summary Specifies a summary of the content of a table
7943 * @cfg {Number} width Specifies the width of a table
7944 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7946 * @cfg {boolean} striped Should the rows be alternative striped
7947 * @cfg {boolean} bordered Add borders to the table
7948 * @cfg {boolean} hover Add hover highlighting
7949 * @cfg {boolean} condensed Format condensed
7950 * @cfg {boolean} responsive Format condensed
7951 * @cfg {Boolean} loadMask (true|false) default false
7952 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7953 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7954 * @cfg {Boolean} rowSelection (true|false) default false
7955 * @cfg {Boolean} cellSelection (true|false) default false
7956 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7957 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7958 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7959 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7963 * Create a new Table
7964 * @param {Object} config The config object
7967 Roo.bootstrap.Table = function(config){
7968 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7973 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7974 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7975 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7976 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7978 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7980 this.sm.grid = this;
7981 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7982 this.sm = this.selModel;
7983 this.sm.xmodule = this.xmodule || false;
7986 if (this.cm && typeof(this.cm.config) == 'undefined') {
7987 this.colModel = new Roo.grid.ColumnModel(this.cm);
7988 this.cm = this.colModel;
7989 this.cm.xmodule = this.xmodule || false;
7992 this.store= Roo.factory(this.store, Roo.data);
7993 this.ds = this.store;
7994 this.ds.xmodule = this.xmodule || false;
7997 if (this.footer && this.store) {
7998 this.footer.dataSource = this.ds;
7999 this.footer = Roo.factory(this.footer);
8006 * Fires when a cell is clicked
8007 * @param {Roo.bootstrap.Table} this
8008 * @param {Roo.Element} el
8009 * @param {Number} rowIndex
8010 * @param {Number} columnIndex
8011 * @param {Roo.EventObject} e
8015 * @event celldblclick
8016 * Fires when a cell is double clicked
8017 * @param {Roo.bootstrap.Table} this
8018 * @param {Roo.Element} el
8019 * @param {Number} rowIndex
8020 * @param {Number} columnIndex
8021 * @param {Roo.EventObject} e
8023 "celldblclick" : true,
8026 * Fires when a row is clicked
8027 * @param {Roo.bootstrap.Table} this
8028 * @param {Roo.Element} el
8029 * @param {Number} rowIndex
8030 * @param {Roo.EventObject} e
8034 * @event rowdblclick
8035 * Fires when a row is double clicked
8036 * @param {Roo.bootstrap.Table} this
8037 * @param {Roo.Element} el
8038 * @param {Number} rowIndex
8039 * @param {Roo.EventObject} e
8041 "rowdblclick" : true,
8044 * Fires when a mouseover occur
8045 * @param {Roo.bootstrap.Table} this
8046 * @param {Roo.Element} el
8047 * @param {Number} rowIndex
8048 * @param {Number} columnIndex
8049 * @param {Roo.EventObject} e
8054 * Fires when a mouseout occur
8055 * @param {Roo.bootstrap.Table} this
8056 * @param {Roo.Element} el
8057 * @param {Number} rowIndex
8058 * @param {Number} columnIndex
8059 * @param {Roo.EventObject} e
8064 * Fires when a row is rendered, so you can change add a style to it.
8065 * @param {Roo.bootstrap.Table} this
8066 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8070 * @event rowsrendered
8071 * Fires when all the rows have been rendered
8072 * @param {Roo.bootstrap.Table} this
8074 'rowsrendered' : true,
8076 * @event contextmenu
8077 * The raw contextmenu event for the entire grid.
8078 * @param {Roo.EventObject} e
8080 "contextmenu" : true,
8082 * @event rowcontextmenu
8083 * Fires when a row is right clicked
8084 * @param {Roo.bootstrap.Table} this
8085 * @param {Number} rowIndex
8086 * @param {Roo.EventObject} e
8088 "rowcontextmenu" : true,
8090 * @event cellcontextmenu
8091 * Fires when a cell is right clicked
8092 * @param {Roo.bootstrap.Table} this
8093 * @param {Number} rowIndex
8094 * @param {Number} cellIndex
8095 * @param {Roo.EventObject} e
8097 "cellcontextmenu" : true,
8099 * @event headercontextmenu
8100 * Fires when a header is right clicked
8101 * @param {Roo.bootstrap.Table} this
8102 * @param {Number} columnIndex
8103 * @param {Roo.EventObject} e
8105 "headercontextmenu" : true
8109 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8135 rowSelection : false,
8136 cellSelection : false,
8139 // Roo.Element - the tbody
8141 // Roo.Element - thead element
8144 container: false, // used by gridpanel...
8150 auto_hide_footer : false,
8152 getAutoCreate : function()
8154 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8161 if (this.scrollBody) {
8162 cfg.cls += ' table-body-fixed';
8165 cfg.cls += ' table-striped';
8169 cfg.cls += ' table-hover';
8171 if (this.bordered) {
8172 cfg.cls += ' table-bordered';
8174 if (this.condensed) {
8175 cfg.cls += ' table-condensed';
8177 if (this.responsive) {
8178 cfg.cls += ' table-responsive';
8182 cfg.cls+= ' ' +this.cls;
8185 // this lot should be simplifed...
8198 ].forEach(function(k) {
8206 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8209 if(this.store || this.cm){
8210 if(this.headerShow){
8211 cfg.cn.push(this.renderHeader());
8214 cfg.cn.push(this.renderBody());
8216 if(this.footerShow){
8217 cfg.cn.push(this.renderFooter());
8219 // where does this come from?
8220 //cfg.cls+= ' TableGrid';
8223 return { cn : [ cfg ] };
8226 initEvents : function()
8228 if(!this.store || !this.cm){
8231 if (this.selModel) {
8232 this.selModel.initEvents();
8236 //Roo.log('initEvents with ds!!!!');
8238 this.mainBody = this.el.select('tbody', true).first();
8239 this.mainHead = this.el.select('thead', true).first();
8240 this.mainFoot = this.el.select('tfoot', true).first();
8246 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8247 e.on('click', _this.sort, _this);
8250 this.mainBody.on("click", this.onClick, this);
8251 this.mainBody.on("dblclick", this.onDblClick, this);
8253 // why is this done????? = it breaks dialogs??
8254 //this.parent().el.setStyle('position', 'relative');
8258 this.footer.parentId = this.id;
8259 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8262 this.el.select('tfoot tr td').first().addClass('hide');
8267 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8270 this.store.on('load', this.onLoad, this);
8271 this.store.on('beforeload', this.onBeforeLoad, this);
8272 this.store.on('update', this.onUpdate, this);
8273 this.store.on('add', this.onAdd, this);
8274 this.store.on("clear", this.clear, this);
8276 this.el.on("contextmenu", this.onContextMenu, this);
8278 this.mainBody.on('scroll', this.onBodyScroll, this);
8280 this.cm.on("headerchange", this.onHeaderChange, this);
8282 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8286 onContextMenu : function(e, t)
8288 this.processEvent("contextmenu", e);
8291 processEvent : function(name, e)
8293 if (name != 'touchstart' ) {
8294 this.fireEvent(name, e);
8297 var t = e.getTarget();
8299 var cell = Roo.get(t);
8305 if(cell.findParent('tfoot', false, true)){
8309 if(cell.findParent('thead', false, true)){
8311 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8312 cell = Roo.get(t).findParent('th', false, true);
8314 Roo.log("failed to find th in thead?");
8315 Roo.log(e.getTarget());
8320 var cellIndex = cell.dom.cellIndex;
8322 var ename = name == 'touchstart' ? 'click' : name;
8323 this.fireEvent("header" + ename, this, cellIndex, e);
8328 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8329 cell = Roo.get(t).findParent('td', false, true);
8331 Roo.log("failed to find th in tbody?");
8332 Roo.log(e.getTarget());
8337 var row = cell.findParent('tr', false, true);
8338 var cellIndex = cell.dom.cellIndex;
8339 var rowIndex = row.dom.rowIndex - 1;
8343 this.fireEvent("row" + name, this, rowIndex, e);
8347 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8353 onMouseover : function(e, el)
8355 var cell = Roo.get(el);
8361 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8362 cell = cell.findParent('td', false, true);
8365 var row = cell.findParent('tr', false, true);
8366 var cellIndex = cell.dom.cellIndex;
8367 var rowIndex = row.dom.rowIndex - 1; // start from 0
8369 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8373 onMouseout : function(e, el)
8375 var cell = Roo.get(el);
8381 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8382 cell = cell.findParent('td', false, true);
8385 var row = cell.findParent('tr', false, true);
8386 var cellIndex = cell.dom.cellIndex;
8387 var rowIndex = row.dom.rowIndex - 1; // start from 0
8389 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8393 onClick : function(e, el)
8395 var cell = Roo.get(el);
8397 if(!cell || (!this.cellSelection && !this.rowSelection)){
8401 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8402 cell = cell.findParent('td', false, true);
8405 if(!cell || typeof(cell) == 'undefined'){
8409 var row = cell.findParent('tr', false, true);
8411 if(!row || typeof(row) == 'undefined'){
8415 var cellIndex = cell.dom.cellIndex;
8416 var rowIndex = this.getRowIndex(row);
8418 // why??? - should these not be based on SelectionModel?
8419 if(this.cellSelection){
8420 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8423 if(this.rowSelection){
8424 this.fireEvent('rowclick', this, row, rowIndex, e);
8430 onDblClick : function(e,el)
8432 var cell = Roo.get(el);
8434 if(!cell || (!this.cellSelection && !this.rowSelection)){
8438 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8439 cell = cell.findParent('td', false, true);
8442 if(!cell || typeof(cell) == 'undefined'){
8446 var row = cell.findParent('tr', false, true);
8448 if(!row || typeof(row) == 'undefined'){
8452 var cellIndex = cell.dom.cellIndex;
8453 var rowIndex = this.getRowIndex(row);
8455 if(this.cellSelection){
8456 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8459 if(this.rowSelection){
8460 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8464 sort : function(e,el)
8466 var col = Roo.get(el);
8468 if(!col.hasClass('sortable')){
8472 var sort = col.attr('sort');
8475 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8479 this.store.sortInfo = {field : sort, direction : dir};
8482 Roo.log("calling footer first");
8483 this.footer.onClick('first');
8486 this.store.load({ params : { start : 0 } });
8490 renderHeader : function()
8498 this.totalWidth = 0;
8500 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8502 var config = cm.config[i];
8506 cls : 'x-hcol-' + i,
8508 html: cm.getColumnHeader(i)
8513 if(typeof(config.sortable) != 'undefined' && config.sortable){
8515 c.html = '<i class="glyphicon"></i>' + c.html;
8518 // could use BS4 hidden-..-down
8520 if(typeof(config.lgHeader) != 'undefined'){
8521 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8524 if(typeof(config.mdHeader) != 'undefined'){
8525 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8528 if(typeof(config.smHeader) != 'undefined'){
8529 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8532 if(typeof(config.xsHeader) != 'undefined'){
8533 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8540 if(typeof(config.tooltip) != 'undefined'){
8541 c.tooltip = config.tooltip;
8544 if(typeof(config.colspan) != 'undefined'){
8545 c.colspan = config.colspan;
8548 if(typeof(config.hidden) != 'undefined' && config.hidden){
8549 c.style += ' display:none;';
8552 if(typeof(config.dataIndex) != 'undefined'){
8553 c.sort = config.dataIndex;
8558 if(typeof(config.align) != 'undefined' && config.align.length){
8559 c.style += ' text-align:' + config.align + ';';
8562 if(typeof(config.width) != 'undefined'){
8563 c.style += ' width:' + config.width + 'px;';
8564 this.totalWidth += config.width;
8566 this.totalWidth += 100; // assume minimum of 100 per column?
8569 if(typeof(config.cls) != 'undefined'){
8570 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8573 ['xs','sm','md','lg'].map(function(size){
8575 if(typeof(config[size]) == 'undefined'){
8579 if (!config[size]) { // 0 = hidden
8580 // BS 4 '0' is treated as hide that column and below.
8581 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8585 c.cls += ' col-' + size + '-' + config[size] + (
8586 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8598 renderBody : function()
8608 colspan : this.cm.getColumnCount()
8618 renderFooter : function()
8628 colspan : this.cm.getColumnCount()
8642 // Roo.log('ds onload');
8647 var ds = this.store;
8649 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8650 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8651 if (_this.store.sortInfo) {
8653 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8654 e.select('i', true).addClass(['glyphicon-arrow-up']);
8657 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8658 e.select('i', true).addClass(['glyphicon-arrow-down']);
8663 var tbody = this.mainBody;
8665 if(ds.getCount() > 0){
8666 ds.data.each(function(d,rowIndex){
8667 var row = this.renderRow(cm, ds, rowIndex);
8669 tbody.createChild(row);
8673 if(row.cellObjects.length){
8674 Roo.each(row.cellObjects, function(r){
8675 _this.renderCellObject(r);
8682 var tfoot = this.el.select('tfoot', true).first();
8684 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8686 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8688 var total = this.ds.getTotalCount();
8690 if(this.footer.pageSize < total){
8691 this.mainFoot.show();
8695 Roo.each(this.el.select('tbody td', true).elements, function(e){
8696 e.on('mouseover', _this.onMouseover, _this);
8699 Roo.each(this.el.select('tbody td', true).elements, function(e){
8700 e.on('mouseout', _this.onMouseout, _this);
8702 this.fireEvent('rowsrendered', this);
8708 onUpdate : function(ds,record)
8710 this.refreshRow(record);
8714 onRemove : function(ds, record, index, isUpdate){
8715 if(isUpdate !== true){
8716 this.fireEvent("beforerowremoved", this, index, record);
8718 var bt = this.mainBody.dom;
8720 var rows = this.el.select('tbody > tr', true).elements;
8722 if(typeof(rows[index]) != 'undefined'){
8723 bt.removeChild(rows[index].dom);
8726 // if(bt.rows[index]){
8727 // bt.removeChild(bt.rows[index]);
8730 if(isUpdate !== true){
8731 //this.stripeRows(index);
8732 //this.syncRowHeights(index, index);
8734 this.fireEvent("rowremoved", this, index, record);
8738 onAdd : function(ds, records, rowIndex)
8740 //Roo.log('on Add called');
8741 // - note this does not handle multiple adding very well..
8742 var bt = this.mainBody.dom;
8743 for (var i =0 ; i < records.length;i++) {
8744 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8745 //Roo.log(records[i]);
8746 //Roo.log(this.store.getAt(rowIndex+i));
8747 this.insertRow(this.store, rowIndex + i, false);
8754 refreshRow : function(record){
8755 var ds = this.store, index;
8756 if(typeof record == 'number'){
8758 record = ds.getAt(index);
8760 index = ds.indexOf(record);
8762 return; // should not happen - but seems to
8765 this.insertRow(ds, index, true);
8767 this.onRemove(ds, record, index+1, true);
8769 //this.syncRowHeights(index, index);
8771 this.fireEvent("rowupdated", this, index, record);
8774 insertRow : function(dm, rowIndex, isUpdate){
8777 this.fireEvent("beforerowsinserted", this, rowIndex);
8779 //var s = this.getScrollState();
8780 var row = this.renderRow(this.cm, this.store, rowIndex);
8781 // insert before rowIndex..
8782 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8786 if(row.cellObjects.length){
8787 Roo.each(row.cellObjects, function(r){
8788 _this.renderCellObject(r);
8793 this.fireEvent("rowsinserted", this, rowIndex);
8794 //this.syncRowHeights(firstRow, lastRow);
8795 //this.stripeRows(firstRow);
8802 getRowDom : function(rowIndex)
8804 var rows = this.el.select('tbody > tr', true).elements;
8806 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8809 // returns the object tree for a tr..
8812 renderRow : function(cm, ds, rowIndex)
8814 var d = ds.getAt(rowIndex);
8818 cls : 'x-row-' + rowIndex,
8822 var cellObjects = [];
8824 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8825 var config = cm.config[i];
8827 var renderer = cm.getRenderer(i);
8831 if(typeof(renderer) !== 'undefined'){
8832 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8834 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8835 // and are rendered into the cells after the row is rendered - using the id for the element.
8837 if(typeof(value) === 'object'){
8847 rowIndex : rowIndex,
8852 this.fireEvent('rowclass', this, rowcfg);
8856 cls : rowcfg.rowClass + ' x-col-' + i,
8858 html: (typeof(value) === 'object') ? '' : value
8865 if(typeof(config.colspan) != 'undefined'){
8866 td.colspan = config.colspan;
8869 if(typeof(config.hidden) != 'undefined' && config.hidden){
8870 td.style += ' display:none;';
8873 if(typeof(config.align) != 'undefined' && config.align.length){
8874 td.style += ' text-align:' + config.align + ';';
8876 if(typeof(config.valign) != 'undefined' && config.valign.length){
8877 td.style += ' vertical-align:' + config.valign + ';';
8880 if(typeof(config.width) != 'undefined'){
8881 td.style += ' width:' + config.width + 'px;';
8884 if(typeof(config.cursor) != 'undefined'){
8885 td.style += ' cursor:' + config.cursor + ';';
8888 if(typeof(config.cls) != 'undefined'){
8889 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8892 ['xs','sm','md','lg'].map(function(size){
8894 if(typeof(config[size]) == 'undefined'){
8900 if (!config[size]) { // 0 = hidden
8901 // BS 4 '0' is treated as hide that column and below.
8902 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8906 td.cls += ' col-' + size + '-' + config[size] + (
8907 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8917 row.cellObjects = cellObjects;
8925 onBeforeLoad : function()
8934 this.el.select('tbody', true).first().dom.innerHTML = '';
8937 * Show or hide a row.
8938 * @param {Number} rowIndex to show or hide
8939 * @param {Boolean} state hide
8941 setRowVisibility : function(rowIndex, state)
8943 var bt = this.mainBody.dom;
8945 var rows = this.el.select('tbody > tr', true).elements;
8947 if(typeof(rows[rowIndex]) == 'undefined'){
8950 rows[rowIndex].dom.style.display = state ? '' : 'none';
8954 getSelectionModel : function(){
8956 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8958 return this.selModel;
8961 * Render the Roo.bootstrap object from renderder
8963 renderCellObject : function(r)
8967 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8969 var t = r.cfg.render(r.container);
8972 Roo.each(r.cfg.cn, function(c){
8974 container: t.getChildContainer(),
8977 _this.renderCellObject(child);
8982 getRowIndex : function(row)
8986 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8997 * Returns the grid's underlying element = used by panel.Grid
8998 * @return {Element} The element
9000 getGridEl : function(){
9004 * Forces a resize - used by panel.Grid
9005 * @return {Element} The element
9007 autoSize : function()
9009 //var ctr = Roo.get(this.container.dom.parentElement);
9010 var ctr = Roo.get(this.el.dom);
9012 var thd = this.getGridEl().select('thead',true).first();
9013 var tbd = this.getGridEl().select('tbody', true).first();
9014 var tfd = this.getGridEl().select('tfoot', true).first();
9016 var cw = ctr.getWidth();
9017 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9021 tbd.setWidth(ctr.getWidth());
9022 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9023 // this needs fixing for various usage - currently only hydra job advers I think..
9025 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9027 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9030 cw = Math.max(cw, this.totalWidth);
9031 this.getGridEl().select('tbody tr',true).setWidth(cw);
9033 // resize 'expandable coloumn?
9035 return; // we doe not have a view in this design..
9038 onBodyScroll: function()
9040 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9042 this.mainHead.setStyle({
9043 'position' : 'relative',
9044 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9050 var scrollHeight = this.mainBody.dom.scrollHeight;
9052 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9054 var height = this.mainBody.getHeight();
9056 if(scrollHeight - height == scrollTop) {
9058 var total = this.ds.getTotalCount();
9060 if(this.footer.cursor + this.footer.pageSize < total){
9062 this.footer.ds.load({
9064 start : this.footer.cursor + this.footer.pageSize,
9065 limit : this.footer.pageSize
9075 onHeaderChange : function()
9077 var header = this.renderHeader();
9078 var table = this.el.select('table', true).first();
9080 this.mainHead.remove();
9081 this.mainHead = table.createChild(header, this.mainBody, false);
9084 onHiddenChange : function(colModel, colIndex, hidden)
9086 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9087 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9089 this.CSS.updateRule(thSelector, "display", "");
9090 this.CSS.updateRule(tdSelector, "display", "");
9093 this.CSS.updateRule(thSelector, "display", "none");
9094 this.CSS.updateRule(tdSelector, "display", "none");
9097 this.onHeaderChange();
9101 setColumnWidth: function(col_index, width)
9103 // width = "md-2 xs-2..."
9104 if(!this.colModel.config[col_index]) {
9108 var w = width.split(" ");
9110 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9112 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9115 for(var j = 0; j < w.length; j++) {
9121 var size_cls = w[j].split("-");
9123 if(!Number.isInteger(size_cls[1] * 1)) {
9127 if(!this.colModel.config[col_index][size_cls[0]]) {
9131 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9135 h_row[0].classList.replace(
9136 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9137 "col-"+size_cls[0]+"-"+size_cls[1]
9140 for(var i = 0; i < rows.length; i++) {
9142 var size_cls = w[j].split("-");
9144 if(!Number.isInteger(size_cls[1] * 1)) {
9148 if(!this.colModel.config[col_index][size_cls[0]]) {
9152 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9156 rows[i].classList.replace(
9157 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9158 "col-"+size_cls[0]+"-"+size_cls[1]
9162 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9177 * @class Roo.bootstrap.TableCell
9178 * @extends Roo.bootstrap.Component
9179 * Bootstrap TableCell class
9180 * @cfg {String} html cell contain text
9181 * @cfg {String} cls cell class
9182 * @cfg {String} tag cell tag (td|th) default td
9183 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9184 * @cfg {String} align Aligns the content in a cell
9185 * @cfg {String} axis Categorizes cells
9186 * @cfg {String} bgcolor Specifies the background color of a cell
9187 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9188 * @cfg {Number} colspan Specifies the number of columns a cell should span
9189 * @cfg {String} headers Specifies one or more header cells a cell is related to
9190 * @cfg {Number} height Sets the height of a cell
9191 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9192 * @cfg {Number} rowspan Sets the number of rows a cell should span
9193 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9194 * @cfg {String} valign Vertical aligns the content in a cell
9195 * @cfg {Number} width Specifies the width of a cell
9198 * Create a new TableCell
9199 * @param {Object} config The config object
9202 Roo.bootstrap.TableCell = function(config){
9203 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9206 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9226 getAutoCreate : function(){
9227 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9247 cfg.align=this.align
9253 cfg.bgcolor=this.bgcolor
9256 cfg.charoff=this.charoff
9259 cfg.colspan=this.colspan
9262 cfg.headers=this.headers
9265 cfg.height=this.height
9268 cfg.nowrap=this.nowrap
9271 cfg.rowspan=this.rowspan
9274 cfg.scope=this.scope
9277 cfg.valign=this.valign
9280 cfg.width=this.width
9299 * @class Roo.bootstrap.TableRow
9300 * @extends Roo.bootstrap.Component
9301 * Bootstrap TableRow class
9302 * @cfg {String} cls row class
9303 * @cfg {String} align Aligns the content in a table row
9304 * @cfg {String} bgcolor Specifies a background color for a table row
9305 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9306 * @cfg {String} valign Vertical aligns the content in a table row
9309 * Create a new TableRow
9310 * @param {Object} config The config object
9313 Roo.bootstrap.TableRow = function(config){
9314 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9317 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9325 getAutoCreate : function(){
9326 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9336 cfg.align = this.align;
9339 cfg.bgcolor = this.bgcolor;
9342 cfg.charoff = this.charoff;
9345 cfg.valign = this.valign;
9363 * @class Roo.bootstrap.TableBody
9364 * @extends Roo.bootstrap.Component
9365 * Bootstrap TableBody class
9366 * @cfg {String} cls element class
9367 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9368 * @cfg {String} align Aligns the content inside the element
9369 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9370 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9373 * Create a new TableBody
9374 * @param {Object} config The config object
9377 Roo.bootstrap.TableBody = function(config){
9378 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9381 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9389 getAutoCreate : function(){
9390 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9404 cfg.align = this.align;
9407 cfg.charoff = this.charoff;
9410 cfg.valign = this.valign;
9417 // initEvents : function()
9424 // this.store = Roo.factory(this.store, Roo.data);
9425 // this.store.on('load', this.onLoad, this);
9427 // this.store.load();
9431 // onLoad: function ()
9433 // this.fireEvent('load', this);
9443 * Ext JS Library 1.1.1
9444 * Copyright(c) 2006-2007, Ext JS, LLC.
9446 * Originally Released Under LGPL - original licence link has changed is not relivant.
9449 * <script type="text/javascript">
9452 // as we use this in bootstrap.
9453 Roo.namespace('Roo.form');
9455 * @class Roo.form.Action
9456 * Internal Class used to handle form actions
9458 * @param {Roo.form.BasicForm} el The form element or its id
9459 * @param {Object} config Configuration options
9464 // define the action interface
9465 Roo.form.Action = function(form, options){
9467 this.options = options || {};
9470 * Client Validation Failed
9473 Roo.form.Action.CLIENT_INVALID = 'client';
9475 * Server Validation Failed
9478 Roo.form.Action.SERVER_INVALID = 'server';
9480 * Connect to Server Failed
9483 Roo.form.Action.CONNECT_FAILURE = 'connect';
9485 * Reading Data from Server Failed
9488 Roo.form.Action.LOAD_FAILURE = 'load';
9490 Roo.form.Action.prototype = {
9492 failureType : undefined,
9493 response : undefined,
9497 run : function(options){
9502 success : function(response){
9507 handleResponse : function(response){
9511 // default connection failure
9512 failure : function(response){
9514 this.response = response;
9515 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9516 this.form.afterAction(this, false);
9519 processResponse : function(response){
9520 this.response = response;
9521 if(!response.responseText){
9524 this.result = this.handleResponse(response);
9528 // utility functions used internally
9529 getUrl : function(appendParams){
9530 var url = this.options.url || this.form.url || this.form.el.dom.action;
9532 var p = this.getParams();
9534 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9540 getMethod : function(){
9541 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9544 getParams : function(){
9545 var bp = this.form.baseParams;
9546 var p = this.options.params;
9548 if(typeof p == "object"){
9549 p = Roo.urlEncode(Roo.applyIf(p, bp));
9550 }else if(typeof p == 'string' && bp){
9551 p += '&' + Roo.urlEncode(bp);
9554 p = Roo.urlEncode(bp);
9559 createCallback : function(){
9561 success: this.success,
9562 failure: this.failure,
9564 timeout: (this.form.timeout*1000),
9565 upload: this.form.fileUpload ? this.success : undefined
9570 Roo.form.Action.Submit = function(form, options){
9571 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9574 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9577 haveProgress : false,
9578 uploadComplete : false,
9580 // uploadProgress indicator.
9581 uploadProgress : function()
9583 if (!this.form.progressUrl) {
9587 if (!this.haveProgress) {
9588 Roo.MessageBox.progress("Uploading", "Uploading");
9590 if (this.uploadComplete) {
9591 Roo.MessageBox.hide();
9595 this.haveProgress = true;
9597 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9599 var c = new Roo.data.Connection();
9601 url : this.form.progressUrl,
9606 success : function(req){
9607 //console.log(data);
9611 rdata = Roo.decode(req.responseText)
9613 Roo.log("Invalid data from server..");
9617 if (!rdata || !rdata.success) {
9619 Roo.MessageBox.alert(Roo.encode(rdata));
9622 var data = rdata.data;
9624 if (this.uploadComplete) {
9625 Roo.MessageBox.hide();
9630 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9631 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9634 this.uploadProgress.defer(2000,this);
9637 failure: function(data) {
9638 Roo.log('progress url failed ');
9649 // run get Values on the form, so it syncs any secondary forms.
9650 this.form.getValues();
9652 var o = this.options;
9653 var method = this.getMethod();
9654 var isPost = method == 'POST';
9655 if(o.clientValidation === false || this.form.isValid()){
9657 if (this.form.progressUrl) {
9658 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9659 (new Date() * 1) + '' + Math.random());
9664 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9665 form:this.form.el.dom,
9666 url:this.getUrl(!isPost),
9668 params:isPost ? this.getParams() : null,
9669 isUpload: this.form.fileUpload,
9670 formData : this.form.formData
9673 this.uploadProgress();
9675 }else if (o.clientValidation !== false){ // client validation failed
9676 this.failureType = Roo.form.Action.CLIENT_INVALID;
9677 this.form.afterAction(this, false);
9681 success : function(response)
9683 this.uploadComplete= true;
9684 if (this.haveProgress) {
9685 Roo.MessageBox.hide();
9689 var result = this.processResponse(response);
9690 if(result === true || result.success){
9691 this.form.afterAction(this, true);
9695 this.form.markInvalid(result.errors);
9696 this.failureType = Roo.form.Action.SERVER_INVALID;
9698 this.form.afterAction(this, false);
9700 failure : function(response)
9702 this.uploadComplete= true;
9703 if (this.haveProgress) {
9704 Roo.MessageBox.hide();
9707 this.response = response;
9708 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9709 this.form.afterAction(this, false);
9712 handleResponse : function(response){
9713 if(this.form.errorReader){
9714 var rs = this.form.errorReader.read(response);
9717 for(var i = 0, len = rs.records.length; i < len; i++) {
9718 var r = rs.records[i];
9722 if(errors.length < 1){
9726 success : rs.success,
9732 ret = Roo.decode(response.responseText);
9736 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9746 Roo.form.Action.Load = function(form, options){
9747 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9748 this.reader = this.form.reader;
9751 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9756 Roo.Ajax.request(Roo.apply(
9757 this.createCallback(), {
9758 method:this.getMethod(),
9759 url:this.getUrl(false),
9760 params:this.getParams()
9764 success : function(response){
9766 var result = this.processResponse(response);
9767 if(result === true || !result.success || !result.data){
9768 this.failureType = Roo.form.Action.LOAD_FAILURE;
9769 this.form.afterAction(this, false);
9772 this.form.clearInvalid();
9773 this.form.setValues(result.data);
9774 this.form.afterAction(this, true);
9777 handleResponse : function(response){
9778 if(this.form.reader){
9779 var rs = this.form.reader.read(response);
9780 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9782 success : rs.success,
9786 return Roo.decode(response.responseText);
9790 Roo.form.Action.ACTION_TYPES = {
9791 'load' : Roo.form.Action.Load,
9792 'submit' : Roo.form.Action.Submit
9801 * @class Roo.bootstrap.Form
9802 * @extends Roo.bootstrap.Component
9803 * Bootstrap Form class
9804 * @cfg {String} method GET | POST (default POST)
9805 * @cfg {String} labelAlign top | left (default top)
9806 * @cfg {String} align left | right - for navbars
9807 * @cfg {Boolean} loadMask load mask when submit (default true)
9812 * @param {Object} config The config object
9816 Roo.bootstrap.Form = function(config){
9818 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9820 Roo.bootstrap.Form.popover.apply();
9824 * @event clientvalidation
9825 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9826 * @param {Form} this
9827 * @param {Boolean} valid true if the form has passed client-side validation
9829 clientvalidation: true,
9831 * @event beforeaction
9832 * Fires before any action is performed. Return false to cancel the action.
9833 * @param {Form} this
9834 * @param {Action} action The action to be performed
9838 * @event actionfailed
9839 * Fires when an action fails.
9840 * @param {Form} this
9841 * @param {Action} action The action that failed
9843 actionfailed : true,
9845 * @event actioncomplete
9846 * Fires when an action is completed.
9847 * @param {Form} this
9848 * @param {Action} action The action that completed
9850 actioncomplete : true
9854 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9857 * @cfg {String} method
9858 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9863 * The URL to use for form actions if one isn't supplied in the action options.
9866 * @cfg {Boolean} fileUpload
9867 * Set to true if this form is a file upload.
9871 * @cfg {Object} baseParams
9872 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9876 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9880 * @cfg {Sting} align (left|right) for navbar forms
9885 activeAction : null,
9888 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9889 * element by passing it or its id or mask the form itself by passing in true.
9892 waitMsgTarget : false,
9897 * @cfg {Boolean} errorMask (true|false) default false
9902 * @cfg {Number} maskOffset Default 100
9907 * @cfg {Boolean} maskBody
9911 getAutoCreate : function(){
9915 method : this.method || 'POST',
9916 id : this.id || Roo.id(),
9919 if (this.parent().xtype.match(/^Nav/)) {
9920 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9924 if (this.labelAlign == 'left' ) {
9925 cfg.cls += ' form-horizontal';
9931 initEvents : function()
9933 this.el.on('submit', this.onSubmit, this);
9934 // this was added as random key presses on the form where triggering form submit.
9935 this.el.on('keypress', function(e) {
9936 if (e.getCharCode() != 13) {
9939 // we might need to allow it for textareas.. and some other items.
9940 // check e.getTarget().
9942 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9946 Roo.log("keypress blocked");
9954 onSubmit : function(e){
9959 * Returns true if client-side validation on the form is successful.
9962 isValid : function(){
9963 var items = this.getItems();
9967 items.each(function(f){
9973 Roo.log('invalid field: ' + f.name);
9977 if(!target && f.el.isVisible(true)){
9983 if(this.errorMask && !valid){
9984 Roo.bootstrap.Form.popover.mask(this, target);
9991 * Returns true if any fields in this form have changed since their original load.
9994 isDirty : function(){
9996 var items = this.getItems();
9997 items.each(function(f){
10007 * Performs a predefined action (submit or load) or custom actions you define on this form.
10008 * @param {String} actionName The name of the action type
10009 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10010 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10011 * accept other config options):
10013 Property Type Description
10014 ---------------- --------------- ----------------------------------------------------------------------------------
10015 url String The url for the action (defaults to the form's url)
10016 method String The form method to use (defaults to the form's method, or POST if not defined)
10017 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10018 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10019 validate the form on the client (defaults to false)
10021 * @return {BasicForm} this
10023 doAction : function(action, options){
10024 if(typeof action == 'string'){
10025 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10027 if(this.fireEvent('beforeaction', this, action) !== false){
10028 this.beforeAction(action);
10029 action.run.defer(100, action);
10035 beforeAction : function(action){
10036 var o = action.options;
10041 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10043 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10046 // not really supported yet.. ??
10048 //if(this.waitMsgTarget === true){
10049 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10050 //}else if(this.waitMsgTarget){
10051 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10052 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10054 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10060 afterAction : function(action, success){
10061 this.activeAction = null;
10062 var o = action.options;
10067 Roo.get(document.body).unmask();
10073 //if(this.waitMsgTarget === true){
10074 // this.el.unmask();
10075 //}else if(this.waitMsgTarget){
10076 // this.waitMsgTarget.unmask();
10078 // Roo.MessageBox.updateProgress(1);
10079 // Roo.MessageBox.hide();
10086 Roo.callback(o.success, o.scope, [this, action]);
10087 this.fireEvent('actioncomplete', this, action);
10091 // failure condition..
10092 // we have a scenario where updates need confirming.
10093 // eg. if a locking scenario exists..
10094 // we look for { errors : { needs_confirm : true }} in the response.
10096 (typeof(action.result) != 'undefined') &&
10097 (typeof(action.result.errors) != 'undefined') &&
10098 (typeof(action.result.errors.needs_confirm) != 'undefined')
10101 Roo.log("not supported yet");
10104 Roo.MessageBox.confirm(
10105 "Change requires confirmation",
10106 action.result.errorMsg,
10111 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10121 Roo.callback(o.failure, o.scope, [this, action]);
10122 // show an error message if no failed handler is set..
10123 if (!this.hasListener('actionfailed')) {
10124 Roo.log("need to add dialog support");
10126 Roo.MessageBox.alert("Error",
10127 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10128 action.result.errorMsg :
10129 "Saving Failed, please check your entries or try again"
10134 this.fireEvent('actionfailed', this, action);
10139 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10140 * @param {String} id The value to search for
10143 findField : function(id){
10144 var items = this.getItems();
10145 var field = items.get(id);
10147 items.each(function(f){
10148 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10155 return field || null;
10158 * Mark fields in this form invalid in bulk.
10159 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10160 * @return {BasicForm} this
10162 markInvalid : function(errors){
10163 if(errors instanceof Array){
10164 for(var i = 0, len = errors.length; i < len; i++){
10165 var fieldError = errors[i];
10166 var f = this.findField(fieldError.id);
10168 f.markInvalid(fieldError.msg);
10174 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10175 field.markInvalid(errors[id]);
10179 //Roo.each(this.childForms || [], function (f) {
10180 // f.markInvalid(errors);
10187 * Set values for fields in this form in bulk.
10188 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10189 * @return {BasicForm} this
10191 setValues : function(values){
10192 if(values instanceof Array){ // array of objects
10193 for(var i = 0, len = values.length; i < len; i++){
10195 var f = this.findField(v.id);
10197 f.setValue(v.value);
10198 if(this.trackResetOnLoad){
10199 f.originalValue = f.getValue();
10203 }else{ // object hash
10206 if(typeof values[id] != 'function' && (field = this.findField(id))){
10208 if (field.setFromData &&
10209 field.valueField &&
10210 field.displayField &&
10211 // combos' with local stores can
10212 // be queried via setValue()
10213 // to set their value..
10214 (field.store && !field.store.isLocal)
10218 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10219 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10220 field.setFromData(sd);
10222 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10224 field.setFromData(values);
10227 field.setValue(values[id]);
10231 if(this.trackResetOnLoad){
10232 field.originalValue = field.getValue();
10238 //Roo.each(this.childForms || [], function (f) {
10239 // f.setValues(values);
10246 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10247 * they are returned as an array.
10248 * @param {Boolean} asString
10251 getValues : function(asString){
10252 //if (this.childForms) {
10253 // copy values from the child forms
10254 // Roo.each(this.childForms, function (f) {
10255 // this.setValues(f.getValues());
10261 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10262 if(asString === true){
10265 return Roo.urlDecode(fs);
10269 * Returns the fields in this form as an object with key/value pairs.
10270 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10273 getFieldValues : function(with_hidden)
10275 var items = this.getItems();
10277 items.each(function(f){
10279 if (!f.getName()) {
10283 var v = f.getValue();
10285 if (f.inputType =='radio') {
10286 if (typeof(ret[f.getName()]) == 'undefined') {
10287 ret[f.getName()] = ''; // empty..
10290 if (!f.el.dom.checked) {
10294 v = f.el.dom.value;
10298 if(f.xtype == 'MoneyField'){
10299 ret[f.currencyName] = f.getCurrency();
10302 // not sure if this supported any more..
10303 if ((typeof(v) == 'object') && f.getRawValue) {
10304 v = f.getRawValue() ; // dates..
10306 // combo boxes where name != hiddenName...
10307 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10308 ret[f.name] = f.getRawValue();
10310 ret[f.getName()] = v;
10317 * Clears all invalid messages in this form.
10318 * @return {BasicForm} this
10320 clearInvalid : function(){
10321 var items = this.getItems();
10323 items.each(function(f){
10331 * Resets this form.
10332 * @return {BasicForm} this
10334 reset : function(){
10335 var items = this.getItems();
10336 items.each(function(f){
10340 Roo.each(this.childForms || [], function (f) {
10348 getItems : function()
10350 var r=new Roo.util.MixedCollection(false, function(o){
10351 return o.id || (o.id = Roo.id());
10353 var iter = function(el) {
10360 Roo.each(el.items,function(e) {
10369 hideFields : function(items)
10371 Roo.each(items, function(i){
10373 var f = this.findField(i);
10384 showFields : function(items)
10386 Roo.each(items, function(i){
10388 var f = this.findField(i);
10401 Roo.apply(Roo.bootstrap.Form, {
10417 intervalID : false,
10423 if(this.isApplied){
10428 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10429 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10430 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10431 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10434 this.maskEl.top.enableDisplayMode("block");
10435 this.maskEl.left.enableDisplayMode("block");
10436 this.maskEl.bottom.enableDisplayMode("block");
10437 this.maskEl.right.enableDisplayMode("block");
10439 this.toolTip = new Roo.bootstrap.Tooltip({
10440 cls : 'roo-form-error-popover',
10442 'left' : ['r-l', [-2,0], 'right'],
10443 'right' : ['l-r', [2,0], 'left'],
10444 'bottom' : ['tl-bl', [0,2], 'top'],
10445 'top' : [ 'bl-tl', [0,-2], 'bottom']
10449 this.toolTip.render(Roo.get(document.body));
10451 this.toolTip.el.enableDisplayMode("block");
10453 Roo.get(document.body).on('click', function(){
10457 Roo.get(document.body).on('touchstart', function(){
10461 this.isApplied = true
10464 mask : function(form, target)
10468 this.target = target;
10470 if(!this.form.errorMask || !target.el){
10474 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10476 Roo.log(scrollable);
10478 var ot = this.target.el.calcOffsetsTo(scrollable);
10480 var scrollTo = ot[1] - this.form.maskOffset;
10482 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10484 scrollable.scrollTo('top', scrollTo);
10486 var box = this.target.el.getBox();
10488 var zIndex = Roo.bootstrap.Modal.zIndex++;
10491 this.maskEl.top.setStyle('position', 'absolute');
10492 this.maskEl.top.setStyle('z-index', zIndex);
10493 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10494 this.maskEl.top.setLeft(0);
10495 this.maskEl.top.setTop(0);
10496 this.maskEl.top.show();
10498 this.maskEl.left.setStyle('position', 'absolute');
10499 this.maskEl.left.setStyle('z-index', zIndex);
10500 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10501 this.maskEl.left.setLeft(0);
10502 this.maskEl.left.setTop(box.y - this.padding);
10503 this.maskEl.left.show();
10505 this.maskEl.bottom.setStyle('position', 'absolute');
10506 this.maskEl.bottom.setStyle('z-index', zIndex);
10507 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10508 this.maskEl.bottom.setLeft(0);
10509 this.maskEl.bottom.setTop(box.bottom + this.padding);
10510 this.maskEl.bottom.show();
10512 this.maskEl.right.setStyle('position', 'absolute');
10513 this.maskEl.right.setStyle('z-index', zIndex);
10514 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10515 this.maskEl.right.setLeft(box.right + this.padding);
10516 this.maskEl.right.setTop(box.y - this.padding);
10517 this.maskEl.right.show();
10519 this.toolTip.bindEl = this.target.el;
10521 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10523 var tip = this.target.blankText;
10525 if(this.target.getValue() !== '' ) {
10527 if (this.target.invalidText.length) {
10528 tip = this.target.invalidText;
10529 } else if (this.target.regexText.length){
10530 tip = this.target.regexText;
10534 this.toolTip.show(tip);
10536 this.intervalID = window.setInterval(function() {
10537 Roo.bootstrap.Form.popover.unmask();
10540 window.onwheel = function(){ return false;};
10542 (function(){ this.isMasked = true; }).defer(500, this);
10546 unmask : function()
10548 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10552 this.maskEl.top.setStyle('position', 'absolute');
10553 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10554 this.maskEl.top.hide();
10556 this.maskEl.left.setStyle('position', 'absolute');
10557 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10558 this.maskEl.left.hide();
10560 this.maskEl.bottom.setStyle('position', 'absolute');
10561 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10562 this.maskEl.bottom.hide();
10564 this.maskEl.right.setStyle('position', 'absolute');
10565 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10566 this.maskEl.right.hide();
10568 this.toolTip.hide();
10570 this.toolTip.el.hide();
10572 window.onwheel = function(){ return true;};
10574 if(this.intervalID){
10575 window.clearInterval(this.intervalID);
10576 this.intervalID = false;
10579 this.isMasked = false;
10589 * Ext JS Library 1.1.1
10590 * Copyright(c) 2006-2007, Ext JS, LLC.
10592 * Originally Released Under LGPL - original licence link has changed is not relivant.
10595 * <script type="text/javascript">
10598 * @class Roo.form.VTypes
10599 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10602 Roo.form.VTypes = function(){
10603 // closure these in so they are only created once.
10604 var alpha = /^[a-zA-Z_]+$/;
10605 var alphanum = /^[a-zA-Z0-9_]+$/;
10606 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10607 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10609 // All these messages and functions are configurable
10612 * The function used to validate email addresses
10613 * @param {String} value The email address
10615 'email' : function(v){
10616 return email.test(v);
10619 * The error text to display when the email validation function returns false
10622 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10624 * The keystroke filter mask to be applied on email input
10627 'emailMask' : /[a-z0-9_\.\-@]/i,
10630 * The function used to validate URLs
10631 * @param {String} value The URL
10633 'url' : function(v){
10634 return url.test(v);
10637 * The error text to display when the url validation function returns false
10640 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10643 * The function used to validate alpha values
10644 * @param {String} value The value
10646 'alpha' : function(v){
10647 return alpha.test(v);
10650 * The error text to display when the alpha validation function returns false
10653 'alphaText' : 'This field should only contain letters and _',
10655 * The keystroke filter mask to be applied on alpha input
10658 'alphaMask' : /[a-z_]/i,
10661 * The function used to validate alphanumeric values
10662 * @param {String} value The value
10664 'alphanum' : function(v){
10665 return alphanum.test(v);
10668 * The error text to display when the alphanumeric validation function returns false
10671 'alphanumText' : 'This field should only contain letters, numbers and _',
10673 * The keystroke filter mask to be applied on alphanumeric input
10676 'alphanumMask' : /[a-z0-9_]/i
10686 * @class Roo.bootstrap.Input
10687 * @extends Roo.bootstrap.Component
10688 * Bootstrap Input class
10689 * @cfg {Boolean} disabled is it disabled
10690 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10691 * @cfg {String} name name of the input
10692 * @cfg {string} fieldLabel - the label associated
10693 * @cfg {string} placeholder - placeholder to put in text.
10694 * @cfg {string} before - input group add on before
10695 * @cfg {string} after - input group add on after
10696 * @cfg {string} size - (lg|sm) or leave empty..
10697 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10698 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10699 * @cfg {Number} md colspan out of 12 for computer-sized screens
10700 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10701 * @cfg {string} value default value of the input
10702 * @cfg {Number} labelWidth set the width of label
10703 * @cfg {Number} labellg set the width of label (1-12)
10704 * @cfg {Number} labelmd set the width of label (1-12)
10705 * @cfg {Number} labelsm set the width of label (1-12)
10706 * @cfg {Number} labelxs set the width of label (1-12)
10707 * @cfg {String} labelAlign (top|left)
10708 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10709 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10710 * @cfg {String} indicatorpos (left|right) default left
10711 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10712 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10713 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10715 * @cfg {String} align (left|center|right) Default left
10716 * @cfg {Boolean} forceFeedback (true|false) Default false
10719 * Create a new Input
10720 * @param {Object} config The config object
10723 Roo.bootstrap.Input = function(config){
10725 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10730 * Fires when this field receives input focus.
10731 * @param {Roo.form.Field} this
10736 * Fires when this field loses input focus.
10737 * @param {Roo.form.Field} this
10741 * @event specialkey
10742 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10743 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10744 * @param {Roo.form.Field} this
10745 * @param {Roo.EventObject} e The event object
10750 * Fires just before the field blurs if the field value has changed.
10751 * @param {Roo.form.Field} this
10752 * @param {Mixed} newValue The new value
10753 * @param {Mixed} oldValue The original value
10758 * Fires after the field has been marked as invalid.
10759 * @param {Roo.form.Field} this
10760 * @param {String} msg The validation message
10765 * Fires after the field has been validated with no errors.
10766 * @param {Roo.form.Field} this
10771 * Fires after the key up
10772 * @param {Roo.form.Field} this
10773 * @param {Roo.EventObject} e The event Object
10778 * Fires after the user pastes into input
10779 * @param {Roo.form.Field} this
10780 * @param {Roo.EventObject} e The event Object
10786 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10788 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10789 automatic validation (defaults to "keyup").
10791 validationEvent : "keyup",
10793 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10795 validateOnBlur : true,
10797 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10799 validationDelay : 250,
10801 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10803 focusClass : "x-form-focus", // not needed???
10807 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10809 invalidClass : "has-warning",
10812 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10814 validClass : "has-success",
10817 * @cfg {Boolean} hasFeedback (true|false) default true
10819 hasFeedback : true,
10822 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10824 invalidFeedbackClass : "glyphicon-warning-sign",
10827 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10829 validFeedbackClass : "glyphicon-ok",
10832 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10834 selectOnFocus : false,
10837 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10841 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10846 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10848 disableKeyFilter : false,
10851 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10855 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10859 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10861 blankText : "Please complete this mandatory field",
10864 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10868 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10870 maxLength : Number.MAX_VALUE,
10872 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10874 minLengthText : "The minimum length for this field is {0}",
10876 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10878 maxLengthText : "The maximum length for this field is {0}",
10882 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10883 * If available, this function will be called only after the basic validators all return true, and will be passed the
10884 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10888 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10889 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10890 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10894 * @cfg {String} regexText -- Depricated - use Invalid Text
10899 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10905 autocomplete: false,
10909 inputType : 'text',
10912 placeholder: false,
10917 preventMark: false,
10918 isFormField : true,
10921 labelAlign : false,
10924 formatedValue : false,
10925 forceFeedback : false,
10927 indicatorpos : 'left',
10937 parentLabelAlign : function()
10940 while (parent.parent()) {
10941 parent = parent.parent();
10942 if (typeof(parent.labelAlign) !='undefined') {
10943 return parent.labelAlign;
10950 getAutoCreate : function()
10952 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10958 if(this.inputType != 'hidden'){
10959 cfg.cls = 'form-group' //input-group
10965 type : this.inputType,
10966 value : this.value,
10967 cls : 'form-control',
10968 placeholder : this.placeholder || '',
10969 autocomplete : this.autocomplete || 'new-password'
10971 if (this.inputType == 'file') {
10972 input.style = 'overflow:hidden'; // why not in CSS?
10975 if(this.capture.length){
10976 input.capture = this.capture;
10979 if(this.accept.length){
10980 input.accept = this.accept + "/*";
10984 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10987 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10988 input.maxLength = this.maxLength;
10991 if (this.disabled) {
10992 input.disabled=true;
10995 if (this.readOnly) {
10996 input.readonly=true;
11000 input.name = this.name;
11004 input.cls += ' input-' + this.size;
11008 ['xs','sm','md','lg'].map(function(size){
11009 if (settings[size]) {
11010 cfg.cls += ' col-' + size + '-' + settings[size];
11014 var inputblock = input;
11018 cls: 'glyphicon form-control-feedback'
11021 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11024 cls : 'has-feedback',
11032 if (this.before || this.after) {
11035 cls : 'input-group',
11039 if (this.before && typeof(this.before) == 'string') {
11041 inputblock.cn.push({
11043 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11047 if (this.before && typeof(this.before) == 'object') {
11048 this.before = Roo.factory(this.before);
11050 inputblock.cn.push({
11052 cls : 'roo-input-before input-group-prepend input-group-' +
11053 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11057 inputblock.cn.push(input);
11059 if (this.after && typeof(this.after) == 'string') {
11060 inputblock.cn.push({
11062 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11066 if (this.after && typeof(this.after) == 'object') {
11067 this.after = Roo.factory(this.after);
11069 inputblock.cn.push({
11071 cls : 'roo-input-after input-group-append input-group-' +
11072 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11076 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11077 inputblock.cls += ' has-feedback';
11078 inputblock.cn.push(feedback);
11083 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11084 tooltip : 'This field is required'
11086 if (this.allowBlank ) {
11087 indicator.style = this.allowBlank ? ' display:none' : '';
11089 if (align ==='left' && this.fieldLabel.length) {
11091 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11098 cls : 'control-label col-form-label',
11099 html : this.fieldLabel
11110 var labelCfg = cfg.cn[1];
11111 var contentCfg = cfg.cn[2];
11113 if(this.indicatorpos == 'right'){
11118 cls : 'control-label col-form-label',
11122 html : this.fieldLabel
11136 labelCfg = cfg.cn[0];
11137 contentCfg = cfg.cn[1];
11141 if(this.labelWidth > 12){
11142 labelCfg.style = "width: " + this.labelWidth + 'px';
11145 if(this.labelWidth < 13 && this.labelmd == 0){
11146 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11149 if(this.labellg > 0){
11150 labelCfg.cls += ' col-lg-' + this.labellg;
11151 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11154 if(this.labelmd > 0){
11155 labelCfg.cls += ' col-md-' + this.labelmd;
11156 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11159 if(this.labelsm > 0){
11160 labelCfg.cls += ' col-sm-' + this.labelsm;
11161 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11164 if(this.labelxs > 0){
11165 labelCfg.cls += ' col-xs-' + this.labelxs;
11166 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11170 } else if ( this.fieldLabel.length) {
11177 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11178 tooltip : 'This field is required',
11179 style : this.allowBlank ? ' display:none' : ''
11183 //cls : 'input-group-addon',
11184 html : this.fieldLabel
11192 if(this.indicatorpos == 'right'){
11197 //cls : 'input-group-addon',
11198 html : this.fieldLabel
11203 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11204 tooltip : 'This field is required',
11205 style : this.allowBlank ? ' display:none' : ''
11225 if (this.parentType === 'Navbar' && this.parent().bar) {
11226 cfg.cls += ' navbar-form';
11229 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11230 // on BS4 we do this only if not form
11231 cfg.cls += ' navbar-form';
11239 * return the real input element.
11241 inputEl: function ()
11243 return this.el.select('input.form-control',true).first();
11246 tooltipEl : function()
11248 return this.inputEl();
11251 indicatorEl : function()
11253 if (Roo.bootstrap.version == 4) {
11254 return false; // not enabled in v4 yet.
11257 var indicator = this.el.select('i.roo-required-indicator',true).first();
11267 setDisabled : function(v)
11269 var i = this.inputEl().dom;
11271 i.removeAttribute('disabled');
11275 i.setAttribute('disabled','true');
11277 initEvents : function()
11280 this.inputEl().on("keydown" , this.fireKey, this);
11281 this.inputEl().on("focus", this.onFocus, this);
11282 this.inputEl().on("blur", this.onBlur, this);
11284 this.inputEl().relayEvent('keyup', this);
11285 this.inputEl().relayEvent('paste', this);
11287 this.indicator = this.indicatorEl();
11289 if(this.indicator){
11290 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11293 // reference to original value for reset
11294 this.originalValue = this.getValue();
11295 //Roo.form.TextField.superclass.initEvents.call(this);
11296 if(this.validationEvent == 'keyup'){
11297 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11298 this.inputEl().on('keyup', this.filterValidation, this);
11300 else if(this.validationEvent !== false){
11301 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11304 if(this.selectOnFocus){
11305 this.on("focus", this.preFocus, this);
11308 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11309 this.inputEl().on("keypress", this.filterKeys, this);
11311 this.inputEl().relayEvent('keypress', this);
11314 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11315 this.el.on("click", this.autoSize, this);
11318 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11319 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11322 if (typeof(this.before) == 'object') {
11323 this.before.render(this.el.select('.roo-input-before',true).first());
11325 if (typeof(this.after) == 'object') {
11326 this.after.render(this.el.select('.roo-input-after',true).first());
11329 this.inputEl().on('change', this.onChange, this);
11332 filterValidation : function(e){
11333 if(!e.isNavKeyPress()){
11334 this.validationTask.delay(this.validationDelay);
11338 * Validates the field value
11339 * @return {Boolean} True if the value is valid, else false
11341 validate : function(){
11342 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11343 if(this.disabled || this.validateValue(this.getRawValue())){
11348 this.markInvalid();
11354 * Validates a value according to the field's validation rules and marks the field as invalid
11355 * if the validation fails
11356 * @param {Mixed} value The value to validate
11357 * @return {Boolean} True if the value is valid, else false
11359 validateValue : function(value)
11361 if(this.getVisibilityEl().hasClass('hidden')){
11365 if(value.length < 1) { // if it's blank
11366 if(this.allowBlank){
11372 if(value.length < this.minLength){
11375 if(value.length > this.maxLength){
11379 var vt = Roo.form.VTypes;
11380 if(!vt[this.vtype](value, this)){
11384 if(typeof this.validator == "function"){
11385 var msg = this.validator(value);
11389 if (typeof(msg) == 'string') {
11390 this.invalidText = msg;
11394 if(this.regex && !this.regex.test(value)){
11402 fireKey : function(e){
11403 //Roo.log('field ' + e.getKey());
11404 if(e.isNavKeyPress()){
11405 this.fireEvent("specialkey", this, e);
11408 focus : function (selectText){
11410 this.inputEl().focus();
11411 if(selectText === true){
11412 this.inputEl().dom.select();
11418 onFocus : function(){
11419 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11420 // this.el.addClass(this.focusClass);
11422 if(!this.hasFocus){
11423 this.hasFocus = true;
11424 this.startValue = this.getValue();
11425 this.fireEvent("focus", this);
11429 beforeBlur : Roo.emptyFn,
11433 onBlur : function(){
11435 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11436 //this.el.removeClass(this.focusClass);
11438 this.hasFocus = false;
11439 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11442 var v = this.getValue();
11443 if(String(v) !== String(this.startValue)){
11444 this.fireEvent('change', this, v, this.startValue);
11446 this.fireEvent("blur", this);
11449 onChange : function(e)
11451 var v = this.getValue();
11452 if(String(v) !== String(this.startValue)){
11453 this.fireEvent('change', this, v, this.startValue);
11459 * Resets the current field value to the originally loaded value and clears any validation messages
11461 reset : function(){
11462 this.setValue(this.originalValue);
11466 * Returns the name of the field
11467 * @return {Mixed} name The name field
11469 getName: function(){
11473 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11474 * @return {Mixed} value The field value
11476 getValue : function(){
11478 var v = this.inputEl().getValue();
11483 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11484 * @return {Mixed} value The field value
11486 getRawValue : function(){
11487 var v = this.inputEl().getValue();
11493 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11494 * @param {Mixed} value The value to set
11496 setRawValue : function(v){
11497 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11500 selectText : function(start, end){
11501 var v = this.getRawValue();
11503 start = start === undefined ? 0 : start;
11504 end = end === undefined ? v.length : end;
11505 var d = this.inputEl().dom;
11506 if(d.setSelectionRange){
11507 d.setSelectionRange(start, end);
11508 }else if(d.createTextRange){
11509 var range = d.createTextRange();
11510 range.moveStart("character", start);
11511 range.moveEnd("character", v.length-end);
11518 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11519 * @param {Mixed} value The value to set
11521 setValue : function(v){
11524 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11530 processValue : function(value){
11531 if(this.stripCharsRe){
11532 var newValue = value.replace(this.stripCharsRe, '');
11533 if(newValue !== value){
11534 this.setRawValue(newValue);
11541 preFocus : function(){
11543 if(this.selectOnFocus){
11544 this.inputEl().dom.select();
11547 filterKeys : function(e){
11548 var k = e.getKey();
11549 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11552 var c = e.getCharCode(), cc = String.fromCharCode(c);
11553 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11556 if(!this.maskRe.test(cc)){
11561 * Clear any invalid styles/messages for this field
11563 clearInvalid : function(){
11565 if(!this.el || this.preventMark){ // not rendered
11570 this.el.removeClass([this.invalidClass, 'is-invalid']);
11572 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11574 var feedback = this.el.select('.form-control-feedback', true).first();
11577 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11582 if(this.indicator){
11583 this.indicator.removeClass('visible');
11584 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11587 this.fireEvent('valid', this);
11591 * Mark this field as valid
11593 markValid : function()
11595 if(!this.el || this.preventMark){ // not rendered...
11599 this.el.removeClass([this.invalidClass, this.validClass]);
11600 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11602 var feedback = this.el.select('.form-control-feedback', true).first();
11605 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11608 if(this.indicator){
11609 this.indicator.removeClass('visible');
11610 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11618 if(this.allowBlank && !this.getRawValue().length){
11621 if (Roo.bootstrap.version == 3) {
11622 this.el.addClass(this.validClass);
11624 this.inputEl().addClass('is-valid');
11627 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11629 var feedback = this.el.select('.form-control-feedback', true).first();
11632 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11633 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11638 this.fireEvent('valid', this);
11642 * Mark this field as invalid
11643 * @param {String} msg The validation message
11645 markInvalid : function(msg)
11647 if(!this.el || this.preventMark){ // not rendered
11651 this.el.removeClass([this.invalidClass, this.validClass]);
11652 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11654 var feedback = this.el.select('.form-control-feedback', true).first();
11657 this.el.select('.form-control-feedback', true).first().removeClass(
11658 [this.invalidFeedbackClass, this.validFeedbackClass]);
11665 if(this.allowBlank && !this.getRawValue().length){
11669 if(this.indicator){
11670 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11671 this.indicator.addClass('visible');
11673 if (Roo.bootstrap.version == 3) {
11674 this.el.addClass(this.invalidClass);
11676 this.inputEl().addClass('is-invalid');
11681 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11683 var feedback = this.el.select('.form-control-feedback', true).first();
11686 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11688 if(this.getValue().length || this.forceFeedback){
11689 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11696 this.fireEvent('invalid', this, msg);
11699 SafariOnKeyDown : function(event)
11701 // this is a workaround for a password hang bug on chrome/ webkit.
11702 if (this.inputEl().dom.type != 'password') {
11706 var isSelectAll = false;
11708 if(this.inputEl().dom.selectionEnd > 0){
11709 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11711 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11712 event.preventDefault();
11717 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11719 event.preventDefault();
11720 // this is very hacky as keydown always get's upper case.
11722 var cc = String.fromCharCode(event.getCharCode());
11723 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11727 adjustWidth : function(tag, w){
11728 tag = tag.toLowerCase();
11729 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11730 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11731 if(tag == 'input'){
11734 if(tag == 'textarea'){
11737 }else if(Roo.isOpera){
11738 if(tag == 'input'){
11741 if(tag == 'textarea'){
11749 setFieldLabel : function(v)
11751 if(!this.rendered){
11755 if(this.indicatorEl()){
11756 var ar = this.el.select('label > span',true);
11758 if (ar.elements.length) {
11759 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11760 this.fieldLabel = v;
11764 var br = this.el.select('label',true);
11766 if(br.elements.length) {
11767 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11768 this.fieldLabel = v;
11772 Roo.log('Cannot Found any of label > span || label in input');
11776 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11777 this.fieldLabel = v;
11792 * @class Roo.bootstrap.TextArea
11793 * @extends Roo.bootstrap.Input
11794 * Bootstrap TextArea class
11795 * @cfg {Number} cols Specifies the visible width of a text area
11796 * @cfg {Number} rows Specifies the visible number of lines in a text area
11797 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11798 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11799 * @cfg {string} html text
11802 * Create a new TextArea
11803 * @param {Object} config The config object
11806 Roo.bootstrap.TextArea = function(config){
11807 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11811 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11821 getAutoCreate : function(){
11823 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11829 if(this.inputType != 'hidden'){
11830 cfg.cls = 'form-group' //input-group
11838 value : this.value || '',
11839 html: this.html || '',
11840 cls : 'form-control',
11841 placeholder : this.placeholder || ''
11845 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11846 input.maxLength = this.maxLength;
11850 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11854 input.cols = this.cols;
11857 if (this.readOnly) {
11858 input.readonly = true;
11862 input.name = this.name;
11866 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11870 ['xs','sm','md','lg'].map(function(size){
11871 if (settings[size]) {
11872 cfg.cls += ' col-' + size + '-' + settings[size];
11876 var inputblock = input;
11878 if(this.hasFeedback && !this.allowBlank){
11882 cls: 'glyphicon form-control-feedback'
11886 cls : 'has-feedback',
11895 if (this.before || this.after) {
11898 cls : 'input-group',
11902 inputblock.cn.push({
11904 cls : 'input-group-addon',
11909 inputblock.cn.push(input);
11911 if(this.hasFeedback && !this.allowBlank){
11912 inputblock.cls += ' has-feedback';
11913 inputblock.cn.push(feedback);
11917 inputblock.cn.push({
11919 cls : 'input-group-addon',
11926 if (align ==='left' && this.fieldLabel.length) {
11931 cls : 'control-label',
11932 html : this.fieldLabel
11943 if(this.labelWidth > 12){
11944 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11947 if(this.labelWidth < 13 && this.labelmd == 0){
11948 this.labelmd = this.labelWidth;
11951 if(this.labellg > 0){
11952 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11953 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11956 if(this.labelmd > 0){
11957 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11958 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11961 if(this.labelsm > 0){
11962 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11963 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11966 if(this.labelxs > 0){
11967 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11968 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11971 } else if ( this.fieldLabel.length) {
11976 //cls : 'input-group-addon',
11977 html : this.fieldLabel
11995 if (this.disabled) {
11996 input.disabled=true;
12003 * return the real textarea element.
12005 inputEl: function ()
12007 return this.el.select('textarea.form-control',true).first();
12011 * Clear any invalid styles/messages for this field
12013 clearInvalid : function()
12016 if(!this.el || this.preventMark){ // not rendered
12020 var label = this.el.select('label', true).first();
12021 var icon = this.el.select('i.fa-star', true).first();
12026 this.el.removeClass( this.validClass);
12027 this.inputEl().removeClass('is-invalid');
12029 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12031 var feedback = this.el.select('.form-control-feedback', true).first();
12034 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12039 this.fireEvent('valid', this);
12043 * Mark this field as valid
12045 markValid : function()
12047 if(!this.el || this.preventMark){ // not rendered
12051 this.el.removeClass([this.invalidClass, this.validClass]);
12052 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12054 var feedback = this.el.select('.form-control-feedback', true).first();
12057 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12060 if(this.disabled || this.allowBlank){
12064 var label = this.el.select('label', true).first();
12065 var icon = this.el.select('i.fa-star', true).first();
12070 if (Roo.bootstrap.version == 3) {
12071 this.el.addClass(this.validClass);
12073 this.inputEl().addClass('is-valid');
12077 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12079 var feedback = this.el.select('.form-control-feedback', true).first();
12082 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12083 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12088 this.fireEvent('valid', this);
12092 * Mark this field as invalid
12093 * @param {String} msg The validation message
12095 markInvalid : function(msg)
12097 if(!this.el || this.preventMark){ // not rendered
12101 this.el.removeClass([this.invalidClass, this.validClass]);
12102 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12104 var feedback = this.el.select('.form-control-feedback', true).first();
12107 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12110 if(this.disabled || this.allowBlank){
12114 var label = this.el.select('label', true).first();
12115 var icon = this.el.select('i.fa-star', true).first();
12117 if(!this.getValue().length && label && !icon){
12118 this.el.createChild({
12120 cls : 'text-danger fa fa-lg fa-star',
12121 tooltip : 'This field is required',
12122 style : 'margin-right:5px;'
12126 if (Roo.bootstrap.version == 3) {
12127 this.el.addClass(this.invalidClass);
12129 this.inputEl().addClass('is-invalid');
12132 // fixme ... this may be depricated need to test..
12133 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12135 var feedback = this.el.select('.form-control-feedback', true).first();
12138 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12140 if(this.getValue().length || this.forceFeedback){
12141 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12148 this.fireEvent('invalid', this, msg);
12156 * trigger field - base class for combo..
12161 * @class Roo.bootstrap.TriggerField
12162 * @extends Roo.bootstrap.Input
12163 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12164 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12165 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12166 * for which you can provide a custom implementation. For example:
12168 var trigger = new Roo.bootstrap.TriggerField();
12169 trigger.onTriggerClick = myTriggerFn;
12170 trigger.applyTo('my-field');
12173 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12174 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12175 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12176 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12177 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12180 * Create a new TriggerField.
12181 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12182 * to the base TextField)
12184 Roo.bootstrap.TriggerField = function(config){
12185 this.mimicing = false;
12186 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12189 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12191 * @cfg {String} triggerClass A CSS class to apply to the trigger
12194 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12199 * @cfg {Boolean} removable (true|false) special filter default false
12203 /** @cfg {Boolean} grow @hide */
12204 /** @cfg {Number} growMin @hide */
12205 /** @cfg {Number} growMax @hide */
12211 autoSize: Roo.emptyFn,
12215 deferHeight : true,
12218 actionMode : 'wrap',
12223 getAutoCreate : function(){
12225 var align = this.labelAlign || this.parentLabelAlign();
12230 cls: 'form-group' //input-group
12237 type : this.inputType,
12238 cls : 'form-control',
12239 autocomplete: 'new-password',
12240 placeholder : this.placeholder || ''
12244 input.name = this.name;
12247 input.cls += ' input-' + this.size;
12250 if (this.disabled) {
12251 input.disabled=true;
12254 var inputblock = input;
12256 if(this.hasFeedback && !this.allowBlank){
12260 cls: 'glyphicon form-control-feedback'
12263 if(this.removable && !this.editable ){
12265 cls : 'has-feedback',
12271 cls : 'roo-combo-removable-btn close'
12278 cls : 'has-feedback',
12287 if(this.removable && !this.editable ){
12289 cls : 'roo-removable',
12295 cls : 'roo-combo-removable-btn close'
12302 if (this.before || this.after) {
12305 cls : 'input-group',
12309 inputblock.cn.push({
12311 cls : 'input-group-addon input-group-prepend input-group-text',
12316 inputblock.cn.push(input);
12318 if(this.hasFeedback && !this.allowBlank){
12319 inputblock.cls += ' has-feedback';
12320 inputblock.cn.push(feedback);
12324 inputblock.cn.push({
12326 cls : 'input-group-addon input-group-append input-group-text',
12335 var ibwrap = inputblock;
12340 cls: 'roo-select2-choices',
12344 cls: 'roo-select2-search-field',
12356 cls: 'roo-select2-container input-group',
12361 cls: 'form-hidden-field'
12367 if(!this.multiple && this.showToggleBtn){
12373 if (this.caret != false) {
12376 cls: 'fa fa-' + this.caret
12383 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12385 Roo.bootstrap.version == 3 ? caret : '',
12388 cls: 'combobox-clear',
12402 combobox.cls += ' roo-select2-container-multi';
12406 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12407 tooltip : 'This field is required'
12409 if (Roo.bootstrap.version == 4) {
12412 style : 'display:none'
12417 if (align ==='left' && this.fieldLabel.length) {
12419 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12426 cls : 'control-label',
12427 html : this.fieldLabel
12439 var labelCfg = cfg.cn[1];
12440 var contentCfg = cfg.cn[2];
12442 if(this.indicatorpos == 'right'){
12447 cls : 'control-label',
12451 html : this.fieldLabel
12465 labelCfg = cfg.cn[0];
12466 contentCfg = cfg.cn[1];
12469 if(this.labelWidth > 12){
12470 labelCfg.style = "width: " + this.labelWidth + 'px';
12473 if(this.labelWidth < 13 && this.labelmd == 0){
12474 this.labelmd = this.labelWidth;
12477 if(this.labellg > 0){
12478 labelCfg.cls += ' col-lg-' + this.labellg;
12479 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12482 if(this.labelmd > 0){
12483 labelCfg.cls += ' col-md-' + this.labelmd;
12484 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12487 if(this.labelsm > 0){
12488 labelCfg.cls += ' col-sm-' + this.labelsm;
12489 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12492 if(this.labelxs > 0){
12493 labelCfg.cls += ' col-xs-' + this.labelxs;
12494 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12497 } else if ( this.fieldLabel.length) {
12498 // Roo.log(" label");
12503 //cls : 'input-group-addon',
12504 html : this.fieldLabel
12512 if(this.indicatorpos == 'right'){
12520 html : this.fieldLabel
12534 // Roo.log(" no label && no align");
12541 ['xs','sm','md','lg'].map(function(size){
12542 if (settings[size]) {
12543 cfg.cls += ' col-' + size + '-' + settings[size];
12554 onResize : function(w, h){
12555 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12556 // if(typeof w == 'number'){
12557 // var x = w - this.trigger.getWidth();
12558 // this.inputEl().setWidth(this.adjustWidth('input', x));
12559 // this.trigger.setStyle('left', x+'px');
12564 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12567 getResizeEl : function(){
12568 return this.inputEl();
12572 getPositionEl : function(){
12573 return this.inputEl();
12577 alignErrorIcon : function(){
12578 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12582 initEvents : function(){
12586 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12587 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12588 if(!this.multiple && this.showToggleBtn){
12589 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12590 if(this.hideTrigger){
12591 this.trigger.setDisplayed(false);
12593 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12597 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12600 if(this.removable && !this.editable && !this.tickable){
12601 var close = this.closeTriggerEl();
12604 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12605 close.on('click', this.removeBtnClick, this, close);
12609 //this.trigger.addClassOnOver('x-form-trigger-over');
12610 //this.trigger.addClassOnClick('x-form-trigger-click');
12613 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12617 closeTriggerEl : function()
12619 var close = this.el.select('.roo-combo-removable-btn', true).first();
12620 return close ? close : false;
12623 removeBtnClick : function(e, h, el)
12625 e.preventDefault();
12627 if(this.fireEvent("remove", this) !== false){
12629 this.fireEvent("afterremove", this)
12633 createList : function()
12635 this.list = Roo.get(document.body).createChild({
12636 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12637 cls: 'typeahead typeahead-long dropdown-menu shadow',
12638 style: 'display:none'
12641 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12646 initTrigger : function(){
12651 onDestroy : function(){
12653 this.trigger.removeAllListeners();
12654 // this.trigger.remove();
12657 // this.wrap.remove();
12659 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12663 onFocus : function(){
12664 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12666 if(!this.mimicing){
12667 this.wrap.addClass('x-trigger-wrap-focus');
12668 this.mimicing = true;
12669 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12670 if(this.monitorTab){
12671 this.el.on("keydown", this.checkTab, this);
12678 checkTab : function(e){
12679 if(e.getKey() == e.TAB){
12680 this.triggerBlur();
12685 onBlur : function(){
12690 mimicBlur : function(e, t){
12692 if(!this.wrap.contains(t) && this.validateBlur()){
12693 this.triggerBlur();
12699 triggerBlur : function(){
12700 this.mimicing = false;
12701 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12702 if(this.monitorTab){
12703 this.el.un("keydown", this.checkTab, this);
12705 //this.wrap.removeClass('x-trigger-wrap-focus');
12706 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12710 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12711 validateBlur : function(e, t){
12716 onDisable : function(){
12717 this.inputEl().dom.disabled = true;
12718 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12720 // this.wrap.addClass('x-item-disabled');
12725 onEnable : function(){
12726 this.inputEl().dom.disabled = false;
12727 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12729 // this.el.removeClass('x-item-disabled');
12734 onShow : function(){
12735 var ae = this.getActionEl();
12738 ae.dom.style.display = '';
12739 ae.dom.style.visibility = 'visible';
12745 onHide : function(){
12746 var ae = this.getActionEl();
12747 ae.dom.style.display = 'none';
12751 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12752 * by an implementing function.
12754 * @param {EventObject} e
12756 onTriggerClick : Roo.emptyFn
12764 * @class Roo.bootstrap.CardUploader
12765 * @extends Roo.bootstrap.Button
12766 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12767 * @cfg {Number} errorTimeout default 3000
12768 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12769 * @cfg {Array} html The button text.
12773 * Create a new CardUploader
12774 * @param {Object} config The config object
12777 Roo.bootstrap.CardUploader = function(config){
12781 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12784 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12792 * When a image is clicked on - and needs to display a slideshow or similar..
12793 * @param {Roo.bootstrap.Card} this
12794 * @param {Object} The image information data
12800 * When a the download link is clicked
12801 * @param {Roo.bootstrap.Card} this
12802 * @param {Object} The image information data contains
12809 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12812 errorTimeout : 3000,
12816 fileCollection : false,
12819 getAutoCreate : function()
12823 cls :'form-group' ,
12828 //cls : 'input-group-addon',
12829 html : this.fieldLabel
12837 value : this.value,
12838 cls : 'd-none form-control'
12843 multiple : 'multiple',
12845 cls : 'd-none roo-card-upload-selector'
12849 cls : 'roo-card-uploader-button-container w-100 mb-2'
12852 cls : 'card-columns roo-card-uploader-container'
12862 getChildContainer : function() /// what children are added to.
12864 return this.containerEl;
12867 getButtonContainer : function() /// what children are added to.
12869 return this.el.select(".roo-card-uploader-button-container").first();
12872 initEvents : function()
12875 Roo.bootstrap.Input.prototype.initEvents.call(this);
12879 xns: Roo.bootstrap,
12882 container_method : 'getButtonContainer' ,
12883 html : this.html, // fix changable?
12886 'click' : function(btn, e) {
12895 this.urlAPI = (window.createObjectURL && window) ||
12896 (window.URL && URL.revokeObjectURL && URL) ||
12897 (window.webkitURL && webkitURL);
12902 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12904 this.selectorEl.on('change', this.onFileSelected, this);
12907 this.images.forEach(function(img) {
12910 this.images = false;
12912 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12918 onClick : function(e)
12920 e.preventDefault();
12922 this.selectorEl.dom.click();
12926 onFileSelected : function(e)
12928 e.preventDefault();
12930 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12934 Roo.each(this.selectorEl.dom.files, function(file){
12935 this.addFile(file);
12944 addFile : function(file)
12947 if(typeof(file) === 'string'){
12948 throw "Add file by name?"; // should not happen
12952 if(!file || !this.urlAPI){
12962 var url = _this.urlAPI.createObjectURL( file);
12965 id : Roo.bootstrap.CardUploader.ID--,
12966 is_uploaded : false,
12970 mimetype : file.type,
12978 * addCard - add an Attachment to the uploader
12979 * @param data - the data about the image to upload
12983 title : "Title of file",
12984 is_uploaded : false,
12985 src : "http://.....",
12986 srcfile : { the File upload object },
12987 mimetype : file.type,
12990 .. any other data...
12996 addCard : function (data)
12998 // hidden input element?
12999 // if the file is not an image...
13000 //then we need to use something other that and header_image
13005 xns : Roo.bootstrap,
13006 xtype : 'CardFooter',
13009 xns : Roo.bootstrap,
13015 xns : Roo.bootstrap,
13017 html : String.format("<small>{0}</small>", data.title),
13018 cls : 'col-10 text-left',
13023 click : function() {
13025 t.fireEvent( "download", t, data );
13031 xns : Roo.bootstrap,
13033 style: 'max-height: 28px; ',
13039 click : function() {
13040 t.removeCard(data.id)
13052 var cn = this.addxtype(
13055 xns : Roo.bootstrap,
13058 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13059 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13060 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13065 initEvents : function() {
13066 Roo.bootstrap.Card.prototype.initEvents.call(this);
13068 this.imgEl = this.el.select('.card-img-top').first();
13070 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13071 this.imgEl.set({ 'pointer' : 'cursor' });
13074 this.getCardFooter().addClass('p-1');
13081 // dont' really need ot update items.
13082 // this.items.push(cn);
13083 this.fileCollection.add(cn);
13085 if (!data.srcfile) {
13086 this.updateInput();
13091 var reader = new FileReader();
13092 reader.addEventListener("load", function() {
13093 data.srcdata = reader.result;
13096 reader.readAsDataURL(data.srcfile);
13101 removeCard : function(id)
13104 var card = this.fileCollection.get(id);
13105 card.data.is_deleted = 1;
13106 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13107 //this.fileCollection.remove(card);
13108 //this.items = this.items.filter(function(e) { return e != card });
13109 // dont' really need ot update items.
13110 card.el.dom.parentNode.removeChild(card.el.dom);
13111 this.updateInput();
13117 this.fileCollection.each(function(card) {
13118 if (card.el.dom && card.el.dom.parentNode) {
13119 card.el.dom.parentNode.removeChild(card.el.dom);
13122 this.fileCollection.clear();
13123 this.updateInput();
13126 updateInput : function()
13129 this.fileCollection.each(function(e) {
13133 this.inputEl().dom.value = JSON.stringify(data);
13143 Roo.bootstrap.CardUploader.ID = -1;/*
13145 * Ext JS Library 1.1.1
13146 * Copyright(c) 2006-2007, Ext JS, LLC.
13148 * Originally Released Under LGPL - original licence link has changed is not relivant.
13151 * <script type="text/javascript">
13156 * @class Roo.data.SortTypes
13158 * Defines the default sorting (casting?) comparison functions used when sorting data.
13160 Roo.data.SortTypes = {
13162 * Default sort that does nothing
13163 * @param {Mixed} s The value being converted
13164 * @return {Mixed} The comparison value
13166 none : function(s){
13171 * The regular expression used to strip tags
13175 stripTagsRE : /<\/?[^>]+>/gi,
13178 * Strips all HTML tags to sort on text only
13179 * @param {Mixed} s The value being converted
13180 * @return {String} The comparison value
13182 asText : function(s){
13183 return String(s).replace(this.stripTagsRE, "");
13187 * Strips all HTML tags to sort on text only - Case insensitive
13188 * @param {Mixed} s The value being converted
13189 * @return {String} The comparison value
13191 asUCText : function(s){
13192 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13196 * Case insensitive string
13197 * @param {Mixed} s The value being converted
13198 * @return {String} The comparison value
13200 asUCString : function(s) {
13201 return String(s).toUpperCase();
13206 * @param {Mixed} s The value being converted
13207 * @return {Number} The comparison value
13209 asDate : function(s) {
13213 if(s instanceof Date){
13214 return s.getTime();
13216 return Date.parse(String(s));
13221 * @param {Mixed} s The value being converted
13222 * @return {Float} The comparison value
13224 asFloat : function(s) {
13225 var val = parseFloat(String(s).replace(/,/g, ""));
13234 * @param {Mixed} s The value being converted
13235 * @return {Number} The comparison value
13237 asInt : function(s) {
13238 var val = parseInt(String(s).replace(/,/g, ""));
13246 * Ext JS Library 1.1.1
13247 * Copyright(c) 2006-2007, Ext JS, LLC.
13249 * Originally Released Under LGPL - original licence link has changed is not relivant.
13252 * <script type="text/javascript">
13256 * @class Roo.data.Record
13257 * Instances of this class encapsulate both record <em>definition</em> information, and record
13258 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13259 * to access Records cached in an {@link Roo.data.Store} object.<br>
13261 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13262 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13265 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13267 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13268 * {@link #create}. The parameters are the same.
13269 * @param {Array} data An associative Array of data values keyed by the field name.
13270 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13271 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13272 * not specified an integer id is generated.
13274 Roo.data.Record = function(data, id){
13275 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13280 * Generate a constructor for a specific record layout.
13281 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13282 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13283 * Each field definition object may contain the following properties: <ul>
13284 * <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,
13285 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13286 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13287 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13288 * is being used, then this is a string containing the javascript expression to reference the data relative to
13289 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13290 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13291 * this may be omitted.</p></li>
13292 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13293 * <ul><li>auto (Default, implies no conversion)</li>
13298 * <li>date</li></ul></p></li>
13299 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13300 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13301 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13302 * by the Reader into an object that will be stored in the Record. It is passed the
13303 * following parameters:<ul>
13304 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13306 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13308 * <br>usage:<br><pre><code>
13309 var TopicRecord = Roo.data.Record.create(
13310 {name: 'title', mapping: 'topic_title'},
13311 {name: 'author', mapping: 'username'},
13312 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13313 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13314 {name: 'lastPoster', mapping: 'user2'},
13315 {name: 'excerpt', mapping: 'post_text'}
13318 var myNewRecord = new TopicRecord({
13319 title: 'Do my job please',
13322 lastPost: new Date(),
13323 lastPoster: 'Animal',
13324 excerpt: 'No way dude!'
13326 myStore.add(myNewRecord);
13331 Roo.data.Record.create = function(o){
13332 var f = function(){
13333 f.superclass.constructor.apply(this, arguments);
13335 Roo.extend(f, Roo.data.Record);
13336 var p = f.prototype;
13337 p.fields = new Roo.util.MixedCollection(false, function(field){
13340 for(var i = 0, len = o.length; i < len; i++){
13341 p.fields.add(new Roo.data.Field(o[i]));
13343 f.getField = function(name){
13344 return p.fields.get(name);
13349 Roo.data.Record.AUTO_ID = 1000;
13350 Roo.data.Record.EDIT = 'edit';
13351 Roo.data.Record.REJECT = 'reject';
13352 Roo.data.Record.COMMIT = 'commit';
13354 Roo.data.Record.prototype = {
13356 * Readonly flag - true if this record has been modified.
13365 join : function(store){
13366 this.store = store;
13370 * Set the named field to the specified value.
13371 * @param {String} name The name of the field to set.
13372 * @param {Object} value The value to set the field to.
13374 set : function(name, value){
13375 if(this.data[name] == value){
13379 if(!this.modified){
13380 this.modified = {};
13382 if(typeof this.modified[name] == 'undefined'){
13383 this.modified[name] = this.data[name];
13385 this.data[name] = value;
13386 if(!this.editing && this.store){
13387 this.store.afterEdit(this);
13392 * Get the value of the named field.
13393 * @param {String} name The name of the field to get the value of.
13394 * @return {Object} The value of the field.
13396 get : function(name){
13397 return this.data[name];
13401 beginEdit : function(){
13402 this.editing = true;
13403 this.modified = {};
13407 cancelEdit : function(){
13408 this.editing = false;
13409 delete this.modified;
13413 endEdit : function(){
13414 this.editing = false;
13415 if(this.dirty && this.store){
13416 this.store.afterEdit(this);
13421 * Usually called by the {@link Roo.data.Store} which owns the Record.
13422 * Rejects all changes made to the Record since either creation, or the last commit operation.
13423 * Modified fields are reverted to their original values.
13425 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13426 * of reject operations.
13428 reject : function(){
13429 var m = this.modified;
13431 if(typeof m[n] != "function"){
13432 this.data[n] = m[n];
13435 this.dirty = false;
13436 delete this.modified;
13437 this.editing = false;
13439 this.store.afterReject(this);
13444 * Usually called by the {@link Roo.data.Store} which owns the Record.
13445 * Commits all changes made to the Record since either creation, or the last commit operation.
13447 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13448 * of commit operations.
13450 commit : function(){
13451 this.dirty = false;
13452 delete this.modified;
13453 this.editing = false;
13455 this.store.afterCommit(this);
13460 hasError : function(){
13461 return this.error != null;
13465 clearError : function(){
13470 * Creates a copy of this record.
13471 * @param {String} id (optional) A new record id if you don't want to use this record's id
13474 copy : function(newId) {
13475 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13479 * Ext JS Library 1.1.1
13480 * Copyright(c) 2006-2007, Ext JS, LLC.
13482 * Originally Released Under LGPL - original licence link has changed is not relivant.
13485 * <script type="text/javascript">
13491 * @class Roo.data.Store
13492 * @extends Roo.util.Observable
13493 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13494 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13496 * 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
13497 * has no knowledge of the format of the data returned by the Proxy.<br>
13499 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13500 * instances from the data object. These records are cached and made available through accessor functions.
13502 * Creates a new Store.
13503 * @param {Object} config A config object containing the objects needed for the Store to access data,
13504 * and read the data into Records.
13506 Roo.data.Store = function(config){
13507 this.data = new Roo.util.MixedCollection(false);
13508 this.data.getKey = function(o){
13511 this.baseParams = {};
13513 this.paramNames = {
13518 "multisort" : "_multisort"
13521 if(config && config.data){
13522 this.inlineData = config.data;
13523 delete config.data;
13526 Roo.apply(this, config);
13528 if(this.reader){ // reader passed
13529 this.reader = Roo.factory(this.reader, Roo.data);
13530 this.reader.xmodule = this.xmodule || false;
13531 if(!this.recordType){
13532 this.recordType = this.reader.recordType;
13534 if(this.reader.onMetaChange){
13535 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13539 if(this.recordType){
13540 this.fields = this.recordType.prototype.fields;
13542 this.modified = [];
13546 * @event datachanged
13547 * Fires when the data cache has changed, and a widget which is using this Store
13548 * as a Record cache should refresh its view.
13549 * @param {Store} this
13551 datachanged : true,
13553 * @event metachange
13554 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13555 * @param {Store} this
13556 * @param {Object} meta The JSON metadata
13561 * Fires when Records have been added to the Store
13562 * @param {Store} this
13563 * @param {Roo.data.Record[]} records The array of Records added
13564 * @param {Number} index The index at which the record(s) were added
13569 * Fires when a Record has been removed from the Store
13570 * @param {Store} this
13571 * @param {Roo.data.Record} record The Record that was removed
13572 * @param {Number} index The index at which the record was removed
13577 * Fires when a Record has been updated
13578 * @param {Store} this
13579 * @param {Roo.data.Record} record The Record that was updated
13580 * @param {String} operation The update operation being performed. Value may be one of:
13582 Roo.data.Record.EDIT
13583 Roo.data.Record.REJECT
13584 Roo.data.Record.COMMIT
13590 * Fires when the data cache has been cleared.
13591 * @param {Store} this
13595 * @event beforeload
13596 * Fires before a request is made for a new data object. If the beforeload handler returns false
13597 * the load action will be canceled.
13598 * @param {Store} this
13599 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13603 * @event beforeloadadd
13604 * Fires after a new set of Records has been loaded.
13605 * @param {Store} this
13606 * @param {Roo.data.Record[]} records The Records that were loaded
13607 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13609 beforeloadadd : true,
13612 * Fires after a new set of Records has been loaded, before they are added to the store.
13613 * @param {Store} this
13614 * @param {Roo.data.Record[]} records The Records that were loaded
13615 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13616 * @params {Object} return from reader
13620 * @event loadexception
13621 * Fires if an exception occurs in the Proxy during loading.
13622 * Called with the signature of the Proxy's "loadexception" event.
13623 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13626 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13627 * @param {Object} load options
13628 * @param {Object} jsonData from your request (normally this contains the Exception)
13630 loadexception : true
13634 this.proxy = Roo.factory(this.proxy, Roo.data);
13635 this.proxy.xmodule = this.xmodule || false;
13636 this.relayEvents(this.proxy, ["loadexception"]);
13638 this.sortToggle = {};
13639 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13641 Roo.data.Store.superclass.constructor.call(this);
13643 if(this.inlineData){
13644 this.loadData(this.inlineData);
13645 delete this.inlineData;
13649 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13651 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13652 * without a remote query - used by combo/forms at present.
13656 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13659 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13662 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13663 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13666 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13667 * on any HTTP request
13670 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13673 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13677 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13678 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13680 remoteSort : false,
13683 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13684 * loaded or when a record is removed. (defaults to false).
13686 pruneModifiedRecords : false,
13689 lastOptions : null,
13692 * Add Records to the Store and fires the add event.
13693 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13695 add : function(records){
13696 records = [].concat(records);
13697 for(var i = 0, len = records.length; i < len; i++){
13698 records[i].join(this);
13700 var index = this.data.length;
13701 this.data.addAll(records);
13702 this.fireEvent("add", this, records, index);
13706 * Remove a Record from the Store and fires the remove event.
13707 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13709 remove : function(record){
13710 var index = this.data.indexOf(record);
13711 this.data.removeAt(index);
13713 if(this.pruneModifiedRecords){
13714 this.modified.remove(record);
13716 this.fireEvent("remove", this, record, index);
13720 * Remove all Records from the Store and fires the clear event.
13722 removeAll : function(){
13724 if(this.pruneModifiedRecords){
13725 this.modified = [];
13727 this.fireEvent("clear", this);
13731 * Inserts Records to the Store at the given index and fires the add event.
13732 * @param {Number} index The start index at which to insert the passed Records.
13733 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13735 insert : function(index, records){
13736 records = [].concat(records);
13737 for(var i = 0, len = records.length; i < len; i++){
13738 this.data.insert(index, records[i]);
13739 records[i].join(this);
13741 this.fireEvent("add", this, records, index);
13745 * Get the index within the cache of the passed Record.
13746 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13747 * @return {Number} The index of the passed Record. Returns -1 if not found.
13749 indexOf : function(record){
13750 return this.data.indexOf(record);
13754 * Get the index within the cache of the Record with the passed id.
13755 * @param {String} id The id of the Record to find.
13756 * @return {Number} The index of the Record. Returns -1 if not found.
13758 indexOfId : function(id){
13759 return this.data.indexOfKey(id);
13763 * Get the Record with the specified id.
13764 * @param {String} id The id of the Record to find.
13765 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13767 getById : function(id){
13768 return this.data.key(id);
13772 * Get the Record at the specified index.
13773 * @param {Number} index The index of the Record to find.
13774 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13776 getAt : function(index){
13777 return this.data.itemAt(index);
13781 * Returns a range of Records between specified indices.
13782 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13783 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13784 * @return {Roo.data.Record[]} An array of Records
13786 getRange : function(start, end){
13787 return this.data.getRange(start, end);
13791 storeOptions : function(o){
13792 o = Roo.apply({}, o);
13795 this.lastOptions = o;
13799 * Loads the Record cache from the configured Proxy using the configured Reader.
13801 * If using remote paging, then the first load call must specify the <em>start</em>
13802 * and <em>limit</em> properties in the options.params property to establish the initial
13803 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13805 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13806 * and this call will return before the new data has been loaded. Perform any post-processing
13807 * in a callback function, or in a "load" event handler.</strong>
13809 * @param {Object} options An object containing properties which control loading options:<ul>
13810 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13811 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13812 * passed the following arguments:<ul>
13813 * <li>r : Roo.data.Record[]</li>
13814 * <li>options: Options object from the load call</li>
13815 * <li>success: Boolean success indicator</li></ul></li>
13816 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13817 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13820 load : function(options){
13821 options = options || {};
13822 if(this.fireEvent("beforeload", this, options) !== false){
13823 this.storeOptions(options);
13824 var p = Roo.apply(options.params || {}, this.baseParams);
13825 // if meta was not loaded from remote source.. try requesting it.
13826 if (!this.reader.metaFromRemote) {
13827 p._requestMeta = 1;
13829 if(this.sortInfo && this.remoteSort){
13830 var pn = this.paramNames;
13831 p[pn["sort"]] = this.sortInfo.field;
13832 p[pn["dir"]] = this.sortInfo.direction;
13834 if (this.multiSort) {
13835 var pn = this.paramNames;
13836 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13839 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13844 * Reloads the Record cache from the configured Proxy using the configured Reader and
13845 * the options from the last load operation performed.
13846 * @param {Object} options (optional) An object containing properties which may override the options
13847 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13848 * the most recently used options are reused).
13850 reload : function(options){
13851 this.load(Roo.applyIf(options||{}, this.lastOptions));
13855 // Called as a callback by the Reader during a load operation.
13856 loadRecords : function(o, options, success){
13857 if(!o || success === false){
13858 if(success !== false){
13859 this.fireEvent("load", this, [], options, o);
13861 if(options.callback){
13862 options.callback.call(options.scope || this, [], options, false);
13866 // if data returned failure - throw an exception.
13867 if (o.success === false) {
13868 // show a message if no listener is registered.
13869 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13870 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13872 // loadmask wil be hooked into this..
13873 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13876 var r = o.records, t = o.totalRecords || r.length;
13878 this.fireEvent("beforeloadadd", this, r, options, o);
13880 if(!options || options.add !== true){
13881 if(this.pruneModifiedRecords){
13882 this.modified = [];
13884 for(var i = 0, len = r.length; i < len; i++){
13888 this.data = this.snapshot;
13889 delete this.snapshot;
13892 this.data.addAll(r);
13893 this.totalLength = t;
13895 this.fireEvent("datachanged", this);
13897 this.totalLength = Math.max(t, this.data.length+r.length);
13901 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13903 var e = new Roo.data.Record({});
13905 e.set(this.parent.displayField, this.parent.emptyTitle);
13906 e.set(this.parent.valueField, '');
13911 this.fireEvent("load", this, r, options, o);
13912 if(options.callback){
13913 options.callback.call(options.scope || this, r, options, true);
13919 * Loads data from a passed data block. A Reader which understands the format of the data
13920 * must have been configured in the constructor.
13921 * @param {Object} data The data block from which to read the Records. The format of the data expected
13922 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13923 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13925 loadData : function(o, append){
13926 var r = this.reader.readRecords(o);
13927 this.loadRecords(r, {add: append}, true);
13931 * using 'cn' the nested child reader read the child array into it's child stores.
13932 * @param {Object} rec The record with a 'children array
13934 loadDataFromChildren : function(rec)
13936 this.loadData(this.reader.toLoadData(rec));
13941 * Gets the number of cached records.
13943 * <em>If using paging, this may not be the total size of the dataset. If the data object
13944 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13945 * the data set size</em>
13947 getCount : function(){
13948 return this.data.length || 0;
13952 * Gets the total number of records in the dataset as returned by the server.
13954 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13955 * the dataset size</em>
13957 getTotalCount : function(){
13958 return this.totalLength || 0;
13962 * Returns the sort state of the Store as an object with two properties:
13964 field {String} The name of the field by which the Records are sorted
13965 direction {String} The sort order, "ASC" or "DESC"
13968 getSortState : function(){
13969 return this.sortInfo;
13973 applySort : function(){
13974 if(this.sortInfo && !this.remoteSort){
13975 var s = this.sortInfo, f = s.field;
13976 var st = this.fields.get(f).sortType;
13977 var fn = function(r1, r2){
13978 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13979 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13981 this.data.sort(s.direction, fn);
13982 if(this.snapshot && this.snapshot != this.data){
13983 this.snapshot.sort(s.direction, fn);
13989 * Sets the default sort column and order to be used by the next load operation.
13990 * @param {String} fieldName The name of the field to sort by.
13991 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13993 setDefaultSort : function(field, dir){
13994 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13998 * Sort the Records.
13999 * If remote sorting is used, the sort is performed on the server, and the cache is
14000 * reloaded. If local sorting is used, the cache is sorted internally.
14001 * @param {String} fieldName The name of the field to sort by.
14002 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14004 sort : function(fieldName, dir){
14005 var f = this.fields.get(fieldName);
14007 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14009 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14010 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14015 this.sortToggle[f.name] = dir;
14016 this.sortInfo = {field: f.name, direction: dir};
14017 if(!this.remoteSort){
14019 this.fireEvent("datachanged", this);
14021 this.load(this.lastOptions);
14026 * Calls the specified function for each of the Records in the cache.
14027 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14028 * Returning <em>false</em> aborts and exits the iteration.
14029 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14031 each : function(fn, scope){
14032 this.data.each(fn, scope);
14036 * Gets all records modified since the last commit. Modified records are persisted across load operations
14037 * (e.g., during paging).
14038 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14040 getModifiedRecords : function(){
14041 return this.modified;
14045 createFilterFn : function(property, value, anyMatch){
14046 if(!value.exec){ // not a regex
14047 value = String(value);
14048 if(value.length == 0){
14051 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14053 return function(r){
14054 return value.test(r.data[property]);
14059 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14060 * @param {String} property A field on your records
14061 * @param {Number} start The record index to start at (defaults to 0)
14062 * @param {Number} end The last record index to include (defaults to length - 1)
14063 * @return {Number} The sum
14065 sum : function(property, start, end){
14066 var rs = this.data.items, v = 0;
14067 start = start || 0;
14068 end = (end || end === 0) ? end : rs.length-1;
14070 for(var i = start; i <= end; i++){
14071 v += (rs[i].data[property] || 0);
14077 * Filter the records by a specified property.
14078 * @param {String} field A field on your records
14079 * @param {String/RegExp} value Either a string that the field
14080 * should start with or a RegExp to test against the field
14081 * @param {Boolean} anyMatch True to match any part not just the beginning
14083 filter : function(property, value, anyMatch){
14084 var fn = this.createFilterFn(property, value, anyMatch);
14085 return fn ? this.filterBy(fn) : this.clearFilter();
14089 * Filter by a function. The specified function will be called with each
14090 * record in this data source. If the function returns true the record is included,
14091 * otherwise it is filtered.
14092 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14093 * @param {Object} scope (optional) The scope of the function (defaults to this)
14095 filterBy : function(fn, scope){
14096 this.snapshot = this.snapshot || this.data;
14097 this.data = this.queryBy(fn, scope||this);
14098 this.fireEvent("datachanged", this);
14102 * Query the records by a specified property.
14103 * @param {String} field A field on your records
14104 * @param {String/RegExp} value Either a string that the field
14105 * should start with or a RegExp to test against the field
14106 * @param {Boolean} anyMatch True to match any part not just the beginning
14107 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14109 query : function(property, value, anyMatch){
14110 var fn = this.createFilterFn(property, value, anyMatch);
14111 return fn ? this.queryBy(fn) : this.data.clone();
14115 * Query by a function. The specified function will be called with each
14116 * record in this data source. If the function returns true the record is included
14118 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14119 * @param {Object} scope (optional) The scope of the function (defaults to this)
14120 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14122 queryBy : function(fn, scope){
14123 var data = this.snapshot || this.data;
14124 return data.filterBy(fn, scope||this);
14128 * Collects unique values for a particular dataIndex from this store.
14129 * @param {String} dataIndex The property to collect
14130 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14131 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14132 * @return {Array} An array of the unique values
14134 collect : function(dataIndex, allowNull, bypassFilter){
14135 var d = (bypassFilter === true && this.snapshot) ?
14136 this.snapshot.items : this.data.items;
14137 var v, sv, r = [], l = {};
14138 for(var i = 0, len = d.length; i < len; i++){
14139 v = d[i].data[dataIndex];
14141 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14150 * Revert to a view of the Record cache with no filtering applied.
14151 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14153 clearFilter : function(suppressEvent){
14154 if(this.snapshot && this.snapshot != this.data){
14155 this.data = this.snapshot;
14156 delete this.snapshot;
14157 if(suppressEvent !== true){
14158 this.fireEvent("datachanged", this);
14164 afterEdit : function(record){
14165 if(this.modified.indexOf(record) == -1){
14166 this.modified.push(record);
14168 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14172 afterReject : function(record){
14173 this.modified.remove(record);
14174 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14178 afterCommit : function(record){
14179 this.modified.remove(record);
14180 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14184 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14185 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14187 commitChanges : function(){
14188 var m = this.modified.slice(0);
14189 this.modified = [];
14190 for(var i = 0, len = m.length; i < len; i++){
14196 * Cancel outstanding changes on all changed records.
14198 rejectChanges : function(){
14199 var m = this.modified.slice(0);
14200 this.modified = [];
14201 for(var i = 0, len = m.length; i < len; i++){
14206 onMetaChange : function(meta, rtype, o){
14207 this.recordType = rtype;
14208 this.fields = rtype.prototype.fields;
14209 delete this.snapshot;
14210 this.sortInfo = meta.sortInfo || this.sortInfo;
14211 this.modified = [];
14212 this.fireEvent('metachange', this, this.reader.meta);
14215 moveIndex : function(data, type)
14217 var index = this.indexOf(data);
14219 var newIndex = index + type;
14223 this.insert(newIndex, data);
14228 * Ext JS Library 1.1.1
14229 * Copyright(c) 2006-2007, Ext JS, LLC.
14231 * Originally Released Under LGPL - original licence link has changed is not relivant.
14234 * <script type="text/javascript">
14238 * @class Roo.data.SimpleStore
14239 * @extends Roo.data.Store
14240 * Small helper class to make creating Stores from Array data easier.
14241 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14242 * @cfg {Array} fields An array of field definition objects, or field name strings.
14243 * @cfg {Object} an existing reader (eg. copied from another store)
14244 * @cfg {Array} data The multi-dimensional array of data
14246 * @param {Object} config
14248 Roo.data.SimpleStore = function(config)
14250 Roo.data.SimpleStore.superclass.constructor.call(this, {
14252 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14255 Roo.data.Record.create(config.fields)
14257 proxy : new Roo.data.MemoryProxy(config.data)
14261 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14263 * Ext JS Library 1.1.1
14264 * Copyright(c) 2006-2007, Ext JS, LLC.
14266 * Originally Released Under LGPL - original licence link has changed is not relivant.
14269 * <script type="text/javascript">
14274 * @extends Roo.data.Store
14275 * @class Roo.data.JsonStore
14276 * Small helper class to make creating Stores for JSON data easier. <br/>
14278 var store = new Roo.data.JsonStore({
14279 url: 'get-images.php',
14281 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14284 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14285 * JsonReader and HttpProxy (unless inline data is provided).</b>
14286 * @cfg {Array} fields An array of field definition objects, or field name strings.
14288 * @param {Object} config
14290 Roo.data.JsonStore = function(c){
14291 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14292 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14293 reader: new Roo.data.JsonReader(c, c.fields)
14296 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14298 * Ext JS Library 1.1.1
14299 * Copyright(c) 2006-2007, Ext JS, LLC.
14301 * Originally Released Under LGPL - original licence link has changed is not relivant.
14304 * <script type="text/javascript">
14308 Roo.data.Field = function(config){
14309 if(typeof config == "string"){
14310 config = {name: config};
14312 Roo.apply(this, config);
14315 this.type = "auto";
14318 var st = Roo.data.SortTypes;
14319 // named sortTypes are supported, here we look them up
14320 if(typeof this.sortType == "string"){
14321 this.sortType = st[this.sortType];
14324 // set default sortType for strings and dates
14325 if(!this.sortType){
14328 this.sortType = st.asUCString;
14331 this.sortType = st.asDate;
14334 this.sortType = st.none;
14339 var stripRe = /[\$,%]/g;
14341 // prebuilt conversion function for this field, instead of
14342 // switching every time we're reading a value
14344 var cv, dateFormat = this.dateFormat;
14349 cv = function(v){ return v; };
14352 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14356 return v !== undefined && v !== null && v !== '' ?
14357 parseInt(String(v).replace(stripRe, ""), 10) : '';
14362 return v !== undefined && v !== null && v !== '' ?
14363 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14368 cv = function(v){ return v === true || v === "true" || v == 1; };
14375 if(v instanceof Date){
14379 if(dateFormat == "timestamp"){
14380 return new Date(v*1000);
14382 return Date.parseDate(v, dateFormat);
14384 var parsed = Date.parse(v);
14385 return parsed ? new Date(parsed) : null;
14394 Roo.data.Field.prototype = {
14402 * Ext JS Library 1.1.1
14403 * Copyright(c) 2006-2007, Ext JS, LLC.
14405 * Originally Released Under LGPL - original licence link has changed is not relivant.
14408 * <script type="text/javascript">
14411 // Base class for reading structured data from a data source. This class is intended to be
14412 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14415 * @class Roo.data.DataReader
14416 * Base class for reading structured data from a data source. This class is intended to be
14417 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14420 Roo.data.DataReader = function(meta, recordType){
14424 this.recordType = recordType instanceof Array ?
14425 Roo.data.Record.create(recordType) : recordType;
14428 Roo.data.DataReader.prototype = {
14431 readerType : 'Data',
14433 * Create an empty record
14434 * @param {Object} data (optional) - overlay some values
14435 * @return {Roo.data.Record} record created.
14437 newRow : function(d) {
14439 this.recordType.prototype.fields.each(function(c) {
14441 case 'int' : da[c.name] = 0; break;
14442 case 'date' : da[c.name] = new Date(); break;
14443 case 'float' : da[c.name] = 0.0; break;
14444 case 'boolean' : da[c.name] = false; break;
14445 default : da[c.name] = ""; break;
14449 return new this.recordType(Roo.apply(da, d));
14455 * Ext JS Library 1.1.1
14456 * Copyright(c) 2006-2007, Ext JS, LLC.
14458 * Originally Released Under LGPL - original licence link has changed is not relivant.
14461 * <script type="text/javascript">
14465 * @class Roo.data.DataProxy
14466 * @extends Roo.data.Observable
14467 * This class is an abstract base class for implementations which provide retrieval of
14468 * unformatted data objects.<br>
14470 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14471 * (of the appropriate type which knows how to parse the data object) to provide a block of
14472 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14474 * Custom implementations must implement the load method as described in
14475 * {@link Roo.data.HttpProxy#load}.
14477 Roo.data.DataProxy = function(){
14480 * @event beforeload
14481 * Fires before a network request is made to retrieve a data object.
14482 * @param {Object} This DataProxy object.
14483 * @param {Object} params The params parameter to the load function.
14488 * Fires before the load method's callback is called.
14489 * @param {Object} This DataProxy object.
14490 * @param {Object} o The data object.
14491 * @param {Object} arg The callback argument object passed to the load function.
14495 * @event loadexception
14496 * Fires if an Exception occurs during data retrieval.
14497 * @param {Object} This DataProxy object.
14498 * @param {Object} o The data object.
14499 * @param {Object} arg The callback argument object passed to the load function.
14500 * @param {Object} e The Exception.
14502 loadexception : true
14504 Roo.data.DataProxy.superclass.constructor.call(this);
14507 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14510 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14514 * Ext JS Library 1.1.1
14515 * Copyright(c) 2006-2007, Ext JS, LLC.
14517 * Originally Released Under LGPL - original licence link has changed is not relivant.
14520 * <script type="text/javascript">
14523 * @class Roo.data.MemoryProxy
14524 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14525 * to the Reader when its load method is called.
14527 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14529 Roo.data.MemoryProxy = function(data){
14533 Roo.data.MemoryProxy.superclass.constructor.call(this);
14537 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14540 * Load data from the requested source (in this case an in-memory
14541 * data object passed to the constructor), read the data object into
14542 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14543 * process that block using the passed callback.
14544 * @param {Object} params This parameter is not used by the MemoryProxy class.
14545 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14546 * object into a block of Roo.data.Records.
14547 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14548 * The function must be passed <ul>
14549 * <li>The Record block object</li>
14550 * <li>The "arg" argument from the load function</li>
14551 * <li>A boolean success indicator</li>
14553 * @param {Object} scope The scope in which to call the callback
14554 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14556 load : function(params, reader, callback, scope, arg){
14557 params = params || {};
14560 result = reader.readRecords(params.data ? params.data :this.data);
14562 this.fireEvent("loadexception", this, arg, null, e);
14563 callback.call(scope, null, arg, false);
14566 callback.call(scope, result, arg, true);
14570 update : function(params, records){
14575 * Ext JS Library 1.1.1
14576 * Copyright(c) 2006-2007, Ext JS, LLC.
14578 * Originally Released Under LGPL - original licence link has changed is not relivant.
14581 * <script type="text/javascript">
14584 * @class Roo.data.HttpProxy
14585 * @extends Roo.data.DataProxy
14586 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14587 * configured to reference a certain URL.<br><br>
14589 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14590 * from which the running page was served.<br><br>
14592 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14594 * Be aware that to enable the browser to parse an XML document, the server must set
14595 * the Content-Type header in the HTTP response to "text/xml".
14597 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14598 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14599 * will be used to make the request.
14601 Roo.data.HttpProxy = function(conn){
14602 Roo.data.HttpProxy.superclass.constructor.call(this);
14603 // is conn a conn config or a real conn?
14605 this.useAjax = !conn || !conn.events;
14609 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14610 // thse are take from connection...
14613 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14616 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14617 * extra parameters to each request made by this object. (defaults to undefined)
14620 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14621 * to each request made by this object. (defaults to undefined)
14624 * @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)
14627 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14630 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14636 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14640 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14641 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14642 * a finer-grained basis than the DataProxy events.
14644 getConnection : function(){
14645 return this.useAjax ? Roo.Ajax : this.conn;
14649 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14650 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14651 * process that block using the passed callback.
14652 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14653 * for the request to the remote server.
14654 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14655 * object into a block of Roo.data.Records.
14656 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14657 * The function must be passed <ul>
14658 * <li>The Record block object</li>
14659 * <li>The "arg" argument from the load function</li>
14660 * <li>A boolean success indicator</li>
14662 * @param {Object} scope The scope in which to call the callback
14663 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14665 load : function(params, reader, callback, scope, arg){
14666 if(this.fireEvent("beforeload", this, params) !== false){
14668 params : params || {},
14670 callback : callback,
14675 callback : this.loadResponse,
14679 Roo.applyIf(o, this.conn);
14680 if(this.activeRequest){
14681 Roo.Ajax.abort(this.activeRequest);
14683 this.activeRequest = Roo.Ajax.request(o);
14685 this.conn.request(o);
14688 callback.call(scope||this, null, arg, false);
14693 loadResponse : function(o, success, response){
14694 delete this.activeRequest;
14696 this.fireEvent("loadexception", this, o, response);
14697 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14702 result = o.reader.read(response);
14704 this.fireEvent("loadexception", this, o, response, e);
14705 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14709 this.fireEvent("load", this, o, o.request.arg);
14710 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14714 update : function(dataSet){
14719 updateResponse : function(dataSet){
14724 * Ext JS Library 1.1.1
14725 * Copyright(c) 2006-2007, Ext JS, LLC.
14727 * Originally Released Under LGPL - original licence link has changed is not relivant.
14730 * <script type="text/javascript">
14734 * @class Roo.data.ScriptTagProxy
14735 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14736 * other than the originating domain of the running page.<br><br>
14738 * <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
14739 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14741 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14742 * source code that is used as the source inside a <script> tag.<br><br>
14744 * In order for the browser to process the returned data, the server must wrap the data object
14745 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14746 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14747 * depending on whether the callback name was passed:
14750 boolean scriptTag = false;
14751 String cb = request.getParameter("callback");
14754 response.setContentType("text/javascript");
14756 response.setContentType("application/x-json");
14758 Writer out = response.getWriter();
14760 out.write(cb + "(");
14762 out.print(dataBlock.toJsonString());
14769 * @param {Object} config A configuration object.
14771 Roo.data.ScriptTagProxy = function(config){
14772 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14773 Roo.apply(this, config);
14774 this.head = document.getElementsByTagName("head")[0];
14777 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14779 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14781 * @cfg {String} url The URL from which to request the data object.
14784 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14788 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14789 * the server the name of the callback function set up by the load call to process the returned data object.
14790 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14791 * javascript output which calls this named function passing the data object as its only parameter.
14793 callbackParam : "callback",
14795 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14796 * name to the request.
14801 * Load data from the configured URL, read the data object into
14802 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14803 * process that block using the passed callback.
14804 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14805 * for the request to the remote server.
14806 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14807 * object into a block of Roo.data.Records.
14808 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14809 * The function must be passed <ul>
14810 * <li>The Record block object</li>
14811 * <li>The "arg" argument from the load function</li>
14812 * <li>A boolean success indicator</li>
14814 * @param {Object} scope The scope in which to call the callback
14815 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14817 load : function(params, reader, callback, scope, arg){
14818 if(this.fireEvent("beforeload", this, params) !== false){
14820 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14822 var url = this.url;
14823 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14825 url += "&_dc=" + (new Date().getTime());
14827 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14830 cb : "stcCallback"+transId,
14831 scriptId : "stcScript"+transId,
14835 callback : callback,
14841 window[trans.cb] = function(o){
14842 conn.handleResponse(o, trans);
14845 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14847 if(this.autoAbort !== false){
14851 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14853 var script = document.createElement("script");
14854 script.setAttribute("src", url);
14855 script.setAttribute("type", "text/javascript");
14856 script.setAttribute("id", trans.scriptId);
14857 this.head.appendChild(script);
14859 this.trans = trans;
14861 callback.call(scope||this, null, arg, false);
14866 isLoading : function(){
14867 return this.trans ? true : false;
14871 * Abort the current server request.
14873 abort : function(){
14874 if(this.isLoading()){
14875 this.destroyTrans(this.trans);
14880 destroyTrans : function(trans, isLoaded){
14881 this.head.removeChild(document.getElementById(trans.scriptId));
14882 clearTimeout(trans.timeoutId);
14884 window[trans.cb] = undefined;
14886 delete window[trans.cb];
14889 // if hasn't been loaded, wait for load to remove it to prevent script error
14890 window[trans.cb] = function(){
14891 window[trans.cb] = undefined;
14893 delete window[trans.cb];
14900 handleResponse : function(o, trans){
14901 this.trans = false;
14902 this.destroyTrans(trans, true);
14905 result = trans.reader.readRecords(o);
14907 this.fireEvent("loadexception", this, o, trans.arg, e);
14908 trans.callback.call(trans.scope||window, null, trans.arg, false);
14911 this.fireEvent("load", this, o, trans.arg);
14912 trans.callback.call(trans.scope||window, result, trans.arg, true);
14916 handleFailure : function(trans){
14917 this.trans = false;
14918 this.destroyTrans(trans, false);
14919 this.fireEvent("loadexception", this, null, trans.arg);
14920 trans.callback.call(trans.scope||window, null, trans.arg, false);
14924 * Ext JS Library 1.1.1
14925 * Copyright(c) 2006-2007, Ext JS, LLC.
14927 * Originally Released Under LGPL - original licence link has changed is not relivant.
14930 * <script type="text/javascript">
14934 * @class Roo.data.JsonReader
14935 * @extends Roo.data.DataReader
14936 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14937 * based on mappings in a provided Roo.data.Record constructor.
14939 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14940 * in the reply previously.
14945 var RecordDef = Roo.data.Record.create([
14946 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14947 {name: 'occupation'} // This field will use "occupation" as the mapping.
14949 var myReader = new Roo.data.JsonReader({
14950 totalProperty: "results", // The property which contains the total dataset size (optional)
14951 root: "rows", // The property which contains an Array of row objects
14952 id: "id" // The property within each row object that provides an ID for the record (optional)
14956 * This would consume a JSON file like this:
14958 { 'results': 2, 'rows': [
14959 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14960 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14963 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14964 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14965 * paged from the remote server.
14966 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14967 * @cfg {String} root name of the property which contains the Array of row objects.
14968 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14969 * @cfg {Array} fields Array of field definition objects
14971 * Create a new JsonReader
14972 * @param {Object} meta Metadata configuration options
14973 * @param {Object} recordType Either an Array of field definition objects,
14974 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14976 Roo.data.JsonReader = function(meta, recordType){
14979 // set some defaults:
14980 Roo.applyIf(meta, {
14981 totalProperty: 'total',
14982 successProperty : 'success',
14987 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14989 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14991 readerType : 'Json',
14994 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14995 * Used by Store query builder to append _requestMeta to params.
14998 metaFromRemote : false,
15000 * This method is only used by a DataProxy which has retrieved data from a remote server.
15001 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15002 * @return {Object} data A data block which is used by an Roo.data.Store object as
15003 * a cache of Roo.data.Records.
15005 read : function(response){
15006 var json = response.responseText;
15008 var o = /* eval:var:o */ eval("("+json+")");
15010 throw {message: "JsonReader.read: Json object not found"};
15016 this.metaFromRemote = true;
15017 this.meta = o.metaData;
15018 this.recordType = Roo.data.Record.create(o.metaData.fields);
15019 this.onMetaChange(this.meta, this.recordType, o);
15021 return this.readRecords(o);
15024 // private function a store will implement
15025 onMetaChange : function(meta, recordType, o){
15032 simpleAccess: function(obj, subsc) {
15039 getJsonAccessor: function(){
15041 return function(expr) {
15043 return(re.test(expr))
15044 ? new Function("obj", "return obj." + expr)
15049 return Roo.emptyFn;
15054 * Create a data block containing Roo.data.Records from an XML document.
15055 * @param {Object} o An object which contains an Array of row objects in the property specified
15056 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15057 * which contains the total size of the dataset.
15058 * @return {Object} data A data block which is used by an Roo.data.Store object as
15059 * a cache of Roo.data.Records.
15061 readRecords : function(o){
15063 * After any data loads, the raw JSON data is available for further custom processing.
15067 var s = this.meta, Record = this.recordType,
15068 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15070 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15072 if(s.totalProperty) {
15073 this.getTotal = this.getJsonAccessor(s.totalProperty);
15075 if(s.successProperty) {
15076 this.getSuccess = this.getJsonAccessor(s.successProperty);
15078 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15080 var g = this.getJsonAccessor(s.id);
15081 this.getId = function(rec) {
15083 return (r === undefined || r === "") ? null : r;
15086 this.getId = function(){return null;};
15089 for(var jj = 0; jj < fl; jj++){
15091 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15092 this.ef[jj] = this.getJsonAccessor(map);
15096 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15097 if(s.totalProperty){
15098 var vt = parseInt(this.getTotal(o), 10);
15103 if(s.successProperty){
15104 var vs = this.getSuccess(o);
15105 if(vs === false || vs === 'false'){
15110 for(var i = 0; i < c; i++){
15113 var id = this.getId(n);
15114 for(var j = 0; j < fl; j++){
15116 var v = this.ef[j](n);
15118 Roo.log('missing convert for ' + f.name);
15122 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15124 var record = new Record(values, id);
15126 records[i] = record;
15132 totalRecords : totalRecords
15135 // used when loading children.. @see loadDataFromChildren
15136 toLoadData: function(rec)
15138 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15139 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15140 return { data : data, total : data.length };
15145 * Ext JS Library 1.1.1
15146 * Copyright(c) 2006-2007, Ext JS, LLC.
15148 * Originally Released Under LGPL - original licence link has changed is not relivant.
15151 * <script type="text/javascript">
15155 * @class Roo.data.ArrayReader
15156 * @extends Roo.data.DataReader
15157 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15158 * Each element of that Array represents a row of data fields. The
15159 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15160 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15164 var RecordDef = Roo.data.Record.create([
15165 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15166 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15168 var myReader = new Roo.data.ArrayReader({
15169 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15173 * This would consume an Array like this:
15175 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15179 * Create a new JsonReader
15180 * @param {Object} meta Metadata configuration options.
15181 * @param {Object|Array} recordType Either an Array of field definition objects
15183 * @cfg {Array} fields Array of field definition objects
15184 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15185 * as specified to {@link Roo.data.Record#create},
15186 * or an {@link Roo.data.Record} object
15189 * created using {@link Roo.data.Record#create}.
15191 Roo.data.ArrayReader = function(meta, recordType)
15193 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15196 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15199 * Create a data block containing Roo.data.Records from an XML document.
15200 * @param {Object} o An Array of row objects which represents the dataset.
15201 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15202 * a cache of Roo.data.Records.
15204 readRecords : function(o)
15206 var sid = this.meta ? this.meta.id : null;
15207 var recordType = this.recordType, fields = recordType.prototype.fields;
15210 for(var i = 0; i < root.length; i++){
15213 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15214 for(var j = 0, jlen = fields.length; j < jlen; j++){
15215 var f = fields.items[j];
15216 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15217 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15219 values[f.name] = v;
15221 var record = new recordType(values, id);
15223 records[records.length] = record;
15227 totalRecords : records.length
15230 // used when loading children.. @see loadDataFromChildren
15231 toLoadData: function(rec)
15233 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15234 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15245 * @class Roo.bootstrap.ComboBox
15246 * @extends Roo.bootstrap.TriggerField
15247 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15248 * @cfg {Boolean} append (true|false) default false
15249 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15250 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15251 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15252 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15253 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15254 * @cfg {Boolean} animate default true
15255 * @cfg {Boolean} emptyResultText only for touch device
15256 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15257 * @cfg {String} emptyTitle default ''
15258 * @cfg {Number} width fixed with? experimental
15260 * Create a new ComboBox.
15261 * @param {Object} config Configuration options
15263 Roo.bootstrap.ComboBox = function(config){
15264 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15268 * Fires when the dropdown list is expanded
15269 * @param {Roo.bootstrap.ComboBox} combo This combo box
15274 * Fires when the dropdown list is collapsed
15275 * @param {Roo.bootstrap.ComboBox} combo This combo box
15279 * @event beforeselect
15280 * Fires before a list item is selected. Return false to cancel the selection.
15281 * @param {Roo.bootstrap.ComboBox} combo This combo box
15282 * @param {Roo.data.Record} record The data record returned from the underlying store
15283 * @param {Number} index The index of the selected item in the dropdown list
15285 'beforeselect' : true,
15288 * Fires when a list item is selected
15289 * @param {Roo.bootstrap.ComboBox} combo This combo box
15290 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15291 * @param {Number} index The index of the selected item in the dropdown list
15295 * @event beforequery
15296 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15297 * The event object passed has these properties:
15298 * @param {Roo.bootstrap.ComboBox} combo This combo box
15299 * @param {String} query The query
15300 * @param {Boolean} forceAll true to force "all" query
15301 * @param {Boolean} cancel true to cancel the query
15302 * @param {Object} e The query event object
15304 'beforequery': true,
15307 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15308 * @param {Roo.bootstrap.ComboBox} combo This combo box
15313 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15314 * @param {Roo.bootstrap.ComboBox} combo This combo box
15315 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15320 * Fires when the remove value from the combobox array
15321 * @param {Roo.bootstrap.ComboBox} combo This combo box
15325 * @event afterremove
15326 * Fires when the remove value from the combobox array
15327 * @param {Roo.bootstrap.ComboBox} combo This combo box
15329 'afterremove' : true,
15331 * @event specialfilter
15332 * Fires when specialfilter
15333 * @param {Roo.bootstrap.ComboBox} combo This combo box
15335 'specialfilter' : true,
15338 * Fires when tick the element
15339 * @param {Roo.bootstrap.ComboBox} combo This combo box
15343 * @event touchviewdisplay
15344 * Fires when touch view require special display (default is using displayField)
15345 * @param {Roo.bootstrap.ComboBox} combo This combo box
15346 * @param {Object} cfg set html .
15348 'touchviewdisplay' : true
15353 this.tickItems = [];
15355 this.selectedIndex = -1;
15356 if(this.mode == 'local'){
15357 if(config.queryDelay === undefined){
15358 this.queryDelay = 10;
15360 if(config.minChars === undefined){
15366 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15369 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15370 * rendering into an Roo.Editor, defaults to false)
15373 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15374 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15377 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15380 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15381 * the dropdown list (defaults to undefined, with no header element)
15385 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15389 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15391 listWidth: undefined,
15393 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15394 * mode = 'remote' or 'text' if mode = 'local')
15396 displayField: undefined,
15399 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15400 * mode = 'remote' or 'value' if mode = 'local').
15401 * Note: use of a valueField requires the user make a selection
15402 * in order for a value to be mapped.
15404 valueField: undefined,
15406 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15411 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15412 * field's data value (defaults to the underlying DOM element's name)
15414 hiddenName: undefined,
15416 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15420 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15422 selectedClass: 'active',
15425 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15429 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15430 * anchor positions (defaults to 'tl-bl')
15432 listAlign: 'tl-bl?',
15434 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15438 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15439 * query specified by the allQuery config option (defaults to 'query')
15441 triggerAction: 'query',
15443 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15444 * (defaults to 4, does not apply if editable = false)
15448 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15449 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15453 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15454 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15458 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15459 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15463 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15464 * when editable = true (defaults to false)
15466 selectOnFocus:false,
15468 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15470 queryParam: 'query',
15472 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15473 * when mode = 'remote' (defaults to 'Loading...')
15475 loadingText: 'Loading...',
15477 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15481 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15485 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15486 * traditional select (defaults to true)
15490 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15494 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15498 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15499 * listWidth has a higher value)
15503 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15504 * allow the user to set arbitrary text into the field (defaults to false)
15506 forceSelection:false,
15508 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15509 * if typeAhead = true (defaults to 250)
15511 typeAheadDelay : 250,
15513 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15514 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15516 valueNotFoundText : undefined,
15518 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15520 blockFocus : false,
15523 * @cfg {Boolean} disableClear Disable showing of clear button.
15525 disableClear : false,
15527 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15529 alwaysQuery : false,
15532 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15537 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15539 invalidClass : "has-warning",
15542 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15544 validClass : "has-success",
15547 * @cfg {Boolean} specialFilter (true|false) special filter default false
15549 specialFilter : false,
15552 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15554 mobileTouchView : true,
15557 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15559 useNativeIOS : false,
15562 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15564 mobile_restrict_height : false,
15566 ios_options : false,
15578 btnPosition : 'right',
15579 triggerList : true,
15580 showToggleBtn : true,
15582 emptyResultText: 'Empty',
15583 triggerText : 'Select',
15587 // element that contains real text value.. (when hidden is used..)
15589 getAutoCreate : function()
15594 * Render classic select for iso
15597 if(Roo.isIOS && this.useNativeIOS){
15598 cfg = this.getAutoCreateNativeIOS();
15606 if(Roo.isTouch && this.mobileTouchView){
15607 cfg = this.getAutoCreateTouchView();
15614 if(!this.tickable){
15615 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15620 * ComboBox with tickable selections
15623 var align = this.labelAlign || this.parentLabelAlign();
15626 cls : 'form-group roo-combobox-tickable' //input-group
15629 var btn_text_select = '';
15630 var btn_text_done = '';
15631 var btn_text_cancel = '';
15633 if (this.btn_text_show) {
15634 btn_text_select = 'Select';
15635 btn_text_done = 'Done';
15636 btn_text_cancel = 'Cancel';
15641 cls : 'tickable-buttons',
15646 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15647 //html : this.triggerText
15648 html: btn_text_select
15654 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15656 html: btn_text_done
15662 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15664 html: btn_text_cancel
15670 buttons.cn.unshift({
15672 cls: 'roo-select2-search-field-input'
15678 Roo.each(buttons.cn, function(c){
15680 c.cls += ' btn-' + _this.size;
15683 if (_this.disabled) {
15690 style : 'display: contents',
15695 cls: 'form-hidden-field'
15699 cls: 'roo-select2-choices',
15703 cls: 'roo-select2-search-field',
15714 cls: 'roo-select2-container input-group roo-select2-container-multi',
15720 // cls: 'typeahead typeahead-long dropdown-menu',
15721 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15726 if(this.hasFeedback && !this.allowBlank){
15730 cls: 'glyphicon form-control-feedback'
15733 combobox.cn.push(feedback);
15740 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15741 tooltip : 'This field is required'
15743 if (Roo.bootstrap.version == 4) {
15746 style : 'display:none'
15749 if (align ==='left' && this.fieldLabel.length) {
15751 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15758 cls : 'control-label col-form-label',
15759 html : this.fieldLabel
15771 var labelCfg = cfg.cn[1];
15772 var contentCfg = cfg.cn[2];
15775 if(this.indicatorpos == 'right'){
15781 cls : 'control-label col-form-label',
15785 html : this.fieldLabel
15801 labelCfg = cfg.cn[0];
15802 contentCfg = cfg.cn[1];
15806 if(this.labelWidth > 12){
15807 labelCfg.style = "width: " + this.labelWidth + 'px';
15809 if(this.width * 1 > 0){
15810 contentCfg.style = "width: " + this.width + 'px';
15812 if(this.labelWidth < 13 && this.labelmd == 0){
15813 this.labelmd = this.labelWidth;
15816 if(this.labellg > 0){
15817 labelCfg.cls += ' col-lg-' + this.labellg;
15818 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15821 if(this.labelmd > 0){
15822 labelCfg.cls += ' col-md-' + this.labelmd;
15823 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15826 if(this.labelsm > 0){
15827 labelCfg.cls += ' col-sm-' + this.labelsm;
15828 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15831 if(this.labelxs > 0){
15832 labelCfg.cls += ' col-xs-' + this.labelxs;
15833 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15837 } else if ( this.fieldLabel.length) {
15838 // Roo.log(" label");
15843 //cls : 'input-group-addon',
15844 html : this.fieldLabel
15849 if(this.indicatorpos == 'right'){
15853 //cls : 'input-group-addon',
15854 html : this.fieldLabel
15864 // Roo.log(" no label && no align");
15871 ['xs','sm','md','lg'].map(function(size){
15872 if (settings[size]) {
15873 cfg.cls += ' col-' + size + '-' + settings[size];
15881 _initEventsCalled : false,
15884 initEvents: function()
15886 if (this._initEventsCalled) { // as we call render... prevent looping...
15889 this._initEventsCalled = true;
15892 throw "can not find store for combo";
15895 this.indicator = this.indicatorEl();
15897 this.store = Roo.factory(this.store, Roo.data);
15898 this.store.parent = this;
15900 // if we are building from html. then this element is so complex, that we can not really
15901 // use the rendered HTML.
15902 // so we have to trash and replace the previous code.
15903 if (Roo.XComponent.build_from_html) {
15904 // remove this element....
15905 var e = this.el.dom, k=0;
15906 while (e ) { e = e.previousSibling; ++k;}
15911 this.rendered = false;
15913 this.render(this.parent().getChildContainer(true), k);
15916 if(Roo.isIOS && this.useNativeIOS){
15917 this.initIOSView();
15925 if(Roo.isTouch && this.mobileTouchView){
15926 this.initTouchView();
15931 this.initTickableEvents();
15935 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15937 if(this.hiddenName){
15939 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15941 this.hiddenField.dom.value =
15942 this.hiddenValue !== undefined ? this.hiddenValue :
15943 this.value !== undefined ? this.value : '';
15945 // prevent input submission
15946 this.el.dom.removeAttribute('name');
15947 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15952 // this.el.dom.setAttribute('autocomplete', 'off');
15955 var cls = 'x-combo-list';
15957 //this.list = new Roo.Layer({
15958 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15964 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15965 _this.list.setWidth(lw);
15968 this.list.on('mouseover', this.onViewOver, this);
15969 this.list.on('mousemove', this.onViewMove, this);
15970 this.list.on('scroll', this.onViewScroll, this);
15973 this.list.swallowEvent('mousewheel');
15974 this.assetHeight = 0;
15977 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15978 this.assetHeight += this.header.getHeight();
15981 this.innerList = this.list.createChild({cls:cls+'-inner'});
15982 this.innerList.on('mouseover', this.onViewOver, this);
15983 this.innerList.on('mousemove', this.onViewMove, this);
15984 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15986 if(this.allowBlank && !this.pageSize && !this.disableClear){
15987 this.footer = this.list.createChild({cls:cls+'-ft'});
15988 this.pageTb = new Roo.Toolbar(this.footer);
15992 this.footer = this.list.createChild({cls:cls+'-ft'});
15993 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15994 {pageSize: this.pageSize});
15998 if (this.pageTb && this.allowBlank && !this.disableClear) {
16000 this.pageTb.add(new Roo.Toolbar.Fill(), {
16001 cls: 'x-btn-icon x-btn-clear',
16003 handler: function()
16006 _this.clearValue();
16007 _this.onSelect(false, -1);
16012 this.assetHeight += this.footer.getHeight();
16017 this.tpl = Roo.bootstrap.version == 4 ?
16018 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16019 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16022 this.view = new Roo.View(this.list, this.tpl, {
16023 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16025 //this.view.wrapEl.setDisplayed(false);
16026 this.view.on('click', this.onViewClick, this);
16029 this.store.on('beforeload', this.onBeforeLoad, this);
16030 this.store.on('load', this.onLoad, this);
16031 this.store.on('loadexception', this.onLoadException, this);
16033 if(this.resizable){
16034 this.resizer = new Roo.Resizable(this.list, {
16035 pinned:true, handles:'se'
16037 this.resizer.on('resize', function(r, w, h){
16038 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16039 this.listWidth = w;
16040 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16041 this.restrictHeight();
16043 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16046 if(!this.editable){
16047 this.editable = true;
16048 this.setEditable(false);
16053 if (typeof(this.events.add.listeners) != 'undefined') {
16055 this.addicon = this.wrap.createChild(
16056 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16058 this.addicon.on('click', function(e) {
16059 this.fireEvent('add', this);
16062 if (typeof(this.events.edit.listeners) != 'undefined') {
16064 this.editicon = this.wrap.createChild(
16065 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16066 if (this.addicon) {
16067 this.editicon.setStyle('margin-left', '40px');
16069 this.editicon.on('click', function(e) {
16071 // we fire even if inothing is selected..
16072 this.fireEvent('edit', this, this.lastData );
16078 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16079 "up" : function(e){
16080 this.inKeyMode = true;
16084 "down" : function(e){
16085 if(!this.isExpanded()){
16086 this.onTriggerClick();
16088 this.inKeyMode = true;
16093 "enter" : function(e){
16094 // this.onViewClick();
16098 if(this.fireEvent("specialkey", this, e)){
16099 this.onViewClick(false);
16105 "esc" : function(e){
16109 "tab" : function(e){
16112 if(this.fireEvent("specialkey", this, e)){
16113 this.onViewClick(false);
16121 doRelay : function(foo, bar, hname){
16122 if(hname == 'down' || this.scope.isExpanded()){
16123 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16132 this.queryDelay = Math.max(this.queryDelay || 10,
16133 this.mode == 'local' ? 10 : 250);
16136 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16138 if(this.typeAhead){
16139 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16141 if(this.editable !== false){
16142 this.inputEl().on("keyup", this.onKeyUp, this);
16144 if(this.forceSelection){
16145 this.inputEl().on('blur', this.doForce, this);
16149 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16150 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16154 initTickableEvents: function()
16158 if(this.hiddenName){
16160 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16162 this.hiddenField.dom.value =
16163 this.hiddenValue !== undefined ? this.hiddenValue :
16164 this.value !== undefined ? this.value : '';
16166 // prevent input submission
16167 this.el.dom.removeAttribute('name');
16168 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16173 // this.list = this.el.select('ul.dropdown-menu',true).first();
16175 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16176 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16177 if(this.triggerList){
16178 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16181 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16182 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16184 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16185 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16187 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16188 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16190 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16191 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16192 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16195 this.cancelBtn.hide();
16200 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16201 _this.list.setWidth(lw);
16204 this.list.on('mouseover', this.onViewOver, this);
16205 this.list.on('mousemove', this.onViewMove, this);
16207 this.list.on('scroll', this.onViewScroll, this);
16210 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16211 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16214 this.view = new Roo.View(this.list, this.tpl, {
16219 selectedClass: this.selectedClass
16222 //this.view.wrapEl.setDisplayed(false);
16223 this.view.on('click', this.onViewClick, this);
16227 this.store.on('beforeload', this.onBeforeLoad, this);
16228 this.store.on('load', this.onLoad, this);
16229 this.store.on('loadexception', this.onLoadException, this);
16232 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16233 "up" : function(e){
16234 this.inKeyMode = true;
16238 "down" : function(e){
16239 this.inKeyMode = true;
16243 "enter" : function(e){
16244 if(this.fireEvent("specialkey", this, e)){
16245 this.onViewClick(false);
16251 "esc" : function(e){
16252 this.onTickableFooterButtonClick(e, false, false);
16255 "tab" : function(e){
16256 this.fireEvent("specialkey", this, e);
16258 this.onTickableFooterButtonClick(e, false, false);
16265 doRelay : function(e, fn, key){
16266 if(this.scope.isExpanded()){
16267 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16276 this.queryDelay = Math.max(this.queryDelay || 10,
16277 this.mode == 'local' ? 10 : 250);
16280 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16282 if(this.typeAhead){
16283 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16286 if(this.editable !== false){
16287 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16290 this.indicator = this.indicatorEl();
16292 if(this.indicator){
16293 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16294 this.indicator.hide();
16299 onDestroy : function(){
16301 this.view.setStore(null);
16302 this.view.el.removeAllListeners();
16303 this.view.el.remove();
16304 this.view.purgeListeners();
16307 this.list.dom.innerHTML = '';
16311 this.store.un('beforeload', this.onBeforeLoad, this);
16312 this.store.un('load', this.onLoad, this);
16313 this.store.un('loadexception', this.onLoadException, this);
16315 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16319 fireKey : function(e){
16320 if(e.isNavKeyPress() && !this.list.isVisible()){
16321 this.fireEvent("specialkey", this, e);
16326 onResize: function(w, h)
16330 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16332 // if(typeof w != 'number'){
16333 // // we do not handle it!?!?
16336 // var tw = this.trigger.getWidth();
16337 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16338 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16340 // this.inputEl().setWidth( this.adjustWidth('input', x));
16342 // //this.trigger.setStyle('left', x+'px');
16344 // if(this.list && this.listWidth === undefined){
16345 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16346 // this.list.setWidth(lw);
16347 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16355 * Allow or prevent the user from directly editing the field text. If false is passed,
16356 * the user will only be able to select from the items defined in the dropdown list. This method
16357 * is the runtime equivalent of setting the 'editable' config option at config time.
16358 * @param {Boolean} value True to allow the user to directly edit the field text
16360 setEditable : function(value){
16361 if(value == this.editable){
16364 this.editable = value;
16366 this.inputEl().dom.setAttribute('readOnly', true);
16367 this.inputEl().on('mousedown', this.onTriggerClick, this);
16368 this.inputEl().addClass('x-combo-noedit');
16370 this.inputEl().dom.setAttribute('readOnly', false);
16371 this.inputEl().un('mousedown', this.onTriggerClick, this);
16372 this.inputEl().removeClass('x-combo-noedit');
16378 onBeforeLoad : function(combo,opts){
16379 if(!this.hasFocus){
16383 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16385 this.restrictHeight();
16386 this.selectedIndex = -1;
16390 onLoad : function(){
16392 this.hasQuery = false;
16394 if(!this.hasFocus){
16398 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16399 this.loading.hide();
16402 if(this.store.getCount() > 0){
16405 this.restrictHeight();
16406 if(this.lastQuery == this.allQuery){
16407 if(this.editable && !this.tickable){
16408 this.inputEl().dom.select();
16412 !this.selectByValue(this.value, true) &&
16415 !this.store.lastOptions ||
16416 typeof(this.store.lastOptions.add) == 'undefined' ||
16417 this.store.lastOptions.add != true
16420 this.select(0, true);
16423 if(this.autoFocus){
16426 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16427 this.taTask.delay(this.typeAheadDelay);
16431 this.onEmptyResults();
16437 onLoadException : function()
16439 this.hasQuery = false;
16441 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16442 this.loading.hide();
16445 if(this.tickable && this.editable){
16450 // only causes errors at present
16451 //Roo.log(this.store.reader.jsonData);
16452 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16454 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16460 onTypeAhead : function(){
16461 if(this.store.getCount() > 0){
16462 var r = this.store.getAt(0);
16463 var newValue = r.data[this.displayField];
16464 var len = newValue.length;
16465 var selStart = this.getRawValue().length;
16467 if(selStart != len){
16468 this.setRawValue(newValue);
16469 this.selectText(selStart, newValue.length);
16475 onSelect : function(record, index){
16477 if(this.fireEvent('beforeselect', this, record, index) !== false){
16479 this.setFromData(index > -1 ? record.data : false);
16482 this.fireEvent('select', this, record, index);
16487 * Returns the currently selected field value or empty string if no value is set.
16488 * @return {String} value The selected value
16490 getValue : function()
16492 if(Roo.isIOS && this.useNativeIOS){
16493 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16497 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16500 if(this.valueField){
16501 return typeof this.value != 'undefined' ? this.value : '';
16503 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16507 getRawValue : function()
16509 if(Roo.isIOS && this.useNativeIOS){
16510 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16513 var v = this.inputEl().getValue();
16519 * Clears any text/value currently set in the field
16521 clearValue : function(){
16523 if(this.hiddenField){
16524 this.hiddenField.dom.value = '';
16527 this.setRawValue('');
16528 this.lastSelectionText = '';
16529 this.lastData = false;
16531 var close = this.closeTriggerEl();
16542 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16543 * will be displayed in the field. If the value does not match the data value of an existing item,
16544 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16545 * Otherwise the field will be blank (although the value will still be set).
16546 * @param {String} value The value to match
16548 setValue : function(v)
16550 if(Roo.isIOS && this.useNativeIOS){
16551 this.setIOSValue(v);
16561 if(this.valueField){
16562 var r = this.findRecord(this.valueField, v);
16564 text = r.data[this.displayField];
16565 }else if(this.valueNotFoundText !== undefined){
16566 text = this.valueNotFoundText;
16569 this.lastSelectionText = text;
16570 if(this.hiddenField){
16571 this.hiddenField.dom.value = v;
16573 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16576 var close = this.closeTriggerEl();
16579 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16585 * @property {Object} the last set data for the element
16590 * Sets the value of the field based on a object which is related to the record format for the store.
16591 * @param {Object} value the value to set as. or false on reset?
16593 setFromData : function(o){
16600 var dv = ''; // display value
16601 var vv = ''; // value value..
16603 if (this.displayField) {
16604 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16606 // this is an error condition!!!
16607 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16610 if(this.valueField){
16611 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16614 var close = this.closeTriggerEl();
16617 if(dv.length || vv * 1 > 0){
16619 this.blockFocus=true;
16625 if(this.hiddenField){
16626 this.hiddenField.dom.value = vv;
16628 this.lastSelectionText = dv;
16629 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16633 // no hidden field.. - we store the value in 'value', but still display
16634 // display field!!!!
16635 this.lastSelectionText = dv;
16636 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16643 reset : function(){
16644 // overridden so that last data is reset..
16651 this.setValue(this.originalValue);
16652 //this.clearInvalid();
16653 this.lastData = false;
16655 this.view.clearSelections();
16661 findRecord : function(prop, value){
16663 if(this.store.getCount() > 0){
16664 this.store.each(function(r){
16665 if(r.data[prop] == value){
16675 getName: function()
16677 // returns hidden if it's set..
16678 if (!this.rendered) {return ''};
16679 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16683 onViewMove : function(e, t){
16684 this.inKeyMode = false;
16688 onViewOver : function(e, t){
16689 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16692 var item = this.view.findItemFromChild(t);
16695 var index = this.view.indexOf(item);
16696 this.select(index, false);
16701 onViewClick : function(view, doFocus, el, e)
16703 var index = this.view.getSelectedIndexes()[0];
16705 var r = this.store.getAt(index);
16709 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16716 Roo.each(this.tickItems, function(v,k){
16718 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16720 _this.tickItems.splice(k, 1);
16722 if(typeof(e) == 'undefined' && view == false){
16723 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16735 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16736 this.tickItems.push(r.data);
16739 if(typeof(e) == 'undefined' && view == false){
16740 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16747 this.onSelect(r, index);
16749 if(doFocus !== false && !this.blockFocus){
16750 this.inputEl().focus();
16755 restrictHeight : function(){
16756 //this.innerList.dom.style.height = '';
16757 //var inner = this.innerList.dom;
16758 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16759 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16760 //this.list.beginUpdate();
16761 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16762 this.list.alignTo(this.inputEl(), this.listAlign);
16763 this.list.alignTo(this.inputEl(), this.listAlign);
16764 //this.list.endUpdate();
16768 onEmptyResults : function(){
16770 if(this.tickable && this.editable){
16771 this.hasFocus = false;
16772 this.restrictHeight();
16780 * Returns true if the dropdown list is expanded, else false.
16782 isExpanded : function(){
16783 return this.list.isVisible();
16787 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16788 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16789 * @param {String} value The data value of the item to select
16790 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16791 * selected item if it is not currently in view (defaults to true)
16792 * @return {Boolean} True if the value matched an item in the list, else false
16794 selectByValue : function(v, scrollIntoView){
16795 if(v !== undefined && v !== null){
16796 var r = this.findRecord(this.valueField || this.displayField, v);
16798 this.select(this.store.indexOf(r), scrollIntoView);
16806 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16807 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16808 * @param {Number} index The zero-based index of the list item to select
16809 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16810 * selected item if it is not currently in view (defaults to true)
16812 select : function(index, scrollIntoView){
16813 this.selectedIndex = index;
16814 this.view.select(index);
16815 if(scrollIntoView !== false){
16816 var el = this.view.getNode(index);
16818 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16821 this.list.scrollChildIntoView(el, false);
16827 selectNext : function(){
16828 var ct = this.store.getCount();
16830 if(this.selectedIndex == -1){
16832 }else if(this.selectedIndex < ct-1){
16833 this.select(this.selectedIndex+1);
16839 selectPrev : function(){
16840 var ct = this.store.getCount();
16842 if(this.selectedIndex == -1){
16844 }else if(this.selectedIndex != 0){
16845 this.select(this.selectedIndex-1);
16851 onKeyUp : function(e){
16852 if(this.editable !== false && !e.isSpecialKey()){
16853 this.lastKey = e.getKey();
16854 this.dqTask.delay(this.queryDelay);
16859 validateBlur : function(){
16860 return !this.list || !this.list.isVisible();
16864 initQuery : function(){
16866 var v = this.getRawValue();
16868 if(this.tickable && this.editable){
16869 v = this.tickableInputEl().getValue();
16876 doForce : function(){
16877 if(this.inputEl().dom.value.length > 0){
16878 this.inputEl().dom.value =
16879 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16885 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16886 * query allowing the query action to be canceled if needed.
16887 * @param {String} query The SQL query to execute
16888 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16889 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16890 * saved in the current store (defaults to false)
16892 doQuery : function(q, forceAll){
16894 if(q === undefined || q === null){
16899 forceAll: forceAll,
16903 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16908 forceAll = qe.forceAll;
16909 if(forceAll === true || (q.length >= this.minChars)){
16911 this.hasQuery = true;
16913 if(this.lastQuery != q || this.alwaysQuery){
16914 this.lastQuery = q;
16915 if(this.mode == 'local'){
16916 this.selectedIndex = -1;
16918 this.store.clearFilter();
16921 if(this.specialFilter){
16922 this.fireEvent('specialfilter', this);
16927 this.store.filter(this.displayField, q);
16930 this.store.fireEvent("datachanged", this.store);
16937 this.store.baseParams[this.queryParam] = q;
16939 var options = {params : this.getParams(q)};
16942 options.add = true;
16943 options.params.start = this.page * this.pageSize;
16946 this.store.load(options);
16949 * this code will make the page width larger, at the beginning, the list not align correctly,
16950 * we should expand the list on onLoad
16951 * so command out it
16956 this.selectedIndex = -1;
16961 this.loadNext = false;
16965 getParams : function(q){
16967 //p[this.queryParam] = q;
16971 p.limit = this.pageSize;
16977 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16979 collapse : function(){
16980 if(!this.isExpanded()){
16986 this.hasFocus = false;
16990 this.cancelBtn.hide();
16991 this.trigger.show();
16994 this.tickableInputEl().dom.value = '';
16995 this.tickableInputEl().blur();
17000 Roo.get(document).un('mousedown', this.collapseIf, this);
17001 Roo.get(document).un('mousewheel', this.collapseIf, this);
17002 if (!this.editable) {
17003 Roo.get(document).un('keydown', this.listKeyPress, this);
17005 this.fireEvent('collapse', this);
17011 collapseIf : function(e){
17012 var in_combo = e.within(this.el);
17013 var in_list = e.within(this.list);
17014 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17016 if (in_combo || in_list || is_list) {
17017 //e.stopPropagation();
17022 this.onTickableFooterButtonClick(e, false, false);
17030 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17032 expand : function(){
17034 if(this.isExpanded() || !this.hasFocus){
17038 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17039 this.list.setWidth(lw);
17045 this.restrictHeight();
17049 this.tickItems = Roo.apply([], this.item);
17052 this.cancelBtn.show();
17053 this.trigger.hide();
17056 this.tickableInputEl().focus();
17061 Roo.get(document).on('mousedown', this.collapseIf, this);
17062 Roo.get(document).on('mousewheel', this.collapseIf, this);
17063 if (!this.editable) {
17064 Roo.get(document).on('keydown', this.listKeyPress, this);
17067 this.fireEvent('expand', this);
17071 // Implements the default empty TriggerField.onTriggerClick function
17072 onTriggerClick : function(e)
17074 Roo.log('trigger click');
17076 if(this.disabled || !this.triggerList){
17081 this.loadNext = false;
17083 if(this.isExpanded()){
17085 if (!this.blockFocus) {
17086 this.inputEl().focus();
17090 this.hasFocus = true;
17091 if(this.triggerAction == 'all') {
17092 this.doQuery(this.allQuery, true);
17094 this.doQuery(this.getRawValue());
17096 if (!this.blockFocus) {
17097 this.inputEl().focus();
17102 onTickableTriggerClick : function(e)
17109 this.loadNext = false;
17110 this.hasFocus = true;
17112 if(this.triggerAction == 'all') {
17113 this.doQuery(this.allQuery, true);
17115 this.doQuery(this.getRawValue());
17119 onSearchFieldClick : function(e)
17121 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17122 this.onTickableFooterButtonClick(e, false, false);
17126 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17131 this.loadNext = false;
17132 this.hasFocus = true;
17134 if(this.triggerAction == 'all') {
17135 this.doQuery(this.allQuery, true);
17137 this.doQuery(this.getRawValue());
17141 listKeyPress : function(e)
17143 //Roo.log('listkeypress');
17144 // scroll to first matching element based on key pres..
17145 if (e.isSpecialKey()) {
17148 var k = String.fromCharCode(e.getKey()).toUpperCase();
17151 var csel = this.view.getSelectedNodes();
17152 var cselitem = false;
17154 var ix = this.view.indexOf(csel[0]);
17155 cselitem = this.store.getAt(ix);
17156 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17162 this.store.each(function(v) {
17164 // start at existing selection.
17165 if (cselitem.id == v.id) {
17171 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17172 match = this.store.indexOf(v);
17178 if (match === false) {
17179 return true; // no more action?
17182 this.view.select(match);
17183 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17184 sn.scrollIntoView(sn.dom.parentNode, false);
17187 onViewScroll : function(e, t){
17189 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){
17193 this.hasQuery = true;
17195 this.loading = this.list.select('.loading', true).first();
17197 if(this.loading === null){
17198 this.list.createChild({
17200 cls: 'loading roo-select2-more-results roo-select2-active',
17201 html: 'Loading more results...'
17204 this.loading = this.list.select('.loading', true).first();
17206 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17208 this.loading.hide();
17211 this.loading.show();
17216 this.loadNext = true;
17218 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17223 addItem : function(o)
17225 var dv = ''; // display value
17227 if (this.displayField) {
17228 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17230 // this is an error condition!!!
17231 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17238 var choice = this.choices.createChild({
17240 cls: 'roo-select2-search-choice',
17249 cls: 'roo-select2-search-choice-close fa fa-times',
17254 }, this.searchField);
17256 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17258 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17266 this.inputEl().dom.value = '';
17271 onRemoveItem : function(e, _self, o)
17273 e.preventDefault();
17275 this.lastItem = Roo.apply([], this.item);
17277 var index = this.item.indexOf(o.data) * 1;
17280 Roo.log('not this item?!');
17284 this.item.splice(index, 1);
17289 this.fireEvent('remove', this, e);
17295 syncValue : function()
17297 if(!this.item.length){
17304 Roo.each(this.item, function(i){
17305 if(_this.valueField){
17306 value.push(i[_this.valueField]);
17313 this.value = value.join(',');
17315 if(this.hiddenField){
17316 this.hiddenField.dom.value = this.value;
17319 this.store.fireEvent("datachanged", this.store);
17324 clearItem : function()
17326 if(!this.multiple){
17332 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17340 if(this.tickable && !Roo.isTouch){
17341 this.view.refresh();
17345 inputEl: function ()
17347 if(Roo.isIOS && this.useNativeIOS){
17348 return this.el.select('select.roo-ios-select', true).first();
17351 if(Roo.isTouch && this.mobileTouchView){
17352 return this.el.select('input.form-control',true).first();
17356 return this.searchField;
17359 return this.el.select('input.form-control',true).first();
17362 onTickableFooterButtonClick : function(e, btn, el)
17364 e.preventDefault();
17366 this.lastItem = Roo.apply([], this.item);
17368 if(btn && btn.name == 'cancel'){
17369 this.tickItems = Roo.apply([], this.item);
17378 Roo.each(this.tickItems, function(o){
17386 validate : function()
17388 if(this.getVisibilityEl().hasClass('hidden')){
17392 var v = this.getRawValue();
17395 v = this.getValue();
17398 if(this.disabled || this.allowBlank || v.length){
17403 this.markInvalid();
17407 tickableInputEl : function()
17409 if(!this.tickable || !this.editable){
17410 return this.inputEl();
17413 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17417 getAutoCreateTouchView : function()
17422 cls: 'form-group' //input-group
17428 type : this.inputType,
17429 cls : 'form-control x-combo-noedit',
17430 autocomplete: 'new-password',
17431 placeholder : this.placeholder || '',
17436 input.name = this.name;
17440 input.cls += ' input-' + this.size;
17443 if (this.disabled) {
17444 input.disabled = true;
17448 cls : 'roo-combobox-wrap',
17455 inputblock.cls += ' input-group';
17457 inputblock.cn.unshift({
17459 cls : 'input-group-addon input-group-prepend input-group-text',
17464 if(this.removable && !this.multiple){
17465 inputblock.cls += ' roo-removable';
17467 inputblock.cn.push({
17470 cls : 'roo-combo-removable-btn close'
17474 if(this.hasFeedback && !this.allowBlank){
17476 inputblock.cls += ' has-feedback';
17478 inputblock.cn.push({
17480 cls: 'glyphicon form-control-feedback'
17487 inputblock.cls += (this.before) ? '' : ' input-group';
17489 inputblock.cn.push({
17491 cls : 'input-group-addon input-group-append input-group-text',
17497 var ibwrap = inputblock;
17502 cls: 'roo-select2-choices',
17506 cls: 'roo-select2-search-field',
17519 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17524 cls: 'form-hidden-field'
17530 if(!this.multiple && this.showToggleBtn){
17536 if (this.caret != false) {
17539 cls: 'fa fa-' + this.caret
17546 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17548 Roo.bootstrap.version == 3 ? caret : '',
17551 cls: 'combobox-clear',
17565 combobox.cls += ' roo-select2-container-multi';
17568 var align = this.labelAlign || this.parentLabelAlign();
17570 if (align ==='left' && this.fieldLabel.length) {
17575 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17576 tooltip : 'This field is required'
17580 cls : 'control-label col-form-label',
17581 html : this.fieldLabel
17585 cls : 'roo-combobox-wrap ',
17592 var labelCfg = cfg.cn[1];
17593 var contentCfg = cfg.cn[2];
17596 if(this.indicatorpos == 'right'){
17601 cls : 'control-label col-form-label',
17605 html : this.fieldLabel
17609 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17610 tooltip : 'This field is required'
17615 cls : "roo-combobox-wrap ",
17623 labelCfg = cfg.cn[0];
17624 contentCfg = cfg.cn[1];
17629 if(this.labelWidth > 12){
17630 labelCfg.style = "width: " + this.labelWidth + 'px';
17633 if(this.labelWidth < 13 && this.labelmd == 0){
17634 this.labelmd = this.labelWidth;
17637 if(this.labellg > 0){
17638 labelCfg.cls += ' col-lg-' + this.labellg;
17639 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17642 if(this.labelmd > 0){
17643 labelCfg.cls += ' col-md-' + this.labelmd;
17644 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17647 if(this.labelsm > 0){
17648 labelCfg.cls += ' col-sm-' + this.labelsm;
17649 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17652 if(this.labelxs > 0){
17653 labelCfg.cls += ' col-xs-' + this.labelxs;
17654 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17658 } else if ( this.fieldLabel.length) {
17662 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17663 tooltip : 'This field is required'
17667 cls : 'control-label',
17668 html : this.fieldLabel
17679 if(this.indicatorpos == 'right'){
17683 cls : 'control-label',
17684 html : this.fieldLabel,
17688 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17689 tooltip : 'This field is required'
17706 var settings = this;
17708 ['xs','sm','md','lg'].map(function(size){
17709 if (settings[size]) {
17710 cfg.cls += ' col-' + size + '-' + settings[size];
17717 initTouchView : function()
17719 this.renderTouchView();
17721 this.touchViewEl.on('scroll', function(){
17722 this.el.dom.scrollTop = 0;
17725 this.originalValue = this.getValue();
17727 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17729 this.inputEl().on("click", this.showTouchView, this);
17730 if (this.triggerEl) {
17731 this.triggerEl.on("click", this.showTouchView, this);
17735 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17736 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17738 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17740 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17741 this.store.on('load', this.onTouchViewLoad, this);
17742 this.store.on('loadexception', this.onTouchViewLoadException, this);
17744 if(this.hiddenName){
17746 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17748 this.hiddenField.dom.value =
17749 this.hiddenValue !== undefined ? this.hiddenValue :
17750 this.value !== undefined ? this.value : '';
17752 this.el.dom.removeAttribute('name');
17753 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17757 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17758 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17761 if(this.removable && !this.multiple){
17762 var close = this.closeTriggerEl();
17764 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17765 close.on('click', this.removeBtnClick, this, close);
17769 * fix the bug in Safari iOS8
17771 this.inputEl().on("focus", function(e){
17772 document.activeElement.blur();
17775 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17782 renderTouchView : function()
17784 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17785 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17787 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17788 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17790 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17791 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17792 this.touchViewBodyEl.setStyle('overflow', 'auto');
17794 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17795 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17797 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17798 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17802 showTouchView : function()
17808 this.touchViewHeaderEl.hide();
17810 if(this.modalTitle.length){
17811 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17812 this.touchViewHeaderEl.show();
17815 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17816 this.touchViewEl.show();
17818 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17820 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17821 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17823 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17825 if(this.modalTitle.length){
17826 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17829 this.touchViewBodyEl.setHeight(bodyHeight);
17833 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17835 this.touchViewEl.addClass(['in','show']);
17838 if(this._touchViewMask){
17839 Roo.get(document.body).addClass("x-body-masked");
17840 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17841 this._touchViewMask.setStyle('z-index', 10000);
17842 this._touchViewMask.addClass('show');
17845 this.doTouchViewQuery();
17849 hideTouchView : function()
17851 this.touchViewEl.removeClass(['in','show']);
17855 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17857 this.touchViewEl.setStyle('display', 'none');
17860 if(this._touchViewMask){
17861 this._touchViewMask.removeClass('show');
17862 Roo.get(document.body).removeClass("x-body-masked");
17866 setTouchViewValue : function()
17873 Roo.each(this.tickItems, function(o){
17878 this.hideTouchView();
17881 doTouchViewQuery : function()
17890 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17894 if(!this.alwaysQuery || this.mode == 'local'){
17895 this.onTouchViewLoad();
17902 onTouchViewBeforeLoad : function(combo,opts)
17908 onTouchViewLoad : function()
17910 if(this.store.getCount() < 1){
17911 this.onTouchViewEmptyResults();
17915 this.clearTouchView();
17917 var rawValue = this.getRawValue();
17919 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17921 this.tickItems = [];
17923 this.store.data.each(function(d, rowIndex){
17924 var row = this.touchViewListGroup.createChild(template);
17926 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17927 row.addClass(d.data.cls);
17930 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17933 html : d.data[this.displayField]
17936 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17937 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17940 row.removeClass('selected');
17941 if(!this.multiple && this.valueField &&
17942 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17945 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17946 row.addClass('selected');
17949 if(this.multiple && this.valueField &&
17950 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17954 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17955 this.tickItems.push(d.data);
17958 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17962 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17964 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17966 if(this.modalTitle.length){
17967 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17970 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17972 if(this.mobile_restrict_height && listHeight < bodyHeight){
17973 this.touchViewBodyEl.setHeight(listHeight);
17978 if(firstChecked && listHeight > bodyHeight){
17979 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17984 onTouchViewLoadException : function()
17986 this.hideTouchView();
17989 onTouchViewEmptyResults : function()
17991 this.clearTouchView();
17993 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17995 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17999 clearTouchView : function()
18001 this.touchViewListGroup.dom.innerHTML = '';
18004 onTouchViewClick : function(e, el, o)
18006 e.preventDefault();
18009 var rowIndex = o.rowIndex;
18011 var r = this.store.getAt(rowIndex);
18013 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18015 if(!this.multiple){
18016 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18017 c.dom.removeAttribute('checked');
18020 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18022 this.setFromData(r.data);
18024 var close = this.closeTriggerEl();
18030 this.hideTouchView();
18032 this.fireEvent('select', this, r, rowIndex);
18037 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18038 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18039 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18043 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18044 this.addItem(r.data);
18045 this.tickItems.push(r.data);
18049 getAutoCreateNativeIOS : function()
18052 cls: 'form-group' //input-group,
18057 cls : 'roo-ios-select'
18061 combobox.name = this.name;
18064 if (this.disabled) {
18065 combobox.disabled = true;
18068 var settings = this;
18070 ['xs','sm','md','lg'].map(function(size){
18071 if (settings[size]) {
18072 cfg.cls += ' col-' + size + '-' + settings[size];
18082 initIOSView : function()
18084 this.store.on('load', this.onIOSViewLoad, this);
18089 onIOSViewLoad : function()
18091 if(this.store.getCount() < 1){
18095 this.clearIOSView();
18097 if(this.allowBlank) {
18099 var default_text = '-- SELECT --';
18101 if(this.placeholder.length){
18102 default_text = this.placeholder;
18105 if(this.emptyTitle.length){
18106 default_text += ' - ' + this.emptyTitle + ' -';
18109 var opt = this.inputEl().createChild({
18112 html : default_text
18116 o[this.valueField] = 0;
18117 o[this.displayField] = default_text;
18119 this.ios_options.push({
18126 this.store.data.each(function(d, rowIndex){
18130 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18131 html = d.data[this.displayField];
18136 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18137 value = d.data[this.valueField];
18146 if(this.value == d.data[this.valueField]){
18147 option['selected'] = true;
18150 var opt = this.inputEl().createChild(option);
18152 this.ios_options.push({
18159 this.inputEl().on('change', function(){
18160 this.fireEvent('select', this);
18165 clearIOSView: function()
18167 this.inputEl().dom.innerHTML = '';
18169 this.ios_options = [];
18172 setIOSValue: function(v)
18176 if(!this.ios_options){
18180 Roo.each(this.ios_options, function(opts){
18182 opts.el.dom.removeAttribute('selected');
18184 if(opts.data[this.valueField] != v){
18188 opts.el.dom.setAttribute('selected', true);
18194 * @cfg {Boolean} grow
18198 * @cfg {Number} growMin
18202 * @cfg {Number} growMax
18211 Roo.apply(Roo.bootstrap.ComboBox, {
18215 cls: 'modal-header',
18237 cls: 'list-group-item',
18241 cls: 'roo-combobox-list-group-item-value'
18245 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18259 listItemCheckbox : {
18261 cls: 'list-group-item',
18265 cls: 'roo-combobox-list-group-item-value'
18269 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18285 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18290 cls: 'modal-footer',
18298 cls: 'col-xs-6 text-left',
18301 cls: 'btn btn-danger roo-touch-view-cancel',
18307 cls: 'col-xs-6 text-right',
18310 cls: 'btn btn-success roo-touch-view-ok',
18321 Roo.apply(Roo.bootstrap.ComboBox, {
18323 touchViewTemplate : {
18325 cls: 'modal fade roo-combobox-touch-view',
18329 cls: 'modal-dialog',
18330 style : 'position:fixed', // we have to fix position....
18334 cls: 'modal-content',
18336 Roo.bootstrap.ComboBox.header,
18337 Roo.bootstrap.ComboBox.body,
18338 Roo.bootstrap.ComboBox.footer
18347 * Ext JS Library 1.1.1
18348 * Copyright(c) 2006-2007, Ext JS, LLC.
18350 * Originally Released Under LGPL - original licence link has changed is not relivant.
18353 * <script type="text/javascript">
18358 * @extends Roo.util.Observable
18359 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18360 * This class also supports single and multi selection modes. <br>
18361 * Create a data model bound view:
18363 var store = new Roo.data.Store(...);
18365 var view = new Roo.View({
18367 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18369 singleSelect: true,
18370 selectedClass: "ydataview-selected",
18374 // listen for node click?
18375 view.on("click", function(vw, index, node, e){
18376 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18380 dataModel.load("foobar.xml");
18382 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18384 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18385 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18387 * Note: old style constructor is still suported (container, template, config)
18390 * Create a new View
18391 * @param {Object} config The config object
18394 Roo.View = function(config, depreciated_tpl, depreciated_config){
18396 this.parent = false;
18398 if (typeof(depreciated_tpl) == 'undefined') {
18399 // new way.. - universal constructor.
18400 Roo.apply(this, config);
18401 this.el = Roo.get(this.el);
18404 this.el = Roo.get(config);
18405 this.tpl = depreciated_tpl;
18406 Roo.apply(this, depreciated_config);
18408 this.wrapEl = this.el.wrap().wrap();
18409 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18412 if(typeof(this.tpl) == "string"){
18413 this.tpl = new Roo.Template(this.tpl);
18415 // support xtype ctors..
18416 this.tpl = new Roo.factory(this.tpl, Roo);
18420 this.tpl.compile();
18425 * @event beforeclick
18426 * Fires before a click is processed. Returns false to cancel the default action.
18427 * @param {Roo.View} this
18428 * @param {Number} index The index of the target node
18429 * @param {HTMLElement} node The target node
18430 * @param {Roo.EventObject} e The raw event object
18432 "beforeclick" : true,
18435 * Fires when a template node is clicked.
18436 * @param {Roo.View} this
18437 * @param {Number} index The index of the target node
18438 * @param {HTMLElement} node The target node
18439 * @param {Roo.EventObject} e The raw event object
18444 * Fires when a template node is double clicked.
18445 * @param {Roo.View} this
18446 * @param {Number} index The index of the target node
18447 * @param {HTMLElement} node The target node
18448 * @param {Roo.EventObject} e The raw event object
18452 * @event contextmenu
18453 * Fires when a template node is right clicked.
18454 * @param {Roo.View} this
18455 * @param {Number} index The index of the target node
18456 * @param {HTMLElement} node The target node
18457 * @param {Roo.EventObject} e The raw event object
18459 "contextmenu" : true,
18461 * @event selectionchange
18462 * Fires when the selected nodes change.
18463 * @param {Roo.View} this
18464 * @param {Array} selections Array of the selected nodes
18466 "selectionchange" : true,
18469 * @event beforeselect
18470 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18471 * @param {Roo.View} this
18472 * @param {HTMLElement} node The node to be selected
18473 * @param {Array} selections Array of currently selected nodes
18475 "beforeselect" : true,
18477 * @event preparedata
18478 * Fires on every row to render, to allow you to change the data.
18479 * @param {Roo.View} this
18480 * @param {Object} data to be rendered (change this)
18482 "preparedata" : true
18490 "click": this.onClick,
18491 "dblclick": this.onDblClick,
18492 "contextmenu": this.onContextMenu,
18496 this.selections = [];
18498 this.cmp = new Roo.CompositeElementLite([]);
18500 this.store = Roo.factory(this.store, Roo.data);
18501 this.setStore(this.store, true);
18504 if ( this.footer && this.footer.xtype) {
18506 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18508 this.footer.dataSource = this.store;
18509 this.footer.container = fctr;
18510 this.footer = Roo.factory(this.footer, Roo);
18511 fctr.insertFirst(this.el);
18513 // this is a bit insane - as the paging toolbar seems to detach the el..
18514 // dom.parentNode.parentNode.parentNode
18515 // they get detached?
18519 Roo.View.superclass.constructor.call(this);
18524 Roo.extend(Roo.View, Roo.util.Observable, {
18527 * @cfg {Roo.data.Store} store Data store to load data from.
18532 * @cfg {String|Roo.Element} el The container element.
18537 * @cfg {String|Roo.Template} tpl The template used by this View
18541 * @cfg {String} dataName the named area of the template to use as the data area
18542 * Works with domtemplates roo-name="name"
18546 * @cfg {String} selectedClass The css class to add to selected nodes
18548 selectedClass : "x-view-selected",
18550 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18555 * @cfg {String} text to display on mask (default Loading)
18559 * @cfg {Boolean} multiSelect Allow multiple selection
18561 multiSelect : false,
18563 * @cfg {Boolean} singleSelect Allow single selection
18565 singleSelect: false,
18568 * @cfg {Boolean} toggleSelect - selecting
18570 toggleSelect : false,
18573 * @cfg {Boolean} tickable - selecting
18578 * Returns the element this view is bound to.
18579 * @return {Roo.Element}
18581 getEl : function(){
18582 return this.wrapEl;
18588 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18590 refresh : function(){
18591 //Roo.log('refresh');
18594 // if we are using something like 'domtemplate', then
18595 // the what gets used is:
18596 // t.applySubtemplate(NAME, data, wrapping data..)
18597 // the outer template then get' applied with
18598 // the store 'extra data'
18599 // and the body get's added to the
18600 // roo-name="data" node?
18601 // <span class='roo-tpl-{name}'></span> ?????
18605 this.clearSelections();
18606 this.el.update("");
18608 var records = this.store.getRange();
18609 if(records.length < 1) {
18611 // is this valid?? = should it render a template??
18613 this.el.update(this.emptyText);
18617 if (this.dataName) {
18618 this.el.update(t.apply(this.store.meta)); //????
18619 el = this.el.child('.roo-tpl-' + this.dataName);
18622 for(var i = 0, len = records.length; i < len; i++){
18623 var data = this.prepareData(records[i].data, i, records[i]);
18624 this.fireEvent("preparedata", this, data, i, records[i]);
18626 var d = Roo.apply({}, data);
18629 Roo.apply(d, {'roo-id' : Roo.id()});
18633 Roo.each(this.parent.item, function(item){
18634 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18637 Roo.apply(d, {'roo-data-checked' : 'checked'});
18641 html[html.length] = Roo.util.Format.trim(
18643 t.applySubtemplate(this.dataName, d, this.store.meta) :
18650 el.update(html.join(""));
18651 this.nodes = el.dom.childNodes;
18652 this.updateIndexes(0);
18657 * Function to override to reformat the data that is sent to
18658 * the template for each node.
18659 * DEPRICATED - use the preparedata event handler.
18660 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18661 * a JSON object for an UpdateManager bound view).
18663 prepareData : function(data, index, record)
18665 this.fireEvent("preparedata", this, data, index, record);
18669 onUpdate : function(ds, record){
18670 // Roo.log('on update');
18671 this.clearSelections();
18672 var index = this.store.indexOf(record);
18673 var n = this.nodes[index];
18674 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18675 n.parentNode.removeChild(n);
18676 this.updateIndexes(index, index);
18682 onAdd : function(ds, records, index)
18684 //Roo.log(['on Add', ds, records, index] );
18685 this.clearSelections();
18686 if(this.nodes.length == 0){
18690 var n = this.nodes[index];
18691 for(var i = 0, len = records.length; i < len; i++){
18692 var d = this.prepareData(records[i].data, i, records[i]);
18694 this.tpl.insertBefore(n, d);
18697 this.tpl.append(this.el, d);
18700 this.updateIndexes(index);
18703 onRemove : function(ds, record, index){
18704 // Roo.log('onRemove');
18705 this.clearSelections();
18706 var el = this.dataName ?
18707 this.el.child('.roo-tpl-' + this.dataName) :
18710 el.dom.removeChild(this.nodes[index]);
18711 this.updateIndexes(index);
18715 * Refresh an individual node.
18716 * @param {Number} index
18718 refreshNode : function(index){
18719 this.onUpdate(this.store, this.store.getAt(index));
18722 updateIndexes : function(startIndex, endIndex){
18723 var ns = this.nodes;
18724 startIndex = startIndex || 0;
18725 endIndex = endIndex || ns.length - 1;
18726 for(var i = startIndex; i <= endIndex; i++){
18727 ns[i].nodeIndex = i;
18732 * Changes the data store this view uses and refresh the view.
18733 * @param {Store} store
18735 setStore : function(store, initial){
18736 if(!initial && this.store){
18737 this.store.un("datachanged", this.refresh);
18738 this.store.un("add", this.onAdd);
18739 this.store.un("remove", this.onRemove);
18740 this.store.un("update", this.onUpdate);
18741 this.store.un("clear", this.refresh);
18742 this.store.un("beforeload", this.onBeforeLoad);
18743 this.store.un("load", this.onLoad);
18744 this.store.un("loadexception", this.onLoad);
18748 store.on("datachanged", this.refresh, this);
18749 store.on("add", this.onAdd, this);
18750 store.on("remove", this.onRemove, this);
18751 store.on("update", this.onUpdate, this);
18752 store.on("clear", this.refresh, this);
18753 store.on("beforeload", this.onBeforeLoad, this);
18754 store.on("load", this.onLoad, this);
18755 store.on("loadexception", this.onLoad, this);
18763 * onbeforeLoad - masks the loading area.
18766 onBeforeLoad : function(store,opts)
18768 //Roo.log('onBeforeLoad');
18770 this.el.update("");
18772 this.el.mask(this.mask ? this.mask : "Loading" );
18774 onLoad : function ()
18781 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18782 * @param {HTMLElement} node
18783 * @return {HTMLElement} The template node
18785 findItemFromChild : function(node){
18786 var el = this.dataName ?
18787 this.el.child('.roo-tpl-' + this.dataName,true) :
18790 if(!node || node.parentNode == el){
18793 var p = node.parentNode;
18794 while(p && p != el){
18795 if(p.parentNode == el){
18804 onClick : function(e){
18805 var item = this.findItemFromChild(e.getTarget());
18807 var index = this.indexOf(item);
18808 if(this.onItemClick(item, index, e) !== false){
18809 this.fireEvent("click", this, index, item, e);
18812 this.clearSelections();
18817 onContextMenu : function(e){
18818 var item = this.findItemFromChild(e.getTarget());
18820 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18825 onDblClick : function(e){
18826 var item = this.findItemFromChild(e.getTarget());
18828 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18832 onItemClick : function(item, index, e)
18834 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18837 if (this.toggleSelect) {
18838 var m = this.isSelected(item) ? 'unselect' : 'select';
18841 _t[m](item, true, false);
18844 if(this.multiSelect || this.singleSelect){
18845 if(this.multiSelect && e.shiftKey && this.lastSelection){
18846 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18848 this.select(item, this.multiSelect && e.ctrlKey);
18849 this.lastSelection = item;
18852 if(!this.tickable){
18853 e.preventDefault();
18861 * Get the number of selected nodes.
18864 getSelectionCount : function(){
18865 return this.selections.length;
18869 * Get the currently selected nodes.
18870 * @return {Array} An array of HTMLElements
18872 getSelectedNodes : function(){
18873 return this.selections;
18877 * Get the indexes of the selected nodes.
18880 getSelectedIndexes : function(){
18881 var indexes = [], s = this.selections;
18882 for(var i = 0, len = s.length; i < len; i++){
18883 indexes.push(s[i].nodeIndex);
18889 * Clear all selections
18890 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18892 clearSelections : function(suppressEvent){
18893 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18894 this.cmp.elements = this.selections;
18895 this.cmp.removeClass(this.selectedClass);
18896 this.selections = [];
18897 if(!suppressEvent){
18898 this.fireEvent("selectionchange", this, this.selections);
18904 * Returns true if the passed node is selected
18905 * @param {HTMLElement/Number} node The node or node index
18906 * @return {Boolean}
18908 isSelected : function(node){
18909 var s = this.selections;
18913 node = this.getNode(node);
18914 return s.indexOf(node) !== -1;
18919 * @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
18920 * @param {Boolean} keepExisting (optional) true to keep existing selections
18921 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18923 select : function(nodeInfo, keepExisting, suppressEvent){
18924 if(nodeInfo instanceof Array){
18926 this.clearSelections(true);
18928 for(var i = 0, len = nodeInfo.length; i < len; i++){
18929 this.select(nodeInfo[i], true, true);
18933 var node = this.getNode(nodeInfo);
18934 if(!node || this.isSelected(node)){
18935 return; // already selected.
18938 this.clearSelections(true);
18941 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18942 Roo.fly(node).addClass(this.selectedClass);
18943 this.selections.push(node);
18944 if(!suppressEvent){
18945 this.fireEvent("selectionchange", this, this.selections);
18953 * @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
18954 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18955 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18957 unselect : function(nodeInfo, keepExisting, suppressEvent)
18959 if(nodeInfo instanceof Array){
18960 Roo.each(this.selections, function(s) {
18961 this.unselect(s, nodeInfo);
18965 var node = this.getNode(nodeInfo);
18966 if(!node || !this.isSelected(node)){
18967 //Roo.log("not selected");
18968 return; // not selected.
18972 Roo.each(this.selections, function(s) {
18974 Roo.fly(node).removeClass(this.selectedClass);
18981 this.selections= ns;
18982 this.fireEvent("selectionchange", this, this.selections);
18986 * Gets a template node.
18987 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18988 * @return {HTMLElement} The node or null if it wasn't found
18990 getNode : function(nodeInfo){
18991 if(typeof nodeInfo == "string"){
18992 return document.getElementById(nodeInfo);
18993 }else if(typeof nodeInfo == "number"){
18994 return this.nodes[nodeInfo];
19000 * Gets a range template nodes.
19001 * @param {Number} startIndex
19002 * @param {Number} endIndex
19003 * @return {Array} An array of nodes
19005 getNodes : function(start, end){
19006 var ns = this.nodes;
19007 start = start || 0;
19008 end = typeof end == "undefined" ? ns.length - 1 : end;
19011 for(var i = start; i <= end; i++){
19015 for(var i = start; i >= end; i--){
19023 * Finds the index of the passed node
19024 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19025 * @return {Number} The index of the node or -1
19027 indexOf : function(node){
19028 node = this.getNode(node);
19029 if(typeof node.nodeIndex == "number"){
19030 return node.nodeIndex;
19032 var ns = this.nodes;
19033 for(var i = 0, len = ns.length; i < len; i++){
19044 * based on jquery fullcalendar
19048 Roo.bootstrap = Roo.bootstrap || {};
19050 * @class Roo.bootstrap.Calendar
19051 * @extends Roo.bootstrap.Component
19052 * Bootstrap Calendar class
19053 * @cfg {Boolean} loadMask (true|false) default false
19054 * @cfg {Object} header generate the user specific header of the calendar, default false
19057 * Create a new Container
19058 * @param {Object} config The config object
19063 Roo.bootstrap.Calendar = function(config){
19064 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19068 * Fires when a date is selected
19069 * @param {DatePicker} this
19070 * @param {Date} date The selected date
19074 * @event monthchange
19075 * Fires when the displayed month changes
19076 * @param {DatePicker} this
19077 * @param {Date} date The selected month
19079 'monthchange': true,
19081 * @event evententer
19082 * Fires when mouse over an event
19083 * @param {Calendar} this
19084 * @param {event} Event
19086 'evententer': true,
19088 * @event eventleave
19089 * Fires when the mouse leaves an
19090 * @param {Calendar} this
19093 'eventleave': true,
19095 * @event eventclick
19096 * Fires when the mouse click an
19097 * @param {Calendar} this
19106 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19109 * @cfg {Number} startDay
19110 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19118 getAutoCreate : function(){
19121 var fc_button = function(name, corner, style, content ) {
19122 return Roo.apply({},{
19124 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19126 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19129 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19140 style : 'width:100%',
19147 cls : 'fc-header-left',
19149 fc_button('prev', 'left', 'arrow', '‹' ),
19150 fc_button('next', 'right', 'arrow', '›' ),
19151 { tag: 'span', cls: 'fc-header-space' },
19152 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19160 cls : 'fc-header-center',
19164 cls: 'fc-header-title',
19167 html : 'month / year'
19175 cls : 'fc-header-right',
19177 /* fc_button('month', 'left', '', 'month' ),
19178 fc_button('week', '', '', 'week' ),
19179 fc_button('day', 'right', '', 'day' )
19191 header = this.header;
19194 var cal_heads = function() {
19196 // fixme - handle this.
19198 for (var i =0; i < Date.dayNames.length; i++) {
19199 var d = Date.dayNames[i];
19202 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19203 html : d.substring(0,3)
19207 ret[0].cls += ' fc-first';
19208 ret[6].cls += ' fc-last';
19211 var cal_cell = function(n) {
19214 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19219 cls: 'fc-day-number',
19223 cls: 'fc-day-content',
19227 style: 'position: relative;' // height: 17px;
19239 var cal_rows = function() {
19242 for (var r = 0; r < 6; r++) {
19249 for (var i =0; i < Date.dayNames.length; i++) {
19250 var d = Date.dayNames[i];
19251 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19254 row.cn[0].cls+=' fc-first';
19255 row.cn[0].cn[0].style = 'min-height:90px';
19256 row.cn[6].cls+=' fc-last';
19260 ret[0].cls += ' fc-first';
19261 ret[4].cls += ' fc-prev-last';
19262 ret[5].cls += ' fc-last';
19269 cls: 'fc-border-separate',
19270 style : 'width:100%',
19278 cls : 'fc-first fc-last',
19296 cls : 'fc-content',
19297 style : "position: relative;",
19300 cls : 'fc-view fc-view-month fc-grid',
19301 style : 'position: relative',
19302 unselectable : 'on',
19305 cls : 'fc-event-container',
19306 style : 'position:absolute;z-index:8;top:0;left:0;'
19324 initEvents : function()
19327 throw "can not find store for calendar";
19333 style: "text-align:center",
19337 style: "background-color:white;width:50%;margin:250 auto",
19341 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19352 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19354 var size = this.el.select('.fc-content', true).first().getSize();
19355 this.maskEl.setSize(size.width, size.height);
19356 this.maskEl.enableDisplayMode("block");
19357 if(!this.loadMask){
19358 this.maskEl.hide();
19361 this.store = Roo.factory(this.store, Roo.data);
19362 this.store.on('load', this.onLoad, this);
19363 this.store.on('beforeload', this.onBeforeLoad, this);
19367 this.cells = this.el.select('.fc-day',true);
19368 //Roo.log(this.cells);
19369 this.textNodes = this.el.query('.fc-day-number');
19370 this.cells.addClassOnOver('fc-state-hover');
19372 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19373 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19374 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19375 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19377 this.on('monthchange', this.onMonthChange, this);
19379 this.update(new Date().clearTime());
19382 resize : function() {
19383 var sz = this.el.getSize();
19385 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19386 this.el.select('.fc-day-content div',true).setHeight(34);
19391 showPrevMonth : function(e){
19392 this.update(this.activeDate.add("mo", -1));
19394 showToday : function(e){
19395 this.update(new Date().clearTime());
19398 showNextMonth : function(e){
19399 this.update(this.activeDate.add("mo", 1));
19403 showPrevYear : function(){
19404 this.update(this.activeDate.add("y", -1));
19408 showNextYear : function(){
19409 this.update(this.activeDate.add("y", 1));
19414 update : function(date)
19416 var vd = this.activeDate;
19417 this.activeDate = date;
19418 // if(vd && this.el){
19419 // var t = date.getTime();
19420 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19421 // Roo.log('using add remove');
19423 // this.fireEvent('monthchange', this, date);
19425 // this.cells.removeClass("fc-state-highlight");
19426 // this.cells.each(function(c){
19427 // if(c.dateValue == t){
19428 // c.addClass("fc-state-highlight");
19429 // setTimeout(function(){
19430 // try{c.dom.firstChild.focus();}catch(e){}
19440 var days = date.getDaysInMonth();
19442 var firstOfMonth = date.getFirstDateOfMonth();
19443 var startingPos = firstOfMonth.getDay()-this.startDay;
19445 if(startingPos < this.startDay){
19449 var pm = date.add(Date.MONTH, -1);
19450 var prevStart = pm.getDaysInMonth()-startingPos;
19452 this.cells = this.el.select('.fc-day',true);
19453 this.textNodes = this.el.query('.fc-day-number');
19454 this.cells.addClassOnOver('fc-state-hover');
19456 var cells = this.cells.elements;
19457 var textEls = this.textNodes;
19459 Roo.each(cells, function(cell){
19460 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19463 days += startingPos;
19465 // convert everything to numbers so it's fast
19466 var day = 86400000;
19467 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19470 //Roo.log(prevStart);
19472 var today = new Date().clearTime().getTime();
19473 var sel = date.clearTime().getTime();
19474 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19475 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19476 var ddMatch = this.disabledDatesRE;
19477 var ddText = this.disabledDatesText;
19478 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19479 var ddaysText = this.disabledDaysText;
19480 var format = this.format;
19482 var setCellClass = function(cal, cell){
19486 //Roo.log('set Cell Class');
19488 var t = d.getTime();
19492 cell.dateValue = t;
19494 cell.className += " fc-today";
19495 cell.className += " fc-state-highlight";
19496 cell.title = cal.todayText;
19499 // disable highlight in other month..
19500 //cell.className += " fc-state-highlight";
19505 cell.className = " fc-state-disabled";
19506 cell.title = cal.minText;
19510 cell.className = " fc-state-disabled";
19511 cell.title = cal.maxText;
19515 if(ddays.indexOf(d.getDay()) != -1){
19516 cell.title = ddaysText;
19517 cell.className = " fc-state-disabled";
19520 if(ddMatch && format){
19521 var fvalue = d.dateFormat(format);
19522 if(ddMatch.test(fvalue)){
19523 cell.title = ddText.replace("%0", fvalue);
19524 cell.className = " fc-state-disabled";
19528 if (!cell.initialClassName) {
19529 cell.initialClassName = cell.dom.className;
19532 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19537 for(; i < startingPos; i++) {
19538 textEls[i].innerHTML = (++prevStart);
19539 d.setDate(d.getDate()+1);
19541 cells[i].className = "fc-past fc-other-month";
19542 setCellClass(this, cells[i]);
19547 for(; i < days; i++){
19548 intDay = i - startingPos + 1;
19549 textEls[i].innerHTML = (intDay);
19550 d.setDate(d.getDate()+1);
19552 cells[i].className = ''; // "x-date-active";
19553 setCellClass(this, cells[i]);
19557 for(; i < 42; i++) {
19558 textEls[i].innerHTML = (++extraDays);
19559 d.setDate(d.getDate()+1);
19561 cells[i].className = "fc-future fc-other-month";
19562 setCellClass(this, cells[i]);
19565 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19567 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19569 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19570 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19572 if(totalRows != 6){
19573 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19574 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19577 this.fireEvent('monthchange', this, date);
19581 if(!this.internalRender){
19582 var main = this.el.dom.firstChild;
19583 var w = main.offsetWidth;
19584 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19585 Roo.fly(main).setWidth(w);
19586 this.internalRender = true;
19587 // opera does not respect the auto grow header center column
19588 // then, after it gets a width opera refuses to recalculate
19589 // without a second pass
19590 if(Roo.isOpera && !this.secondPass){
19591 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19592 this.secondPass = true;
19593 this.update.defer(10, this, [date]);
19600 findCell : function(dt) {
19601 dt = dt.clearTime().getTime();
19603 this.cells.each(function(c){
19604 //Roo.log("check " +c.dateValue + '?=' + dt);
19605 if(c.dateValue == dt){
19615 findCells : function(ev) {
19616 var s = ev.start.clone().clearTime().getTime();
19618 var e= ev.end.clone().clearTime().getTime();
19621 this.cells.each(function(c){
19622 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19624 if(c.dateValue > e){
19627 if(c.dateValue < s){
19636 // findBestRow: function(cells)
19640 // for (var i =0 ; i < cells.length;i++) {
19641 // ret = Math.max(cells[i].rows || 0,ret);
19648 addItem : function(ev)
19650 // look for vertical location slot in
19651 var cells = this.findCells(ev);
19653 // ev.row = this.findBestRow(cells);
19655 // work out the location.
19659 for(var i =0; i < cells.length; i++) {
19661 cells[i].row = cells[0].row;
19664 cells[i].row = cells[i].row + 1;
19674 if (crow.start.getY() == cells[i].getY()) {
19676 crow.end = cells[i];
19693 cells[0].events.push(ev);
19695 this.calevents.push(ev);
19698 clearEvents: function() {
19700 if(!this.calevents){
19704 Roo.each(this.cells.elements, function(c){
19710 Roo.each(this.calevents, function(e) {
19711 Roo.each(e.els, function(el) {
19712 el.un('mouseenter' ,this.onEventEnter, this);
19713 el.un('mouseleave' ,this.onEventLeave, this);
19718 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19724 renderEvents: function()
19728 this.cells.each(function(c) {
19737 if(c.row != c.events.length){
19738 r = 4 - (4 - (c.row - c.events.length));
19741 c.events = ev.slice(0, r);
19742 c.more = ev.slice(r);
19744 if(c.more.length && c.more.length == 1){
19745 c.events.push(c.more.pop());
19748 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19752 this.cells.each(function(c) {
19754 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19757 for (var e = 0; e < c.events.length; e++){
19758 var ev = c.events[e];
19759 var rows = ev.rows;
19761 for(var i = 0; i < rows.length; i++) {
19763 // how many rows should it span..
19766 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19767 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19769 unselectable : "on",
19772 cls: 'fc-event-inner',
19776 // cls: 'fc-event-time',
19777 // html : cells.length > 1 ? '' : ev.time
19781 cls: 'fc-event-title',
19782 html : String.format('{0}', ev.title)
19789 cls: 'ui-resizable-handle ui-resizable-e',
19790 html : '  '
19797 cfg.cls += ' fc-event-start';
19799 if ((i+1) == rows.length) {
19800 cfg.cls += ' fc-event-end';
19803 var ctr = _this.el.select('.fc-event-container',true).first();
19804 var cg = ctr.createChild(cfg);
19806 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19807 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19809 var r = (c.more.length) ? 1 : 0;
19810 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19811 cg.setWidth(ebox.right - sbox.x -2);
19813 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19814 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19815 cg.on('click', _this.onEventClick, _this, ev);
19826 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19827 style : 'position: absolute',
19828 unselectable : "on",
19831 cls: 'fc-event-inner',
19835 cls: 'fc-event-title',
19843 cls: 'ui-resizable-handle ui-resizable-e',
19844 html : '  '
19850 var ctr = _this.el.select('.fc-event-container',true).first();
19851 var cg = ctr.createChild(cfg);
19853 var sbox = c.select('.fc-day-content',true).first().getBox();
19854 var ebox = c.select('.fc-day-content',true).first().getBox();
19856 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19857 cg.setWidth(ebox.right - sbox.x -2);
19859 cg.on('click', _this.onMoreEventClick, _this, c.more);
19869 onEventEnter: function (e, el,event,d) {
19870 this.fireEvent('evententer', this, el, event);
19873 onEventLeave: function (e, el,event,d) {
19874 this.fireEvent('eventleave', this, el, event);
19877 onEventClick: function (e, el,event,d) {
19878 this.fireEvent('eventclick', this, el, event);
19881 onMonthChange: function () {
19885 onMoreEventClick: function(e, el, more)
19889 this.calpopover.placement = 'right';
19890 this.calpopover.setTitle('More');
19892 this.calpopover.setContent('');
19894 var ctr = this.calpopover.el.select('.popover-content', true).first();
19896 Roo.each(more, function(m){
19898 cls : 'fc-event-hori fc-event-draggable',
19901 var cg = ctr.createChild(cfg);
19903 cg.on('click', _this.onEventClick, _this, m);
19906 this.calpopover.show(el);
19911 onLoad: function ()
19913 this.calevents = [];
19916 if(this.store.getCount() > 0){
19917 this.store.data.each(function(d){
19920 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19921 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19922 time : d.data.start_time,
19923 title : d.data.title,
19924 description : d.data.description,
19925 venue : d.data.venue
19930 this.renderEvents();
19932 if(this.calevents.length && this.loadMask){
19933 this.maskEl.hide();
19937 onBeforeLoad: function()
19939 this.clearEvents();
19941 this.maskEl.show();
19955 * @class Roo.bootstrap.Popover
19956 * @extends Roo.bootstrap.Component
19957 * Bootstrap Popover class
19958 * @cfg {String} html contents of the popover (or false to use children..)
19959 * @cfg {String} title of popover (or false to hide)
19960 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19961 * @cfg {String} trigger click || hover (or false to trigger manually)
19962 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19963 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19964 * - if false and it has a 'parent' then it will be automatically added to that element
19965 * - if string - Roo.get will be called
19966 * @cfg {Number} delay - delay before showing
19969 * Create a new Popover
19970 * @param {Object} config The config object
19973 Roo.bootstrap.Popover = function(config){
19974 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19980 * After the popover show
19982 * @param {Roo.bootstrap.Popover} this
19987 * After the popover hide
19989 * @param {Roo.bootstrap.Popover} this
19995 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20000 placement : 'right',
20001 trigger : 'hover', // hover
20007 can_build_overlaid : false,
20009 maskEl : false, // the mask element
20012 alignEl : false, // when show is called with an element - this get's stored.
20014 getChildContainer : function()
20016 return this.contentEl;
20019 getPopoverHeader : function()
20021 this.title = true; // flag not to hide it..
20022 this.headerEl.addClass('p-0');
20023 return this.headerEl
20027 getAutoCreate : function(){
20030 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20031 style: 'display:block',
20037 cls : 'popover-inner ',
20041 cls: 'popover-title popover-header',
20042 html : this.title === false ? '' : this.title
20045 cls : 'popover-content popover-body ' + (this.cls || ''),
20046 html : this.html || ''
20057 * @param {string} the title
20059 setTitle: function(str)
20063 this.headerEl.dom.innerHTML = str;
20068 * @param {string} the body content
20070 setContent: function(str)
20073 if (this.contentEl) {
20074 this.contentEl.dom.innerHTML = str;
20078 // as it get's added to the bottom of the page.
20079 onRender : function(ct, position)
20081 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20086 var cfg = Roo.apply({}, this.getAutoCreate());
20090 cfg.cls += ' ' + this.cls;
20093 cfg.style = this.style;
20095 //Roo.log("adding to ");
20096 this.el = Roo.get(document.body).createChild(cfg, position);
20097 // Roo.log(this.el);
20100 this.contentEl = this.el.select('.popover-content',true).first();
20101 this.headerEl = this.el.select('.popover-title',true).first();
20104 if(typeof(this.items) != 'undefined'){
20105 var items = this.items;
20108 for(var i =0;i < items.length;i++) {
20109 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20113 this.items = nitems;
20115 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20116 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20123 resizeMask : function()
20125 this.maskEl.setSize(
20126 Roo.lib.Dom.getViewWidth(true),
20127 Roo.lib.Dom.getViewHeight(true)
20131 initEvents : function()
20135 Roo.bootstrap.Popover.register(this);
20138 this.arrowEl = this.el.select('.arrow',true).first();
20139 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20140 this.el.enableDisplayMode('block');
20144 if (this.over === false && !this.parent()) {
20147 if (this.triggers === false) {
20152 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20153 var triggers = this.trigger ? this.trigger.split(' ') : [];
20154 Roo.each(triggers, function(trigger) {
20156 if (trigger == 'click') {
20157 on_el.on('click', this.toggle, this);
20158 } else if (trigger != 'manual') {
20159 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20160 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20162 on_el.on(eventIn ,this.enter, this);
20163 on_el.on(eventOut, this.leave, this);
20173 toggle : function () {
20174 this.hoverState == 'in' ? this.leave() : this.enter();
20177 enter : function () {
20179 clearTimeout(this.timeout);
20181 this.hoverState = 'in';
20183 if (!this.delay || !this.delay.show) {
20188 this.timeout = setTimeout(function () {
20189 if (_t.hoverState == 'in') {
20192 }, this.delay.show)
20195 leave : function() {
20196 clearTimeout(this.timeout);
20198 this.hoverState = 'out';
20200 if (!this.delay || !this.delay.hide) {
20205 this.timeout = setTimeout(function () {
20206 if (_t.hoverState == 'out') {
20209 }, this.delay.hide)
20213 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20214 * @param {string} (left|right|top|bottom) position
20216 show : function (on_el, placement)
20218 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20219 on_el = on_el || false; // default to false
20222 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20223 on_el = this.parent().el;
20224 } else if (this.over) {
20225 Roo.get(this.over);
20230 this.alignEl = Roo.get( on_el );
20233 this.render(document.body);
20239 if (this.title === false) {
20240 this.headerEl.hide();
20245 this.el.dom.style.display = 'block';
20248 if (this.alignEl) {
20249 this.updatePosition(this.placement, true);
20252 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20253 var es = this.el.getSize();
20254 var x = Roo.lib.Dom.getViewWidth()/2;
20255 var y = Roo.lib.Dom.getViewHeight()/2;
20256 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20261 //var arrow = this.el.select('.arrow',true).first();
20262 //arrow.set(align[2],
20264 this.el.addClass('in');
20268 this.hoverState = 'in';
20271 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20272 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20273 this.maskEl.dom.style.display = 'block';
20274 this.maskEl.addClass('show');
20276 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20278 this.fireEvent('show', this);
20282 * fire this manually after loading a grid in the table for example
20283 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20284 * @param {Boolean} try and move it if we cant get right position.
20286 updatePosition : function(placement, try_move)
20288 // allow for calling with no parameters
20289 placement = placement ? placement : this.placement;
20290 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20292 this.el.removeClass([
20293 'fade','top','bottom', 'left', 'right','in',
20294 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20296 this.el.addClass(placement + ' bs-popover-' + placement);
20298 if (!this.alignEl ) {
20302 switch (placement) {
20304 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20305 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20306 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20307 //normal display... or moved up/down.
20308 this.el.setXY(offset);
20309 var xy = this.alignEl.getAnchorXY('tr', false);
20311 this.arrowEl.setXY(xy);
20314 // continue through...
20315 return this.updatePosition('left', false);
20319 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20320 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20321 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20322 //normal display... or moved up/down.
20323 this.el.setXY(offset);
20324 var xy = this.alignEl.getAnchorXY('tl', false);
20325 xy[0]-=10;xy[1]+=5; // << fix me
20326 this.arrowEl.setXY(xy);
20330 return this.updatePosition('right', false);
20333 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20334 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20335 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20336 //normal display... or moved up/down.
20337 this.el.setXY(offset);
20338 var xy = this.alignEl.getAnchorXY('t', false);
20339 xy[1]-=10; // << fix me
20340 this.arrowEl.setXY(xy);
20344 return this.updatePosition('bottom', false);
20347 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20348 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20349 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20350 //normal display... or moved up/down.
20351 this.el.setXY(offset);
20352 var xy = this.alignEl.getAnchorXY('b', false);
20353 xy[1]+=2; // << fix me
20354 this.arrowEl.setXY(xy);
20358 return this.updatePosition('top', false);
20369 this.el.setXY([0,0]);
20370 this.el.removeClass('in');
20372 this.hoverState = null;
20373 this.maskEl.hide(); // always..
20374 this.fireEvent('hide', this);
20380 Roo.apply(Roo.bootstrap.Popover, {
20383 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20384 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20385 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20386 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20391 clickHander : false,
20394 onMouseDown : function(e)
20396 if (!e.getTarget(".roo-popover")) {
20404 register : function(popup)
20406 if (!Roo.bootstrap.Popover.clickHandler) {
20407 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20409 // hide other popups.
20411 this.popups.push(popup);
20413 hideAll : function()
20415 this.popups.forEach(function(p) {
20423 * Card header - holder for the card header elements.
20428 * @class Roo.bootstrap.PopoverNav
20429 * @extends Roo.bootstrap.NavGroup
20430 * Bootstrap Popover header navigation class
20432 * Create a new Popover Header Navigation
20433 * @param {Object} config The config object
20436 Roo.bootstrap.PopoverNav = function(config){
20437 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20440 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20443 container_method : 'getPopoverHeader'
20461 * @class Roo.bootstrap.Progress
20462 * @extends Roo.bootstrap.Component
20463 * Bootstrap Progress class
20464 * @cfg {Boolean} striped striped of the progress bar
20465 * @cfg {Boolean} active animated of the progress bar
20469 * Create a new Progress
20470 * @param {Object} config The config object
20473 Roo.bootstrap.Progress = function(config){
20474 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20477 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20482 getAutoCreate : function(){
20490 cfg.cls += ' progress-striped';
20494 cfg.cls += ' active';
20513 * @class Roo.bootstrap.ProgressBar
20514 * @extends Roo.bootstrap.Component
20515 * Bootstrap ProgressBar class
20516 * @cfg {Number} aria_valuenow aria-value now
20517 * @cfg {Number} aria_valuemin aria-value min
20518 * @cfg {Number} aria_valuemax aria-value max
20519 * @cfg {String} label label for the progress bar
20520 * @cfg {String} panel (success | info | warning | danger )
20521 * @cfg {String} role role of the progress bar
20522 * @cfg {String} sr_only text
20526 * Create a new ProgressBar
20527 * @param {Object} config The config object
20530 Roo.bootstrap.ProgressBar = function(config){
20531 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20534 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20538 aria_valuemax : 100,
20544 getAutoCreate : function()
20549 cls: 'progress-bar',
20550 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20562 cfg.role = this.role;
20565 if(this.aria_valuenow){
20566 cfg['aria-valuenow'] = this.aria_valuenow;
20569 if(this.aria_valuemin){
20570 cfg['aria-valuemin'] = this.aria_valuemin;
20573 if(this.aria_valuemax){
20574 cfg['aria-valuemax'] = this.aria_valuemax;
20577 if(this.label && !this.sr_only){
20578 cfg.html = this.label;
20582 cfg.cls += ' progress-bar-' + this.panel;
20588 update : function(aria_valuenow)
20590 this.aria_valuenow = aria_valuenow;
20592 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20607 * @class Roo.bootstrap.TabGroup
20608 * @extends Roo.bootstrap.Column
20609 * Bootstrap Column class
20610 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20611 * @cfg {Boolean} carousel true to make the group behave like a carousel
20612 * @cfg {Boolean} bullets show bullets for the panels
20613 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20614 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20615 * @cfg {Boolean} showarrow (true|false) show arrow default true
20618 * Create a new TabGroup
20619 * @param {Object} config The config object
20622 Roo.bootstrap.TabGroup = function(config){
20623 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20625 this.navId = Roo.id();
20628 Roo.bootstrap.TabGroup.register(this);
20632 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20635 transition : false,
20640 slideOnTouch : false,
20643 getAutoCreate : function()
20645 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20647 cfg.cls += ' tab-content';
20649 if (this.carousel) {
20650 cfg.cls += ' carousel slide';
20653 cls : 'carousel-inner',
20657 if(this.bullets && !Roo.isTouch){
20660 cls : 'carousel-bullets',
20664 if(this.bullets_cls){
20665 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20672 cfg.cn[0].cn.push(bullets);
20675 if(this.showarrow){
20676 cfg.cn[0].cn.push({
20678 class : 'carousel-arrow',
20682 class : 'carousel-prev',
20686 class : 'fa fa-chevron-left'
20692 class : 'carousel-next',
20696 class : 'fa fa-chevron-right'
20709 initEvents: function()
20711 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20712 // this.el.on("touchstart", this.onTouchStart, this);
20715 if(this.autoslide){
20718 this.slideFn = window.setInterval(function() {
20719 _this.showPanelNext();
20723 if(this.showarrow){
20724 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20725 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20731 // onTouchStart : function(e, el, o)
20733 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20737 // this.showPanelNext();
20741 getChildContainer : function()
20743 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20747 * register a Navigation item
20748 * @param {Roo.bootstrap.NavItem} the navitem to add
20750 register : function(item)
20752 this.tabs.push( item);
20753 item.navId = this.navId; // not really needed..
20758 getActivePanel : function()
20761 Roo.each(this.tabs, function(t) {
20771 getPanelByName : function(n)
20774 Roo.each(this.tabs, function(t) {
20775 if (t.tabId == n) {
20783 indexOfPanel : function(p)
20786 Roo.each(this.tabs, function(t,i) {
20787 if (t.tabId == p.tabId) {
20796 * show a specific panel
20797 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20798 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20800 showPanel : function (pan)
20802 if(this.transition || typeof(pan) == 'undefined'){
20803 Roo.log("waiting for the transitionend");
20807 if (typeof(pan) == 'number') {
20808 pan = this.tabs[pan];
20811 if (typeof(pan) == 'string') {
20812 pan = this.getPanelByName(pan);
20815 var cur = this.getActivePanel();
20818 Roo.log('pan or acitve pan is undefined');
20822 if (pan.tabId == this.getActivePanel().tabId) {
20826 if (false === cur.fireEvent('beforedeactivate')) {
20830 if(this.bullets > 0 && !Roo.isTouch){
20831 this.setActiveBullet(this.indexOfPanel(pan));
20834 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20836 //class="carousel-item carousel-item-next carousel-item-left"
20838 this.transition = true;
20839 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20840 var lr = dir == 'next' ? 'left' : 'right';
20841 pan.el.addClass(dir); // or prev
20842 pan.el.addClass('carousel-item-' + dir); // or prev
20843 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20844 cur.el.addClass(lr); // or right
20845 pan.el.addClass(lr);
20846 cur.el.addClass('carousel-item-' +lr); // or right
20847 pan.el.addClass('carousel-item-' +lr);
20851 cur.el.on('transitionend', function() {
20852 Roo.log("trans end?");
20854 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20855 pan.setActive(true);
20857 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20858 cur.setActive(false);
20860 _this.transition = false;
20862 }, this, { single: true } );
20867 cur.setActive(false);
20868 pan.setActive(true);
20873 showPanelNext : function()
20875 var i = this.indexOfPanel(this.getActivePanel());
20877 if (i >= this.tabs.length - 1 && !this.autoslide) {
20881 if (i >= this.tabs.length - 1 && this.autoslide) {
20885 this.showPanel(this.tabs[i+1]);
20888 showPanelPrev : function()
20890 var i = this.indexOfPanel(this.getActivePanel());
20892 if (i < 1 && !this.autoslide) {
20896 if (i < 1 && this.autoslide) {
20897 i = this.tabs.length;
20900 this.showPanel(this.tabs[i-1]);
20904 addBullet: function()
20906 if(!this.bullets || Roo.isTouch){
20909 var ctr = this.el.select('.carousel-bullets',true).first();
20910 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20911 var bullet = ctr.createChild({
20912 cls : 'bullet bullet-' + i
20913 },ctr.dom.lastChild);
20918 bullet.on('click', (function(e, el, o, ii, t){
20920 e.preventDefault();
20922 this.showPanel(ii);
20924 if(this.autoslide && this.slideFn){
20925 clearInterval(this.slideFn);
20926 this.slideFn = window.setInterval(function() {
20927 _this.showPanelNext();
20931 }).createDelegate(this, [i, bullet], true));
20936 setActiveBullet : function(i)
20942 Roo.each(this.el.select('.bullet', true).elements, function(el){
20943 el.removeClass('selected');
20946 var bullet = this.el.select('.bullet-' + i, true).first();
20952 bullet.addClass('selected');
20963 Roo.apply(Roo.bootstrap.TabGroup, {
20967 * register a Navigation Group
20968 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20970 register : function(navgrp)
20972 this.groups[navgrp.navId] = navgrp;
20976 * fetch a Navigation Group based on the navigation ID
20977 * if one does not exist , it will get created.
20978 * @param {string} the navgroup to add
20979 * @returns {Roo.bootstrap.NavGroup} the navgroup
20981 get: function(navId) {
20982 if (typeof(this.groups[navId]) == 'undefined') {
20983 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20985 return this.groups[navId] ;
21000 * @class Roo.bootstrap.TabPanel
21001 * @extends Roo.bootstrap.Component
21002 * Bootstrap TabPanel class
21003 * @cfg {Boolean} active panel active
21004 * @cfg {String} html panel content
21005 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21006 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21007 * @cfg {String} href click to link..
21008 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21012 * Create a new TabPanel
21013 * @param {Object} config The config object
21016 Roo.bootstrap.TabPanel = function(config){
21017 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21021 * Fires when the active status changes
21022 * @param {Roo.bootstrap.TabPanel} this
21023 * @param {Boolean} state the new state
21028 * @event beforedeactivate
21029 * Fires before a tab is de-activated - can be used to do validation on a form.
21030 * @param {Roo.bootstrap.TabPanel} this
21031 * @return {Boolean} false if there is an error
21034 'beforedeactivate': true
21037 this.tabId = this.tabId || Roo.id();
21041 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21048 touchSlide : false,
21049 getAutoCreate : function(){
21054 // item is needed for carousel - not sure if it has any effect otherwise
21055 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21056 html: this.html || ''
21060 cfg.cls += ' active';
21064 cfg.tabId = this.tabId;
21072 initEvents: function()
21074 var p = this.parent();
21076 this.navId = this.navId || p.navId;
21078 if (typeof(this.navId) != 'undefined') {
21079 // not really needed.. but just in case.. parent should be a NavGroup.
21080 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21084 var i = tg.tabs.length - 1;
21086 if(this.active && tg.bullets > 0 && i < tg.bullets){
21087 tg.setActiveBullet(i);
21091 this.el.on('click', this.onClick, this);
21093 if(Roo.isTouch && this.touchSlide){
21094 this.el.on("touchstart", this.onTouchStart, this);
21095 this.el.on("touchmove", this.onTouchMove, this);
21096 this.el.on("touchend", this.onTouchEnd, this);
21101 onRender : function(ct, position)
21103 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21106 setActive : function(state)
21108 Roo.log("panel - set active " + this.tabId + "=" + state);
21110 this.active = state;
21112 this.el.removeClass('active');
21114 } else if (!this.el.hasClass('active')) {
21115 this.el.addClass('active');
21118 this.fireEvent('changed', this, state);
21121 onClick : function(e)
21123 e.preventDefault();
21125 if(!this.href.length){
21129 window.location.href = this.href;
21138 onTouchStart : function(e)
21140 this.swiping = false;
21142 this.startX = e.browserEvent.touches[0].clientX;
21143 this.startY = e.browserEvent.touches[0].clientY;
21146 onTouchMove : function(e)
21148 this.swiping = true;
21150 this.endX = e.browserEvent.touches[0].clientX;
21151 this.endY = e.browserEvent.touches[0].clientY;
21154 onTouchEnd : function(e)
21161 var tabGroup = this.parent();
21163 if(this.endX > this.startX){ // swiping right
21164 tabGroup.showPanelPrev();
21168 if(this.startX > this.endX){ // swiping left
21169 tabGroup.showPanelNext();
21188 * @class Roo.bootstrap.DateField
21189 * @extends Roo.bootstrap.Input
21190 * Bootstrap DateField class
21191 * @cfg {Number} weekStart default 0
21192 * @cfg {String} viewMode default empty, (months|years)
21193 * @cfg {String} minViewMode default empty, (months|years)
21194 * @cfg {Number} startDate default -Infinity
21195 * @cfg {Number} endDate default Infinity
21196 * @cfg {Boolean} todayHighlight default false
21197 * @cfg {Boolean} todayBtn default false
21198 * @cfg {Boolean} calendarWeeks default false
21199 * @cfg {Object} daysOfWeekDisabled default empty
21200 * @cfg {Boolean} singleMode default false (true | false)
21202 * @cfg {Boolean} keyboardNavigation default true
21203 * @cfg {String} language default en
21206 * Create a new DateField
21207 * @param {Object} config The config object
21210 Roo.bootstrap.DateField = function(config){
21211 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21215 * Fires when this field show.
21216 * @param {Roo.bootstrap.DateField} this
21217 * @param {Mixed} date The date value
21222 * Fires when this field hide.
21223 * @param {Roo.bootstrap.DateField} this
21224 * @param {Mixed} date The date value
21229 * Fires when select a date.
21230 * @param {Roo.bootstrap.DateField} this
21231 * @param {Mixed} date The date value
21235 * @event beforeselect
21236 * Fires when before select a date.
21237 * @param {Roo.bootstrap.DateField} this
21238 * @param {Mixed} date The date value
21240 beforeselect : true
21244 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21247 * @cfg {String} format
21248 * The default date format string which can be overriden for localization support. The format must be
21249 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21253 * @cfg {String} altFormats
21254 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21255 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21257 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21265 todayHighlight : false,
21271 keyboardNavigation: true,
21273 calendarWeeks: false,
21275 startDate: -Infinity,
21279 daysOfWeekDisabled: [],
21283 singleMode : false,
21285 UTCDate: function()
21287 return new Date(Date.UTC.apply(Date, arguments));
21290 UTCToday: function()
21292 var today = new Date();
21293 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21296 getDate: function() {
21297 var d = this.getUTCDate();
21298 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21301 getUTCDate: function() {
21305 setDate: function(d) {
21306 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21309 setUTCDate: function(d) {
21311 this.setValue(this.formatDate(this.date));
21314 onRender: function(ct, position)
21317 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21319 this.language = this.language || 'en';
21320 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21321 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21323 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21324 this.format = this.format || 'm/d/y';
21325 this.isInline = false;
21326 this.isInput = true;
21327 this.component = this.el.select('.add-on', true).first() || false;
21328 this.component = (this.component && this.component.length === 0) ? false : this.component;
21329 this.hasInput = this.component && this.inputEl().length;
21331 if (typeof(this.minViewMode === 'string')) {
21332 switch (this.minViewMode) {
21334 this.minViewMode = 1;
21337 this.minViewMode = 2;
21340 this.minViewMode = 0;
21345 if (typeof(this.viewMode === 'string')) {
21346 switch (this.viewMode) {
21359 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21361 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21363 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21365 this.picker().on('mousedown', this.onMousedown, this);
21366 this.picker().on('click', this.onClick, this);
21368 this.picker().addClass('datepicker-dropdown');
21370 this.startViewMode = this.viewMode;
21372 if(this.singleMode){
21373 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21374 v.setVisibilityMode(Roo.Element.DISPLAY);
21378 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21379 v.setStyle('width', '189px');
21383 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21384 if(!this.calendarWeeks){
21389 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21390 v.attr('colspan', function(i, val){
21391 return parseInt(val) + 1;
21396 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21398 this.setStartDate(this.startDate);
21399 this.setEndDate(this.endDate);
21401 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21408 if(this.isInline) {
21413 picker : function()
21415 return this.pickerEl;
21416 // return this.el.select('.datepicker', true).first();
21419 fillDow: function()
21421 var dowCnt = this.weekStart;
21430 if(this.calendarWeeks){
21438 while (dowCnt < this.weekStart + 7) {
21442 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21446 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21449 fillMonths: function()
21452 var months = this.picker().select('>.datepicker-months td', true).first();
21454 months.dom.innerHTML = '';
21460 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21463 months.createChild(month);
21470 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;
21472 if (this.date < this.startDate) {
21473 this.viewDate = new Date(this.startDate);
21474 } else if (this.date > this.endDate) {
21475 this.viewDate = new Date(this.endDate);
21477 this.viewDate = new Date(this.date);
21485 var d = new Date(this.viewDate),
21486 year = d.getUTCFullYear(),
21487 month = d.getUTCMonth(),
21488 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21489 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21490 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21491 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21492 currentDate = this.date && this.date.valueOf(),
21493 today = this.UTCToday();
21495 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21497 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21499 // this.picker.select('>tfoot th.today').
21500 // .text(dates[this.language].today)
21501 // .toggle(this.todayBtn !== false);
21503 this.updateNavArrows();
21506 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21508 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21510 prevMonth.setUTCDate(day);
21512 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21514 var nextMonth = new Date(prevMonth);
21516 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21518 nextMonth = nextMonth.valueOf();
21520 var fillMonths = false;
21522 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21524 while(prevMonth.valueOf() <= nextMonth) {
21527 if (prevMonth.getUTCDay() === this.weekStart) {
21529 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21537 if(this.calendarWeeks){
21538 // ISO 8601: First week contains first thursday.
21539 // ISO also states week starts on Monday, but we can be more abstract here.
21541 // Start of current week: based on weekstart/current date
21542 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21543 // Thursday of this week
21544 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21545 // First Thursday of year, year from thursday
21546 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21547 // Calendar week: ms between thursdays, div ms per day, div 7 days
21548 calWeek = (th - yth) / 864e5 / 7 + 1;
21550 fillMonths.cn.push({
21558 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21560 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21563 if (this.todayHighlight &&
21564 prevMonth.getUTCFullYear() == today.getFullYear() &&
21565 prevMonth.getUTCMonth() == today.getMonth() &&
21566 prevMonth.getUTCDate() == today.getDate()) {
21567 clsName += ' today';
21570 if (currentDate && prevMonth.valueOf() === currentDate) {
21571 clsName += ' active';
21574 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21575 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21576 clsName += ' disabled';
21579 fillMonths.cn.push({
21581 cls: 'day ' + clsName,
21582 html: prevMonth.getDate()
21585 prevMonth.setDate(prevMonth.getDate()+1);
21588 var currentYear = this.date && this.date.getUTCFullYear();
21589 var currentMonth = this.date && this.date.getUTCMonth();
21591 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21593 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21594 v.removeClass('active');
21596 if(currentYear === year && k === currentMonth){
21597 v.addClass('active');
21600 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21601 v.addClass('disabled');
21607 year = parseInt(year/10, 10) * 10;
21609 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21611 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21614 for (var i = -1; i < 11; i++) {
21615 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21617 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21625 showMode: function(dir)
21628 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21631 Roo.each(this.picker().select('>div',true).elements, function(v){
21632 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21635 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21640 if(this.isInline) {
21644 this.picker().removeClass(['bottom', 'top']);
21646 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21648 * place to the top of element!
21652 this.picker().addClass('top');
21653 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21658 this.picker().addClass('bottom');
21660 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21663 parseDate : function(value)
21665 if(!value || value instanceof Date){
21668 var v = Date.parseDate(value, this.format);
21669 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21670 v = Date.parseDate(value, 'Y-m-d');
21672 if(!v && this.altFormats){
21673 if(!this.altFormatsArray){
21674 this.altFormatsArray = this.altFormats.split("|");
21676 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21677 v = Date.parseDate(value, this.altFormatsArray[i]);
21683 formatDate : function(date, fmt)
21685 return (!date || !(date instanceof Date)) ?
21686 date : date.dateFormat(fmt || this.format);
21689 onFocus : function()
21691 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21695 onBlur : function()
21697 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21699 var d = this.inputEl().getValue();
21706 showPopup : function()
21708 this.picker().show();
21712 this.fireEvent('showpopup', this, this.date);
21715 hidePopup : function()
21717 if(this.isInline) {
21720 this.picker().hide();
21721 this.viewMode = this.startViewMode;
21724 this.fireEvent('hidepopup', this, this.date);
21728 onMousedown: function(e)
21730 e.stopPropagation();
21731 e.preventDefault();
21736 Roo.bootstrap.DateField.superclass.keyup.call(this);
21740 setValue: function(v)
21742 if(this.fireEvent('beforeselect', this, v) !== false){
21743 var d = new Date(this.parseDate(v) ).clearTime();
21745 if(isNaN(d.getTime())){
21746 this.date = this.viewDate = '';
21747 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21751 v = this.formatDate(d);
21753 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21755 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21759 this.fireEvent('select', this, this.date);
21763 getValue: function()
21765 return this.formatDate(this.date);
21768 fireKey: function(e)
21770 if (!this.picker().isVisible()){
21771 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21777 var dateChanged = false,
21779 newDate, newViewDate;
21784 e.preventDefault();
21788 if (!this.keyboardNavigation) {
21791 dir = e.keyCode == 37 ? -1 : 1;
21794 newDate = this.moveYear(this.date, dir);
21795 newViewDate = this.moveYear(this.viewDate, dir);
21796 } else if (e.shiftKey){
21797 newDate = this.moveMonth(this.date, dir);
21798 newViewDate = this.moveMonth(this.viewDate, dir);
21800 newDate = new Date(this.date);
21801 newDate.setUTCDate(this.date.getUTCDate() + dir);
21802 newViewDate = new Date(this.viewDate);
21803 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21805 if (this.dateWithinRange(newDate)){
21806 this.date = newDate;
21807 this.viewDate = newViewDate;
21808 this.setValue(this.formatDate(this.date));
21810 e.preventDefault();
21811 dateChanged = true;
21816 if (!this.keyboardNavigation) {
21819 dir = e.keyCode == 38 ? -1 : 1;
21821 newDate = this.moveYear(this.date, dir);
21822 newViewDate = this.moveYear(this.viewDate, dir);
21823 } else if (e.shiftKey){
21824 newDate = this.moveMonth(this.date, dir);
21825 newViewDate = this.moveMonth(this.viewDate, dir);
21827 newDate = new Date(this.date);
21828 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21829 newViewDate = new Date(this.viewDate);
21830 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21832 if (this.dateWithinRange(newDate)){
21833 this.date = newDate;
21834 this.viewDate = newViewDate;
21835 this.setValue(this.formatDate(this.date));
21837 e.preventDefault();
21838 dateChanged = true;
21842 this.setValue(this.formatDate(this.date));
21844 e.preventDefault();
21847 this.setValue(this.formatDate(this.date));
21861 onClick: function(e)
21863 e.stopPropagation();
21864 e.preventDefault();
21866 var target = e.getTarget();
21868 if(target.nodeName.toLowerCase() === 'i'){
21869 target = Roo.get(target).dom.parentNode;
21872 var nodeName = target.nodeName;
21873 var className = target.className;
21874 var html = target.innerHTML;
21875 //Roo.log(nodeName);
21877 switch(nodeName.toLowerCase()) {
21879 switch(className) {
21885 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21886 switch(this.viewMode){
21888 this.viewDate = this.moveMonth(this.viewDate, dir);
21892 this.viewDate = this.moveYear(this.viewDate, dir);
21898 var date = new Date();
21899 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21901 this.setValue(this.formatDate(this.date));
21908 if (className.indexOf('disabled') < 0) {
21909 this.viewDate.setUTCDate(1);
21910 if (className.indexOf('month') > -1) {
21911 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21913 var year = parseInt(html, 10) || 0;
21914 this.viewDate.setUTCFullYear(year);
21918 if(this.singleMode){
21919 this.setValue(this.formatDate(this.viewDate));
21930 //Roo.log(className);
21931 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21932 var day = parseInt(html, 10) || 1;
21933 var year = (this.viewDate || new Date()).getUTCFullYear(),
21934 month = (this.viewDate || new Date()).getUTCMonth();
21936 if (className.indexOf('old') > -1) {
21943 } else if (className.indexOf('new') > -1) {
21951 //Roo.log([year,month,day]);
21952 this.date = this.UTCDate(year, month, day,0,0,0,0);
21953 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21955 //Roo.log(this.formatDate(this.date));
21956 this.setValue(this.formatDate(this.date));
21963 setStartDate: function(startDate)
21965 this.startDate = startDate || -Infinity;
21966 if (this.startDate !== -Infinity) {
21967 this.startDate = this.parseDate(this.startDate);
21970 this.updateNavArrows();
21973 setEndDate: function(endDate)
21975 this.endDate = endDate || Infinity;
21976 if (this.endDate !== Infinity) {
21977 this.endDate = this.parseDate(this.endDate);
21980 this.updateNavArrows();
21983 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21985 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21986 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21987 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21989 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21990 return parseInt(d, 10);
21993 this.updateNavArrows();
21996 updateNavArrows: function()
21998 if(this.singleMode){
22002 var d = new Date(this.viewDate),
22003 year = d.getUTCFullYear(),
22004 month = d.getUTCMonth();
22006 Roo.each(this.picker().select('.prev', true).elements, function(v){
22008 switch (this.viewMode) {
22011 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22017 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22024 Roo.each(this.picker().select('.next', true).elements, function(v){
22026 switch (this.viewMode) {
22029 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22035 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22043 moveMonth: function(date, dir)
22048 var new_date = new Date(date.valueOf()),
22049 day = new_date.getUTCDate(),
22050 month = new_date.getUTCMonth(),
22051 mag = Math.abs(dir),
22053 dir = dir > 0 ? 1 : -1;
22056 // If going back one month, make sure month is not current month
22057 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22059 return new_date.getUTCMonth() == month;
22061 // If going forward one month, make sure month is as expected
22062 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22064 return new_date.getUTCMonth() != new_month;
22066 new_month = month + dir;
22067 new_date.setUTCMonth(new_month);
22068 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22069 if (new_month < 0 || new_month > 11) {
22070 new_month = (new_month + 12) % 12;
22073 // For magnitudes >1, move one month at a time...
22074 for (var i=0; i<mag; i++) {
22075 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22076 new_date = this.moveMonth(new_date, dir);
22078 // ...then reset the day, keeping it in the new month
22079 new_month = new_date.getUTCMonth();
22080 new_date.setUTCDate(day);
22082 return new_month != new_date.getUTCMonth();
22085 // Common date-resetting loop -- if date is beyond end of month, make it
22088 new_date.setUTCDate(--day);
22089 new_date.setUTCMonth(new_month);
22094 moveYear: function(date, dir)
22096 return this.moveMonth(date, dir*12);
22099 dateWithinRange: function(date)
22101 return date >= this.startDate && date <= this.endDate;
22107 this.picker().remove();
22110 validateValue : function(value)
22112 if(this.getVisibilityEl().hasClass('hidden')){
22116 if(value.length < 1) {
22117 if(this.allowBlank){
22123 if(value.length < this.minLength){
22126 if(value.length > this.maxLength){
22130 var vt = Roo.form.VTypes;
22131 if(!vt[this.vtype](value, this)){
22135 if(typeof this.validator == "function"){
22136 var msg = this.validator(value);
22142 if(this.regex && !this.regex.test(value)){
22146 if(typeof(this.parseDate(value)) == 'undefined'){
22150 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22154 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22164 this.date = this.viewDate = '';
22166 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22171 Roo.apply(Roo.bootstrap.DateField, {
22182 html: '<i class="fa fa-arrow-left"/>'
22192 html: '<i class="fa fa-arrow-right"/>'
22234 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22235 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22236 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22237 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22238 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22251 navFnc: 'FullYear',
22256 navFnc: 'FullYear',
22261 Roo.apply(Roo.bootstrap.DateField, {
22265 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22269 cls: 'datepicker-days',
22273 cls: 'table-condensed',
22275 Roo.bootstrap.DateField.head,
22279 Roo.bootstrap.DateField.footer
22286 cls: 'datepicker-months',
22290 cls: 'table-condensed',
22292 Roo.bootstrap.DateField.head,
22293 Roo.bootstrap.DateField.content,
22294 Roo.bootstrap.DateField.footer
22301 cls: 'datepicker-years',
22305 cls: 'table-condensed',
22307 Roo.bootstrap.DateField.head,
22308 Roo.bootstrap.DateField.content,
22309 Roo.bootstrap.DateField.footer
22328 * @class Roo.bootstrap.TimeField
22329 * @extends Roo.bootstrap.Input
22330 * Bootstrap DateField class
22334 * Create a new TimeField
22335 * @param {Object} config The config object
22338 Roo.bootstrap.TimeField = function(config){
22339 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22343 * Fires when this field show.
22344 * @param {Roo.bootstrap.DateField} thisthis
22345 * @param {Mixed} date The date value
22350 * Fires when this field hide.
22351 * @param {Roo.bootstrap.DateField} this
22352 * @param {Mixed} date The date value
22357 * Fires when select a date.
22358 * @param {Roo.bootstrap.DateField} this
22359 * @param {Mixed} date The date value
22365 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22368 * @cfg {String} format
22369 * The default time format string which can be overriden for localization support. The format must be
22370 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22374 getAutoCreate : function()
22376 this.after = '<i class="fa far fa-clock"></i>';
22377 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22381 onRender: function(ct, position)
22384 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22386 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22388 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22390 this.pop = this.picker().select('>.datepicker-time',true).first();
22391 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22393 this.picker().on('mousedown', this.onMousedown, this);
22394 this.picker().on('click', this.onClick, this);
22396 this.picker().addClass('datepicker-dropdown');
22401 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22402 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22403 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22404 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22405 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22406 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22410 fireKey: function(e){
22411 if (!this.picker().isVisible()){
22412 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22418 e.preventDefault();
22426 this.onTogglePeriod();
22429 this.onIncrementMinutes();
22432 this.onDecrementMinutes();
22441 onClick: function(e) {
22442 e.stopPropagation();
22443 e.preventDefault();
22446 picker : function()
22448 return this.pickerEl;
22451 fillTime: function()
22453 var time = this.pop.select('tbody', true).first();
22455 time.dom.innerHTML = '';
22470 cls: 'hours-up fa fas fa-chevron-up'
22490 cls: 'minutes-up fa fas fa-chevron-up'
22511 cls: 'timepicker-hour',
22526 cls: 'timepicker-minute',
22541 cls: 'btn btn-primary period',
22563 cls: 'hours-down fa fas fa-chevron-down'
22583 cls: 'minutes-down fa fas fa-chevron-down'
22601 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22608 var hours = this.time.getHours();
22609 var minutes = this.time.getMinutes();
22622 hours = hours - 12;
22626 hours = '0' + hours;
22630 minutes = '0' + minutes;
22633 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22634 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22635 this.pop.select('button', true).first().dom.innerHTML = period;
22641 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22643 var cls = ['bottom'];
22645 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22652 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22656 //this.picker().setXY(20000,20000);
22657 this.picker().addClass(cls.join('-'));
22661 Roo.each(cls, function(c){
22666 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22667 //_this.picker().setTop(_this.inputEl().getHeight());
22671 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22673 //_this.picker().setTop(0 - _this.picker().getHeight());
22678 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22682 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22690 onFocus : function()
22692 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22696 onBlur : function()
22698 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22704 this.picker().show();
22709 this.fireEvent('show', this, this.date);
22714 this.picker().hide();
22717 this.fireEvent('hide', this, this.date);
22720 setTime : function()
22723 this.setValue(this.time.format(this.format));
22725 this.fireEvent('select', this, this.date);
22730 onMousedown: function(e){
22731 e.stopPropagation();
22732 e.preventDefault();
22735 onIncrementHours: function()
22737 Roo.log('onIncrementHours');
22738 this.time = this.time.add(Date.HOUR, 1);
22743 onDecrementHours: function()
22745 Roo.log('onDecrementHours');
22746 this.time = this.time.add(Date.HOUR, -1);
22750 onIncrementMinutes: function()
22752 Roo.log('onIncrementMinutes');
22753 this.time = this.time.add(Date.MINUTE, 1);
22757 onDecrementMinutes: function()
22759 Roo.log('onDecrementMinutes');
22760 this.time = this.time.add(Date.MINUTE, -1);
22764 onTogglePeriod: function()
22766 Roo.log('onTogglePeriod');
22767 this.time = this.time.add(Date.HOUR, 12);
22775 Roo.apply(Roo.bootstrap.TimeField, {
22779 cls: 'datepicker dropdown-menu',
22783 cls: 'datepicker-time',
22787 cls: 'table-condensed',
22816 cls: 'btn btn-info ok',
22844 * @class Roo.bootstrap.MonthField
22845 * @extends Roo.bootstrap.Input
22846 * Bootstrap MonthField class
22848 * @cfg {String} language default en
22851 * Create a new MonthField
22852 * @param {Object} config The config object
22855 Roo.bootstrap.MonthField = function(config){
22856 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22861 * Fires when this field show.
22862 * @param {Roo.bootstrap.MonthField} this
22863 * @param {Mixed} date The date value
22868 * Fires when this field hide.
22869 * @param {Roo.bootstrap.MonthField} this
22870 * @param {Mixed} date The date value
22875 * Fires when select a date.
22876 * @param {Roo.bootstrap.MonthField} this
22877 * @param {String} oldvalue The old value
22878 * @param {String} newvalue The new value
22884 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22886 onRender: function(ct, position)
22889 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22891 this.language = this.language || 'en';
22892 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22893 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22895 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22896 this.isInline = false;
22897 this.isInput = true;
22898 this.component = this.el.select('.add-on', true).first() || false;
22899 this.component = (this.component && this.component.length === 0) ? false : this.component;
22900 this.hasInput = this.component && this.inputEL().length;
22902 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22904 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22906 this.picker().on('mousedown', this.onMousedown, this);
22907 this.picker().on('click', this.onClick, this);
22909 this.picker().addClass('datepicker-dropdown');
22911 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22912 v.setStyle('width', '189px');
22919 if(this.isInline) {
22925 setValue: function(v, suppressEvent)
22927 var o = this.getValue();
22929 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22933 if(suppressEvent !== true){
22934 this.fireEvent('select', this, o, v);
22939 getValue: function()
22944 onClick: function(e)
22946 e.stopPropagation();
22947 e.preventDefault();
22949 var target = e.getTarget();
22951 if(target.nodeName.toLowerCase() === 'i'){
22952 target = Roo.get(target).dom.parentNode;
22955 var nodeName = target.nodeName;
22956 var className = target.className;
22957 var html = target.innerHTML;
22959 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22963 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22965 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22971 picker : function()
22973 return this.pickerEl;
22976 fillMonths: function()
22979 var months = this.picker().select('>.datepicker-months td', true).first();
22981 months.dom.innerHTML = '';
22987 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22990 months.createChild(month);
22999 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23000 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23003 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23004 e.removeClass('active');
23006 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23007 e.addClass('active');
23014 if(this.isInline) {
23018 this.picker().removeClass(['bottom', 'top']);
23020 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23022 * place to the top of element!
23026 this.picker().addClass('top');
23027 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23032 this.picker().addClass('bottom');
23034 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23037 onFocus : function()
23039 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23043 onBlur : function()
23045 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23047 var d = this.inputEl().getValue();
23056 this.picker().show();
23057 this.picker().select('>.datepicker-months', true).first().show();
23061 this.fireEvent('show', this, this.date);
23066 if(this.isInline) {
23069 this.picker().hide();
23070 this.fireEvent('hide', this, this.date);
23074 onMousedown: function(e)
23076 e.stopPropagation();
23077 e.preventDefault();
23082 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23086 fireKey: function(e)
23088 if (!this.picker().isVisible()){
23089 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23100 e.preventDefault();
23104 dir = e.keyCode == 37 ? -1 : 1;
23106 this.vIndex = this.vIndex + dir;
23108 if(this.vIndex < 0){
23112 if(this.vIndex > 11){
23116 if(isNaN(this.vIndex)){
23120 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23126 dir = e.keyCode == 38 ? -1 : 1;
23128 this.vIndex = this.vIndex + dir * 4;
23130 if(this.vIndex < 0){
23134 if(this.vIndex > 11){
23138 if(isNaN(this.vIndex)){
23142 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23147 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23148 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23152 e.preventDefault();
23155 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23156 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23172 this.picker().remove();
23177 Roo.apply(Roo.bootstrap.MonthField, {
23196 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23197 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23202 Roo.apply(Roo.bootstrap.MonthField, {
23206 cls: 'datepicker dropdown-menu roo-dynamic',
23210 cls: 'datepicker-months',
23214 cls: 'table-condensed',
23216 Roo.bootstrap.DateField.content
23236 * @class Roo.bootstrap.CheckBox
23237 * @extends Roo.bootstrap.Input
23238 * Bootstrap CheckBox class
23240 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23241 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23242 * @cfg {String} boxLabel The text that appears beside the checkbox
23243 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23244 * @cfg {Boolean} checked initnal the element
23245 * @cfg {Boolean} inline inline the element (default false)
23246 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23247 * @cfg {String} tooltip label tooltip
23250 * Create a new CheckBox
23251 * @param {Object} config The config object
23254 Roo.bootstrap.CheckBox = function(config){
23255 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23260 * Fires when the element is checked or unchecked.
23261 * @param {Roo.bootstrap.CheckBox} this This input
23262 * @param {Boolean} checked The new checked value
23267 * Fires when the element is click.
23268 * @param {Roo.bootstrap.CheckBox} this This input
23275 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23277 inputType: 'checkbox',
23286 // checkbox success does not make any sense really..
23291 getAutoCreate : function()
23293 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23299 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23302 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23308 type : this.inputType,
23309 value : this.inputValue,
23310 cls : 'roo-' + this.inputType, //'form-box',
23311 placeholder : this.placeholder || ''
23315 if(this.inputType != 'radio'){
23319 cls : 'roo-hidden-value',
23320 value : this.checked ? this.inputValue : this.valueOff
23325 if (this.weight) { // Validity check?
23326 cfg.cls += " " + this.inputType + "-" + this.weight;
23329 if (this.disabled) {
23330 input.disabled=true;
23334 input.checked = this.checked;
23339 input.name = this.name;
23341 if(this.inputType != 'radio'){
23342 hidden.name = this.name;
23343 input.name = '_hidden_' + this.name;
23348 input.cls += ' input-' + this.size;
23353 ['xs','sm','md','lg'].map(function(size){
23354 if (settings[size]) {
23355 cfg.cls += ' col-' + size + '-' + settings[size];
23359 var inputblock = input;
23361 if (this.before || this.after) {
23364 cls : 'input-group',
23369 inputblock.cn.push({
23371 cls : 'input-group-addon',
23376 inputblock.cn.push(input);
23378 if(this.inputType != 'radio'){
23379 inputblock.cn.push(hidden);
23383 inputblock.cn.push({
23385 cls : 'input-group-addon',
23391 var boxLabelCfg = false;
23397 //'for': id, // box label is handled by onclick - so no for...
23399 html: this.boxLabel
23402 boxLabelCfg.tooltip = this.tooltip;
23408 if (align ==='left' && this.fieldLabel.length) {
23409 // Roo.log("left and has label");
23414 cls : 'control-label',
23415 html : this.fieldLabel
23426 cfg.cn[1].cn.push(boxLabelCfg);
23429 if(this.labelWidth > 12){
23430 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23433 if(this.labelWidth < 13 && this.labelmd == 0){
23434 this.labelmd = this.labelWidth;
23437 if(this.labellg > 0){
23438 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23439 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23442 if(this.labelmd > 0){
23443 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23444 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23447 if(this.labelsm > 0){
23448 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23449 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23452 if(this.labelxs > 0){
23453 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23454 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23457 } else if ( this.fieldLabel.length) {
23458 // Roo.log(" label");
23462 tag: this.boxLabel ? 'span' : 'label',
23464 cls: 'control-label box-input-label',
23465 //cls : 'input-group-addon',
23466 html : this.fieldLabel
23473 cfg.cn.push(boxLabelCfg);
23478 // Roo.log(" no label && no align");
23479 cfg.cn = [ inputblock ] ;
23481 cfg.cn.push(boxLabelCfg);
23489 if(this.inputType != 'radio'){
23490 cfg.cn.push(hidden);
23498 * return the real input element.
23500 inputEl: function ()
23502 return this.el.select('input.roo-' + this.inputType,true).first();
23504 hiddenEl: function ()
23506 return this.el.select('input.roo-hidden-value',true).first();
23509 labelEl: function()
23511 return this.el.select('label.control-label',true).first();
23513 /* depricated... */
23517 return this.labelEl();
23520 boxLabelEl: function()
23522 return this.el.select('label.box-label',true).first();
23525 initEvents : function()
23527 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23529 this.inputEl().on('click', this.onClick, this);
23531 if (this.boxLabel) {
23532 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23535 this.startValue = this.getValue();
23538 Roo.bootstrap.CheckBox.register(this);
23542 onClick : function(e)
23544 if(this.fireEvent('click', this, e) !== false){
23545 this.setChecked(!this.checked);
23550 setChecked : function(state,suppressEvent)
23552 this.startValue = this.getValue();
23554 if(this.inputType == 'radio'){
23556 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23557 e.dom.checked = false;
23560 this.inputEl().dom.checked = true;
23562 this.inputEl().dom.value = this.inputValue;
23564 if(suppressEvent !== true){
23565 this.fireEvent('check', this, true);
23573 this.checked = state;
23575 this.inputEl().dom.checked = state;
23578 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23580 if(suppressEvent !== true){
23581 this.fireEvent('check', this, state);
23587 getValue : function()
23589 if(this.inputType == 'radio'){
23590 return this.getGroupValue();
23593 return this.hiddenEl().dom.value;
23597 getGroupValue : function()
23599 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23603 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23606 setValue : function(v,suppressEvent)
23608 if(this.inputType == 'radio'){
23609 this.setGroupValue(v, suppressEvent);
23613 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23618 setGroupValue : function(v, suppressEvent)
23620 this.startValue = this.getValue();
23622 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23623 e.dom.checked = false;
23625 if(e.dom.value == v){
23626 e.dom.checked = true;
23630 if(suppressEvent !== true){
23631 this.fireEvent('check', this, true);
23639 validate : function()
23641 if(this.getVisibilityEl().hasClass('hidden')){
23647 (this.inputType == 'radio' && this.validateRadio()) ||
23648 (this.inputType == 'checkbox' && this.validateCheckbox())
23654 this.markInvalid();
23658 validateRadio : function()
23660 if(this.getVisibilityEl().hasClass('hidden')){
23664 if(this.allowBlank){
23670 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23671 if(!e.dom.checked){
23683 validateCheckbox : function()
23686 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23687 //return (this.getValue() == this.inputValue) ? true : false;
23690 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23698 for(var i in group){
23699 if(group[i].el.isVisible(true)){
23707 for(var i in group){
23712 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23719 * Mark this field as valid
23721 markValid : function()
23725 this.fireEvent('valid', this);
23727 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23730 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23737 if(this.inputType == 'radio'){
23738 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23739 var fg = e.findParent('.form-group', false, true);
23740 if (Roo.bootstrap.version == 3) {
23741 fg.removeClass([_this.invalidClass, _this.validClass]);
23742 fg.addClass(_this.validClass);
23744 fg.removeClass(['is-valid', 'is-invalid']);
23745 fg.addClass('is-valid');
23753 var fg = this.el.findParent('.form-group', false, true);
23754 if (Roo.bootstrap.version == 3) {
23755 fg.removeClass([this.invalidClass, this.validClass]);
23756 fg.addClass(this.validClass);
23758 fg.removeClass(['is-valid', 'is-invalid']);
23759 fg.addClass('is-valid');
23764 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23770 for(var i in group){
23771 var fg = group[i].el.findParent('.form-group', false, true);
23772 if (Roo.bootstrap.version == 3) {
23773 fg.removeClass([this.invalidClass, this.validClass]);
23774 fg.addClass(this.validClass);
23776 fg.removeClass(['is-valid', 'is-invalid']);
23777 fg.addClass('is-valid');
23783 * Mark this field as invalid
23784 * @param {String} msg The validation message
23786 markInvalid : function(msg)
23788 if(this.allowBlank){
23794 this.fireEvent('invalid', this, msg);
23796 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23799 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23803 label.markInvalid();
23806 if(this.inputType == 'radio'){
23808 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23809 var fg = e.findParent('.form-group', false, true);
23810 if (Roo.bootstrap.version == 3) {
23811 fg.removeClass([_this.invalidClass, _this.validClass]);
23812 fg.addClass(_this.invalidClass);
23814 fg.removeClass(['is-invalid', 'is-valid']);
23815 fg.addClass('is-invalid');
23823 var fg = this.el.findParent('.form-group', false, true);
23824 if (Roo.bootstrap.version == 3) {
23825 fg.removeClass([_this.invalidClass, _this.validClass]);
23826 fg.addClass(_this.invalidClass);
23828 fg.removeClass(['is-invalid', 'is-valid']);
23829 fg.addClass('is-invalid');
23834 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23840 for(var i in group){
23841 var fg = group[i].el.findParent('.form-group', false, true);
23842 if (Roo.bootstrap.version == 3) {
23843 fg.removeClass([_this.invalidClass, _this.validClass]);
23844 fg.addClass(_this.invalidClass);
23846 fg.removeClass(['is-invalid', 'is-valid']);
23847 fg.addClass('is-invalid');
23853 clearInvalid : function()
23855 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23857 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23859 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23861 if (label && label.iconEl) {
23862 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23863 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23867 disable : function()
23869 if(this.inputType != 'radio'){
23870 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23877 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23878 _this.getActionEl().addClass(this.disabledClass);
23879 e.dom.disabled = true;
23883 this.disabled = true;
23884 this.fireEvent("disable", this);
23888 enable : function()
23890 if(this.inputType != 'radio'){
23891 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23898 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23899 _this.getActionEl().removeClass(this.disabledClass);
23900 e.dom.disabled = false;
23904 this.disabled = false;
23905 this.fireEvent("enable", this);
23909 setBoxLabel : function(v)
23914 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23920 Roo.apply(Roo.bootstrap.CheckBox, {
23925 * register a CheckBox Group
23926 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23928 register : function(checkbox)
23930 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23931 this.groups[checkbox.groupId] = {};
23934 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23938 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23942 * fetch a CheckBox Group based on the group ID
23943 * @param {string} the group ID
23944 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23946 get: function(groupId) {
23947 if (typeof(this.groups[groupId]) == 'undefined') {
23951 return this.groups[groupId] ;
23964 * @class Roo.bootstrap.Radio
23965 * @extends Roo.bootstrap.Component
23966 * Bootstrap Radio class
23967 * @cfg {String} boxLabel - the label associated
23968 * @cfg {String} value - the value of radio
23971 * Create a new Radio
23972 * @param {Object} config The config object
23974 Roo.bootstrap.Radio = function(config){
23975 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23979 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23985 getAutoCreate : function()
23989 cls : 'form-group radio',
23994 html : this.boxLabel
24002 initEvents : function()
24004 this.parent().register(this);
24006 this.el.on('click', this.onClick, this);
24010 onClick : function(e)
24012 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24013 this.setChecked(true);
24017 setChecked : function(state, suppressEvent)
24019 this.parent().setValue(this.value, suppressEvent);
24023 setBoxLabel : function(v)
24028 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24043 * @class Roo.bootstrap.SecurePass
24044 * @extends Roo.bootstrap.Input
24045 * Bootstrap SecurePass class
24049 * Create a new SecurePass
24050 * @param {Object} config The config object
24053 Roo.bootstrap.SecurePass = function (config) {
24054 // these go here, so the translation tool can replace them..
24056 PwdEmpty: "Please type a password, and then retype it to confirm.",
24057 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24058 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24059 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24060 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24061 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24062 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24063 TooWeak: "Your password is Too Weak."
24065 this.meterLabel = "Password strength:";
24066 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24067 this.meterClass = [
24068 "roo-password-meter-tooweak",
24069 "roo-password-meter-weak",
24070 "roo-password-meter-medium",
24071 "roo-password-meter-strong",
24072 "roo-password-meter-grey"
24077 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24080 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24082 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24084 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24085 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24086 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24087 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24088 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24089 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24090 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24100 * @cfg {String/Object} Label for the strength meter (defaults to
24101 * 'Password strength:')
24106 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24107 * ['Weak', 'Medium', 'Strong'])
24110 pwdStrengths: false,
24123 initEvents: function ()
24125 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24127 if (this.el.is('input[type=password]') && Roo.isSafari) {
24128 this.el.on('keydown', this.SafariOnKeyDown, this);
24131 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24134 onRender: function (ct, position)
24136 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24137 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24138 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24140 this.trigger.createChild({
24145 cls: 'roo-password-meter-grey col-xs-12',
24148 //width: this.meterWidth + 'px'
24152 cls: 'roo-password-meter-text'
24158 if (this.hideTrigger) {
24159 this.trigger.setDisplayed(false);
24161 this.setSize(this.width || '', this.height || '');
24164 onDestroy: function ()
24166 if (this.trigger) {
24167 this.trigger.removeAllListeners();
24168 this.trigger.remove();
24171 this.wrap.remove();
24173 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24176 checkStrength: function ()
24178 var pwd = this.inputEl().getValue();
24179 if (pwd == this._lastPwd) {
24184 if (this.ClientSideStrongPassword(pwd)) {
24186 } else if (this.ClientSideMediumPassword(pwd)) {
24188 } else if (this.ClientSideWeakPassword(pwd)) {
24194 Roo.log('strength1: ' + strength);
24196 //var pm = this.trigger.child('div/div/div').dom;
24197 var pm = this.trigger.child('div/div');
24198 pm.removeClass(this.meterClass);
24199 pm.addClass(this.meterClass[strength]);
24202 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24204 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24206 this._lastPwd = pwd;
24210 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24212 this._lastPwd = '';
24214 var pm = this.trigger.child('div/div');
24215 pm.removeClass(this.meterClass);
24216 pm.addClass('roo-password-meter-grey');
24219 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24222 this.inputEl().dom.type='password';
24225 validateValue: function (value)
24227 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24230 if (value.length == 0) {
24231 if (this.allowBlank) {
24232 this.clearInvalid();
24236 this.markInvalid(this.errors.PwdEmpty);
24237 this.errorMsg = this.errors.PwdEmpty;
24245 if (!value.match(/[\x21-\x7e]+/)) {
24246 this.markInvalid(this.errors.PwdBadChar);
24247 this.errorMsg = this.errors.PwdBadChar;
24250 if (value.length < 6) {
24251 this.markInvalid(this.errors.PwdShort);
24252 this.errorMsg = this.errors.PwdShort;
24255 if (value.length > 16) {
24256 this.markInvalid(this.errors.PwdLong);
24257 this.errorMsg = this.errors.PwdLong;
24261 if (this.ClientSideStrongPassword(value)) {
24263 } else if (this.ClientSideMediumPassword(value)) {
24265 } else if (this.ClientSideWeakPassword(value)) {
24272 if (strength < 2) {
24273 //this.markInvalid(this.errors.TooWeak);
24274 this.errorMsg = this.errors.TooWeak;
24279 console.log('strength2: ' + strength);
24281 //var pm = this.trigger.child('div/div/div').dom;
24283 var pm = this.trigger.child('div/div');
24284 pm.removeClass(this.meterClass);
24285 pm.addClass(this.meterClass[strength]);
24287 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24289 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24291 this.errorMsg = '';
24295 CharacterSetChecks: function (type)
24298 this.fResult = false;
24301 isctype: function (character, type)
24304 case this.kCapitalLetter:
24305 if (character >= 'A' && character <= 'Z') {
24310 case this.kSmallLetter:
24311 if (character >= 'a' && character <= 'z') {
24317 if (character >= '0' && character <= '9') {
24322 case this.kPunctuation:
24323 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24334 IsLongEnough: function (pwd, size)
24336 return !(pwd == null || isNaN(size) || pwd.length < size);
24339 SpansEnoughCharacterSets: function (word, nb)
24341 if (!this.IsLongEnough(word, nb))
24346 var characterSetChecks = new Array(
24347 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24348 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24351 for (var index = 0; index < word.length; ++index) {
24352 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24353 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24354 characterSetChecks[nCharSet].fResult = true;
24361 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24362 if (characterSetChecks[nCharSet].fResult) {
24367 if (nCharSets < nb) {
24373 ClientSideStrongPassword: function (pwd)
24375 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24378 ClientSideMediumPassword: function (pwd)
24380 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24383 ClientSideWeakPassword: function (pwd)
24385 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24388 })//<script type="text/javascript">
24391 * Based Ext JS Library 1.1.1
24392 * Copyright(c) 2006-2007, Ext JS, LLC.
24398 * @class Roo.HtmlEditorCore
24399 * @extends Roo.Component
24400 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24402 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24405 Roo.HtmlEditorCore = function(config){
24408 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24413 * @event initialize
24414 * Fires when the editor is fully initialized (including the iframe)
24415 * @param {Roo.HtmlEditorCore} this
24420 * Fires when the editor is first receives the focus. Any insertion must wait
24421 * until after this event.
24422 * @param {Roo.HtmlEditorCore} this
24426 * @event beforesync
24427 * Fires before the textarea is updated with content from the editor iframe. Return false
24428 * to cancel the sync.
24429 * @param {Roo.HtmlEditorCore} this
24430 * @param {String} html
24434 * @event beforepush
24435 * Fires before the iframe editor is updated with content from the textarea. Return false
24436 * to cancel the push.
24437 * @param {Roo.HtmlEditorCore} this
24438 * @param {String} html
24443 * Fires when the textarea is updated with content from the editor iframe.
24444 * @param {Roo.HtmlEditorCore} this
24445 * @param {String} html
24450 * Fires when the iframe editor is updated with content from the textarea.
24451 * @param {Roo.HtmlEditorCore} this
24452 * @param {String} html
24457 * @event editorevent
24458 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24459 * @param {Roo.HtmlEditorCore} this
24465 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24467 // defaults : white / black...
24468 this.applyBlacklists();
24475 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24479 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24485 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24490 * @cfg {Number} height (in pixels)
24494 * @cfg {Number} width (in pixels)
24499 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24502 stylesheets: false,
24507 // private properties
24508 validationEvent : false,
24510 initialized : false,
24512 sourceEditMode : false,
24513 onFocus : Roo.emptyFn,
24515 hideMode:'offsets',
24519 // blacklist + whitelisted elements..
24526 * Protected method that will not generally be called directly. It
24527 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24528 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24530 getDocMarkup : function(){
24534 // inherit styels from page...??
24535 if (this.stylesheets === false) {
24537 Roo.get(document.head).select('style').each(function(node) {
24538 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24541 Roo.get(document.head).select('link').each(function(node) {
24542 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24545 } else if (!this.stylesheets.length) {
24547 st = '<style type="text/css">' +
24548 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24551 for (var i in this.stylesheets) {
24552 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24557 st += '<style type="text/css">' +
24558 'IMG { cursor: pointer } ' +
24561 var cls = 'roo-htmleditor-body';
24563 if(this.bodyCls.length){
24564 cls += ' ' + this.bodyCls;
24567 return '<html><head>' + st +
24568 //<style type="text/css">' +
24569 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24571 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24575 onRender : function(ct, position)
24578 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24579 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24582 this.el.dom.style.border = '0 none';
24583 this.el.dom.setAttribute('tabIndex', -1);
24584 this.el.addClass('x-hidden hide');
24588 if(Roo.isIE){ // fix IE 1px bogus margin
24589 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24593 this.frameId = Roo.id();
24597 var iframe = this.owner.wrap.createChild({
24599 cls: 'form-control', // bootstrap..
24601 name: this.frameId,
24602 frameBorder : 'no',
24603 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24608 this.iframe = iframe.dom;
24610 this.assignDocWin();
24612 this.doc.designMode = 'on';
24615 this.doc.write(this.getDocMarkup());
24619 var task = { // must defer to wait for browser to be ready
24621 //console.log("run task?" + this.doc.readyState);
24622 this.assignDocWin();
24623 if(this.doc.body || this.doc.readyState == 'complete'){
24625 this.doc.designMode="on";
24629 Roo.TaskMgr.stop(task);
24630 this.initEditor.defer(10, this);
24637 Roo.TaskMgr.start(task);
24642 onResize : function(w, h)
24644 Roo.log('resize: ' +w + ',' + h );
24645 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24649 if(typeof w == 'number'){
24651 this.iframe.style.width = w + 'px';
24653 if(typeof h == 'number'){
24655 this.iframe.style.height = h + 'px';
24657 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24664 * Toggles the editor between standard and source edit mode.
24665 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24667 toggleSourceEdit : function(sourceEditMode){
24669 this.sourceEditMode = sourceEditMode === true;
24671 if(this.sourceEditMode){
24673 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24676 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24677 //this.iframe.className = '';
24680 //this.setSize(this.owner.wrap.getSize());
24681 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24688 * Protected method that will not generally be called directly. If you need/want
24689 * custom HTML cleanup, this is the method you should override.
24690 * @param {String} html The HTML to be cleaned
24691 * return {String} The cleaned HTML
24693 cleanHtml : function(html){
24694 html = String(html);
24695 if(html.length > 5){
24696 if(Roo.isSafari){ // strip safari nonsense
24697 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24700 if(html == ' '){
24707 * HTML Editor -> Textarea
24708 * Protected method that will not generally be called directly. Syncs the contents
24709 * of the editor iframe with the textarea.
24711 syncValue : function(){
24712 if(this.initialized){
24713 var bd = (this.doc.body || this.doc.documentElement);
24714 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24715 var html = bd.innerHTML;
24717 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24718 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24720 html = '<div style="'+m[0]+'">' + html + '</div>';
24723 html = this.cleanHtml(html);
24724 // fix up the special chars.. normaly like back quotes in word...
24725 // however we do not want to do this with chinese..
24726 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24728 var cc = match.charCodeAt();
24730 // Get the character value, handling surrogate pairs
24731 if (match.length == 2) {
24732 // It's a surrogate pair, calculate the Unicode code point
24733 var high = match.charCodeAt(0) - 0xD800;
24734 var low = match.charCodeAt(1) - 0xDC00;
24735 cc = (high * 0x400) + low + 0x10000;
24737 (cc >= 0x4E00 && cc < 0xA000 ) ||
24738 (cc >= 0x3400 && cc < 0x4E00 ) ||
24739 (cc >= 0xf900 && cc < 0xfb00 )
24744 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24745 return "&#" + cc + ";";
24752 if(this.owner.fireEvent('beforesync', this, html) !== false){
24753 this.el.dom.value = html;
24754 this.owner.fireEvent('sync', this, html);
24760 * Protected method that will not generally be called directly. Pushes the value of the textarea
24761 * into the iframe editor.
24763 pushValue : function(){
24764 if(this.initialized){
24765 var v = this.el.dom.value.trim();
24767 // if(v.length < 1){
24771 if(this.owner.fireEvent('beforepush', this, v) !== false){
24772 var d = (this.doc.body || this.doc.documentElement);
24774 this.cleanUpPaste();
24775 this.el.dom.value = d.innerHTML;
24776 this.owner.fireEvent('push', this, v);
24782 deferFocus : function(){
24783 this.focus.defer(10, this);
24787 focus : function(){
24788 if(this.win && !this.sourceEditMode){
24795 assignDocWin: function()
24797 var iframe = this.iframe;
24800 this.doc = iframe.contentWindow.document;
24801 this.win = iframe.contentWindow;
24803 // if (!Roo.get(this.frameId)) {
24806 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24807 // this.win = Roo.get(this.frameId).dom.contentWindow;
24809 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24813 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24814 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24819 initEditor : function(){
24820 //console.log("INIT EDITOR");
24821 this.assignDocWin();
24825 this.doc.designMode="on";
24827 this.doc.write(this.getDocMarkup());
24830 var dbody = (this.doc.body || this.doc.documentElement);
24831 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24832 // this copies styles from the containing element into thsi one..
24833 // not sure why we need all of this..
24834 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24836 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24837 //ss['background-attachment'] = 'fixed'; // w3c
24838 dbody.bgProperties = 'fixed'; // ie
24839 //Roo.DomHelper.applyStyles(dbody, ss);
24840 Roo.EventManager.on(this.doc, {
24841 //'mousedown': this.onEditorEvent,
24842 'mouseup': this.onEditorEvent,
24843 'dblclick': this.onEditorEvent,
24844 'click': this.onEditorEvent,
24845 'keyup': this.onEditorEvent,
24850 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24852 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24853 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24855 this.initialized = true;
24857 this.owner.fireEvent('initialize', this);
24862 onDestroy : function(){
24868 //for (var i =0; i < this.toolbars.length;i++) {
24869 // // fixme - ask toolbars for heights?
24870 // this.toolbars[i].onDestroy();
24873 //this.wrap.dom.innerHTML = '';
24874 //this.wrap.remove();
24879 onFirstFocus : function(){
24881 this.assignDocWin();
24884 this.activated = true;
24887 if(Roo.isGecko){ // prevent silly gecko errors
24889 var s = this.win.getSelection();
24890 if(!s.focusNode || s.focusNode.nodeType != 3){
24891 var r = s.getRangeAt(0);
24892 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24897 this.execCmd('useCSS', true);
24898 this.execCmd('styleWithCSS', false);
24901 this.owner.fireEvent('activate', this);
24905 adjustFont: function(btn){
24906 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24907 //if(Roo.isSafari){ // safari
24910 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24911 if(Roo.isSafari){ // safari
24912 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24913 v = (v < 10) ? 10 : v;
24914 v = (v > 48) ? 48 : v;
24915 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24920 v = Math.max(1, v+adjust);
24922 this.execCmd('FontSize', v );
24925 onEditorEvent : function(e)
24927 this.owner.fireEvent('editorevent', this, e);
24928 // this.updateToolbar();
24929 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24932 insertTag : function(tg)
24934 // could be a bit smarter... -> wrap the current selected tRoo..
24935 if (tg.toLowerCase() == 'span' ||
24936 tg.toLowerCase() == 'code' ||
24937 tg.toLowerCase() == 'sup' ||
24938 tg.toLowerCase() == 'sub'
24941 range = this.createRange(this.getSelection());
24942 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24943 wrappingNode.appendChild(range.extractContents());
24944 range.insertNode(wrappingNode);
24951 this.execCmd("formatblock", tg);
24955 insertText : function(txt)
24959 var range = this.createRange();
24960 range.deleteContents();
24961 //alert(Sender.getAttribute('label'));
24963 range.insertNode(this.doc.createTextNode(txt));
24969 * Executes a Midas editor command on the editor document and performs necessary focus and
24970 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24971 * @param {String} cmd The Midas command
24972 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24974 relayCmd : function(cmd, value){
24976 this.execCmd(cmd, value);
24977 this.owner.fireEvent('editorevent', this);
24978 //this.updateToolbar();
24979 this.owner.deferFocus();
24983 * Executes a Midas editor command directly on the editor document.
24984 * For visual commands, you should use {@link #relayCmd} instead.
24985 * <b>This should only be called after the editor is initialized.</b>
24986 * @param {String} cmd The Midas command
24987 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24989 execCmd : function(cmd, value){
24990 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24997 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24999 * @param {String} text | dom node..
25001 insertAtCursor : function(text)
25004 if(!this.activated){
25010 var r = this.doc.selection.createRange();
25021 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25025 // from jquery ui (MIT licenced)
25027 var win = this.win;
25029 if (win.getSelection && win.getSelection().getRangeAt) {
25030 range = win.getSelection().getRangeAt(0);
25031 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25032 range.insertNode(node);
25033 } else if (win.document.selection && win.document.selection.createRange) {
25034 // no firefox support
25035 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25036 win.document.selection.createRange().pasteHTML(txt);
25038 // no firefox support
25039 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25040 this.execCmd('InsertHTML', txt);
25049 mozKeyPress : function(e){
25051 var c = e.getCharCode(), cmd;
25054 c = String.fromCharCode(c).toLowerCase();
25068 this.cleanUpPaste.defer(100, this);
25076 e.preventDefault();
25084 fixKeys : function(){ // load time branching for fastest keydown performance
25086 return function(e){
25087 var k = e.getKey(), r;
25090 r = this.doc.selection.createRange();
25093 r.pasteHTML('    ');
25100 r = this.doc.selection.createRange();
25102 var target = r.parentElement();
25103 if(!target || target.tagName.toLowerCase() != 'li'){
25105 r.pasteHTML('<br />');
25111 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25112 this.cleanUpPaste.defer(100, this);
25118 }else if(Roo.isOpera){
25119 return function(e){
25120 var k = e.getKey();
25124 this.execCmd('InsertHTML','    ');
25127 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25128 this.cleanUpPaste.defer(100, this);
25133 }else if(Roo.isSafari){
25134 return function(e){
25135 var k = e.getKey();
25139 this.execCmd('InsertText','\t');
25143 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25144 this.cleanUpPaste.defer(100, this);
25152 getAllAncestors: function()
25154 var p = this.getSelectedNode();
25157 a.push(p); // push blank onto stack..
25158 p = this.getParentElement();
25162 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25166 a.push(this.doc.body);
25170 lastSelNode : false,
25173 getSelection : function()
25175 this.assignDocWin();
25176 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25179 getSelectedNode: function()
25181 // this may only work on Gecko!!!
25183 // should we cache this!!!!
25188 var range = this.createRange(this.getSelection()).cloneRange();
25191 var parent = range.parentElement();
25193 var testRange = range.duplicate();
25194 testRange.moveToElementText(parent);
25195 if (testRange.inRange(range)) {
25198 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25201 parent = parent.parentElement;
25206 // is ancestor a text element.
25207 var ac = range.commonAncestorContainer;
25208 if (ac.nodeType == 3) {
25209 ac = ac.parentNode;
25212 var ar = ac.childNodes;
25215 var other_nodes = [];
25216 var has_other_nodes = false;
25217 for (var i=0;i<ar.length;i++) {
25218 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25221 // fullly contained node.
25223 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25228 // probably selected..
25229 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25230 other_nodes.push(ar[i]);
25234 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25239 has_other_nodes = true;
25241 if (!nodes.length && other_nodes.length) {
25242 nodes= other_nodes;
25244 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25250 createRange: function(sel)
25252 // this has strange effects when using with
25253 // top toolbar - not sure if it's a great idea.
25254 //this.editor.contentWindow.focus();
25255 if (typeof sel != "undefined") {
25257 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25259 return this.doc.createRange();
25262 return this.doc.createRange();
25265 getParentElement: function()
25268 this.assignDocWin();
25269 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25271 var range = this.createRange(sel);
25274 var p = range.commonAncestorContainer;
25275 while (p.nodeType == 3) { // text node
25286 * Range intersection.. the hard stuff...
25290 * [ -- selected range --- ]
25294 * if end is before start or hits it. fail.
25295 * if start is after end or hits it fail.
25297 * if either hits (but other is outside. - then it's not
25303 // @see http://www.thismuchiknow.co.uk/?p=64.
25304 rangeIntersectsNode : function(range, node)
25306 var nodeRange = node.ownerDocument.createRange();
25308 nodeRange.selectNode(node);
25310 nodeRange.selectNodeContents(node);
25313 var rangeStartRange = range.cloneRange();
25314 rangeStartRange.collapse(true);
25316 var rangeEndRange = range.cloneRange();
25317 rangeEndRange.collapse(false);
25319 var nodeStartRange = nodeRange.cloneRange();
25320 nodeStartRange.collapse(true);
25322 var nodeEndRange = nodeRange.cloneRange();
25323 nodeEndRange.collapse(false);
25325 return rangeStartRange.compareBoundaryPoints(
25326 Range.START_TO_START, nodeEndRange) == -1 &&
25327 rangeEndRange.compareBoundaryPoints(
25328 Range.START_TO_START, nodeStartRange) == 1;
25332 rangeCompareNode : function(range, node)
25334 var nodeRange = node.ownerDocument.createRange();
25336 nodeRange.selectNode(node);
25338 nodeRange.selectNodeContents(node);
25342 range.collapse(true);
25344 nodeRange.collapse(true);
25346 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25347 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25349 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25351 var nodeIsBefore = ss == 1;
25352 var nodeIsAfter = ee == -1;
25354 if (nodeIsBefore && nodeIsAfter) {
25357 if (!nodeIsBefore && nodeIsAfter) {
25358 return 1; //right trailed.
25361 if (nodeIsBefore && !nodeIsAfter) {
25362 return 2; // left trailed.
25368 // private? - in a new class?
25369 cleanUpPaste : function()
25371 // cleans up the whole document..
25372 Roo.log('cleanuppaste');
25374 this.cleanUpChildren(this.doc.body);
25375 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25376 if (clean != this.doc.body.innerHTML) {
25377 this.doc.body.innerHTML = clean;
25382 cleanWordChars : function(input) {// change the chars to hex code
25383 var he = Roo.HtmlEditorCore;
25385 var output = input;
25386 Roo.each(he.swapCodes, function(sw) {
25387 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25389 output = output.replace(swapper, sw[1]);
25396 cleanUpChildren : function (n)
25398 if (!n.childNodes.length) {
25401 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25402 this.cleanUpChild(n.childNodes[i]);
25409 cleanUpChild : function (node)
25412 //console.log(node);
25413 if (node.nodeName == "#text") {
25414 // clean up silly Windows -- stuff?
25417 if (node.nodeName == "#comment") {
25418 node.parentNode.removeChild(node);
25419 // clean up silly Windows -- stuff?
25422 var lcname = node.tagName.toLowerCase();
25423 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25424 // whitelist of tags..
25426 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25428 node.parentNode.removeChild(node);
25433 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25435 // spans with no attributes - just remove them..
25436 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25437 remove_keep_children = true;
25440 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25441 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25443 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25444 // remove_keep_children = true;
25447 if (remove_keep_children) {
25448 this.cleanUpChildren(node);
25449 // inserts everything just before this node...
25450 while (node.childNodes.length) {
25451 var cn = node.childNodes[0];
25452 node.removeChild(cn);
25453 node.parentNode.insertBefore(cn, node);
25455 node.parentNode.removeChild(node);
25459 if (!node.attributes || !node.attributes.length) {
25464 this.cleanUpChildren(node);
25468 function cleanAttr(n,v)
25471 if (v.match(/^\./) || v.match(/^\//)) {
25474 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25477 if (v.match(/^#/)) {
25480 if (v.match(/^\{/)) { // allow template editing.
25483 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25484 node.removeAttribute(n);
25488 var cwhite = this.cwhite;
25489 var cblack = this.cblack;
25491 function cleanStyle(n,v)
25493 if (v.match(/expression/)) { //XSS?? should we even bother..
25494 node.removeAttribute(n);
25498 var parts = v.split(/;/);
25501 Roo.each(parts, function(p) {
25502 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25506 var l = p.split(':').shift().replace(/\s+/g,'');
25507 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25509 if ( cwhite.length && cblack.indexOf(l) > -1) {
25510 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25511 //node.removeAttribute(n);
25515 // only allow 'c whitelisted system attributes'
25516 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25517 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25518 //node.removeAttribute(n);
25528 if (clean.length) {
25529 node.setAttribute(n, clean.join(';'));
25531 node.removeAttribute(n);
25537 for (var i = node.attributes.length-1; i > -1 ; i--) {
25538 var a = node.attributes[i];
25541 if (a.name.toLowerCase().substr(0,2)=='on') {
25542 node.removeAttribute(a.name);
25545 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25546 node.removeAttribute(a.name);
25549 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25550 cleanAttr(a.name,a.value); // fixme..
25553 if (a.name == 'style') {
25554 cleanStyle(a.name,a.value);
25557 /// clean up MS crap..
25558 // tecnically this should be a list of valid class'es..
25561 if (a.name == 'class') {
25562 if (a.value.match(/^Mso/)) {
25563 node.removeAttribute('class');
25566 if (a.value.match(/^body$/)) {
25567 node.removeAttribute('class');
25578 this.cleanUpChildren(node);
25584 * Clean up MS wordisms...
25586 cleanWord : function(node)
25589 this.cleanWord(this.doc.body);
25594 node.nodeName == 'SPAN' &&
25595 !node.hasAttributes() &&
25596 node.childNodes.length == 1 &&
25597 node.firstChild.nodeName == "#text"
25599 var textNode = node.firstChild;
25600 node.removeChild(textNode);
25601 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25602 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25604 node.parentNode.insertBefore(textNode, node);
25605 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25606 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25608 node.parentNode.removeChild(node);
25611 if (node.nodeName == "#text") {
25612 // clean up silly Windows -- stuff?
25615 if (node.nodeName == "#comment") {
25616 node.parentNode.removeChild(node);
25617 // clean up silly Windows -- stuff?
25621 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25622 node.parentNode.removeChild(node);
25625 //Roo.log(node.tagName);
25626 // remove - but keep children..
25627 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25628 //Roo.log('-- removed');
25629 while (node.childNodes.length) {
25630 var cn = node.childNodes[0];
25631 node.removeChild(cn);
25632 node.parentNode.insertBefore(cn, node);
25633 // move node to parent - and clean it..
25634 this.cleanWord(cn);
25636 node.parentNode.removeChild(node);
25637 /// no need to iterate chidlren = it's got none..
25638 //this.iterateChildren(node, this.cleanWord);
25642 if (node.className.length) {
25644 var cn = node.className.split(/\W+/);
25646 Roo.each(cn, function(cls) {
25647 if (cls.match(/Mso[a-zA-Z]+/)) {
25652 node.className = cna.length ? cna.join(' ') : '';
25654 node.removeAttribute("class");
25658 if (node.hasAttribute("lang")) {
25659 node.removeAttribute("lang");
25662 if (node.hasAttribute("style")) {
25664 var styles = node.getAttribute("style").split(";");
25666 Roo.each(styles, function(s) {
25667 if (!s.match(/:/)) {
25670 var kv = s.split(":");
25671 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25674 // what ever is left... we allow.
25677 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25678 if (!nstyle.length) {
25679 node.removeAttribute('style');
25682 this.iterateChildren(node, this.cleanWord);
25688 * iterateChildren of a Node, calling fn each time, using this as the scole..
25689 * @param {DomNode} node node to iterate children of.
25690 * @param {Function} fn method of this class to call on each item.
25692 iterateChildren : function(node, fn)
25694 if (!node.childNodes.length) {
25697 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25698 fn.call(this, node.childNodes[i])
25704 * cleanTableWidths.
25706 * Quite often pasting from word etc.. results in tables with column and widths.
25707 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25710 cleanTableWidths : function(node)
25715 this.cleanTableWidths(this.doc.body);
25720 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25723 Roo.log(node.tagName);
25724 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25725 this.iterateChildren(node, this.cleanTableWidths);
25728 if (node.hasAttribute('width')) {
25729 node.removeAttribute('width');
25733 if (node.hasAttribute("style")) {
25736 var styles = node.getAttribute("style").split(";");
25738 Roo.each(styles, function(s) {
25739 if (!s.match(/:/)) {
25742 var kv = s.split(":");
25743 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25746 // what ever is left... we allow.
25749 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25750 if (!nstyle.length) {
25751 node.removeAttribute('style');
25755 this.iterateChildren(node, this.cleanTableWidths);
25763 domToHTML : function(currentElement, depth, nopadtext) {
25765 depth = depth || 0;
25766 nopadtext = nopadtext || false;
25768 if (!currentElement) {
25769 return this.domToHTML(this.doc.body);
25772 //Roo.log(currentElement);
25774 var allText = false;
25775 var nodeName = currentElement.nodeName;
25776 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25778 if (nodeName == '#text') {
25780 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25785 if (nodeName != 'BODY') {
25788 // Prints the node tagName, such as <A>, <IMG>, etc
25791 for(i = 0; i < currentElement.attributes.length;i++) {
25793 var aname = currentElement.attributes.item(i).name;
25794 if (!currentElement.attributes.item(i).value.length) {
25797 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25800 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25809 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25812 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25817 // Traverse the tree
25819 var currentElementChild = currentElement.childNodes.item(i);
25820 var allText = true;
25821 var innerHTML = '';
25823 while (currentElementChild) {
25824 // Formatting code (indent the tree so it looks nice on the screen)
25825 var nopad = nopadtext;
25826 if (lastnode == 'SPAN') {
25830 if (currentElementChild.nodeName == '#text') {
25831 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25832 toadd = nopadtext ? toadd : toadd.trim();
25833 if (!nopad && toadd.length > 80) {
25834 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25836 innerHTML += toadd;
25839 currentElementChild = currentElement.childNodes.item(i);
25845 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25847 // Recursively traverse the tree structure of the child node
25848 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25849 lastnode = currentElementChild.nodeName;
25851 currentElementChild=currentElement.childNodes.item(i);
25857 // The remaining code is mostly for formatting the tree
25858 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25863 ret+= "</"+tagName+">";
25869 applyBlacklists : function()
25871 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25872 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25876 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25877 if (b.indexOf(tag) > -1) {
25880 this.white.push(tag);
25884 Roo.each(w, function(tag) {
25885 if (b.indexOf(tag) > -1) {
25888 if (this.white.indexOf(tag) > -1) {
25891 this.white.push(tag);
25896 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25897 if (w.indexOf(tag) > -1) {
25900 this.black.push(tag);
25904 Roo.each(b, function(tag) {
25905 if (w.indexOf(tag) > -1) {
25908 if (this.black.indexOf(tag) > -1) {
25911 this.black.push(tag);
25916 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25917 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25921 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25922 if (b.indexOf(tag) > -1) {
25925 this.cwhite.push(tag);
25929 Roo.each(w, function(tag) {
25930 if (b.indexOf(tag) > -1) {
25933 if (this.cwhite.indexOf(tag) > -1) {
25936 this.cwhite.push(tag);
25941 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25942 if (w.indexOf(tag) > -1) {
25945 this.cblack.push(tag);
25949 Roo.each(b, function(tag) {
25950 if (w.indexOf(tag) > -1) {
25953 if (this.cblack.indexOf(tag) > -1) {
25956 this.cblack.push(tag);
25961 setStylesheets : function(stylesheets)
25963 if(typeof(stylesheets) == 'string'){
25964 Roo.get(this.iframe.contentDocument.head).createChild({
25966 rel : 'stylesheet',
25975 Roo.each(stylesheets, function(s) {
25980 Roo.get(_this.iframe.contentDocument.head).createChild({
25982 rel : 'stylesheet',
25991 removeStylesheets : function()
25995 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26000 setStyle : function(style)
26002 Roo.get(this.iframe.contentDocument.head).createChild({
26011 // hide stuff that is not compatible
26025 * @event specialkey
26029 * @cfg {String} fieldClass @hide
26032 * @cfg {String} focusClass @hide
26035 * @cfg {String} autoCreate @hide
26038 * @cfg {String} inputType @hide
26041 * @cfg {String} invalidClass @hide
26044 * @cfg {String} invalidText @hide
26047 * @cfg {String} msgFx @hide
26050 * @cfg {String} validateOnBlur @hide
26054 Roo.HtmlEditorCore.white = [
26055 'area', 'br', 'img', 'input', 'hr', 'wbr',
26057 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26058 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26059 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26060 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26061 'table', 'ul', 'xmp',
26063 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26066 'dir', 'menu', 'ol', 'ul', 'dl',
26072 Roo.HtmlEditorCore.black = [
26073 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26075 'base', 'basefont', 'bgsound', 'blink', 'body',
26076 'frame', 'frameset', 'head', 'html', 'ilayer',
26077 'iframe', 'layer', 'link', 'meta', 'object',
26078 'script', 'style' ,'title', 'xml' // clean later..
26080 Roo.HtmlEditorCore.clean = [
26081 'script', 'style', 'title', 'xml'
26083 Roo.HtmlEditorCore.remove = [
26088 Roo.HtmlEditorCore.ablack = [
26092 Roo.HtmlEditorCore.aclean = [
26093 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26097 Roo.HtmlEditorCore.pwhite= [
26098 'http', 'https', 'mailto'
26101 // white listed style attributes.
26102 Roo.HtmlEditorCore.cwhite= [
26103 // 'text-align', /// default is to allow most things..
26109 // black listed style attributes.
26110 Roo.HtmlEditorCore.cblack= [
26111 // 'font-size' -- this can be set by the project
26115 Roo.HtmlEditorCore.swapCodes =[
26116 [ 8211, "–" ],
26117 [ 8212, "—" ],
26134 * @class Roo.bootstrap.HtmlEditor
26135 * @extends Roo.bootstrap.TextArea
26136 * Bootstrap HtmlEditor class
26139 * Create a new HtmlEditor
26140 * @param {Object} config The config object
26143 Roo.bootstrap.HtmlEditor = function(config){
26144 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26145 if (!this.toolbars) {
26146 this.toolbars = [];
26149 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26152 * @event initialize
26153 * Fires when the editor is fully initialized (including the iframe)
26154 * @param {HtmlEditor} this
26159 * Fires when the editor is first receives the focus. Any insertion must wait
26160 * until after this event.
26161 * @param {HtmlEditor} this
26165 * @event beforesync
26166 * Fires before the textarea is updated with content from the editor iframe. Return false
26167 * to cancel the sync.
26168 * @param {HtmlEditor} this
26169 * @param {String} html
26173 * @event beforepush
26174 * Fires before the iframe editor is updated with content from the textarea. Return false
26175 * to cancel the push.
26176 * @param {HtmlEditor} this
26177 * @param {String} html
26182 * Fires when the textarea is updated with content from the editor iframe.
26183 * @param {HtmlEditor} this
26184 * @param {String} html
26189 * Fires when the iframe editor is updated with content from the textarea.
26190 * @param {HtmlEditor} this
26191 * @param {String} html
26195 * @event editmodechange
26196 * Fires when the editor switches edit modes
26197 * @param {HtmlEditor} this
26198 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26200 editmodechange: true,
26202 * @event editorevent
26203 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26204 * @param {HtmlEditor} this
26208 * @event firstfocus
26209 * Fires when on first focus - needed by toolbars..
26210 * @param {HtmlEditor} this
26215 * Auto save the htmlEditor value as a file into Events
26216 * @param {HtmlEditor} this
26220 * @event savedpreview
26221 * preview the saved version of htmlEditor
26222 * @param {HtmlEditor} this
26229 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26233 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26238 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26243 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26248 * @cfg {Number} height (in pixels)
26252 * @cfg {Number} width (in pixels)
26257 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26260 stylesheets: false,
26265 // private properties
26266 validationEvent : false,
26268 initialized : false,
26271 onFocus : Roo.emptyFn,
26273 hideMode:'offsets',
26275 tbContainer : false,
26279 toolbarContainer :function() {
26280 return this.wrap.select('.x-html-editor-tb',true).first();
26284 * Protected method that will not generally be called directly. It
26285 * is called when the editor creates its toolbar. Override this method if you need to
26286 * add custom toolbar buttons.
26287 * @param {HtmlEditor} editor
26289 createToolbar : function(){
26290 Roo.log('renewing');
26291 Roo.log("create toolbars");
26293 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26294 this.toolbars[0].render(this.toolbarContainer());
26298 // if (!editor.toolbars || !editor.toolbars.length) {
26299 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26302 // for (var i =0 ; i < editor.toolbars.length;i++) {
26303 // editor.toolbars[i] = Roo.factory(
26304 // typeof(editor.toolbars[i]) == 'string' ?
26305 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26306 // Roo.bootstrap.HtmlEditor);
26307 // editor.toolbars[i].init(editor);
26313 onRender : function(ct, position)
26315 // Roo.log("Call onRender: " + this.xtype);
26317 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26319 this.wrap = this.inputEl().wrap({
26320 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26323 this.editorcore.onRender(ct, position);
26325 if (this.resizable) {
26326 this.resizeEl = new Roo.Resizable(this.wrap, {
26330 minHeight : this.height,
26331 height: this.height,
26332 handles : this.resizable,
26335 resize : function(r, w, h) {
26336 _t.onResize(w,h); // -something
26342 this.createToolbar(this);
26345 if(!this.width && this.resizable){
26346 this.setSize(this.wrap.getSize());
26348 if (this.resizeEl) {
26349 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26350 // should trigger onReize..
26356 onResize : function(w, h)
26358 Roo.log('resize: ' +w + ',' + h );
26359 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26363 if(this.inputEl() ){
26364 if(typeof w == 'number'){
26365 var aw = w - this.wrap.getFrameWidth('lr');
26366 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26369 if(typeof h == 'number'){
26370 var tbh = -11; // fixme it needs to tool bar size!
26371 for (var i =0; i < this.toolbars.length;i++) {
26372 // fixme - ask toolbars for heights?
26373 tbh += this.toolbars[i].el.getHeight();
26374 //if (this.toolbars[i].footer) {
26375 // tbh += this.toolbars[i].footer.el.getHeight();
26383 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26384 ah -= 5; // knock a few pixes off for look..
26385 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26389 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26390 this.editorcore.onResize(ew,eh);
26395 * Toggles the editor between standard and source edit mode.
26396 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26398 toggleSourceEdit : function(sourceEditMode)
26400 this.editorcore.toggleSourceEdit(sourceEditMode);
26402 if(this.editorcore.sourceEditMode){
26403 Roo.log('editor - showing textarea');
26406 // Roo.log(this.syncValue());
26408 this.inputEl().removeClass(['hide', 'x-hidden']);
26409 this.inputEl().dom.removeAttribute('tabIndex');
26410 this.inputEl().focus();
26412 Roo.log('editor - hiding textarea');
26414 // Roo.log(this.pushValue());
26417 this.inputEl().addClass(['hide', 'x-hidden']);
26418 this.inputEl().dom.setAttribute('tabIndex', -1);
26419 //this.deferFocus();
26422 if(this.resizable){
26423 this.setSize(this.wrap.getSize());
26426 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26429 // private (for BoxComponent)
26430 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26432 // private (for BoxComponent)
26433 getResizeEl : function(){
26437 // private (for BoxComponent)
26438 getPositionEl : function(){
26443 initEvents : function(){
26444 this.originalValue = this.getValue();
26448 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26451 // markInvalid : Roo.emptyFn,
26453 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26456 // clearInvalid : Roo.emptyFn,
26458 setValue : function(v){
26459 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26460 this.editorcore.pushValue();
26465 deferFocus : function(){
26466 this.focus.defer(10, this);
26470 focus : function(){
26471 this.editorcore.focus();
26477 onDestroy : function(){
26483 for (var i =0; i < this.toolbars.length;i++) {
26484 // fixme - ask toolbars for heights?
26485 this.toolbars[i].onDestroy();
26488 this.wrap.dom.innerHTML = '';
26489 this.wrap.remove();
26494 onFirstFocus : function(){
26495 //Roo.log("onFirstFocus");
26496 this.editorcore.onFirstFocus();
26497 for (var i =0; i < this.toolbars.length;i++) {
26498 this.toolbars[i].onFirstFocus();
26504 syncValue : function()
26506 this.editorcore.syncValue();
26509 pushValue : function()
26511 this.editorcore.pushValue();
26515 // hide stuff that is not compatible
26529 * @event specialkey
26533 * @cfg {String} fieldClass @hide
26536 * @cfg {String} focusClass @hide
26539 * @cfg {String} autoCreate @hide
26542 * @cfg {String} inputType @hide
26546 * @cfg {String} invalidText @hide
26549 * @cfg {String} msgFx @hide
26552 * @cfg {String} validateOnBlur @hide
26561 Roo.namespace('Roo.bootstrap.htmleditor');
26563 * @class Roo.bootstrap.HtmlEditorToolbar1
26569 new Roo.bootstrap.HtmlEditor({
26572 new Roo.bootstrap.HtmlEditorToolbar1({
26573 disable : { fonts: 1 , format: 1, ..., ... , ...],
26579 * @cfg {Object} disable List of elements to disable..
26580 * @cfg {Array} btns List of additional buttons.
26584 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26587 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26590 Roo.apply(this, config);
26592 // default disabled, based on 'good practice'..
26593 this.disable = this.disable || {};
26594 Roo.applyIf(this.disable, {
26597 specialElements : true
26599 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26601 this.editor = config.editor;
26602 this.editorcore = config.editor.editorcore;
26604 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26606 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26607 // dont call parent... till later.
26609 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26614 editorcore : false,
26619 "h1","h2","h3","h4","h5","h6",
26621 "abbr", "acronym", "address", "cite", "samp", "var",
26625 onRender : function(ct, position)
26627 // Roo.log("Call onRender: " + this.xtype);
26629 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26631 this.el.dom.style.marginBottom = '0';
26633 var editorcore = this.editorcore;
26634 var editor= this.editor;
26637 var btn = function(id,cmd , toggle, handler, html){
26639 var event = toggle ? 'toggle' : 'click';
26644 xns: Roo.bootstrap,
26648 enableToggle:toggle !== false,
26650 pressed : toggle ? false : null,
26653 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26654 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26660 // var cb_box = function...
26665 xns: Roo.bootstrap,
26670 xns: Roo.bootstrap,
26674 Roo.each(this.formats, function(f) {
26675 style.menu.items.push({
26677 xns: Roo.bootstrap,
26678 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26683 editorcore.insertTag(this.tagname);
26690 children.push(style);
26692 btn('bold',false,true);
26693 btn('italic',false,true);
26694 btn('align-left', 'justifyleft',true);
26695 btn('align-center', 'justifycenter',true);
26696 btn('align-right' , 'justifyright',true);
26697 btn('link', false, false, function(btn) {
26698 //Roo.log("create link?");
26699 var url = prompt(this.createLinkText, this.defaultLinkValue);
26700 if(url && url != 'http:/'+'/'){
26701 this.editorcore.relayCmd('createlink', url);
26704 btn('list','insertunorderedlist',true);
26705 btn('pencil', false,true, function(btn){
26707 this.toggleSourceEdit(btn.pressed);
26710 if (this.editor.btns.length > 0) {
26711 for (var i = 0; i<this.editor.btns.length; i++) {
26712 children.push(this.editor.btns[i]);
26720 xns: Roo.bootstrap,
26725 xns: Roo.bootstrap,
26730 cog.menu.items.push({
26732 xns: Roo.bootstrap,
26733 html : Clean styles,
26738 editorcore.insertTag(this.tagname);
26747 this.xtype = 'NavSimplebar';
26749 for(var i=0;i< children.length;i++) {
26751 this.buttons.add(this.addxtypeChild(children[i]));
26755 editor.on('editorevent', this.updateToolbar, this);
26757 onBtnClick : function(id)
26759 this.editorcore.relayCmd(id);
26760 this.editorcore.focus();
26764 * Protected method that will not generally be called directly. It triggers
26765 * a toolbar update by reading the markup state of the current selection in the editor.
26767 updateToolbar: function(){
26769 if(!this.editorcore.activated){
26770 this.editor.onFirstFocus(); // is this neeed?
26774 var btns = this.buttons;
26775 var doc = this.editorcore.doc;
26776 btns.get('bold').setActive(doc.queryCommandState('bold'));
26777 btns.get('italic').setActive(doc.queryCommandState('italic'));
26778 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26780 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26781 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26782 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26784 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26785 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26788 var ans = this.editorcore.getAllAncestors();
26789 if (this.formatCombo) {
26792 var store = this.formatCombo.store;
26793 this.formatCombo.setValue("");
26794 for (var i =0; i < ans.length;i++) {
26795 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26797 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26805 // hides menus... - so this cant be on a menu...
26806 Roo.bootstrap.MenuMgr.hideAll();
26808 Roo.bootstrap.MenuMgr.hideAll();
26809 //this.editorsyncValue();
26811 onFirstFocus: function() {
26812 this.buttons.each(function(item){
26816 toggleSourceEdit : function(sourceEditMode){
26819 if(sourceEditMode){
26820 Roo.log("disabling buttons");
26821 this.buttons.each( function(item){
26822 if(item.cmd != 'pencil'){
26828 Roo.log("enabling buttons");
26829 if(this.editorcore.initialized){
26830 this.buttons.each( function(item){
26836 Roo.log("calling toggole on editor");
26837 // tell the editor that it's been pressed..
26838 this.editor.toggleSourceEdit(sourceEditMode);
26852 * @class Roo.bootstrap.Markdown
26853 * @extends Roo.bootstrap.TextArea
26854 * Bootstrap Showdown editable area
26855 * @cfg {string} content
26858 * Create a new Showdown
26861 Roo.bootstrap.Markdown = function(config){
26862 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26866 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26870 initEvents : function()
26873 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26874 this.markdownEl = this.el.createChild({
26875 cls : 'roo-markdown-area'
26877 this.inputEl().addClass('d-none');
26878 if (this.getValue() == '') {
26879 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26882 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26884 this.markdownEl.on('click', this.toggleTextEdit, this);
26885 this.on('blur', this.toggleTextEdit, this);
26886 this.on('specialkey', this.resizeTextArea, this);
26889 toggleTextEdit : function()
26891 var sh = this.markdownEl.getHeight();
26892 this.inputEl().addClass('d-none');
26893 this.markdownEl.addClass('d-none');
26894 if (!this.editing) {
26896 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26897 this.inputEl().removeClass('d-none');
26898 this.inputEl().focus();
26899 this.editing = true;
26902 // show showdown...
26903 this.updateMarkdown();
26904 this.markdownEl.removeClass('d-none');
26905 this.editing = false;
26908 updateMarkdown : function()
26910 if (this.getValue() == '') {
26911 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26915 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26918 resizeTextArea: function () {
26921 Roo.log([sh, this.getValue().split("\n").length * 30]);
26922 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26924 setValue : function(val)
26926 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26927 if (!this.editing) {
26928 this.updateMarkdown();
26934 if (!this.editing) {
26935 this.toggleTextEdit();
26943 * @class Roo.bootstrap.Table.AbstractSelectionModel
26944 * @extends Roo.util.Observable
26945 * Abstract base class for grid SelectionModels. It provides the interface that should be
26946 * implemented by descendant classes. This class should not be directly instantiated.
26949 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26950 this.locked = false;
26951 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26955 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26956 /** @ignore Called by the grid automatically. Do not call directly. */
26957 init : function(grid){
26963 * Locks the selections.
26966 this.locked = true;
26970 * Unlocks the selections.
26972 unlock : function(){
26973 this.locked = false;
26977 * Returns true if the selections are locked.
26978 * @return {Boolean}
26980 isLocked : function(){
26981 return this.locked;
26985 initEvents : function ()
26991 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26992 * @class Roo.bootstrap.Table.RowSelectionModel
26993 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26994 * It supports multiple selections and keyboard selection/navigation.
26996 * @param {Object} config
26999 Roo.bootstrap.Table.RowSelectionModel = function(config){
27000 Roo.apply(this, config);
27001 this.selections = new Roo.util.MixedCollection(false, function(o){
27006 this.lastActive = false;
27010 * @event selectionchange
27011 * Fires when the selection changes
27012 * @param {SelectionModel} this
27014 "selectionchange" : true,
27016 * @event afterselectionchange
27017 * Fires after the selection changes (eg. by key press or clicking)
27018 * @param {SelectionModel} this
27020 "afterselectionchange" : true,
27022 * @event beforerowselect
27023 * Fires when a row is selected being selected, return false to cancel.
27024 * @param {SelectionModel} this
27025 * @param {Number} rowIndex The selected index
27026 * @param {Boolean} keepExisting False if other selections will be cleared
27028 "beforerowselect" : true,
27031 * Fires when a row is selected.
27032 * @param {SelectionModel} this
27033 * @param {Number} rowIndex The selected index
27034 * @param {Roo.data.Record} r The record
27036 "rowselect" : true,
27038 * @event rowdeselect
27039 * Fires when a row is deselected.
27040 * @param {SelectionModel} this
27041 * @param {Number} rowIndex The selected index
27043 "rowdeselect" : true
27045 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27046 this.locked = false;
27049 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27051 * @cfg {Boolean} singleSelect
27052 * True to allow selection of only one row at a time (defaults to false)
27054 singleSelect : false,
27057 initEvents : function()
27060 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27061 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27062 //}else{ // allow click to work like normal
27063 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27065 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27066 this.grid.on("rowclick", this.handleMouseDown, this);
27068 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27069 "up" : function(e){
27071 this.selectPrevious(e.shiftKey);
27072 }else if(this.last !== false && this.lastActive !== false){
27073 var last = this.last;
27074 this.selectRange(this.last, this.lastActive-1);
27075 this.grid.getView().focusRow(this.lastActive);
27076 if(last !== false){
27080 this.selectFirstRow();
27082 this.fireEvent("afterselectionchange", this);
27084 "down" : function(e){
27086 this.selectNext(e.shiftKey);
27087 }else if(this.last !== false && this.lastActive !== false){
27088 var last = this.last;
27089 this.selectRange(this.last, this.lastActive+1);
27090 this.grid.getView().focusRow(this.lastActive);
27091 if(last !== false){
27095 this.selectFirstRow();
27097 this.fireEvent("afterselectionchange", this);
27101 this.grid.store.on('load', function(){
27102 this.selections.clear();
27105 var view = this.grid.view;
27106 view.on("refresh", this.onRefresh, this);
27107 view.on("rowupdated", this.onRowUpdated, this);
27108 view.on("rowremoved", this.onRemove, this);
27113 onRefresh : function()
27115 var ds = this.grid.store, i, v = this.grid.view;
27116 var s = this.selections;
27117 s.each(function(r){
27118 if((i = ds.indexOfId(r.id)) != -1){
27127 onRemove : function(v, index, r){
27128 this.selections.remove(r);
27132 onRowUpdated : function(v, index, r){
27133 if(this.isSelected(r)){
27134 v.onRowSelect(index);
27140 * @param {Array} records The records to select
27141 * @param {Boolean} keepExisting (optional) True to keep existing selections
27143 selectRecords : function(records, keepExisting)
27146 this.clearSelections();
27148 var ds = this.grid.store;
27149 for(var i = 0, len = records.length; i < len; i++){
27150 this.selectRow(ds.indexOf(records[i]), true);
27155 * Gets the number of selected rows.
27158 getCount : function(){
27159 return this.selections.length;
27163 * Selects the first row in the grid.
27165 selectFirstRow : function(){
27170 * Select the last row.
27171 * @param {Boolean} keepExisting (optional) True to keep existing selections
27173 selectLastRow : function(keepExisting){
27174 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27175 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27179 * Selects the row immediately following the last selected row.
27180 * @param {Boolean} keepExisting (optional) True to keep existing selections
27182 selectNext : function(keepExisting)
27184 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27185 this.selectRow(this.last+1, keepExisting);
27186 this.grid.getView().focusRow(this.last);
27191 * Selects the row that precedes the last selected row.
27192 * @param {Boolean} keepExisting (optional) True to keep existing selections
27194 selectPrevious : function(keepExisting){
27196 this.selectRow(this.last-1, keepExisting);
27197 this.grid.getView().focusRow(this.last);
27202 * Returns the selected records
27203 * @return {Array} Array of selected records
27205 getSelections : function(){
27206 return [].concat(this.selections.items);
27210 * Returns the first selected record.
27213 getSelected : function(){
27214 return this.selections.itemAt(0);
27219 * Clears all selections.
27221 clearSelections : function(fast)
27227 var ds = this.grid.store;
27228 var s = this.selections;
27229 s.each(function(r){
27230 this.deselectRow(ds.indexOfId(r.id));
27234 this.selections.clear();
27241 * Selects all rows.
27243 selectAll : function(){
27247 this.selections.clear();
27248 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27249 this.selectRow(i, true);
27254 * Returns True if there is a selection.
27255 * @return {Boolean}
27257 hasSelection : function(){
27258 return this.selections.length > 0;
27262 * Returns True if the specified row is selected.
27263 * @param {Number/Record} record The record or index of the record to check
27264 * @return {Boolean}
27266 isSelected : function(index){
27267 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27268 return (r && this.selections.key(r.id) ? true : false);
27272 * Returns True if the specified record id is selected.
27273 * @param {String} id The id of record to check
27274 * @return {Boolean}
27276 isIdSelected : function(id){
27277 return (this.selections.key(id) ? true : false);
27282 handleMouseDBClick : function(e, t){
27286 handleMouseDown : function(e, t)
27288 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27289 if(this.isLocked() || rowIndex < 0 ){
27292 if(e.shiftKey && this.last !== false){
27293 var last = this.last;
27294 this.selectRange(last, rowIndex, e.ctrlKey);
27295 this.last = last; // reset the last
27299 var isSelected = this.isSelected(rowIndex);
27300 //Roo.log("select row:" + rowIndex);
27302 this.deselectRow(rowIndex);
27304 this.selectRow(rowIndex, true);
27308 if(e.button !== 0 && isSelected){
27309 alert('rowIndex 2: ' + rowIndex);
27310 view.focusRow(rowIndex);
27311 }else if(e.ctrlKey && isSelected){
27312 this.deselectRow(rowIndex);
27313 }else if(!isSelected){
27314 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27315 view.focusRow(rowIndex);
27319 this.fireEvent("afterselectionchange", this);
27322 handleDragableRowClick : function(grid, rowIndex, e)
27324 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27325 this.selectRow(rowIndex, false);
27326 grid.view.focusRow(rowIndex);
27327 this.fireEvent("afterselectionchange", this);
27332 * Selects multiple rows.
27333 * @param {Array} rows Array of the indexes of the row to select
27334 * @param {Boolean} keepExisting (optional) True to keep existing selections
27336 selectRows : function(rows, keepExisting){
27338 this.clearSelections();
27340 for(var i = 0, len = rows.length; i < len; i++){
27341 this.selectRow(rows[i], true);
27346 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27347 * @param {Number} startRow The index of the first row in the range
27348 * @param {Number} endRow The index of the last row in the range
27349 * @param {Boolean} keepExisting (optional) True to retain existing selections
27351 selectRange : function(startRow, endRow, keepExisting){
27356 this.clearSelections();
27358 if(startRow <= endRow){
27359 for(var i = startRow; i <= endRow; i++){
27360 this.selectRow(i, true);
27363 for(var i = startRow; i >= endRow; i--){
27364 this.selectRow(i, true);
27370 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27371 * @param {Number} startRow The index of the first row in the range
27372 * @param {Number} endRow The index of the last row in the range
27374 deselectRange : function(startRow, endRow, preventViewNotify){
27378 for(var i = startRow; i <= endRow; i++){
27379 this.deselectRow(i, preventViewNotify);
27385 * @param {Number} row The index of the row to select
27386 * @param {Boolean} keepExisting (optional) True to keep existing selections
27388 selectRow : function(index, keepExisting, preventViewNotify)
27390 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27393 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27394 if(!keepExisting || this.singleSelect){
27395 this.clearSelections();
27398 var r = this.grid.store.getAt(index);
27399 //console.log('selectRow - record id :' + r.id);
27401 this.selections.add(r);
27402 this.last = this.lastActive = index;
27403 if(!preventViewNotify){
27404 var proxy = new Roo.Element(
27405 this.grid.getRowDom(index)
27407 proxy.addClass('bg-info info');
27409 this.fireEvent("rowselect", this, index, r);
27410 this.fireEvent("selectionchange", this);
27416 * @param {Number} row The index of the row to deselect
27418 deselectRow : function(index, preventViewNotify)
27423 if(this.last == index){
27426 if(this.lastActive == index){
27427 this.lastActive = false;
27430 var r = this.grid.store.getAt(index);
27435 this.selections.remove(r);
27436 //.console.log('deselectRow - record id :' + r.id);
27437 if(!preventViewNotify){
27439 var proxy = new Roo.Element(
27440 this.grid.getRowDom(index)
27442 proxy.removeClass('bg-info info');
27444 this.fireEvent("rowdeselect", this, index);
27445 this.fireEvent("selectionchange", this);
27449 restoreLast : function(){
27451 this.last = this._last;
27456 acceptsNav : function(row, col, cm){
27457 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27461 onEditorKey : function(field, e){
27462 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27467 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27469 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27471 }else if(k == e.ENTER && !e.ctrlKey){
27475 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27477 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27479 }else if(k == e.ESC){
27483 g.startEditing(newCell[0], newCell[1]);
27489 * Ext JS Library 1.1.1
27490 * Copyright(c) 2006-2007, Ext JS, LLC.
27492 * Originally Released Under LGPL - original licence link has changed is not relivant.
27495 * <script type="text/javascript">
27499 * @class Roo.bootstrap.PagingToolbar
27500 * @extends Roo.bootstrap.NavSimplebar
27501 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27503 * Create a new PagingToolbar
27504 * @param {Object} config The config object
27505 * @param {Roo.data.Store} store
27507 Roo.bootstrap.PagingToolbar = function(config)
27509 // old args format still supported... - xtype is prefered..
27510 // created from xtype...
27512 this.ds = config.dataSource;
27514 if (config.store && !this.ds) {
27515 this.store= Roo.factory(config.store, Roo.data);
27516 this.ds = this.store;
27517 this.ds.xmodule = this.xmodule || false;
27520 this.toolbarItems = [];
27521 if (config.items) {
27522 this.toolbarItems = config.items;
27525 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27530 this.bind(this.ds);
27533 if (Roo.bootstrap.version == 4) {
27534 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27536 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27541 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27543 * @cfg {Roo.data.Store} dataSource
27544 * The underlying data store providing the paged data
27547 * @cfg {String/HTMLElement/Element} container
27548 * container The id or element that will contain the toolbar
27551 * @cfg {Boolean} displayInfo
27552 * True to display the displayMsg (defaults to false)
27555 * @cfg {Number} pageSize
27556 * The number of records to display per page (defaults to 20)
27560 * @cfg {String} displayMsg
27561 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27563 displayMsg : 'Displaying {0} - {1} of {2}',
27565 * @cfg {String} emptyMsg
27566 * The message to display when no records are found (defaults to "No data to display")
27568 emptyMsg : 'No data to display',
27570 * Customizable piece of the default paging text (defaults to "Page")
27573 beforePageText : "Page",
27575 * Customizable piece of the default paging text (defaults to "of %0")
27578 afterPageText : "of {0}",
27580 * Customizable piece of the default paging text (defaults to "First Page")
27583 firstText : "First Page",
27585 * Customizable piece of the default paging text (defaults to "Previous Page")
27588 prevText : "Previous Page",
27590 * Customizable piece of the default paging text (defaults to "Next Page")
27593 nextText : "Next Page",
27595 * Customizable piece of the default paging text (defaults to "Last Page")
27598 lastText : "Last Page",
27600 * Customizable piece of the default paging text (defaults to "Refresh")
27603 refreshText : "Refresh",
27607 onRender : function(ct, position)
27609 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27610 this.navgroup.parentId = this.id;
27611 this.navgroup.onRender(this.el, null);
27612 // add the buttons to the navgroup
27614 if(this.displayInfo){
27615 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27616 this.displayEl = this.el.select('.x-paging-info', true).first();
27617 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27618 // this.displayEl = navel.el.select('span',true).first();
27624 Roo.each(_this.buttons, function(e){ // this might need to use render????
27625 Roo.factory(e).render(_this.el);
27629 Roo.each(_this.toolbarItems, function(e) {
27630 _this.navgroup.addItem(e);
27634 this.first = this.navgroup.addItem({
27635 tooltip: this.firstText,
27636 cls: "prev btn-outline-secondary",
27637 html : ' <i class="fa fa-step-backward"></i>',
27639 preventDefault: true,
27640 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27643 this.prev = this.navgroup.addItem({
27644 tooltip: this.prevText,
27645 cls: "prev btn-outline-secondary",
27646 html : ' <i class="fa fa-backward"></i>',
27648 preventDefault: true,
27649 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27651 //this.addSeparator();
27654 var field = this.navgroup.addItem( {
27656 cls : 'x-paging-position btn-outline-secondary',
27658 html : this.beforePageText +
27659 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27660 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27663 this.field = field.el.select('input', true).first();
27664 this.field.on("keydown", this.onPagingKeydown, this);
27665 this.field.on("focus", function(){this.dom.select();});
27668 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27669 //this.field.setHeight(18);
27670 //this.addSeparator();
27671 this.next = this.navgroup.addItem({
27672 tooltip: this.nextText,
27673 cls: "next btn-outline-secondary",
27674 html : ' <i class="fa fa-forward"></i>',
27676 preventDefault: true,
27677 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27679 this.last = this.navgroup.addItem({
27680 tooltip: this.lastText,
27681 html : ' <i class="fa fa-step-forward"></i>',
27682 cls: "next btn-outline-secondary",
27684 preventDefault: true,
27685 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27687 //this.addSeparator();
27688 this.loading = this.navgroup.addItem({
27689 tooltip: this.refreshText,
27690 cls: "btn-outline-secondary",
27691 html : ' <i class="fa fa-refresh"></i>',
27692 preventDefault: true,
27693 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27699 updateInfo : function(){
27700 if(this.displayEl){
27701 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27702 var msg = count == 0 ?
27706 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27708 this.displayEl.update(msg);
27713 onLoad : function(ds, r, o)
27715 this.cursor = o.params && o.params.start ? o.params.start : 0;
27717 var d = this.getPageData(),
27722 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27723 this.field.dom.value = ap;
27724 this.first.setDisabled(ap == 1);
27725 this.prev.setDisabled(ap == 1);
27726 this.next.setDisabled(ap == ps);
27727 this.last.setDisabled(ap == ps);
27728 this.loading.enable();
27733 getPageData : function(){
27734 var total = this.ds.getTotalCount();
27737 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27738 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27743 onLoadError : function(){
27744 this.loading.enable();
27748 onPagingKeydown : function(e){
27749 var k = e.getKey();
27750 var d = this.getPageData();
27752 var v = this.field.dom.value, pageNum;
27753 if(!v || isNaN(pageNum = parseInt(v, 10))){
27754 this.field.dom.value = d.activePage;
27757 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27758 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27761 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))
27763 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27764 this.field.dom.value = pageNum;
27765 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27768 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27770 var v = this.field.dom.value, pageNum;
27771 var increment = (e.shiftKey) ? 10 : 1;
27772 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27775 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27776 this.field.dom.value = d.activePage;
27779 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27781 this.field.dom.value = parseInt(v, 10) + increment;
27782 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27783 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27790 beforeLoad : function(){
27792 this.loading.disable();
27797 onClick : function(which){
27806 ds.load({params:{start: 0, limit: this.pageSize}});
27809 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27812 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27815 var total = ds.getTotalCount();
27816 var extra = total % this.pageSize;
27817 var lastStart = extra ? (total - extra) : total-this.pageSize;
27818 ds.load({params:{start: lastStart, limit: this.pageSize}});
27821 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27827 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27828 * @param {Roo.data.Store} store The data store to unbind
27830 unbind : function(ds){
27831 ds.un("beforeload", this.beforeLoad, this);
27832 ds.un("load", this.onLoad, this);
27833 ds.un("loadexception", this.onLoadError, this);
27834 ds.un("remove", this.updateInfo, this);
27835 ds.un("add", this.updateInfo, this);
27836 this.ds = undefined;
27840 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27841 * @param {Roo.data.Store} store The data store to bind
27843 bind : function(ds){
27844 ds.on("beforeload", this.beforeLoad, this);
27845 ds.on("load", this.onLoad, this);
27846 ds.on("loadexception", this.onLoadError, this);
27847 ds.on("remove", this.updateInfo, this);
27848 ds.on("add", this.updateInfo, this);
27859 * @class Roo.bootstrap.MessageBar
27860 * @extends Roo.bootstrap.Component
27861 * Bootstrap MessageBar class
27862 * @cfg {String} html contents of the MessageBar
27863 * @cfg {String} weight (info | success | warning | danger) default info
27864 * @cfg {String} beforeClass insert the bar before the given class
27865 * @cfg {Boolean} closable (true | false) default false
27866 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27869 * Create a new Element
27870 * @param {Object} config The config object
27873 Roo.bootstrap.MessageBar = function(config){
27874 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27877 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27883 beforeClass: 'bootstrap-sticky-wrap',
27885 getAutoCreate : function(){
27889 cls: 'alert alert-dismissable alert-' + this.weight,
27894 html: this.html || ''
27900 cfg.cls += ' alert-messages-fixed';
27914 onRender : function(ct, position)
27916 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27919 var cfg = Roo.apply({}, this.getAutoCreate());
27923 cfg.cls += ' ' + this.cls;
27926 cfg.style = this.style;
27928 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27930 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27933 this.el.select('>button.close').on('click', this.hide, this);
27939 if (!this.rendered) {
27945 this.fireEvent('show', this);
27951 if (!this.rendered) {
27957 this.fireEvent('hide', this);
27960 update : function()
27962 // var e = this.el.dom.firstChild;
27964 // if(this.closable){
27965 // e = e.nextSibling;
27968 // e.data = this.html || '';
27970 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27986 * @class Roo.bootstrap.Graph
27987 * @extends Roo.bootstrap.Component
27988 * Bootstrap Graph class
27992 @cfg {String} graphtype bar | vbar | pie
27993 @cfg {number} g_x coodinator | centre x (pie)
27994 @cfg {number} g_y coodinator | centre y (pie)
27995 @cfg {number} g_r radius (pie)
27996 @cfg {number} g_height height of the chart (respected by all elements in the set)
27997 @cfg {number} g_width width of the chart (respected by all elements in the set)
27998 @cfg {Object} title The title of the chart
28001 -opts (object) options for the chart
28003 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28004 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28006 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.
28007 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28009 o stretch (boolean)
28011 -opts (object) options for the pie
28014 o startAngle (number)
28015 o endAngle (number)
28019 * Create a new Input
28020 * @param {Object} config The config object
28023 Roo.bootstrap.Graph = function(config){
28024 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28030 * The img click event for the img.
28031 * @param {Roo.EventObject} e
28037 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28048 //g_colors: this.colors,
28055 getAutoCreate : function(){
28066 onRender : function(ct,position){
28069 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28071 if (typeof(Raphael) == 'undefined') {
28072 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28076 this.raphael = Raphael(this.el.dom);
28078 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28079 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28080 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28081 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28083 r.text(160, 10, "Single Series Chart").attr(txtattr);
28084 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28085 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28086 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28088 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28089 r.barchart(330, 10, 300, 220, data1);
28090 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28091 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28094 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28095 // r.barchart(30, 30, 560, 250, xdata, {
28096 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28097 // axis : "0 0 1 1",
28098 // axisxlabels : xdata
28099 // //yvalues : cols,
28102 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28104 // this.load(null,xdata,{
28105 // axis : "0 0 1 1",
28106 // axisxlabels : xdata
28111 load : function(graphtype,xdata,opts)
28113 this.raphael.clear();
28115 graphtype = this.graphtype;
28120 var r = this.raphael,
28121 fin = function () {
28122 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28124 fout = function () {
28125 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28127 pfin = function() {
28128 this.sector.stop();
28129 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28132 this.label[0].stop();
28133 this.label[0].attr({ r: 7.5 });
28134 this.label[1].attr({ "font-weight": 800 });
28137 pfout = function() {
28138 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28141 this.label[0].animate({ r: 5 }, 500, "bounce");
28142 this.label[1].attr({ "font-weight": 400 });
28148 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28151 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28154 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28155 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28157 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28164 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28169 setTitle: function(o)
28174 initEvents: function() {
28177 this.el.on('click', this.onClick, this);
28181 onClick : function(e)
28183 Roo.log('img onclick');
28184 this.fireEvent('click', this, e);
28196 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28199 * @class Roo.bootstrap.dash.NumberBox
28200 * @extends Roo.bootstrap.Component
28201 * Bootstrap NumberBox class
28202 * @cfg {String} headline Box headline
28203 * @cfg {String} content Box content
28204 * @cfg {String} icon Box icon
28205 * @cfg {String} footer Footer text
28206 * @cfg {String} fhref Footer href
28209 * Create a new NumberBox
28210 * @param {Object} config The config object
28214 Roo.bootstrap.dash.NumberBox = function(config){
28215 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28219 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28228 getAutoCreate : function(){
28232 cls : 'small-box ',
28240 cls : 'roo-headline',
28241 html : this.headline
28245 cls : 'roo-content',
28246 html : this.content
28260 cls : 'ion ' + this.icon
28269 cls : 'small-box-footer',
28270 href : this.fhref || '#',
28274 cfg.cn.push(footer);
28281 onRender : function(ct,position){
28282 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28289 setHeadline: function (value)
28291 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28294 setFooter: function (value, href)
28296 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28299 this.el.select('a.small-box-footer',true).first().attr('href', href);
28304 setContent: function (value)
28306 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28309 initEvents: function()
28323 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28326 * @class Roo.bootstrap.dash.TabBox
28327 * @extends Roo.bootstrap.Component
28328 * Bootstrap TabBox class
28329 * @cfg {String} title Title of the TabBox
28330 * @cfg {String} icon Icon of the TabBox
28331 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28332 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28335 * Create a new TabBox
28336 * @param {Object} config The config object
28340 Roo.bootstrap.dash.TabBox = function(config){
28341 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28346 * When a pane is added
28347 * @param {Roo.bootstrap.dash.TabPane} pane
28351 * @event activatepane
28352 * When a pane is activated
28353 * @param {Roo.bootstrap.dash.TabPane} pane
28355 "activatepane" : true
28363 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28368 tabScrollable : false,
28370 getChildContainer : function()
28372 return this.el.select('.tab-content', true).first();
28375 getAutoCreate : function(){
28379 cls: 'pull-left header',
28387 cls: 'fa ' + this.icon
28393 cls: 'nav nav-tabs pull-right',
28399 if(this.tabScrollable){
28406 cls: 'nav nav-tabs pull-right',
28417 cls: 'nav-tabs-custom',
28422 cls: 'tab-content no-padding',
28430 initEvents : function()
28432 //Roo.log('add add pane handler');
28433 this.on('addpane', this.onAddPane, this);
28436 * Updates the box title
28437 * @param {String} html to set the title to.
28439 setTitle : function(value)
28441 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28443 onAddPane : function(pane)
28445 this.panes.push(pane);
28446 //Roo.log('addpane');
28448 // tabs are rendere left to right..
28449 if(!this.showtabs){
28453 var ctr = this.el.select('.nav-tabs', true).first();
28456 var existing = ctr.select('.nav-tab',true);
28457 var qty = existing.getCount();;
28460 var tab = ctr.createChild({
28462 cls : 'nav-tab' + (qty ? '' : ' active'),
28470 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28473 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28475 pane.el.addClass('active');
28480 onTabClick : function(ev,un,ob,pane)
28482 //Roo.log('tab - prev default');
28483 ev.preventDefault();
28486 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28487 pane.tab.addClass('active');
28488 //Roo.log(pane.title);
28489 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28490 // technically we should have a deactivate event.. but maybe add later.
28491 // and it should not de-activate the selected tab...
28492 this.fireEvent('activatepane', pane);
28493 pane.el.addClass('active');
28494 pane.fireEvent('activate');
28499 getActivePane : function()
28502 Roo.each(this.panes, function(p) {
28503 if(p.el.hasClass('active')){
28524 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28526 * @class Roo.bootstrap.TabPane
28527 * @extends Roo.bootstrap.Component
28528 * Bootstrap TabPane class
28529 * @cfg {Boolean} active (false | true) Default false
28530 * @cfg {String} title title of panel
28534 * Create a new TabPane
28535 * @param {Object} config The config object
28538 Roo.bootstrap.dash.TabPane = function(config){
28539 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28545 * When a pane is activated
28546 * @param {Roo.bootstrap.dash.TabPane} pane
28553 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28558 // the tabBox that this is attached to.
28561 getAutoCreate : function()
28569 cfg.cls += ' active';
28574 initEvents : function()
28576 //Roo.log('trigger add pane handler');
28577 this.parent().fireEvent('addpane', this)
28581 * Updates the tab title
28582 * @param {String} html to set the title to.
28584 setTitle: function(str)
28590 this.tab.select('a', true).first().dom.innerHTML = str;
28607 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28610 * @class Roo.bootstrap.menu.Menu
28611 * @extends Roo.bootstrap.Component
28612 * Bootstrap Menu class - container for Menu
28613 * @cfg {String} html Text of the menu
28614 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28615 * @cfg {String} icon Font awesome icon
28616 * @cfg {String} pos Menu align to (top | bottom) default bottom
28620 * Create a new Menu
28621 * @param {Object} config The config object
28625 Roo.bootstrap.menu.Menu = function(config){
28626 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28630 * @event beforeshow
28631 * Fires before this menu is displayed
28632 * @param {Roo.bootstrap.menu.Menu} this
28636 * @event beforehide
28637 * Fires before this menu is hidden
28638 * @param {Roo.bootstrap.menu.Menu} this
28643 * Fires after this menu is displayed
28644 * @param {Roo.bootstrap.menu.Menu} this
28649 * Fires after this menu is hidden
28650 * @param {Roo.bootstrap.menu.Menu} this
28655 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28656 * @param {Roo.bootstrap.menu.Menu} this
28657 * @param {Roo.EventObject} e
28664 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28668 weight : 'default',
28673 getChildContainer : function() {
28674 if(this.isSubMenu){
28678 return this.el.select('ul.dropdown-menu', true).first();
28681 getAutoCreate : function()
28686 cls : 'roo-menu-text',
28694 cls : 'fa ' + this.icon
28705 cls : 'dropdown-button btn btn-' + this.weight,
28710 cls : 'dropdown-toggle btn btn-' + this.weight,
28720 cls : 'dropdown-menu'
28726 if(this.pos == 'top'){
28727 cfg.cls += ' dropup';
28730 if(this.isSubMenu){
28733 cls : 'dropdown-menu'
28740 onRender : function(ct, position)
28742 this.isSubMenu = ct.hasClass('dropdown-submenu');
28744 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28747 initEvents : function()
28749 if(this.isSubMenu){
28753 this.hidden = true;
28755 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28756 this.triggerEl.on('click', this.onTriggerPress, this);
28758 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28759 this.buttonEl.on('click', this.onClick, this);
28765 if(this.isSubMenu){
28769 return this.el.select('ul.dropdown-menu', true).first();
28772 onClick : function(e)
28774 this.fireEvent("click", this, e);
28777 onTriggerPress : function(e)
28779 if (this.isVisible()) {
28786 isVisible : function(){
28787 return !this.hidden;
28792 this.fireEvent("beforeshow", this);
28794 this.hidden = false;
28795 this.el.addClass('open');
28797 Roo.get(document).on("mouseup", this.onMouseUp, this);
28799 this.fireEvent("show", this);
28806 this.fireEvent("beforehide", this);
28808 this.hidden = true;
28809 this.el.removeClass('open');
28811 Roo.get(document).un("mouseup", this.onMouseUp);
28813 this.fireEvent("hide", this);
28816 onMouseUp : function()
28830 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28833 * @class Roo.bootstrap.menu.Item
28834 * @extends Roo.bootstrap.Component
28835 * Bootstrap MenuItem class
28836 * @cfg {Boolean} submenu (true | false) default false
28837 * @cfg {String} html text of the item
28838 * @cfg {String} href the link
28839 * @cfg {Boolean} disable (true | false) default false
28840 * @cfg {Boolean} preventDefault (true | false) default true
28841 * @cfg {String} icon Font awesome icon
28842 * @cfg {String} pos Submenu align to (left | right) default right
28846 * Create a new Item
28847 * @param {Object} config The config object
28851 Roo.bootstrap.menu.Item = function(config){
28852 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28856 * Fires when the mouse is hovering over this menu
28857 * @param {Roo.bootstrap.menu.Item} this
28858 * @param {Roo.EventObject} e
28863 * Fires when the mouse exits this menu
28864 * @param {Roo.bootstrap.menu.Item} this
28865 * @param {Roo.EventObject} e
28871 * The raw click event for the entire grid.
28872 * @param {Roo.EventObject} e
28878 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28883 preventDefault: true,
28888 getAutoCreate : function()
28893 cls : 'roo-menu-item-text',
28901 cls : 'fa ' + this.icon
28910 href : this.href || '#',
28917 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28921 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28923 if(this.pos == 'left'){
28924 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28931 initEvents : function()
28933 this.el.on('mouseover', this.onMouseOver, this);
28934 this.el.on('mouseout', this.onMouseOut, this);
28936 this.el.select('a', true).first().on('click', this.onClick, this);
28940 onClick : function(e)
28942 if(this.preventDefault){
28943 e.preventDefault();
28946 this.fireEvent("click", this, e);
28949 onMouseOver : function(e)
28951 if(this.submenu && this.pos == 'left'){
28952 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28955 this.fireEvent("mouseover", this, e);
28958 onMouseOut : function(e)
28960 this.fireEvent("mouseout", this, e);
28972 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28975 * @class Roo.bootstrap.menu.Separator
28976 * @extends Roo.bootstrap.Component
28977 * Bootstrap Separator class
28980 * Create a new Separator
28981 * @param {Object} config The config object
28985 Roo.bootstrap.menu.Separator = function(config){
28986 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28989 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28991 getAutoCreate : function(){
28994 cls: 'dropdown-divider divider'
29012 * @class Roo.bootstrap.Tooltip
29013 * Bootstrap Tooltip class
29014 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29015 * to determine which dom element triggers the tooltip.
29017 * It needs to add support for additional attributes like tooltip-position
29020 * Create a new Toolti
29021 * @param {Object} config The config object
29024 Roo.bootstrap.Tooltip = function(config){
29025 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29027 this.alignment = Roo.bootstrap.Tooltip.alignment;
29029 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29030 this.alignment = config.alignment;
29035 Roo.apply(Roo.bootstrap.Tooltip, {
29037 * @function init initialize tooltip monitoring.
29041 currentTip : false,
29042 currentRegion : false,
29048 Roo.get(document).on('mouseover', this.enter ,this);
29049 Roo.get(document).on('mouseout', this.leave, this);
29052 this.currentTip = new Roo.bootstrap.Tooltip();
29055 enter : function(ev)
29057 var dom = ev.getTarget();
29059 //Roo.log(['enter',dom]);
29060 var el = Roo.fly(dom);
29061 if (this.currentEl) {
29063 //Roo.log(this.currentEl);
29064 //Roo.log(this.currentEl.contains(dom));
29065 if (this.currentEl == el) {
29068 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29074 if (this.currentTip.el) {
29075 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29079 if(!el || el.dom == document){
29085 // you can not look for children, as if el is the body.. then everythign is the child..
29086 if (!el.attr('tooltip')) { //
29087 if (!el.select("[tooltip]").elements.length) {
29090 // is the mouse over this child...?
29091 bindEl = el.select("[tooltip]").first();
29092 var xy = ev.getXY();
29093 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29094 //Roo.log("not in region.");
29097 //Roo.log("child element over..");
29100 this.currentEl = bindEl;
29101 this.currentTip.bind(bindEl);
29102 this.currentRegion = Roo.lib.Region.getRegion(dom);
29103 this.currentTip.enter();
29106 leave : function(ev)
29108 var dom = ev.getTarget();
29109 //Roo.log(['leave',dom]);
29110 if (!this.currentEl) {
29115 if (dom != this.currentEl.dom) {
29118 var xy = ev.getXY();
29119 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29122 // only activate leave if mouse cursor is outside... bounding box..
29127 if (this.currentTip) {
29128 this.currentTip.leave();
29130 //Roo.log('clear currentEl');
29131 this.currentEl = false;
29136 'left' : ['r-l', [-2,0], 'right'],
29137 'right' : ['l-r', [2,0], 'left'],
29138 'bottom' : ['t-b', [0,2], 'top'],
29139 'top' : [ 'b-t', [0,-2], 'bottom']
29145 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29150 delay : null, // can be { show : 300 , hide: 500}
29154 hoverState : null, //???
29156 placement : 'bottom',
29160 getAutoCreate : function(){
29167 cls : 'tooltip-arrow arrow'
29170 cls : 'tooltip-inner'
29177 bind : function(el)
29182 initEvents : function()
29184 this.arrowEl = this.el.select('.arrow', true).first();
29185 this.innerEl = this.el.select('.tooltip-inner', true).first();
29188 enter : function () {
29190 if (this.timeout != null) {
29191 clearTimeout(this.timeout);
29194 this.hoverState = 'in';
29195 //Roo.log("enter - show");
29196 if (!this.delay || !this.delay.show) {
29201 this.timeout = setTimeout(function () {
29202 if (_t.hoverState == 'in') {
29205 }, this.delay.show);
29209 clearTimeout(this.timeout);
29211 this.hoverState = 'out';
29212 if (!this.delay || !this.delay.hide) {
29218 this.timeout = setTimeout(function () {
29219 //Roo.log("leave - timeout");
29221 if (_t.hoverState == 'out') {
29223 Roo.bootstrap.Tooltip.currentEl = false;
29228 show : function (msg)
29231 this.render(document.body);
29234 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29236 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29238 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29240 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29241 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29243 var placement = typeof this.placement == 'function' ?
29244 this.placement.call(this, this.el, on_el) :
29247 var autoToken = /\s?auto?\s?/i;
29248 var autoPlace = autoToken.test(placement);
29250 placement = placement.replace(autoToken, '') || 'top';
29254 //this.el.setXY([0,0]);
29256 //this.el.dom.style.display='block';
29258 //this.el.appendTo(on_el);
29260 var p = this.getPosition();
29261 var box = this.el.getBox();
29267 var align = this.alignment[placement];
29269 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29271 if(placement == 'top' || placement == 'bottom'){
29273 placement = 'right';
29276 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29277 placement = 'left';
29280 var scroll = Roo.select('body', true).first().getScroll();
29282 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29286 align = this.alignment[placement];
29288 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29292 this.el.alignTo(this.bindEl, align[0],align[1]);
29293 //var arrow = this.el.select('.arrow',true).first();
29294 //arrow.set(align[2],
29296 this.el.addClass(placement);
29297 this.el.addClass("bs-tooltip-"+ placement);
29299 this.el.addClass('in fade show');
29301 this.hoverState = null;
29303 if (this.el.hasClass('fade')) {
29318 //this.el.setXY([0,0]);
29319 this.el.removeClass(['show', 'in']);
29335 * @class Roo.bootstrap.LocationPicker
29336 * @extends Roo.bootstrap.Component
29337 * Bootstrap LocationPicker class
29338 * @cfg {Number} latitude Position when init default 0
29339 * @cfg {Number} longitude Position when init default 0
29340 * @cfg {Number} zoom default 15
29341 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29342 * @cfg {Boolean} mapTypeControl default false
29343 * @cfg {Boolean} disableDoubleClickZoom default false
29344 * @cfg {Boolean} scrollwheel default true
29345 * @cfg {Boolean} streetViewControl default false
29346 * @cfg {Number} radius default 0
29347 * @cfg {String} locationName
29348 * @cfg {Boolean} draggable default true
29349 * @cfg {Boolean} enableAutocomplete default false
29350 * @cfg {Boolean} enableReverseGeocode default true
29351 * @cfg {String} markerTitle
29354 * Create a new LocationPicker
29355 * @param {Object} config The config object
29359 Roo.bootstrap.LocationPicker = function(config){
29361 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29366 * Fires when the picker initialized.
29367 * @param {Roo.bootstrap.LocationPicker} this
29368 * @param {Google Location} location
29372 * @event positionchanged
29373 * Fires when the picker position changed.
29374 * @param {Roo.bootstrap.LocationPicker} this
29375 * @param {Google Location} location
29377 positionchanged : true,
29380 * Fires when the map resize.
29381 * @param {Roo.bootstrap.LocationPicker} this
29386 * Fires when the map show.
29387 * @param {Roo.bootstrap.LocationPicker} this
29392 * Fires when the map hide.
29393 * @param {Roo.bootstrap.LocationPicker} this
29398 * Fires when click the map.
29399 * @param {Roo.bootstrap.LocationPicker} this
29400 * @param {Map event} e
29404 * @event mapRightClick
29405 * Fires when right click the map.
29406 * @param {Roo.bootstrap.LocationPicker} this
29407 * @param {Map event} e
29409 mapRightClick : true,
29411 * @event markerClick
29412 * Fires when click the marker.
29413 * @param {Roo.bootstrap.LocationPicker} this
29414 * @param {Map event} e
29416 markerClick : true,
29418 * @event markerRightClick
29419 * Fires when right click the marker.
29420 * @param {Roo.bootstrap.LocationPicker} this
29421 * @param {Map event} e
29423 markerRightClick : true,
29425 * @event OverlayViewDraw
29426 * Fires when OverlayView Draw
29427 * @param {Roo.bootstrap.LocationPicker} this
29429 OverlayViewDraw : true,
29431 * @event OverlayViewOnAdd
29432 * Fires when OverlayView Draw
29433 * @param {Roo.bootstrap.LocationPicker} this
29435 OverlayViewOnAdd : true,
29437 * @event OverlayViewOnRemove
29438 * Fires when OverlayView Draw
29439 * @param {Roo.bootstrap.LocationPicker} this
29441 OverlayViewOnRemove : true,
29443 * @event OverlayViewShow
29444 * Fires when OverlayView Draw
29445 * @param {Roo.bootstrap.LocationPicker} this
29446 * @param {Pixel} cpx
29448 OverlayViewShow : true,
29450 * @event OverlayViewHide
29451 * Fires when OverlayView Draw
29452 * @param {Roo.bootstrap.LocationPicker} this
29454 OverlayViewHide : true,
29456 * @event loadexception
29457 * Fires when load google lib failed.
29458 * @param {Roo.bootstrap.LocationPicker} this
29460 loadexception : true
29465 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29467 gMapContext: false,
29473 mapTypeControl: false,
29474 disableDoubleClickZoom: false,
29476 streetViewControl: false,
29480 enableAutocomplete: false,
29481 enableReverseGeocode: true,
29484 getAutoCreate: function()
29489 cls: 'roo-location-picker'
29495 initEvents: function(ct, position)
29497 if(!this.el.getWidth() || this.isApplied()){
29501 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29506 initial: function()
29508 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29509 this.fireEvent('loadexception', this);
29513 if(!this.mapTypeId){
29514 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29517 this.gMapContext = this.GMapContext();
29519 this.initOverlayView();
29521 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29525 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29526 _this.setPosition(_this.gMapContext.marker.position);
29529 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29530 _this.fireEvent('mapClick', this, event);
29534 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29535 _this.fireEvent('mapRightClick', this, event);
29539 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29540 _this.fireEvent('markerClick', this, event);
29544 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29545 _this.fireEvent('markerRightClick', this, event);
29549 this.setPosition(this.gMapContext.location);
29551 this.fireEvent('initial', this, this.gMapContext.location);
29554 initOverlayView: function()
29558 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29562 _this.fireEvent('OverlayViewDraw', _this);
29567 _this.fireEvent('OverlayViewOnAdd', _this);
29570 onRemove: function()
29572 _this.fireEvent('OverlayViewOnRemove', _this);
29575 show: function(cpx)
29577 _this.fireEvent('OverlayViewShow', _this, cpx);
29582 _this.fireEvent('OverlayViewHide', _this);
29588 fromLatLngToContainerPixel: function(event)
29590 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29593 isApplied: function()
29595 return this.getGmapContext() == false ? false : true;
29598 getGmapContext: function()
29600 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29603 GMapContext: function()
29605 var position = new google.maps.LatLng(this.latitude, this.longitude);
29607 var _map = new google.maps.Map(this.el.dom, {
29610 mapTypeId: this.mapTypeId,
29611 mapTypeControl: this.mapTypeControl,
29612 disableDoubleClickZoom: this.disableDoubleClickZoom,
29613 scrollwheel: this.scrollwheel,
29614 streetViewControl: this.streetViewControl,
29615 locationName: this.locationName,
29616 draggable: this.draggable,
29617 enableAutocomplete: this.enableAutocomplete,
29618 enableReverseGeocode: this.enableReverseGeocode
29621 var _marker = new google.maps.Marker({
29622 position: position,
29624 title: this.markerTitle,
29625 draggable: this.draggable
29632 location: position,
29633 radius: this.radius,
29634 locationName: this.locationName,
29635 addressComponents: {
29636 formatted_address: null,
29637 addressLine1: null,
29638 addressLine2: null,
29640 streetNumber: null,
29644 stateOrProvince: null
29647 domContainer: this.el.dom,
29648 geodecoder: new google.maps.Geocoder()
29652 drawCircle: function(center, radius, options)
29654 if (this.gMapContext.circle != null) {
29655 this.gMapContext.circle.setMap(null);
29659 options = Roo.apply({}, options, {
29660 strokeColor: "#0000FF",
29661 strokeOpacity: .35,
29663 fillColor: "#0000FF",
29667 options.map = this.gMapContext.map;
29668 options.radius = radius;
29669 options.center = center;
29670 this.gMapContext.circle = new google.maps.Circle(options);
29671 return this.gMapContext.circle;
29677 setPosition: function(location)
29679 this.gMapContext.location = location;
29680 this.gMapContext.marker.setPosition(location);
29681 this.gMapContext.map.panTo(location);
29682 this.drawCircle(location, this.gMapContext.radius, {});
29686 if (this.gMapContext.settings.enableReverseGeocode) {
29687 this.gMapContext.geodecoder.geocode({
29688 latLng: this.gMapContext.location
29689 }, function(results, status) {
29691 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29692 _this.gMapContext.locationName = results[0].formatted_address;
29693 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29695 _this.fireEvent('positionchanged', this, location);
29702 this.fireEvent('positionchanged', this, location);
29707 google.maps.event.trigger(this.gMapContext.map, "resize");
29709 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29711 this.fireEvent('resize', this);
29714 setPositionByLatLng: function(latitude, longitude)
29716 this.setPosition(new google.maps.LatLng(latitude, longitude));
29719 getCurrentPosition: function()
29722 latitude: this.gMapContext.location.lat(),
29723 longitude: this.gMapContext.location.lng()
29727 getAddressName: function()
29729 return this.gMapContext.locationName;
29732 getAddressComponents: function()
29734 return this.gMapContext.addressComponents;
29737 address_component_from_google_geocode: function(address_components)
29741 for (var i = 0; i < address_components.length; i++) {
29742 var component = address_components[i];
29743 if (component.types.indexOf("postal_code") >= 0) {
29744 result.postalCode = component.short_name;
29745 } else if (component.types.indexOf("street_number") >= 0) {
29746 result.streetNumber = component.short_name;
29747 } else if (component.types.indexOf("route") >= 0) {
29748 result.streetName = component.short_name;
29749 } else if (component.types.indexOf("neighborhood") >= 0) {
29750 result.city = component.short_name;
29751 } else if (component.types.indexOf("locality") >= 0) {
29752 result.city = component.short_name;
29753 } else if (component.types.indexOf("sublocality") >= 0) {
29754 result.district = component.short_name;
29755 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29756 result.stateOrProvince = component.short_name;
29757 } else if (component.types.indexOf("country") >= 0) {
29758 result.country = component.short_name;
29762 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29763 result.addressLine2 = "";
29767 setZoomLevel: function(zoom)
29769 this.gMapContext.map.setZoom(zoom);
29782 this.fireEvent('show', this);
29793 this.fireEvent('hide', this);
29798 Roo.apply(Roo.bootstrap.LocationPicker, {
29800 OverlayView : function(map, options)
29802 options = options || {};
29809 * @class Roo.bootstrap.Alert
29810 * @extends Roo.bootstrap.Component
29811 * Bootstrap Alert class - shows an alert area box
29813 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29814 Enter a valid email address
29817 * @cfg {String} title The title of alert
29818 * @cfg {String} html The content of alert
29819 * @cfg {String} weight ( success | info | warning | danger )
29820 * @cfg {String} fa font-awesomeicon
29821 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29822 * @cfg {Boolean} close true to show a x closer
29826 * Create a new alert
29827 * @param {Object} config The config object
29831 Roo.bootstrap.Alert = function(config){
29832 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29836 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29842 faicon: false, // BC
29846 getAutoCreate : function()
29858 style : this.close ? '' : 'display:none'
29862 cls : 'roo-alert-icon'
29867 cls : 'roo-alert-title',
29872 cls : 'roo-alert-text',
29879 cfg.cn[0].cls += ' fa ' + this.faicon;
29882 cfg.cn[0].cls += ' fa ' + this.fa;
29886 cfg.cls += ' alert-' + this.weight;
29892 initEvents: function()
29894 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29895 this.titleEl = this.el.select('.roo-alert-title',true).first();
29896 this.iconEl = this.el.select('.roo-alert-icon',true).first();
29899 setTitle : function(str)
29901 this.titleEl.dom.innerHTML = str;
29904 setText : function(str)
29906 this.titleEl.dom.innerHTML = str;
29909 setWeight : function(weight)
29912 this.el.removeClass('alert-' + this.weight);
29915 this.weight = weight;
29917 this.el.addClass('alert-' + this.weight);
29920 setIcon : function(icon)
29923 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29926 this.faicon = icon;
29928 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
29949 * @class Roo.bootstrap.UploadCropbox
29950 * @extends Roo.bootstrap.Component
29951 * Bootstrap UploadCropbox class
29952 * @cfg {String} emptyText show when image has been loaded
29953 * @cfg {String} rotateNotify show when image too small to rotate
29954 * @cfg {Number} errorTimeout default 3000
29955 * @cfg {Number} minWidth default 300
29956 * @cfg {Number} minHeight default 300
29957 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29958 * @cfg {Boolean} isDocument (true|false) default false
29959 * @cfg {String} url action url
29960 * @cfg {String} paramName default 'imageUpload'
29961 * @cfg {String} method default POST
29962 * @cfg {Boolean} loadMask (true|false) default true
29963 * @cfg {Boolean} loadingText default 'Loading...'
29966 * Create a new UploadCropbox
29967 * @param {Object} config The config object
29970 Roo.bootstrap.UploadCropbox = function(config){
29971 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29975 * @event beforeselectfile
29976 * Fire before select file
29977 * @param {Roo.bootstrap.UploadCropbox} this
29979 "beforeselectfile" : true,
29982 * Fire after initEvent
29983 * @param {Roo.bootstrap.UploadCropbox} this
29988 * Fire after initEvent
29989 * @param {Roo.bootstrap.UploadCropbox} this
29990 * @param {String} data
29995 * Fire when preparing the file data
29996 * @param {Roo.bootstrap.UploadCropbox} this
29997 * @param {Object} file
30002 * Fire when get exception
30003 * @param {Roo.bootstrap.UploadCropbox} this
30004 * @param {XMLHttpRequest} xhr
30006 "exception" : true,
30008 * @event beforeloadcanvas
30009 * Fire before load the canvas
30010 * @param {Roo.bootstrap.UploadCropbox} this
30011 * @param {String} src
30013 "beforeloadcanvas" : true,
30016 * Fire when trash image
30017 * @param {Roo.bootstrap.UploadCropbox} this
30022 * Fire when download the image
30023 * @param {Roo.bootstrap.UploadCropbox} this
30027 * @event footerbuttonclick
30028 * Fire when footerbuttonclick
30029 * @param {Roo.bootstrap.UploadCropbox} this
30030 * @param {String} type
30032 "footerbuttonclick" : true,
30036 * @param {Roo.bootstrap.UploadCropbox} this
30041 * Fire when rotate the image
30042 * @param {Roo.bootstrap.UploadCropbox} this
30043 * @param {String} pos
30048 * Fire when inspect the file
30049 * @param {Roo.bootstrap.UploadCropbox} this
30050 * @param {Object} file
30055 * Fire when xhr upload the file
30056 * @param {Roo.bootstrap.UploadCropbox} this
30057 * @param {Object} data
30062 * Fire when arrange the file data
30063 * @param {Roo.bootstrap.UploadCropbox} this
30064 * @param {Object} formData
30069 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30072 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30074 emptyText : 'Click to upload image',
30075 rotateNotify : 'Image is too small to rotate',
30076 errorTimeout : 3000,
30090 cropType : 'image/jpeg',
30092 canvasLoaded : false,
30093 isDocument : false,
30095 paramName : 'imageUpload',
30097 loadingText : 'Loading...',
30100 getAutoCreate : function()
30104 cls : 'roo-upload-cropbox',
30108 cls : 'roo-upload-cropbox-selector',
30113 cls : 'roo-upload-cropbox-body',
30114 style : 'cursor:pointer',
30118 cls : 'roo-upload-cropbox-preview'
30122 cls : 'roo-upload-cropbox-thumb'
30126 cls : 'roo-upload-cropbox-empty-notify',
30127 html : this.emptyText
30131 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30132 html : this.rotateNotify
30138 cls : 'roo-upload-cropbox-footer',
30141 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30151 onRender : function(ct, position)
30153 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30155 if (this.buttons.length) {
30157 Roo.each(this.buttons, function(bb) {
30159 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30161 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30167 this.maskEl = this.el;
30171 initEvents : function()
30173 this.urlAPI = (window.createObjectURL && window) ||
30174 (window.URL && URL.revokeObjectURL && URL) ||
30175 (window.webkitURL && webkitURL);
30177 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30178 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30180 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30181 this.selectorEl.hide();
30183 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30184 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30186 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30187 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30188 this.thumbEl.hide();
30190 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30191 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30193 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30194 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30195 this.errorEl.hide();
30197 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30198 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30199 this.footerEl.hide();
30201 this.setThumbBoxSize();
30207 this.fireEvent('initial', this);
30214 window.addEventListener("resize", function() { _this.resize(); } );
30216 this.bodyEl.on('click', this.beforeSelectFile, this);
30219 this.bodyEl.on('touchstart', this.onTouchStart, this);
30220 this.bodyEl.on('touchmove', this.onTouchMove, this);
30221 this.bodyEl.on('touchend', this.onTouchEnd, this);
30225 this.bodyEl.on('mousedown', this.onMouseDown, this);
30226 this.bodyEl.on('mousemove', this.onMouseMove, this);
30227 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30228 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30229 Roo.get(document).on('mouseup', this.onMouseUp, this);
30232 this.selectorEl.on('change', this.onFileSelected, this);
30238 this.baseScale = 1;
30240 this.baseRotate = 1;
30241 this.dragable = false;
30242 this.pinching = false;
30245 this.cropData = false;
30246 this.notifyEl.dom.innerHTML = this.emptyText;
30248 this.selectorEl.dom.value = '';
30252 resize : function()
30254 if(this.fireEvent('resize', this) != false){
30255 this.setThumbBoxPosition();
30256 this.setCanvasPosition();
30260 onFooterButtonClick : function(e, el, o, type)
30263 case 'rotate-left' :
30264 this.onRotateLeft(e);
30266 case 'rotate-right' :
30267 this.onRotateRight(e);
30270 this.beforeSelectFile(e);
30285 this.fireEvent('footerbuttonclick', this, type);
30288 beforeSelectFile : function(e)
30290 e.preventDefault();
30292 if(this.fireEvent('beforeselectfile', this) != false){
30293 this.selectorEl.dom.click();
30297 onFileSelected : function(e)
30299 e.preventDefault();
30301 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30305 var file = this.selectorEl.dom.files[0];
30307 if(this.fireEvent('inspect', this, file) != false){
30308 this.prepare(file);
30313 trash : function(e)
30315 this.fireEvent('trash', this);
30318 download : function(e)
30320 this.fireEvent('download', this);
30323 loadCanvas : function(src)
30325 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30329 this.imageEl = document.createElement('img');
30333 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30335 this.imageEl.src = src;
30339 onLoadCanvas : function()
30341 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30342 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30344 this.bodyEl.un('click', this.beforeSelectFile, this);
30346 this.notifyEl.hide();
30347 this.thumbEl.show();
30348 this.footerEl.show();
30350 this.baseRotateLevel();
30352 if(this.isDocument){
30353 this.setThumbBoxSize();
30356 this.setThumbBoxPosition();
30358 this.baseScaleLevel();
30364 this.canvasLoaded = true;
30367 this.maskEl.unmask();
30372 setCanvasPosition : function()
30374 if(!this.canvasEl){
30378 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30379 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30381 this.previewEl.setLeft(pw);
30382 this.previewEl.setTop(ph);
30386 onMouseDown : function(e)
30390 this.dragable = true;
30391 this.pinching = false;
30393 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30394 this.dragable = false;
30398 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30399 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30403 onMouseMove : function(e)
30407 if(!this.canvasLoaded){
30411 if (!this.dragable){
30415 var minX = Math.ceil(this.thumbEl.getLeft(true));
30416 var minY = Math.ceil(this.thumbEl.getTop(true));
30418 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30419 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30421 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30422 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30424 x = x - this.mouseX;
30425 y = y - this.mouseY;
30427 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30428 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30430 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30431 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30433 this.previewEl.setLeft(bgX);
30434 this.previewEl.setTop(bgY);
30436 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30437 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30440 onMouseUp : function(e)
30444 this.dragable = false;
30447 onMouseWheel : function(e)
30451 this.startScale = this.scale;
30453 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30455 if(!this.zoomable()){
30456 this.scale = this.startScale;
30465 zoomable : function()
30467 var minScale = this.thumbEl.getWidth() / this.minWidth;
30469 if(this.minWidth < this.minHeight){
30470 minScale = this.thumbEl.getHeight() / this.minHeight;
30473 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30474 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30478 (this.rotate == 0 || this.rotate == 180) &&
30480 width > this.imageEl.OriginWidth ||
30481 height > this.imageEl.OriginHeight ||
30482 (width < this.minWidth && height < this.minHeight)
30490 (this.rotate == 90 || this.rotate == 270) &&
30492 width > this.imageEl.OriginWidth ||
30493 height > this.imageEl.OriginHeight ||
30494 (width < this.minHeight && height < this.minWidth)
30501 !this.isDocument &&
30502 (this.rotate == 0 || this.rotate == 180) &&
30504 width < this.minWidth ||
30505 width > this.imageEl.OriginWidth ||
30506 height < this.minHeight ||
30507 height > this.imageEl.OriginHeight
30514 !this.isDocument &&
30515 (this.rotate == 90 || this.rotate == 270) &&
30517 width < this.minHeight ||
30518 width > this.imageEl.OriginWidth ||
30519 height < this.minWidth ||
30520 height > this.imageEl.OriginHeight
30530 onRotateLeft : function(e)
30532 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30534 var minScale = this.thumbEl.getWidth() / this.minWidth;
30536 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30537 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30539 this.startScale = this.scale;
30541 while (this.getScaleLevel() < minScale){
30543 this.scale = this.scale + 1;
30545 if(!this.zoomable()){
30550 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30551 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30556 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30563 this.scale = this.startScale;
30565 this.onRotateFail();
30570 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30572 if(this.isDocument){
30573 this.setThumbBoxSize();
30574 this.setThumbBoxPosition();
30575 this.setCanvasPosition();
30580 this.fireEvent('rotate', this, 'left');
30584 onRotateRight : function(e)
30586 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30588 var minScale = this.thumbEl.getWidth() / this.minWidth;
30590 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30591 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30593 this.startScale = this.scale;
30595 while (this.getScaleLevel() < minScale){
30597 this.scale = this.scale + 1;
30599 if(!this.zoomable()){
30604 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30605 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30610 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30617 this.scale = this.startScale;
30619 this.onRotateFail();
30624 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30626 if(this.isDocument){
30627 this.setThumbBoxSize();
30628 this.setThumbBoxPosition();
30629 this.setCanvasPosition();
30634 this.fireEvent('rotate', this, 'right');
30637 onRotateFail : function()
30639 this.errorEl.show(true);
30643 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30648 this.previewEl.dom.innerHTML = '';
30650 var canvasEl = document.createElement("canvas");
30652 var contextEl = canvasEl.getContext("2d");
30654 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30655 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30656 var center = this.imageEl.OriginWidth / 2;
30658 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30659 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30660 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30661 center = this.imageEl.OriginHeight / 2;
30664 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30666 contextEl.translate(center, center);
30667 contextEl.rotate(this.rotate * Math.PI / 180);
30669 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30671 this.canvasEl = document.createElement("canvas");
30673 this.contextEl = this.canvasEl.getContext("2d");
30675 switch (this.rotate) {
30678 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30679 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30681 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30686 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30687 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30689 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30690 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);
30694 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30699 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30700 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30702 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30703 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);
30707 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);
30712 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30713 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30715 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30716 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30720 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);
30727 this.previewEl.appendChild(this.canvasEl);
30729 this.setCanvasPosition();
30734 if(!this.canvasLoaded){
30738 var imageCanvas = document.createElement("canvas");
30740 var imageContext = imageCanvas.getContext("2d");
30742 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30743 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30745 var center = imageCanvas.width / 2;
30747 imageContext.translate(center, center);
30749 imageContext.rotate(this.rotate * Math.PI / 180);
30751 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30753 var canvas = document.createElement("canvas");
30755 var context = canvas.getContext("2d");
30757 canvas.width = this.minWidth;
30758 canvas.height = this.minHeight;
30760 switch (this.rotate) {
30763 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30764 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30766 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30767 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30769 var targetWidth = this.minWidth - 2 * x;
30770 var targetHeight = this.minHeight - 2 * y;
30774 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30775 scale = targetWidth / width;
30778 if(x > 0 && y == 0){
30779 scale = targetHeight / height;
30782 if(x > 0 && y > 0){
30783 scale = targetWidth / width;
30785 if(width < height){
30786 scale = targetHeight / height;
30790 context.scale(scale, scale);
30792 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30793 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30795 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30796 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30798 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30803 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30804 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30806 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30807 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30809 var targetWidth = this.minWidth - 2 * x;
30810 var targetHeight = this.minHeight - 2 * y;
30814 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30815 scale = targetWidth / width;
30818 if(x > 0 && y == 0){
30819 scale = targetHeight / height;
30822 if(x > 0 && y > 0){
30823 scale = targetWidth / width;
30825 if(width < height){
30826 scale = targetHeight / height;
30830 context.scale(scale, scale);
30832 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30833 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30835 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30836 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30838 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30840 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30845 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30846 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30848 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30849 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30851 var targetWidth = this.minWidth - 2 * x;
30852 var targetHeight = this.minHeight - 2 * y;
30856 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30857 scale = targetWidth / width;
30860 if(x > 0 && y == 0){
30861 scale = targetHeight / height;
30864 if(x > 0 && y > 0){
30865 scale = targetWidth / width;
30867 if(width < height){
30868 scale = targetHeight / height;
30872 context.scale(scale, scale);
30874 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30875 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30877 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30878 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30880 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30881 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30883 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30888 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30889 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30891 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30892 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30894 var targetWidth = this.minWidth - 2 * x;
30895 var targetHeight = this.minHeight - 2 * y;
30899 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30900 scale = targetWidth / width;
30903 if(x > 0 && y == 0){
30904 scale = targetHeight / height;
30907 if(x > 0 && y > 0){
30908 scale = targetWidth / width;
30910 if(width < height){
30911 scale = targetHeight / height;
30915 context.scale(scale, scale);
30917 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30918 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30920 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30921 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30923 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30925 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30932 this.cropData = canvas.toDataURL(this.cropType);
30934 if(this.fireEvent('crop', this, this.cropData) !== false){
30935 this.process(this.file, this.cropData);
30942 setThumbBoxSize : function()
30946 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30947 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30948 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30950 this.minWidth = width;
30951 this.minHeight = height;
30953 if(this.rotate == 90 || this.rotate == 270){
30954 this.minWidth = height;
30955 this.minHeight = width;
30960 width = Math.ceil(this.minWidth * height / this.minHeight);
30962 if(this.minWidth > this.minHeight){
30964 height = Math.ceil(this.minHeight * width / this.minWidth);
30967 this.thumbEl.setStyle({
30968 width : width + 'px',
30969 height : height + 'px'
30976 setThumbBoxPosition : function()
30978 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30979 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30981 this.thumbEl.setLeft(x);
30982 this.thumbEl.setTop(y);
30986 baseRotateLevel : function()
30988 this.baseRotate = 1;
30991 typeof(this.exif) != 'undefined' &&
30992 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30993 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30995 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30998 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31002 baseScaleLevel : function()
31006 if(this.isDocument){
31008 if(this.baseRotate == 6 || this.baseRotate == 8){
31010 height = this.thumbEl.getHeight();
31011 this.baseScale = height / this.imageEl.OriginWidth;
31013 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31014 width = this.thumbEl.getWidth();
31015 this.baseScale = width / this.imageEl.OriginHeight;
31021 height = this.thumbEl.getHeight();
31022 this.baseScale = height / this.imageEl.OriginHeight;
31024 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31025 width = this.thumbEl.getWidth();
31026 this.baseScale = width / this.imageEl.OriginWidth;
31032 if(this.baseRotate == 6 || this.baseRotate == 8){
31034 width = this.thumbEl.getHeight();
31035 this.baseScale = width / this.imageEl.OriginHeight;
31037 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31038 height = this.thumbEl.getWidth();
31039 this.baseScale = height / this.imageEl.OriginHeight;
31042 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31043 height = this.thumbEl.getWidth();
31044 this.baseScale = height / this.imageEl.OriginHeight;
31046 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31047 width = this.thumbEl.getHeight();
31048 this.baseScale = width / this.imageEl.OriginWidth;
31055 width = this.thumbEl.getWidth();
31056 this.baseScale = width / this.imageEl.OriginWidth;
31058 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31059 height = this.thumbEl.getHeight();
31060 this.baseScale = height / this.imageEl.OriginHeight;
31063 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31065 height = this.thumbEl.getHeight();
31066 this.baseScale = height / this.imageEl.OriginHeight;
31068 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31069 width = this.thumbEl.getWidth();
31070 this.baseScale = width / this.imageEl.OriginWidth;
31078 getScaleLevel : function()
31080 return this.baseScale * Math.pow(1.1, this.scale);
31083 onTouchStart : function(e)
31085 if(!this.canvasLoaded){
31086 this.beforeSelectFile(e);
31090 var touches = e.browserEvent.touches;
31096 if(touches.length == 1){
31097 this.onMouseDown(e);
31101 if(touches.length != 2){
31107 for(var i = 0, finger; finger = touches[i]; i++){
31108 coords.push(finger.pageX, finger.pageY);
31111 var x = Math.pow(coords[0] - coords[2], 2);
31112 var y = Math.pow(coords[1] - coords[3], 2);
31114 this.startDistance = Math.sqrt(x + y);
31116 this.startScale = this.scale;
31118 this.pinching = true;
31119 this.dragable = false;
31123 onTouchMove : function(e)
31125 if(!this.pinching && !this.dragable){
31129 var touches = e.browserEvent.touches;
31136 this.onMouseMove(e);
31142 for(var i = 0, finger; finger = touches[i]; i++){
31143 coords.push(finger.pageX, finger.pageY);
31146 var x = Math.pow(coords[0] - coords[2], 2);
31147 var y = Math.pow(coords[1] - coords[3], 2);
31149 this.endDistance = Math.sqrt(x + y);
31151 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31153 if(!this.zoomable()){
31154 this.scale = this.startScale;
31162 onTouchEnd : function(e)
31164 this.pinching = false;
31165 this.dragable = false;
31169 process : function(file, crop)
31172 this.maskEl.mask(this.loadingText);
31175 this.xhr = new XMLHttpRequest();
31177 file.xhr = this.xhr;
31179 this.xhr.open(this.method, this.url, true);
31182 "Accept": "application/json",
31183 "Cache-Control": "no-cache",
31184 "X-Requested-With": "XMLHttpRequest"
31187 for (var headerName in headers) {
31188 var headerValue = headers[headerName];
31190 this.xhr.setRequestHeader(headerName, headerValue);
31196 this.xhr.onload = function()
31198 _this.xhrOnLoad(_this.xhr);
31201 this.xhr.onerror = function()
31203 _this.xhrOnError(_this.xhr);
31206 var formData = new FormData();
31208 formData.append('returnHTML', 'NO');
31211 formData.append('crop', crop);
31214 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31215 formData.append(this.paramName, file, file.name);
31218 if(typeof(file.filename) != 'undefined'){
31219 formData.append('filename', file.filename);
31222 if(typeof(file.mimetype) != 'undefined'){
31223 formData.append('mimetype', file.mimetype);
31226 if(this.fireEvent('arrange', this, formData) != false){
31227 this.xhr.send(formData);
31231 xhrOnLoad : function(xhr)
31234 this.maskEl.unmask();
31237 if (xhr.readyState !== 4) {
31238 this.fireEvent('exception', this, xhr);
31242 var response = Roo.decode(xhr.responseText);
31244 if(!response.success){
31245 this.fireEvent('exception', this, xhr);
31249 var response = Roo.decode(xhr.responseText);
31251 this.fireEvent('upload', this, response);
31255 xhrOnError : function()
31258 this.maskEl.unmask();
31261 Roo.log('xhr on error');
31263 var response = Roo.decode(xhr.responseText);
31269 prepare : function(file)
31272 this.maskEl.mask(this.loadingText);
31278 if(typeof(file) === 'string'){
31279 this.loadCanvas(file);
31283 if(!file || !this.urlAPI){
31288 this.cropType = file.type;
31292 if(this.fireEvent('prepare', this, this.file) != false){
31294 var reader = new FileReader();
31296 reader.onload = function (e) {
31297 if (e.target.error) {
31298 Roo.log(e.target.error);
31302 var buffer = e.target.result,
31303 dataView = new DataView(buffer),
31305 maxOffset = dataView.byteLength - 4,
31309 if (dataView.getUint16(0) === 0xffd8) {
31310 while (offset < maxOffset) {
31311 markerBytes = dataView.getUint16(offset);
31313 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31314 markerLength = dataView.getUint16(offset + 2) + 2;
31315 if (offset + markerLength > dataView.byteLength) {
31316 Roo.log('Invalid meta data: Invalid segment size.');
31320 if(markerBytes == 0xffe1){
31321 _this.parseExifData(
31328 offset += markerLength;
31338 var url = _this.urlAPI.createObjectURL(_this.file);
31340 _this.loadCanvas(url);
31345 reader.readAsArrayBuffer(this.file);
31351 parseExifData : function(dataView, offset, length)
31353 var tiffOffset = offset + 10,
31357 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31358 // No Exif data, might be XMP data instead
31362 // Check for the ASCII code for "Exif" (0x45786966):
31363 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31364 // No Exif data, might be XMP data instead
31367 if (tiffOffset + 8 > dataView.byteLength) {
31368 Roo.log('Invalid Exif data: Invalid segment size.');
31371 // Check for the two null bytes:
31372 if (dataView.getUint16(offset + 8) !== 0x0000) {
31373 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31376 // Check the byte alignment:
31377 switch (dataView.getUint16(tiffOffset)) {
31379 littleEndian = true;
31382 littleEndian = false;
31385 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31388 // Check for the TIFF tag marker (0x002A):
31389 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31390 Roo.log('Invalid Exif data: Missing TIFF marker.');
31393 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31394 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31396 this.parseExifTags(
31399 tiffOffset + dirOffset,
31404 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31409 if (dirOffset + 6 > dataView.byteLength) {
31410 Roo.log('Invalid Exif data: Invalid directory offset.');
31413 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31414 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31415 if (dirEndOffset + 4 > dataView.byteLength) {
31416 Roo.log('Invalid Exif data: Invalid directory size.');
31419 for (i = 0; i < tagsNumber; i += 1) {
31423 dirOffset + 2 + 12 * i, // tag offset
31427 // Return the offset to the next directory:
31428 return dataView.getUint32(dirEndOffset, littleEndian);
31431 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31433 var tag = dataView.getUint16(offset, littleEndian);
31435 this.exif[tag] = this.getExifValue(
31439 dataView.getUint16(offset + 2, littleEndian), // tag type
31440 dataView.getUint32(offset + 4, littleEndian), // tag length
31445 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31447 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31456 Roo.log('Invalid Exif data: Invalid tag type.');
31460 tagSize = tagType.size * length;
31461 // Determine if the value is contained in the dataOffset bytes,
31462 // or if the value at the dataOffset is a pointer to the actual data:
31463 dataOffset = tagSize > 4 ?
31464 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31465 if (dataOffset + tagSize > dataView.byteLength) {
31466 Roo.log('Invalid Exif data: Invalid data offset.');
31469 if (length === 1) {
31470 return tagType.getValue(dataView, dataOffset, littleEndian);
31473 for (i = 0; i < length; i += 1) {
31474 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31477 if (tagType.ascii) {
31479 // Concatenate the chars:
31480 for (i = 0; i < values.length; i += 1) {
31482 // Ignore the terminating NULL byte(s):
31483 if (c === '\u0000') {
31495 Roo.apply(Roo.bootstrap.UploadCropbox, {
31497 'Orientation': 0x0112
31501 1: 0, //'top-left',
31503 3: 180, //'bottom-right',
31504 // 4: 'bottom-left',
31506 6: 90, //'right-top',
31507 // 7: 'right-bottom',
31508 8: 270 //'left-bottom'
31512 // byte, 8-bit unsigned int:
31514 getValue: function (dataView, dataOffset) {
31515 return dataView.getUint8(dataOffset);
31519 // ascii, 8-bit byte:
31521 getValue: function (dataView, dataOffset) {
31522 return String.fromCharCode(dataView.getUint8(dataOffset));
31527 // short, 16 bit int:
31529 getValue: function (dataView, dataOffset, littleEndian) {
31530 return dataView.getUint16(dataOffset, littleEndian);
31534 // long, 32 bit int:
31536 getValue: function (dataView, dataOffset, littleEndian) {
31537 return dataView.getUint32(dataOffset, littleEndian);
31541 // rational = two long values, first is numerator, second is denominator:
31543 getValue: function (dataView, dataOffset, littleEndian) {
31544 return dataView.getUint32(dataOffset, littleEndian) /
31545 dataView.getUint32(dataOffset + 4, littleEndian);
31549 // slong, 32 bit signed int:
31551 getValue: function (dataView, dataOffset, littleEndian) {
31552 return dataView.getInt32(dataOffset, littleEndian);
31556 // srational, two slongs, first is numerator, second is denominator:
31558 getValue: function (dataView, dataOffset, littleEndian) {
31559 return dataView.getInt32(dataOffset, littleEndian) /
31560 dataView.getInt32(dataOffset + 4, littleEndian);
31570 cls : 'btn-group roo-upload-cropbox-rotate-left',
31571 action : 'rotate-left',
31575 cls : 'btn btn-default',
31576 html : '<i class="fa fa-undo"></i>'
31582 cls : 'btn-group roo-upload-cropbox-picture',
31583 action : 'picture',
31587 cls : 'btn btn-default',
31588 html : '<i class="fa fa-picture-o"></i>'
31594 cls : 'btn-group roo-upload-cropbox-rotate-right',
31595 action : 'rotate-right',
31599 cls : 'btn btn-default',
31600 html : '<i class="fa fa-repeat"></i>'
31608 cls : 'btn-group roo-upload-cropbox-rotate-left',
31609 action : 'rotate-left',
31613 cls : 'btn btn-default',
31614 html : '<i class="fa fa-undo"></i>'
31620 cls : 'btn-group roo-upload-cropbox-download',
31621 action : 'download',
31625 cls : 'btn btn-default',
31626 html : '<i class="fa fa-download"></i>'
31632 cls : 'btn-group roo-upload-cropbox-crop',
31637 cls : 'btn btn-default',
31638 html : '<i class="fa fa-crop"></i>'
31644 cls : 'btn-group roo-upload-cropbox-trash',
31649 cls : 'btn btn-default',
31650 html : '<i class="fa fa-trash"></i>'
31656 cls : 'btn-group roo-upload-cropbox-rotate-right',
31657 action : 'rotate-right',
31661 cls : 'btn btn-default',
31662 html : '<i class="fa fa-repeat"></i>'
31670 cls : 'btn-group roo-upload-cropbox-rotate-left',
31671 action : 'rotate-left',
31675 cls : 'btn btn-default',
31676 html : '<i class="fa fa-undo"></i>'
31682 cls : 'btn-group roo-upload-cropbox-rotate-right',
31683 action : 'rotate-right',
31687 cls : 'btn btn-default',
31688 html : '<i class="fa fa-repeat"></i>'
31701 * @class Roo.bootstrap.DocumentManager
31702 * @extends Roo.bootstrap.Component
31703 * Bootstrap DocumentManager class
31704 * @cfg {String} paramName default 'imageUpload'
31705 * @cfg {String} toolTipName default 'filename'
31706 * @cfg {String} method default POST
31707 * @cfg {String} url action url
31708 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31709 * @cfg {Boolean} multiple multiple upload default true
31710 * @cfg {Number} thumbSize default 300
31711 * @cfg {String} fieldLabel
31712 * @cfg {Number} labelWidth default 4
31713 * @cfg {String} labelAlign (left|top) default left
31714 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31715 * @cfg {Number} labellg set the width of label (1-12)
31716 * @cfg {Number} labelmd set the width of label (1-12)
31717 * @cfg {Number} labelsm set the width of label (1-12)
31718 * @cfg {Number} labelxs set the width of label (1-12)
31721 * Create a new DocumentManager
31722 * @param {Object} config The config object
31725 Roo.bootstrap.DocumentManager = function(config){
31726 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31729 this.delegates = [];
31734 * Fire when initial the DocumentManager
31735 * @param {Roo.bootstrap.DocumentManager} this
31740 * inspect selected file
31741 * @param {Roo.bootstrap.DocumentManager} this
31742 * @param {File} file
31747 * Fire when xhr load exception
31748 * @param {Roo.bootstrap.DocumentManager} this
31749 * @param {XMLHttpRequest} xhr
31751 "exception" : true,
31753 * @event afterupload
31754 * Fire when xhr load exception
31755 * @param {Roo.bootstrap.DocumentManager} this
31756 * @param {XMLHttpRequest} xhr
31758 "afterupload" : true,
31761 * prepare the form data
31762 * @param {Roo.bootstrap.DocumentManager} this
31763 * @param {Object} formData
31768 * Fire when remove the file
31769 * @param {Roo.bootstrap.DocumentManager} this
31770 * @param {Object} file
31775 * Fire after refresh the file
31776 * @param {Roo.bootstrap.DocumentManager} this
31781 * Fire after click the image
31782 * @param {Roo.bootstrap.DocumentManager} this
31783 * @param {Object} file
31788 * Fire when upload a image and editable set to true
31789 * @param {Roo.bootstrap.DocumentManager} this
31790 * @param {Object} file
31794 * @event beforeselectfile
31795 * Fire before select file
31796 * @param {Roo.bootstrap.DocumentManager} this
31798 "beforeselectfile" : true,
31801 * Fire before process file
31802 * @param {Roo.bootstrap.DocumentManager} this
31803 * @param {Object} file
31807 * @event previewrendered
31808 * Fire when preview rendered
31809 * @param {Roo.bootstrap.DocumentManager} this
31810 * @param {Object} file
31812 "previewrendered" : true,
31815 "previewResize" : true
31820 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31829 paramName : 'imageUpload',
31830 toolTipName : 'filename',
31833 labelAlign : 'left',
31843 getAutoCreate : function()
31845 var managerWidget = {
31847 cls : 'roo-document-manager',
31851 cls : 'roo-document-manager-selector',
31856 cls : 'roo-document-manager-uploader',
31860 cls : 'roo-document-manager-upload-btn',
31861 html : '<i class="fa fa-plus"></i>'
31872 cls : 'column col-md-12',
31877 if(this.fieldLabel.length){
31882 cls : 'column col-md-12',
31883 html : this.fieldLabel
31887 cls : 'column col-md-12',
31892 if(this.labelAlign == 'left'){
31897 html : this.fieldLabel
31906 if(this.labelWidth > 12){
31907 content[0].style = "width: " + this.labelWidth + 'px';
31910 if(this.labelWidth < 13 && this.labelmd == 0){
31911 this.labelmd = this.labelWidth;
31914 if(this.labellg > 0){
31915 content[0].cls += ' col-lg-' + this.labellg;
31916 content[1].cls += ' col-lg-' + (12 - this.labellg);
31919 if(this.labelmd > 0){
31920 content[0].cls += ' col-md-' + this.labelmd;
31921 content[1].cls += ' col-md-' + (12 - this.labelmd);
31924 if(this.labelsm > 0){
31925 content[0].cls += ' col-sm-' + this.labelsm;
31926 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31929 if(this.labelxs > 0){
31930 content[0].cls += ' col-xs-' + this.labelxs;
31931 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31939 cls : 'row clearfix',
31947 initEvents : function()
31949 this.managerEl = this.el.select('.roo-document-manager', true).first();
31950 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31952 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31953 this.selectorEl.hide();
31956 this.selectorEl.attr('multiple', 'multiple');
31959 this.selectorEl.on('change', this.onFileSelected, this);
31961 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31962 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31964 this.uploader.on('click', this.onUploaderClick, this);
31966 this.renderProgressDialog();
31970 window.addEventListener("resize", function() { _this.refresh(); } );
31972 this.fireEvent('initial', this);
31975 renderProgressDialog : function()
31979 this.progressDialog = new Roo.bootstrap.Modal({
31980 cls : 'roo-document-manager-progress-dialog',
31981 allow_close : false,
31992 btnclick : function() {
31993 _this.uploadCancel();
31999 this.progressDialog.render(Roo.get(document.body));
32001 this.progress = new Roo.bootstrap.Progress({
32002 cls : 'roo-document-manager-progress',
32007 this.progress.render(this.progressDialog.getChildContainer());
32009 this.progressBar = new Roo.bootstrap.ProgressBar({
32010 cls : 'roo-document-manager-progress-bar',
32013 aria_valuemax : 12,
32017 this.progressBar.render(this.progress.getChildContainer());
32020 onUploaderClick : function(e)
32022 e.preventDefault();
32024 if(this.fireEvent('beforeselectfile', this) != false){
32025 this.selectorEl.dom.click();
32030 onFileSelected : function(e)
32032 e.preventDefault();
32034 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32038 Roo.each(this.selectorEl.dom.files, function(file){
32039 if(this.fireEvent('inspect', this, file) != false){
32040 this.files.push(file);
32050 this.selectorEl.dom.value = '';
32052 if(!this.files || !this.files.length){
32056 if(this.boxes > 0 && this.files.length > this.boxes){
32057 this.files = this.files.slice(0, this.boxes);
32060 this.uploader.show();
32062 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32063 this.uploader.hide();
32072 Roo.each(this.files, function(file){
32074 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32075 var f = this.renderPreview(file);
32080 if(file.type.indexOf('image') != -1){
32081 this.delegates.push(
32083 _this.process(file);
32084 }).createDelegate(this)
32092 _this.process(file);
32093 }).createDelegate(this)
32098 this.files = files;
32100 this.delegates = this.delegates.concat(docs);
32102 if(!this.delegates.length){
32107 this.progressBar.aria_valuemax = this.delegates.length;
32114 arrange : function()
32116 if(!this.delegates.length){
32117 this.progressDialog.hide();
32122 var delegate = this.delegates.shift();
32124 this.progressDialog.show();
32126 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32128 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32133 refresh : function()
32135 this.uploader.show();
32137 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32138 this.uploader.hide();
32141 Roo.isTouch ? this.closable(false) : this.closable(true);
32143 this.fireEvent('refresh', this);
32146 onRemove : function(e, el, o)
32148 e.preventDefault();
32150 this.fireEvent('remove', this, o);
32154 remove : function(o)
32158 Roo.each(this.files, function(file){
32159 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32168 this.files = files;
32175 Roo.each(this.files, function(file){
32180 file.target.remove();
32189 onClick : function(e, el, o)
32191 e.preventDefault();
32193 this.fireEvent('click', this, o);
32197 closable : function(closable)
32199 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32201 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32213 xhrOnLoad : function(xhr)
32215 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32219 if (xhr.readyState !== 4) {
32221 this.fireEvent('exception', this, xhr);
32225 var response = Roo.decode(xhr.responseText);
32227 if(!response.success){
32229 this.fireEvent('exception', this, xhr);
32233 var file = this.renderPreview(response.data);
32235 this.files.push(file);
32239 this.fireEvent('afterupload', this, xhr);
32243 xhrOnError : function(xhr)
32245 Roo.log('xhr on error');
32247 var response = Roo.decode(xhr.responseText);
32254 process : function(file)
32256 if(this.fireEvent('process', this, file) !== false){
32257 if(this.editable && file.type.indexOf('image') != -1){
32258 this.fireEvent('edit', this, file);
32262 this.uploadStart(file, false);
32269 uploadStart : function(file, crop)
32271 this.xhr = new XMLHttpRequest();
32273 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32278 file.xhr = this.xhr;
32280 this.managerEl.createChild({
32282 cls : 'roo-document-manager-loading',
32286 tooltip : file.name,
32287 cls : 'roo-document-manager-thumb',
32288 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32294 this.xhr.open(this.method, this.url, true);
32297 "Accept": "application/json",
32298 "Cache-Control": "no-cache",
32299 "X-Requested-With": "XMLHttpRequest"
32302 for (var headerName in headers) {
32303 var headerValue = headers[headerName];
32305 this.xhr.setRequestHeader(headerName, headerValue);
32311 this.xhr.onload = function()
32313 _this.xhrOnLoad(_this.xhr);
32316 this.xhr.onerror = function()
32318 _this.xhrOnError(_this.xhr);
32321 var formData = new FormData();
32323 formData.append('returnHTML', 'NO');
32326 formData.append('crop', crop);
32329 formData.append(this.paramName, file, file.name);
32336 if(this.fireEvent('prepare', this, formData, options) != false){
32338 if(options.manually){
32342 this.xhr.send(formData);
32346 this.uploadCancel();
32349 uploadCancel : function()
32355 this.delegates = [];
32357 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32364 renderPreview : function(file)
32366 if(typeof(file.target) != 'undefined' && file.target){
32370 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32372 var previewEl = this.managerEl.createChild({
32374 cls : 'roo-document-manager-preview',
32378 tooltip : file[this.toolTipName],
32379 cls : 'roo-document-manager-thumb',
32380 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32385 html : '<i class="fa fa-times-circle"></i>'
32390 var close = previewEl.select('button.close', true).first();
32392 close.on('click', this.onRemove, this, file);
32394 file.target = previewEl;
32396 var image = previewEl.select('img', true).first();
32400 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32402 image.on('click', this.onClick, this, file);
32404 this.fireEvent('previewrendered', this, file);
32410 onPreviewLoad : function(file, image)
32412 if(typeof(file.target) == 'undefined' || !file.target){
32416 var width = image.dom.naturalWidth || image.dom.width;
32417 var height = image.dom.naturalHeight || image.dom.height;
32419 if(!this.previewResize) {
32423 if(width > height){
32424 file.target.addClass('wide');
32428 file.target.addClass('tall');
32433 uploadFromSource : function(file, crop)
32435 this.xhr = new XMLHttpRequest();
32437 this.managerEl.createChild({
32439 cls : 'roo-document-manager-loading',
32443 tooltip : file.name,
32444 cls : 'roo-document-manager-thumb',
32445 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32451 this.xhr.open(this.method, this.url, true);
32454 "Accept": "application/json",
32455 "Cache-Control": "no-cache",
32456 "X-Requested-With": "XMLHttpRequest"
32459 for (var headerName in headers) {
32460 var headerValue = headers[headerName];
32462 this.xhr.setRequestHeader(headerName, headerValue);
32468 this.xhr.onload = function()
32470 _this.xhrOnLoad(_this.xhr);
32473 this.xhr.onerror = function()
32475 _this.xhrOnError(_this.xhr);
32478 var formData = new FormData();
32480 formData.append('returnHTML', 'NO');
32482 formData.append('crop', crop);
32484 if(typeof(file.filename) != 'undefined'){
32485 formData.append('filename', file.filename);
32488 if(typeof(file.mimetype) != 'undefined'){
32489 formData.append('mimetype', file.mimetype);
32494 if(this.fireEvent('prepare', this, formData) != false){
32495 this.xhr.send(formData);
32505 * @class Roo.bootstrap.DocumentViewer
32506 * @extends Roo.bootstrap.Component
32507 * Bootstrap DocumentViewer class
32508 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32509 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32512 * Create a new DocumentViewer
32513 * @param {Object} config The config object
32516 Roo.bootstrap.DocumentViewer = function(config){
32517 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32522 * Fire after initEvent
32523 * @param {Roo.bootstrap.DocumentViewer} this
32529 * @param {Roo.bootstrap.DocumentViewer} this
32534 * Fire after download button
32535 * @param {Roo.bootstrap.DocumentViewer} this
32540 * Fire after trash button
32541 * @param {Roo.bootstrap.DocumentViewer} this
32548 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32550 showDownload : true,
32554 getAutoCreate : function()
32558 cls : 'roo-document-viewer',
32562 cls : 'roo-document-viewer-body',
32566 cls : 'roo-document-viewer-thumb',
32570 cls : 'roo-document-viewer-image'
32578 cls : 'roo-document-viewer-footer',
32581 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32585 cls : 'btn-group roo-document-viewer-download',
32589 cls : 'btn btn-default',
32590 html : '<i class="fa fa-download"></i>'
32596 cls : 'btn-group roo-document-viewer-trash',
32600 cls : 'btn btn-default',
32601 html : '<i class="fa fa-trash"></i>'
32614 initEvents : function()
32616 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32617 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32619 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32620 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32622 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32623 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32625 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32626 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32628 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32629 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32631 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32632 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32634 this.bodyEl.on('click', this.onClick, this);
32635 this.downloadBtn.on('click', this.onDownload, this);
32636 this.trashBtn.on('click', this.onTrash, this);
32638 this.downloadBtn.hide();
32639 this.trashBtn.hide();
32641 if(this.showDownload){
32642 this.downloadBtn.show();
32645 if(this.showTrash){
32646 this.trashBtn.show();
32649 if(!this.showDownload && !this.showTrash) {
32650 this.footerEl.hide();
32655 initial : function()
32657 this.fireEvent('initial', this);
32661 onClick : function(e)
32663 e.preventDefault();
32665 this.fireEvent('click', this);
32668 onDownload : function(e)
32670 e.preventDefault();
32672 this.fireEvent('download', this);
32675 onTrash : function(e)
32677 e.preventDefault();
32679 this.fireEvent('trash', this);
32691 * @class Roo.bootstrap.NavProgressBar
32692 * @extends Roo.bootstrap.Component
32693 * Bootstrap NavProgressBar class
32696 * Create a new nav progress bar
32697 * @param {Object} config The config object
32700 Roo.bootstrap.NavProgressBar = function(config){
32701 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32703 this.bullets = this.bullets || [];
32705 // Roo.bootstrap.NavProgressBar.register(this);
32709 * Fires when the active item changes
32710 * @param {Roo.bootstrap.NavProgressBar} this
32711 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32712 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32719 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32724 getAutoCreate : function()
32726 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32730 cls : 'roo-navigation-bar-group',
32734 cls : 'roo-navigation-top-bar'
32738 cls : 'roo-navigation-bullets-bar',
32742 cls : 'roo-navigation-bar'
32749 cls : 'roo-navigation-bottom-bar'
32759 initEvents: function()
32764 onRender : function(ct, position)
32766 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32768 if(this.bullets.length){
32769 Roo.each(this.bullets, function(b){
32778 addItem : function(cfg)
32780 var item = new Roo.bootstrap.NavProgressItem(cfg);
32782 item.parentId = this.id;
32783 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32786 var top = new Roo.bootstrap.Element({
32788 cls : 'roo-navigation-bar-text'
32791 var bottom = new Roo.bootstrap.Element({
32793 cls : 'roo-navigation-bar-text'
32796 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32797 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32799 var topText = new Roo.bootstrap.Element({
32801 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32804 var bottomText = new Roo.bootstrap.Element({
32806 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32809 topText.onRender(top.el, null);
32810 bottomText.onRender(bottom.el, null);
32813 item.bottomEl = bottom;
32816 this.barItems.push(item);
32821 getActive : function()
32823 var active = false;
32825 Roo.each(this.barItems, function(v){
32827 if (!v.isActive()) {
32839 setActiveItem : function(item)
32843 Roo.each(this.barItems, function(v){
32844 if (v.rid == item.rid) {
32848 if (v.isActive()) {
32849 v.setActive(false);
32854 item.setActive(true);
32856 this.fireEvent('changed', this, item, prev);
32859 getBarItem: function(rid)
32863 Roo.each(this.barItems, function(e) {
32864 if (e.rid != rid) {
32875 indexOfItem : function(item)
32879 Roo.each(this.barItems, function(v, i){
32881 if (v.rid != item.rid) {
32892 setActiveNext : function()
32894 var i = this.indexOfItem(this.getActive());
32896 if (i > this.barItems.length) {
32900 this.setActiveItem(this.barItems[i+1]);
32903 setActivePrev : function()
32905 var i = this.indexOfItem(this.getActive());
32911 this.setActiveItem(this.barItems[i-1]);
32914 format : function()
32916 if(!this.barItems.length){
32920 var width = 100 / this.barItems.length;
32922 Roo.each(this.barItems, function(i){
32923 i.el.setStyle('width', width + '%');
32924 i.topEl.el.setStyle('width', width + '%');
32925 i.bottomEl.el.setStyle('width', width + '%');
32934 * Nav Progress Item
32939 * @class Roo.bootstrap.NavProgressItem
32940 * @extends Roo.bootstrap.Component
32941 * Bootstrap NavProgressItem class
32942 * @cfg {String} rid the reference id
32943 * @cfg {Boolean} active (true|false) Is item active default false
32944 * @cfg {Boolean} disabled (true|false) Is item active default false
32945 * @cfg {String} html
32946 * @cfg {String} position (top|bottom) text position default bottom
32947 * @cfg {String} icon show icon instead of number
32950 * Create a new NavProgressItem
32951 * @param {Object} config The config object
32953 Roo.bootstrap.NavProgressItem = function(config){
32954 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32959 * The raw click event for the entire grid.
32960 * @param {Roo.bootstrap.NavProgressItem} this
32961 * @param {Roo.EventObject} e
32968 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32974 position : 'bottom',
32977 getAutoCreate : function()
32979 var iconCls = 'roo-navigation-bar-item-icon';
32981 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32985 cls: 'roo-navigation-bar-item',
32995 cfg.cls += ' active';
32998 cfg.cls += ' disabled';
33004 disable : function()
33006 this.setDisabled(true);
33009 enable : function()
33011 this.setDisabled(false);
33014 initEvents: function()
33016 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33018 this.iconEl.on('click', this.onClick, this);
33021 onClick : function(e)
33023 e.preventDefault();
33029 if(this.fireEvent('click', this, e) === false){
33033 this.parent().setActiveItem(this);
33036 isActive: function ()
33038 return this.active;
33041 setActive : function(state)
33043 if(this.active == state){
33047 this.active = state;
33050 this.el.addClass('active');
33054 this.el.removeClass('active');
33059 setDisabled : function(state)
33061 if(this.disabled == state){
33065 this.disabled = state;
33068 this.el.addClass('disabled');
33072 this.el.removeClass('disabled');
33075 tooltipEl : function()
33077 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33090 * @class Roo.bootstrap.FieldLabel
33091 * @extends Roo.bootstrap.Component
33092 * Bootstrap FieldLabel class
33093 * @cfg {String} html contents of the element
33094 * @cfg {String} tag tag of the element default label
33095 * @cfg {String} cls class of the element
33096 * @cfg {String} target label target
33097 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33098 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33099 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33100 * @cfg {String} iconTooltip default "This field is required"
33101 * @cfg {String} indicatorpos (left|right) default left
33104 * Create a new FieldLabel
33105 * @param {Object} config The config object
33108 Roo.bootstrap.FieldLabel = function(config){
33109 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33114 * Fires after the field has been marked as invalid.
33115 * @param {Roo.form.FieldLabel} this
33116 * @param {String} msg The validation message
33121 * Fires after the field has been validated with no errors.
33122 * @param {Roo.form.FieldLabel} this
33128 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33135 invalidClass : 'has-warning',
33136 validClass : 'has-success',
33137 iconTooltip : 'This field is required',
33138 indicatorpos : 'left',
33140 getAutoCreate : function(){
33143 if (!this.allowBlank) {
33149 cls : 'roo-bootstrap-field-label ' + this.cls,
33154 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33155 tooltip : this.iconTooltip
33164 if(this.indicatorpos == 'right'){
33167 cls : 'roo-bootstrap-field-label ' + this.cls,
33176 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33177 tooltip : this.iconTooltip
33186 initEvents: function()
33188 Roo.bootstrap.Element.superclass.initEvents.call(this);
33190 this.indicator = this.indicatorEl();
33192 if(this.indicator){
33193 this.indicator.removeClass('visible');
33194 this.indicator.addClass('invisible');
33197 Roo.bootstrap.FieldLabel.register(this);
33200 indicatorEl : function()
33202 var indicator = this.el.select('i.roo-required-indicator',true).first();
33213 * Mark this field as valid
33215 markValid : function()
33217 if(this.indicator){
33218 this.indicator.removeClass('visible');
33219 this.indicator.addClass('invisible');
33221 if (Roo.bootstrap.version == 3) {
33222 this.el.removeClass(this.invalidClass);
33223 this.el.addClass(this.validClass);
33225 this.el.removeClass('is-invalid');
33226 this.el.addClass('is-valid');
33230 this.fireEvent('valid', this);
33234 * Mark this field as invalid
33235 * @param {String} msg The validation message
33237 markInvalid : function(msg)
33239 if(this.indicator){
33240 this.indicator.removeClass('invisible');
33241 this.indicator.addClass('visible');
33243 if (Roo.bootstrap.version == 3) {
33244 this.el.removeClass(this.validClass);
33245 this.el.addClass(this.invalidClass);
33247 this.el.removeClass('is-valid');
33248 this.el.addClass('is-invalid');
33252 this.fireEvent('invalid', this, msg);
33258 Roo.apply(Roo.bootstrap.FieldLabel, {
33263 * register a FieldLabel Group
33264 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33266 register : function(label)
33268 if(this.groups.hasOwnProperty(label.target)){
33272 this.groups[label.target] = label;
33276 * fetch a FieldLabel Group based on the target
33277 * @param {string} target
33278 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33280 get: function(target) {
33281 if (typeof(this.groups[target]) == 'undefined') {
33285 return this.groups[target] ;
33294 * page DateSplitField.
33300 * @class Roo.bootstrap.DateSplitField
33301 * @extends Roo.bootstrap.Component
33302 * Bootstrap DateSplitField class
33303 * @cfg {string} fieldLabel - the label associated
33304 * @cfg {Number} labelWidth set the width of label (0-12)
33305 * @cfg {String} labelAlign (top|left)
33306 * @cfg {Boolean} dayAllowBlank (true|false) default false
33307 * @cfg {Boolean} monthAllowBlank (true|false) default false
33308 * @cfg {Boolean} yearAllowBlank (true|false) default false
33309 * @cfg {string} dayPlaceholder
33310 * @cfg {string} monthPlaceholder
33311 * @cfg {string} yearPlaceholder
33312 * @cfg {string} dayFormat default 'd'
33313 * @cfg {string} monthFormat default 'm'
33314 * @cfg {string} yearFormat default 'Y'
33315 * @cfg {Number} labellg set the width of label (1-12)
33316 * @cfg {Number} labelmd set the width of label (1-12)
33317 * @cfg {Number} labelsm set the width of label (1-12)
33318 * @cfg {Number} labelxs set the width of label (1-12)
33322 * Create a new DateSplitField
33323 * @param {Object} config The config object
33326 Roo.bootstrap.DateSplitField = function(config){
33327 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33333 * getting the data of years
33334 * @param {Roo.bootstrap.DateSplitField} this
33335 * @param {Object} years
33340 * getting the data of days
33341 * @param {Roo.bootstrap.DateSplitField} this
33342 * @param {Object} days
33347 * Fires after the field has been marked as invalid.
33348 * @param {Roo.form.Field} this
33349 * @param {String} msg The validation message
33354 * Fires after the field has been validated with no errors.
33355 * @param {Roo.form.Field} this
33361 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33364 labelAlign : 'top',
33366 dayAllowBlank : false,
33367 monthAllowBlank : false,
33368 yearAllowBlank : false,
33369 dayPlaceholder : '',
33370 monthPlaceholder : '',
33371 yearPlaceholder : '',
33375 isFormField : true,
33381 getAutoCreate : function()
33385 cls : 'row roo-date-split-field-group',
33390 cls : 'form-hidden-field roo-date-split-field-group-value',
33396 var labelCls = 'col-md-12';
33397 var contentCls = 'col-md-4';
33399 if(this.fieldLabel){
33403 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33407 html : this.fieldLabel
33412 if(this.labelAlign == 'left'){
33414 if(this.labelWidth > 12){
33415 label.style = "width: " + this.labelWidth + 'px';
33418 if(this.labelWidth < 13 && this.labelmd == 0){
33419 this.labelmd = this.labelWidth;
33422 if(this.labellg > 0){
33423 labelCls = ' col-lg-' + this.labellg;
33424 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33427 if(this.labelmd > 0){
33428 labelCls = ' col-md-' + this.labelmd;
33429 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33432 if(this.labelsm > 0){
33433 labelCls = ' col-sm-' + this.labelsm;
33434 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33437 if(this.labelxs > 0){
33438 labelCls = ' col-xs-' + this.labelxs;
33439 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33443 label.cls += ' ' + labelCls;
33445 cfg.cn.push(label);
33448 Roo.each(['day', 'month', 'year'], function(t){
33451 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33458 inputEl: function ()
33460 return this.el.select('.roo-date-split-field-group-value', true).first();
33463 onRender : function(ct, position)
33467 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33469 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33471 this.dayField = new Roo.bootstrap.ComboBox({
33472 allowBlank : this.dayAllowBlank,
33473 alwaysQuery : true,
33474 displayField : 'value',
33477 forceSelection : true,
33479 placeholder : this.dayPlaceholder,
33480 selectOnFocus : true,
33481 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33482 triggerAction : 'all',
33484 valueField : 'value',
33485 store : new Roo.data.SimpleStore({
33486 data : (function() {
33488 _this.fireEvent('days', _this, days);
33491 fields : [ 'value' ]
33494 select : function (_self, record, index)
33496 _this.setValue(_this.getValue());
33501 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33503 this.monthField = new Roo.bootstrap.MonthField({
33504 after : '<i class=\"fa fa-calendar\"></i>',
33505 allowBlank : this.monthAllowBlank,
33506 placeholder : this.monthPlaceholder,
33509 render : function (_self)
33511 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33512 e.preventDefault();
33516 select : function (_self, oldvalue, newvalue)
33518 _this.setValue(_this.getValue());
33523 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33525 this.yearField = new Roo.bootstrap.ComboBox({
33526 allowBlank : this.yearAllowBlank,
33527 alwaysQuery : true,
33528 displayField : 'value',
33531 forceSelection : true,
33533 placeholder : this.yearPlaceholder,
33534 selectOnFocus : true,
33535 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33536 triggerAction : 'all',
33538 valueField : 'value',
33539 store : new Roo.data.SimpleStore({
33540 data : (function() {
33542 _this.fireEvent('years', _this, years);
33545 fields : [ 'value' ]
33548 select : function (_self, record, index)
33550 _this.setValue(_this.getValue());
33555 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33558 setValue : function(v, format)
33560 this.inputEl.dom.value = v;
33562 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33564 var d = Date.parseDate(v, f);
33571 this.setDay(d.format(this.dayFormat));
33572 this.setMonth(d.format(this.monthFormat));
33573 this.setYear(d.format(this.yearFormat));
33580 setDay : function(v)
33582 this.dayField.setValue(v);
33583 this.inputEl.dom.value = this.getValue();
33588 setMonth : function(v)
33590 this.monthField.setValue(v, true);
33591 this.inputEl.dom.value = this.getValue();
33596 setYear : function(v)
33598 this.yearField.setValue(v);
33599 this.inputEl.dom.value = this.getValue();
33604 getDay : function()
33606 return this.dayField.getValue();
33609 getMonth : function()
33611 return this.monthField.getValue();
33614 getYear : function()
33616 return this.yearField.getValue();
33619 getValue : function()
33621 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33623 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33633 this.inputEl.dom.value = '';
33638 validate : function()
33640 var d = this.dayField.validate();
33641 var m = this.monthField.validate();
33642 var y = this.yearField.validate();
33647 (!this.dayAllowBlank && !d) ||
33648 (!this.monthAllowBlank && !m) ||
33649 (!this.yearAllowBlank && !y)
33654 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33663 this.markInvalid();
33668 markValid : function()
33671 var label = this.el.select('label', true).first();
33672 var icon = this.el.select('i.fa-star', true).first();
33678 this.fireEvent('valid', this);
33682 * Mark this field as invalid
33683 * @param {String} msg The validation message
33685 markInvalid : function(msg)
33688 var label = this.el.select('label', true).first();
33689 var icon = this.el.select('i.fa-star', true).first();
33691 if(label && !icon){
33692 this.el.select('.roo-date-split-field-label', true).createChild({
33694 cls : 'text-danger fa fa-lg fa-star',
33695 tooltip : 'This field is required',
33696 style : 'margin-right:5px;'
33700 this.fireEvent('invalid', this, msg);
33703 clearInvalid : function()
33705 var label = this.el.select('label', true).first();
33706 var icon = this.el.select('i.fa-star', true).first();
33712 this.fireEvent('valid', this);
33715 getName: function()
33725 * http://masonry.desandro.com
33727 * The idea is to render all the bricks based on vertical width...
33729 * The original code extends 'outlayer' - we might need to use that....
33735 * @class Roo.bootstrap.LayoutMasonry
33736 * @extends Roo.bootstrap.Component
33737 * Bootstrap Layout Masonry class
33740 * Create a new Element
33741 * @param {Object} config The config object
33744 Roo.bootstrap.LayoutMasonry = function(config){
33746 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33750 Roo.bootstrap.LayoutMasonry.register(this);
33756 * Fire after layout the items
33757 * @param {Roo.bootstrap.LayoutMasonry} this
33758 * @param {Roo.EventObject} e
33765 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33768 * @cfg {Boolean} isLayoutInstant = no animation?
33770 isLayoutInstant : false, // needed?
33773 * @cfg {Number} boxWidth width of the columns
33778 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33783 * @cfg {Number} padWidth padding below box..
33788 * @cfg {Number} gutter gutter width..
33793 * @cfg {Number} maxCols maximum number of columns
33799 * @cfg {Boolean} isAutoInitial defalut true
33801 isAutoInitial : true,
33806 * @cfg {Boolean} isHorizontal defalut false
33808 isHorizontal : false,
33810 currentSize : null,
33816 bricks: null, //CompositeElement
33820 _isLayoutInited : false,
33822 // isAlternative : false, // only use for vertical layout...
33825 * @cfg {Number} alternativePadWidth padding below box..
33827 alternativePadWidth : 50,
33829 selectedBrick : [],
33831 getAutoCreate : function(){
33833 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33837 cls: 'blog-masonary-wrapper ' + this.cls,
33839 cls : 'mas-boxes masonary'
33846 getChildContainer: function( )
33848 if (this.boxesEl) {
33849 return this.boxesEl;
33852 this.boxesEl = this.el.select('.mas-boxes').first();
33854 return this.boxesEl;
33858 initEvents : function()
33862 if(this.isAutoInitial){
33863 Roo.log('hook children rendered');
33864 this.on('childrenrendered', function() {
33865 Roo.log('children rendered');
33871 initial : function()
33873 this.selectedBrick = [];
33875 this.currentSize = this.el.getBox(true);
33877 Roo.EventManager.onWindowResize(this.resize, this);
33879 if(!this.isAutoInitial){
33887 //this.layout.defer(500,this);
33891 resize : function()
33893 var cs = this.el.getBox(true);
33896 this.currentSize.width == cs.width &&
33897 this.currentSize.x == cs.x &&
33898 this.currentSize.height == cs.height &&
33899 this.currentSize.y == cs.y
33901 Roo.log("no change in with or X or Y");
33905 this.currentSize = cs;
33911 layout : function()
33913 this._resetLayout();
33915 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33917 this.layoutItems( isInstant );
33919 this._isLayoutInited = true;
33921 this.fireEvent('layout', this);
33925 _resetLayout : function()
33927 if(this.isHorizontal){
33928 this.horizontalMeasureColumns();
33932 this.verticalMeasureColumns();
33936 verticalMeasureColumns : function()
33938 this.getContainerWidth();
33940 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33941 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33945 var boxWidth = this.boxWidth + this.padWidth;
33947 if(this.containerWidth < this.boxWidth){
33948 boxWidth = this.containerWidth
33951 var containerWidth = this.containerWidth;
33953 var cols = Math.floor(containerWidth / boxWidth);
33955 this.cols = Math.max( cols, 1 );
33957 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33959 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33961 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33963 this.colWidth = boxWidth + avail - this.padWidth;
33965 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33966 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33969 horizontalMeasureColumns : function()
33971 this.getContainerWidth();
33973 var boxWidth = this.boxWidth;
33975 if(this.containerWidth < boxWidth){
33976 boxWidth = this.containerWidth;
33979 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33981 this.el.setHeight(boxWidth);
33985 getContainerWidth : function()
33987 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33990 layoutItems : function( isInstant )
33992 Roo.log(this.bricks);
33994 var items = Roo.apply([], this.bricks);
33996 if(this.isHorizontal){
33997 this._horizontalLayoutItems( items , isInstant );
34001 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34002 // this._verticalAlternativeLayoutItems( items , isInstant );
34006 this._verticalLayoutItems( items , isInstant );
34010 _verticalLayoutItems : function ( items , isInstant)
34012 if ( !items || !items.length ) {
34017 ['xs', 'xs', 'xs', 'tall'],
34018 ['xs', 'xs', 'tall'],
34019 ['xs', 'xs', 'sm'],
34020 ['xs', 'xs', 'xs'],
34026 ['sm', 'xs', 'xs'],
34030 ['tall', 'xs', 'xs', 'xs'],
34031 ['tall', 'xs', 'xs'],
34043 Roo.each(items, function(item, k){
34045 switch (item.size) {
34046 // these layouts take up a full box,
34057 boxes.push([item]);
34080 var filterPattern = function(box, length)
34088 var pattern = box.slice(0, length);
34092 Roo.each(pattern, function(i){
34093 format.push(i.size);
34096 Roo.each(standard, function(s){
34098 if(String(s) != String(format)){
34107 if(!match && length == 1){
34112 filterPattern(box, length - 1);
34116 queue.push(pattern);
34118 box = box.slice(length, box.length);
34120 filterPattern(box, 4);
34126 Roo.each(boxes, function(box, k){
34132 if(box.length == 1){
34137 filterPattern(box, 4);
34141 this._processVerticalLayoutQueue( queue, isInstant );
34145 // _verticalAlternativeLayoutItems : function( items , isInstant )
34147 // if ( !items || !items.length ) {
34151 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34155 _horizontalLayoutItems : function ( items , isInstant)
34157 if ( !items || !items.length || items.length < 3) {
34163 var eItems = items.slice(0, 3);
34165 items = items.slice(3, items.length);
34168 ['xs', 'xs', 'xs', 'wide'],
34169 ['xs', 'xs', 'wide'],
34170 ['xs', 'xs', 'sm'],
34171 ['xs', 'xs', 'xs'],
34177 ['sm', 'xs', 'xs'],
34181 ['wide', 'xs', 'xs', 'xs'],
34182 ['wide', 'xs', 'xs'],
34195 Roo.each(items, function(item, k){
34197 switch (item.size) {
34208 boxes.push([item]);
34232 var filterPattern = function(box, length)
34240 var pattern = box.slice(0, length);
34244 Roo.each(pattern, function(i){
34245 format.push(i.size);
34248 Roo.each(standard, function(s){
34250 if(String(s) != String(format)){
34259 if(!match && length == 1){
34264 filterPattern(box, length - 1);
34268 queue.push(pattern);
34270 box = box.slice(length, box.length);
34272 filterPattern(box, 4);
34278 Roo.each(boxes, function(box, k){
34284 if(box.length == 1){
34289 filterPattern(box, 4);
34296 var pos = this.el.getBox(true);
34300 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34302 var hit_end = false;
34304 Roo.each(queue, function(box){
34308 Roo.each(box, function(b){
34310 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34320 Roo.each(box, function(b){
34322 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34325 mx = Math.max(mx, b.x);
34329 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34333 Roo.each(box, function(b){
34335 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34349 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34352 /** Sets position of item in DOM
34353 * @param {Element} item
34354 * @param {Number} x - horizontal position
34355 * @param {Number} y - vertical position
34356 * @param {Boolean} isInstant - disables transitions
34358 _processVerticalLayoutQueue : function( queue, isInstant )
34360 var pos = this.el.getBox(true);
34365 for (var i = 0; i < this.cols; i++){
34369 Roo.each(queue, function(box, k){
34371 var col = k % this.cols;
34373 Roo.each(box, function(b,kk){
34375 b.el.position('absolute');
34377 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34378 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34380 if(b.size == 'md-left' || b.size == 'md-right'){
34381 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34382 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34385 b.el.setWidth(width);
34386 b.el.setHeight(height);
34388 b.el.select('iframe',true).setSize(width,height);
34392 for (var i = 0; i < this.cols; i++){
34394 if(maxY[i] < maxY[col]){
34399 col = Math.min(col, i);
34403 x = pos.x + col * (this.colWidth + this.padWidth);
34407 var positions = [];
34409 switch (box.length){
34411 positions = this.getVerticalOneBoxColPositions(x, y, box);
34414 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34417 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34420 positions = this.getVerticalFourBoxColPositions(x, y, box);
34426 Roo.each(box, function(b,kk){
34428 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34430 var sz = b.el.getSize();
34432 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34440 for (var i = 0; i < this.cols; i++){
34441 mY = Math.max(mY, maxY[i]);
34444 this.el.setHeight(mY - pos.y);
34448 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34450 // var pos = this.el.getBox(true);
34453 // var maxX = pos.right;
34455 // var maxHeight = 0;
34457 // Roo.each(items, function(item, k){
34461 // item.el.position('absolute');
34463 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34465 // item.el.setWidth(width);
34467 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34469 // item.el.setHeight(height);
34472 // item.el.setXY([x, y], isInstant ? false : true);
34474 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34477 // y = y + height + this.alternativePadWidth;
34479 // maxHeight = maxHeight + height + this.alternativePadWidth;
34483 // this.el.setHeight(maxHeight);
34487 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34489 var pos = this.el.getBox(true);
34494 var maxX = pos.right;
34496 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34498 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34500 Roo.each(queue, function(box, k){
34502 Roo.each(box, function(b, kk){
34504 b.el.position('absolute');
34506 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34507 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34509 if(b.size == 'md-left' || b.size == 'md-right'){
34510 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34511 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34514 b.el.setWidth(width);
34515 b.el.setHeight(height);
34523 var positions = [];
34525 switch (box.length){
34527 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34530 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34533 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34536 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34542 Roo.each(box, function(b,kk){
34544 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34546 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34554 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34556 Roo.each(eItems, function(b,k){
34558 b.size = (k == 0) ? 'sm' : 'xs';
34559 b.x = (k == 0) ? 2 : 1;
34560 b.y = (k == 0) ? 2 : 1;
34562 b.el.position('absolute');
34564 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34566 b.el.setWidth(width);
34568 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34570 b.el.setHeight(height);
34574 var positions = [];
34577 x : maxX - this.unitWidth * 2 - this.gutter,
34582 x : maxX - this.unitWidth,
34583 y : minY + (this.unitWidth + this.gutter) * 2
34587 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34591 Roo.each(eItems, function(b,k){
34593 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34599 getVerticalOneBoxColPositions : function(x, y, box)
34603 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34605 if(box[0].size == 'md-left'){
34609 if(box[0].size == 'md-right'){
34614 x : x + (this.unitWidth + this.gutter) * rand,
34621 getVerticalTwoBoxColPositions : function(x, y, box)
34625 if(box[0].size == 'xs'){
34629 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34633 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34647 x : x + (this.unitWidth + this.gutter) * 2,
34648 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34655 getVerticalThreeBoxColPositions : function(x, y, box)
34659 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34667 x : x + (this.unitWidth + this.gutter) * 1,
34672 x : x + (this.unitWidth + this.gutter) * 2,
34680 if(box[0].size == 'xs' && box[1].size == 'xs'){
34689 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34693 x : x + (this.unitWidth + this.gutter) * 1,
34707 x : x + (this.unitWidth + this.gutter) * 2,
34712 x : x + (this.unitWidth + this.gutter) * 2,
34713 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34720 getVerticalFourBoxColPositions : function(x, y, box)
34724 if(box[0].size == 'xs'){
34733 y : y + (this.unitHeight + this.gutter) * 1
34738 y : y + (this.unitHeight + this.gutter) * 2
34742 x : x + (this.unitWidth + this.gutter) * 1,
34756 x : x + (this.unitWidth + this.gutter) * 2,
34761 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34762 y : y + (this.unitHeight + this.gutter) * 1
34766 x : x + (this.unitWidth + this.gutter) * 2,
34767 y : y + (this.unitWidth + this.gutter) * 2
34774 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34778 if(box[0].size == 'md-left'){
34780 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34787 if(box[0].size == 'md-right'){
34789 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34790 y : minY + (this.unitWidth + this.gutter) * 1
34796 var rand = Math.floor(Math.random() * (4 - box[0].y));
34799 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34800 y : minY + (this.unitWidth + this.gutter) * rand
34807 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34811 if(box[0].size == 'xs'){
34814 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34819 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34820 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34828 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34833 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34834 y : minY + (this.unitWidth + this.gutter) * 2
34841 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34845 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34848 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34853 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34854 y : minY + (this.unitWidth + this.gutter) * 1
34858 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34859 y : minY + (this.unitWidth + this.gutter) * 2
34866 if(box[0].size == 'xs' && box[1].size == 'xs'){
34869 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34874 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34879 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34880 y : minY + (this.unitWidth + this.gutter) * 1
34888 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34893 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34894 y : minY + (this.unitWidth + this.gutter) * 2
34898 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34899 y : minY + (this.unitWidth + this.gutter) * 2
34906 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34910 if(box[0].size == 'xs'){
34913 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34918 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34923 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),
34928 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34929 y : minY + (this.unitWidth + this.gutter) * 1
34937 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34942 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34943 y : minY + (this.unitWidth + this.gutter) * 2
34947 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34948 y : minY + (this.unitWidth + this.gutter) * 2
34952 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),
34953 y : minY + (this.unitWidth + this.gutter) * 2
34961 * remove a Masonry Brick
34962 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34964 removeBrick : function(brick_id)
34970 for (var i = 0; i<this.bricks.length; i++) {
34971 if (this.bricks[i].id == brick_id) {
34972 this.bricks.splice(i,1);
34973 this.el.dom.removeChild(Roo.get(brick_id).dom);
34980 * adds a Masonry Brick
34981 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34983 addBrick : function(cfg)
34985 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34986 //this.register(cn);
34987 cn.parentId = this.id;
34988 cn.render(this.el);
34993 * register a Masonry Brick
34994 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34997 register : function(brick)
34999 this.bricks.push(brick);
35000 brick.masonryId = this.id;
35004 * clear all the Masonry Brick
35006 clearAll : function()
35009 //this.getChildContainer().dom.innerHTML = "";
35010 this.el.dom.innerHTML = '';
35013 getSelected : function()
35015 if (!this.selectedBrick) {
35019 return this.selectedBrick;
35023 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35027 * register a Masonry Layout
35028 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35031 register : function(layout)
35033 this.groups[layout.id] = layout;
35036 * fetch a Masonry Layout based on the masonry layout ID
35037 * @param {string} the masonry layout to add
35038 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35041 get: function(layout_id) {
35042 if (typeof(this.groups[layout_id]) == 'undefined') {
35045 return this.groups[layout_id] ;
35057 * http://masonry.desandro.com
35059 * The idea is to render all the bricks based on vertical width...
35061 * The original code extends 'outlayer' - we might need to use that....
35067 * @class Roo.bootstrap.LayoutMasonryAuto
35068 * @extends Roo.bootstrap.Component
35069 * Bootstrap Layout Masonry class
35072 * Create a new Element
35073 * @param {Object} config The config object
35076 Roo.bootstrap.LayoutMasonryAuto = function(config){
35077 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35080 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35083 * @cfg {Boolean} isFitWidth - resize the width..
35085 isFitWidth : false, // options..
35087 * @cfg {Boolean} isOriginLeft = left align?
35089 isOriginLeft : true,
35091 * @cfg {Boolean} isOriginTop = top align?
35093 isOriginTop : false,
35095 * @cfg {Boolean} isLayoutInstant = no animation?
35097 isLayoutInstant : false, // needed?
35099 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35101 isResizingContainer : true,
35103 * @cfg {Number} columnWidth width of the columns
35109 * @cfg {Number} maxCols maximum number of columns
35114 * @cfg {Number} padHeight padding below box..
35120 * @cfg {Boolean} isAutoInitial defalut true
35123 isAutoInitial : true,
35129 initialColumnWidth : 0,
35130 currentSize : null,
35132 colYs : null, // array.
35139 bricks: null, //CompositeElement
35140 cols : 0, // array?
35141 // element : null, // wrapped now this.el
35142 _isLayoutInited : null,
35145 getAutoCreate : function(){
35149 cls: 'blog-masonary-wrapper ' + this.cls,
35151 cls : 'mas-boxes masonary'
35158 getChildContainer: function( )
35160 if (this.boxesEl) {
35161 return this.boxesEl;
35164 this.boxesEl = this.el.select('.mas-boxes').first();
35166 return this.boxesEl;
35170 initEvents : function()
35174 if(this.isAutoInitial){
35175 Roo.log('hook children rendered');
35176 this.on('childrenrendered', function() {
35177 Roo.log('children rendered');
35184 initial : function()
35186 this.reloadItems();
35188 this.currentSize = this.el.getBox(true);
35190 /// was window resize... - let's see if this works..
35191 Roo.EventManager.onWindowResize(this.resize, this);
35193 if(!this.isAutoInitial){
35198 this.layout.defer(500,this);
35201 reloadItems: function()
35203 this.bricks = this.el.select('.masonry-brick', true);
35205 this.bricks.each(function(b) {
35206 //Roo.log(b.getSize());
35207 if (!b.attr('originalwidth')) {
35208 b.attr('originalwidth', b.getSize().width);
35213 Roo.log(this.bricks.elements.length);
35216 resize : function()
35219 var cs = this.el.getBox(true);
35221 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35222 Roo.log("no change in with or X");
35225 this.currentSize = cs;
35229 layout : function()
35232 this._resetLayout();
35233 //this._manageStamps();
35235 // don't animate first layout
35236 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35237 this.layoutItems( isInstant );
35239 // flag for initalized
35240 this._isLayoutInited = true;
35243 layoutItems : function( isInstant )
35245 //var items = this._getItemsForLayout( this.items );
35246 // original code supports filtering layout items.. we just ignore it..
35248 this._layoutItems( this.bricks , isInstant );
35250 this._postLayout();
35252 _layoutItems : function ( items , isInstant)
35254 //this.fireEvent( 'layout', this, items );
35257 if ( !items || !items.elements.length ) {
35258 // no items, emit event with empty array
35263 items.each(function(item) {
35264 Roo.log("layout item");
35266 // get x/y object from method
35267 var position = this._getItemLayoutPosition( item );
35269 position.item = item;
35270 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35271 queue.push( position );
35274 this._processLayoutQueue( queue );
35276 /** Sets position of item in DOM
35277 * @param {Element} item
35278 * @param {Number} x - horizontal position
35279 * @param {Number} y - vertical position
35280 * @param {Boolean} isInstant - disables transitions
35282 _processLayoutQueue : function( queue )
35284 for ( var i=0, len = queue.length; i < len; i++ ) {
35285 var obj = queue[i];
35286 obj.item.position('absolute');
35287 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35293 * Any logic you want to do after each layout,
35294 * i.e. size the container
35296 _postLayout : function()
35298 this.resizeContainer();
35301 resizeContainer : function()
35303 if ( !this.isResizingContainer ) {
35306 var size = this._getContainerSize();
35308 this.el.setSize(size.width,size.height);
35309 this.boxesEl.setSize(size.width,size.height);
35315 _resetLayout : function()
35317 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35318 this.colWidth = this.el.getWidth();
35319 //this.gutter = this.el.getWidth();
35321 this.measureColumns();
35327 this.colYs.push( 0 );
35333 measureColumns : function()
35335 this.getContainerWidth();
35336 // if columnWidth is 0, default to outerWidth of first item
35337 if ( !this.columnWidth ) {
35338 var firstItem = this.bricks.first();
35339 Roo.log(firstItem);
35340 this.columnWidth = this.containerWidth;
35341 if (firstItem && firstItem.attr('originalwidth') ) {
35342 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35344 // columnWidth fall back to item of first element
35345 Roo.log("set column width?");
35346 this.initialColumnWidth = this.columnWidth ;
35348 // if first elem has no width, default to size of container
35353 if (this.initialColumnWidth) {
35354 this.columnWidth = this.initialColumnWidth;
35359 // column width is fixed at the top - however if container width get's smaller we should
35362 // this bit calcs how man columns..
35364 var columnWidth = this.columnWidth += this.gutter;
35366 // calculate columns
35367 var containerWidth = this.containerWidth + this.gutter;
35369 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35370 // fix rounding errors, typically with gutters
35371 var excess = columnWidth - containerWidth % columnWidth;
35374 // if overshoot is less than a pixel, round up, otherwise floor it
35375 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35376 cols = Math[ mathMethod ]( cols );
35377 this.cols = Math.max( cols, 1 );
35378 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35380 // padding positioning..
35381 var totalColWidth = this.cols * this.columnWidth;
35382 var padavail = this.containerWidth - totalColWidth;
35383 // so for 2 columns - we need 3 'pads'
35385 var padNeeded = (1+this.cols) * this.padWidth;
35387 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35389 this.columnWidth += padExtra
35390 //this.padWidth = Math.floor(padavail / ( this.cols));
35392 // adjust colum width so that padding is fixed??
35394 // we have 3 columns ... total = width * 3
35395 // we have X left over... that should be used by
35397 //if (this.expandC) {
35405 getContainerWidth : function()
35407 /* // container is parent if fit width
35408 var container = this.isFitWidth ? this.element.parentNode : this.element;
35409 // check that this.size and size are there
35410 // IE8 triggers resize on body size change, so they might not be
35412 var size = getSize( container ); //FIXME
35413 this.containerWidth = size && size.innerWidth; //FIXME
35416 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35420 _getItemLayoutPosition : function( item ) // what is item?
35422 // we resize the item to our columnWidth..
35424 item.setWidth(this.columnWidth);
35425 item.autoBoxAdjust = false;
35427 var sz = item.getSize();
35429 // how many columns does this brick span
35430 var remainder = this.containerWidth % this.columnWidth;
35432 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35433 // round if off by 1 pixel, otherwise use ceil
35434 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35435 colSpan = Math.min( colSpan, this.cols );
35437 // normally this should be '1' as we dont' currently allow multi width columns..
35439 var colGroup = this._getColGroup( colSpan );
35440 // get the minimum Y value from the columns
35441 var minimumY = Math.min.apply( Math, colGroup );
35442 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35444 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35446 // position the brick
35448 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35449 y: this.currentSize.y + minimumY + this.padHeight
35453 // apply setHeight to necessary columns
35454 var setHeight = minimumY + sz.height + this.padHeight;
35455 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35457 var setSpan = this.cols + 1 - colGroup.length;
35458 for ( var i = 0; i < setSpan; i++ ) {
35459 this.colYs[ shortColIndex + i ] = setHeight ;
35466 * @param {Number} colSpan - number of columns the element spans
35467 * @returns {Array} colGroup
35469 _getColGroup : function( colSpan )
35471 if ( colSpan < 2 ) {
35472 // if brick spans only one column, use all the column Ys
35477 // how many different places could this brick fit horizontally
35478 var groupCount = this.cols + 1 - colSpan;
35479 // for each group potential horizontal position
35480 for ( var i = 0; i < groupCount; i++ ) {
35481 // make an array of colY values for that one group
35482 var groupColYs = this.colYs.slice( i, i + colSpan );
35483 // and get the max value of the array
35484 colGroup[i] = Math.max.apply( Math, groupColYs );
35489 _manageStamp : function( stamp )
35491 var stampSize = stamp.getSize();
35492 var offset = stamp.getBox();
35493 // get the columns that this stamp affects
35494 var firstX = this.isOriginLeft ? offset.x : offset.right;
35495 var lastX = firstX + stampSize.width;
35496 var firstCol = Math.floor( firstX / this.columnWidth );
35497 firstCol = Math.max( 0, firstCol );
35499 var lastCol = Math.floor( lastX / this.columnWidth );
35500 // lastCol should not go over if multiple of columnWidth #425
35501 lastCol -= lastX % this.columnWidth ? 0 : 1;
35502 lastCol = Math.min( this.cols - 1, lastCol );
35504 // set colYs to bottom of the stamp
35505 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35508 for ( var i = firstCol; i <= lastCol; i++ ) {
35509 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35514 _getContainerSize : function()
35516 this.maxY = Math.max.apply( Math, this.colYs );
35521 if ( this.isFitWidth ) {
35522 size.width = this._getContainerFitWidth();
35528 _getContainerFitWidth : function()
35530 var unusedCols = 0;
35531 // count unused columns
35534 if ( this.colYs[i] !== 0 ) {
35539 // fit container to columns that have been used
35540 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35543 needsResizeLayout : function()
35545 var previousWidth = this.containerWidth;
35546 this.getContainerWidth();
35547 return previousWidth !== this.containerWidth;
35562 * @class Roo.bootstrap.MasonryBrick
35563 * @extends Roo.bootstrap.Component
35564 * Bootstrap MasonryBrick class
35567 * Create a new MasonryBrick
35568 * @param {Object} config The config object
35571 Roo.bootstrap.MasonryBrick = function(config){
35573 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35575 Roo.bootstrap.MasonryBrick.register(this);
35581 * When a MasonryBrick is clcik
35582 * @param {Roo.bootstrap.MasonryBrick} this
35583 * @param {Roo.EventObject} e
35589 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35592 * @cfg {String} title
35596 * @cfg {String} html
35600 * @cfg {String} bgimage
35604 * @cfg {String} videourl
35608 * @cfg {String} cls
35612 * @cfg {String} href
35616 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35621 * @cfg {String} placetitle (center|bottom)
35626 * @cfg {Boolean} isFitContainer defalut true
35628 isFitContainer : true,
35631 * @cfg {Boolean} preventDefault defalut false
35633 preventDefault : false,
35636 * @cfg {Boolean} inverse defalut false
35638 maskInverse : false,
35640 getAutoCreate : function()
35642 if(!this.isFitContainer){
35643 return this.getSplitAutoCreate();
35646 var cls = 'masonry-brick masonry-brick-full';
35648 if(this.href.length){
35649 cls += ' masonry-brick-link';
35652 if(this.bgimage.length){
35653 cls += ' masonry-brick-image';
35656 if(this.maskInverse){
35657 cls += ' mask-inverse';
35660 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35661 cls += ' enable-mask';
35665 cls += ' masonry-' + this.size + '-brick';
35668 if(this.placetitle.length){
35670 switch (this.placetitle) {
35672 cls += ' masonry-center-title';
35675 cls += ' masonry-bottom-title';
35682 if(!this.html.length && !this.bgimage.length){
35683 cls += ' masonry-center-title';
35686 if(!this.html.length && this.bgimage.length){
35687 cls += ' masonry-bottom-title';
35692 cls += ' ' + this.cls;
35696 tag: (this.href.length) ? 'a' : 'div',
35701 cls: 'masonry-brick-mask'
35705 cls: 'masonry-brick-paragraph',
35711 if(this.href.length){
35712 cfg.href = this.href;
35715 var cn = cfg.cn[1].cn;
35717 if(this.title.length){
35720 cls: 'masonry-brick-title',
35725 if(this.html.length){
35728 cls: 'masonry-brick-text',
35733 if (!this.title.length && !this.html.length) {
35734 cfg.cn[1].cls += ' hide';
35737 if(this.bgimage.length){
35740 cls: 'masonry-brick-image-view',
35745 if(this.videourl.length){
35746 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35747 // youtube support only?
35750 cls: 'masonry-brick-image-view',
35753 allowfullscreen : true
35761 getSplitAutoCreate : function()
35763 var cls = 'masonry-brick masonry-brick-split';
35765 if(this.href.length){
35766 cls += ' masonry-brick-link';
35769 if(this.bgimage.length){
35770 cls += ' masonry-brick-image';
35774 cls += ' masonry-' + this.size + '-brick';
35777 switch (this.placetitle) {
35779 cls += ' masonry-center-title';
35782 cls += ' masonry-bottom-title';
35785 if(!this.bgimage.length){
35786 cls += ' masonry-center-title';
35789 if(this.bgimage.length){
35790 cls += ' masonry-bottom-title';
35796 cls += ' ' + this.cls;
35800 tag: (this.href.length) ? 'a' : 'div',
35805 cls: 'masonry-brick-split-head',
35809 cls: 'masonry-brick-paragraph',
35816 cls: 'masonry-brick-split-body',
35822 if(this.href.length){
35823 cfg.href = this.href;
35826 if(this.title.length){
35827 cfg.cn[0].cn[0].cn.push({
35829 cls: 'masonry-brick-title',
35834 if(this.html.length){
35835 cfg.cn[1].cn.push({
35837 cls: 'masonry-brick-text',
35842 if(this.bgimage.length){
35843 cfg.cn[0].cn.push({
35845 cls: 'masonry-brick-image-view',
35850 if(this.videourl.length){
35851 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35852 // youtube support only?
35853 cfg.cn[0].cn.cn.push({
35855 cls: 'masonry-brick-image-view',
35858 allowfullscreen : true
35865 initEvents: function()
35867 switch (this.size) {
35900 this.el.on('touchstart', this.onTouchStart, this);
35901 this.el.on('touchmove', this.onTouchMove, this);
35902 this.el.on('touchend', this.onTouchEnd, this);
35903 this.el.on('contextmenu', this.onContextMenu, this);
35905 this.el.on('mouseenter' ,this.enter, this);
35906 this.el.on('mouseleave', this.leave, this);
35907 this.el.on('click', this.onClick, this);
35910 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35911 this.parent().bricks.push(this);
35916 onClick: function(e, el)
35918 var time = this.endTimer - this.startTimer;
35919 // Roo.log(e.preventDefault());
35922 e.preventDefault();
35927 if(!this.preventDefault){
35931 e.preventDefault();
35933 if (this.activeClass != '') {
35934 this.selectBrick();
35937 this.fireEvent('click', this, e);
35940 enter: function(e, el)
35942 e.preventDefault();
35944 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35948 if(this.bgimage.length && this.html.length){
35949 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35953 leave: function(e, el)
35955 e.preventDefault();
35957 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35961 if(this.bgimage.length && this.html.length){
35962 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35966 onTouchStart: function(e, el)
35968 // e.preventDefault();
35970 this.touchmoved = false;
35972 if(!this.isFitContainer){
35976 if(!this.bgimage.length || !this.html.length){
35980 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35982 this.timer = new Date().getTime();
35986 onTouchMove: function(e, el)
35988 this.touchmoved = true;
35991 onContextMenu : function(e,el)
35993 e.preventDefault();
35994 e.stopPropagation();
35998 onTouchEnd: function(e, el)
36000 // e.preventDefault();
36002 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36009 if(!this.bgimage.length || !this.html.length){
36011 if(this.href.length){
36012 window.location.href = this.href;
36018 if(!this.isFitContainer){
36022 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36024 window.location.href = this.href;
36027 //selection on single brick only
36028 selectBrick : function() {
36030 if (!this.parentId) {
36034 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36035 var index = m.selectedBrick.indexOf(this.id);
36038 m.selectedBrick.splice(index,1);
36039 this.el.removeClass(this.activeClass);
36043 for(var i = 0; i < m.selectedBrick.length; i++) {
36044 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36045 b.el.removeClass(b.activeClass);
36048 m.selectedBrick = [];
36050 m.selectedBrick.push(this.id);
36051 this.el.addClass(this.activeClass);
36055 isSelected : function(){
36056 return this.el.hasClass(this.activeClass);
36061 Roo.apply(Roo.bootstrap.MasonryBrick, {
36064 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36066 * register a Masonry Brick
36067 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36070 register : function(brick)
36072 //this.groups[brick.id] = brick;
36073 this.groups.add(brick.id, brick);
36076 * fetch a masonry brick based on the masonry brick ID
36077 * @param {string} the masonry brick to add
36078 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36081 get: function(brick_id)
36083 // if (typeof(this.groups[brick_id]) == 'undefined') {
36086 // return this.groups[brick_id] ;
36088 if(this.groups.key(brick_id)) {
36089 return this.groups.key(brick_id);
36107 * @class Roo.bootstrap.Brick
36108 * @extends Roo.bootstrap.Component
36109 * Bootstrap Brick class
36112 * Create a new Brick
36113 * @param {Object} config The config object
36116 Roo.bootstrap.Brick = function(config){
36117 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36123 * When a Brick is click
36124 * @param {Roo.bootstrap.Brick} this
36125 * @param {Roo.EventObject} e
36131 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36134 * @cfg {String} title
36138 * @cfg {String} html
36142 * @cfg {String} bgimage
36146 * @cfg {String} cls
36150 * @cfg {String} href
36154 * @cfg {String} video
36158 * @cfg {Boolean} square
36162 getAutoCreate : function()
36164 var cls = 'roo-brick';
36166 if(this.href.length){
36167 cls += ' roo-brick-link';
36170 if(this.bgimage.length){
36171 cls += ' roo-brick-image';
36174 if(!this.html.length && !this.bgimage.length){
36175 cls += ' roo-brick-center-title';
36178 if(!this.html.length && this.bgimage.length){
36179 cls += ' roo-brick-bottom-title';
36183 cls += ' ' + this.cls;
36187 tag: (this.href.length) ? 'a' : 'div',
36192 cls: 'roo-brick-paragraph',
36198 if(this.href.length){
36199 cfg.href = this.href;
36202 var cn = cfg.cn[0].cn;
36204 if(this.title.length){
36207 cls: 'roo-brick-title',
36212 if(this.html.length){
36215 cls: 'roo-brick-text',
36222 if(this.bgimage.length){
36225 cls: 'roo-brick-image-view',
36233 initEvents: function()
36235 if(this.title.length || this.html.length){
36236 this.el.on('mouseenter' ,this.enter, this);
36237 this.el.on('mouseleave', this.leave, this);
36240 Roo.EventManager.onWindowResize(this.resize, this);
36242 if(this.bgimage.length){
36243 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36244 this.imageEl.on('load', this.onImageLoad, this);
36251 onImageLoad : function()
36256 resize : function()
36258 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36260 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36262 if(this.bgimage.length){
36263 var image = this.el.select('.roo-brick-image-view', true).first();
36265 image.setWidth(paragraph.getWidth());
36268 image.setHeight(paragraph.getWidth());
36271 this.el.setHeight(image.getHeight());
36272 paragraph.setHeight(image.getHeight());
36278 enter: function(e, el)
36280 e.preventDefault();
36282 if(this.bgimage.length){
36283 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36284 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36288 leave: function(e, el)
36290 e.preventDefault();
36292 if(this.bgimage.length){
36293 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36294 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36309 * @class Roo.bootstrap.NumberField
36310 * @extends Roo.bootstrap.Input
36311 * Bootstrap NumberField class
36317 * Create a new NumberField
36318 * @param {Object} config The config object
36321 Roo.bootstrap.NumberField = function(config){
36322 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36325 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36328 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36330 allowDecimals : true,
36332 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36334 decimalSeparator : ".",
36336 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36338 decimalPrecision : 2,
36340 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36342 allowNegative : true,
36345 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36349 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36351 minValue : Number.NEGATIVE_INFINITY,
36353 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36355 maxValue : Number.MAX_VALUE,
36357 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36359 minText : "The minimum value for this field is {0}",
36361 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36363 maxText : "The maximum value for this field is {0}",
36365 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36366 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36368 nanText : "{0} is not a valid number",
36370 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36372 thousandsDelimiter : false,
36374 * @cfg {String} valueAlign alignment of value
36376 valueAlign : "left",
36378 getAutoCreate : function()
36380 var hiddenInput = {
36384 cls: 'hidden-number-input'
36388 hiddenInput.name = this.name;
36393 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36395 this.name = hiddenInput.name;
36397 if(cfg.cn.length > 0) {
36398 cfg.cn.push(hiddenInput);
36405 initEvents : function()
36407 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36409 var allowed = "0123456789";
36411 if(this.allowDecimals){
36412 allowed += this.decimalSeparator;
36415 if(this.allowNegative){
36419 if(this.thousandsDelimiter) {
36423 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36425 var keyPress = function(e){
36427 var k = e.getKey();
36429 var c = e.getCharCode();
36432 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36433 allowed.indexOf(String.fromCharCode(c)) === -1
36439 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36443 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36448 this.el.on("keypress", keyPress, this);
36451 validateValue : function(value)
36454 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36458 var num = this.parseValue(value);
36461 this.markInvalid(String.format(this.nanText, value));
36465 if(num < this.minValue){
36466 this.markInvalid(String.format(this.minText, this.minValue));
36470 if(num > this.maxValue){
36471 this.markInvalid(String.format(this.maxText, this.maxValue));
36478 getValue : function()
36480 var v = this.hiddenEl().getValue();
36482 return this.fixPrecision(this.parseValue(v));
36485 parseValue : function(value)
36487 if(this.thousandsDelimiter) {
36489 r = new RegExp(",", "g");
36490 value = value.replace(r, "");
36493 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36494 return isNaN(value) ? '' : value;
36497 fixPrecision : function(value)
36499 if(this.thousandsDelimiter) {
36501 r = new RegExp(",", "g");
36502 value = value.replace(r, "");
36505 var nan = isNaN(value);
36507 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36508 return nan ? '' : value;
36510 return parseFloat(value).toFixed(this.decimalPrecision);
36513 setValue : function(v)
36515 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36521 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36523 this.inputEl().dom.value = (v == '') ? '' :
36524 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36526 if(!this.allowZero && v === '0') {
36527 this.hiddenEl().dom.value = '';
36528 this.inputEl().dom.value = '';
36535 decimalPrecisionFcn : function(v)
36537 return Math.floor(v);
36540 beforeBlur : function()
36542 var v = this.parseValue(this.getRawValue());
36544 if(v || v === 0 || v === ''){
36549 hiddenEl : function()
36551 return this.el.select('input.hidden-number-input',true).first();
36563 * @class Roo.bootstrap.DocumentSlider
36564 * @extends Roo.bootstrap.Component
36565 * Bootstrap DocumentSlider class
36568 * Create a new DocumentViewer
36569 * @param {Object} config The config object
36572 Roo.bootstrap.DocumentSlider = function(config){
36573 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36580 * Fire after initEvent
36581 * @param {Roo.bootstrap.DocumentSlider} this
36586 * Fire after update
36587 * @param {Roo.bootstrap.DocumentSlider} this
36593 * @param {Roo.bootstrap.DocumentSlider} this
36599 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36605 getAutoCreate : function()
36609 cls : 'roo-document-slider',
36613 cls : 'roo-document-slider-header',
36617 cls : 'roo-document-slider-header-title'
36623 cls : 'roo-document-slider-body',
36627 cls : 'roo-document-slider-prev',
36631 cls : 'fa fa-chevron-left'
36637 cls : 'roo-document-slider-thumb',
36641 cls : 'roo-document-slider-image'
36647 cls : 'roo-document-slider-next',
36651 cls : 'fa fa-chevron-right'
36663 initEvents : function()
36665 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36666 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36668 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36669 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36671 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36672 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36674 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36675 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36677 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36678 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36680 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36681 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36683 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36684 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36686 this.thumbEl.on('click', this.onClick, this);
36688 this.prevIndicator.on('click', this.prev, this);
36690 this.nextIndicator.on('click', this.next, this);
36694 initial : function()
36696 if(this.files.length){
36697 this.indicator = 1;
36701 this.fireEvent('initial', this);
36704 update : function()
36706 this.imageEl.attr('src', this.files[this.indicator - 1]);
36708 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36710 this.prevIndicator.show();
36712 if(this.indicator == 1){
36713 this.prevIndicator.hide();
36716 this.nextIndicator.show();
36718 if(this.indicator == this.files.length){
36719 this.nextIndicator.hide();
36722 this.thumbEl.scrollTo('top');
36724 this.fireEvent('update', this);
36727 onClick : function(e)
36729 e.preventDefault();
36731 this.fireEvent('click', this);
36736 e.preventDefault();
36738 this.indicator = Math.max(1, this.indicator - 1);
36745 e.preventDefault();
36747 this.indicator = Math.min(this.files.length, this.indicator + 1);
36761 * @class Roo.bootstrap.RadioSet
36762 * @extends Roo.bootstrap.Input
36763 * Bootstrap RadioSet class
36764 * @cfg {String} indicatorpos (left|right) default left
36765 * @cfg {Boolean} inline (true|false) inline the element (default true)
36766 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36768 * Create a new RadioSet
36769 * @param {Object} config The config object
36772 Roo.bootstrap.RadioSet = function(config){
36774 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36778 Roo.bootstrap.RadioSet.register(this);
36783 * Fires when the element is checked or unchecked.
36784 * @param {Roo.bootstrap.RadioSet} this This radio
36785 * @param {Roo.bootstrap.Radio} item The checked item
36790 * Fires when the element is click.
36791 * @param {Roo.bootstrap.RadioSet} this This radio set
36792 * @param {Roo.bootstrap.Radio} item The checked item
36793 * @param {Roo.EventObject} e The event object
36800 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36808 indicatorpos : 'left',
36810 getAutoCreate : function()
36814 cls : 'roo-radio-set-label',
36818 html : this.fieldLabel
36822 if (Roo.bootstrap.version == 3) {
36825 if(this.indicatorpos == 'left'){
36828 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36829 tooltip : 'This field is required'
36834 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36835 tooltip : 'This field is required'
36841 cls : 'roo-radio-set-items'
36844 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36846 if (align === 'left' && this.fieldLabel.length) {
36849 cls : "roo-radio-set-right",
36855 if(this.labelWidth > 12){
36856 label.style = "width: " + this.labelWidth + 'px';
36859 if(this.labelWidth < 13 && this.labelmd == 0){
36860 this.labelmd = this.labelWidth;
36863 if(this.labellg > 0){
36864 label.cls += ' col-lg-' + this.labellg;
36865 items.cls += ' col-lg-' + (12 - this.labellg);
36868 if(this.labelmd > 0){
36869 label.cls += ' col-md-' + this.labelmd;
36870 items.cls += ' col-md-' + (12 - this.labelmd);
36873 if(this.labelsm > 0){
36874 label.cls += ' col-sm-' + this.labelsm;
36875 items.cls += ' col-sm-' + (12 - this.labelsm);
36878 if(this.labelxs > 0){
36879 label.cls += ' col-xs-' + this.labelxs;
36880 items.cls += ' col-xs-' + (12 - this.labelxs);
36886 cls : 'roo-radio-set',
36890 cls : 'roo-radio-set-input',
36893 value : this.value ? this.value : ''
36900 if(this.weight.length){
36901 cfg.cls += ' roo-radio-' + this.weight;
36905 cfg.cls += ' roo-radio-set-inline';
36909 ['xs','sm','md','lg'].map(function(size){
36910 if (settings[size]) {
36911 cfg.cls += ' col-' + size + '-' + settings[size];
36919 initEvents : function()
36921 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36922 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36924 if(!this.fieldLabel.length){
36925 this.labelEl.hide();
36928 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36929 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36931 this.indicator = this.indicatorEl();
36933 if(this.indicator){
36934 this.indicator.addClass('invisible');
36937 this.originalValue = this.getValue();
36941 inputEl: function ()
36943 return this.el.select('.roo-radio-set-input', true).first();
36946 getChildContainer : function()
36948 return this.itemsEl;
36951 register : function(item)
36953 this.radioes.push(item);
36957 validate : function()
36959 if(this.getVisibilityEl().hasClass('hidden')){
36965 Roo.each(this.radioes, function(i){
36974 if(this.allowBlank) {
36978 if(this.disabled || valid){
36983 this.markInvalid();
36988 markValid : function()
36990 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36991 this.indicatorEl().removeClass('visible');
36992 this.indicatorEl().addClass('invisible');
36996 if (Roo.bootstrap.version == 3) {
36997 this.el.removeClass([this.invalidClass, this.validClass]);
36998 this.el.addClass(this.validClass);
37000 this.el.removeClass(['is-invalid','is-valid']);
37001 this.el.addClass(['is-valid']);
37003 this.fireEvent('valid', this);
37006 markInvalid : function(msg)
37008 if(this.allowBlank || this.disabled){
37012 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37013 this.indicatorEl().removeClass('invisible');
37014 this.indicatorEl().addClass('visible');
37016 if (Roo.bootstrap.version == 3) {
37017 this.el.removeClass([this.invalidClass, this.validClass]);
37018 this.el.addClass(this.invalidClass);
37020 this.el.removeClass(['is-invalid','is-valid']);
37021 this.el.addClass(['is-invalid']);
37024 this.fireEvent('invalid', this, msg);
37028 setValue : function(v, suppressEvent)
37030 if(this.value === v){
37037 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37040 Roo.each(this.radioes, function(i){
37042 i.el.removeClass('checked');
37045 Roo.each(this.radioes, function(i){
37047 if(i.value === v || i.value.toString() === v.toString()){
37049 i.el.addClass('checked');
37051 if(suppressEvent !== true){
37052 this.fireEvent('check', this, i);
37063 clearInvalid : function(){
37065 if(!this.el || this.preventMark){
37069 this.el.removeClass([this.invalidClass]);
37071 this.fireEvent('valid', this);
37076 Roo.apply(Roo.bootstrap.RadioSet, {
37080 register : function(set)
37082 this.groups[set.name] = set;
37085 get: function(name)
37087 if (typeof(this.groups[name]) == 'undefined') {
37091 return this.groups[name] ;
37097 * Ext JS Library 1.1.1
37098 * Copyright(c) 2006-2007, Ext JS, LLC.
37100 * Originally Released Under LGPL - original licence link has changed is not relivant.
37103 * <script type="text/javascript">
37108 * @class Roo.bootstrap.SplitBar
37109 * @extends Roo.util.Observable
37110 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37114 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37115 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37116 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37117 split.minSize = 100;
37118 split.maxSize = 600;
37119 split.animate = true;
37120 split.on('moved', splitterMoved);
37123 * Create a new SplitBar
37124 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37125 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37126 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37127 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37128 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37129 position of the SplitBar).
37131 Roo.bootstrap.SplitBar = function(cfg){
37136 // dragElement : elm
37137 // resizingElement: el,
37139 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37140 // placement : Roo.bootstrap.SplitBar.LEFT ,
37141 // existingProxy ???
37144 this.el = Roo.get(cfg.dragElement, true);
37145 this.el.dom.unselectable = "on";
37147 this.resizingEl = Roo.get(cfg.resizingElement, true);
37151 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37152 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37155 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37158 * The minimum size of the resizing element. (Defaults to 0)
37164 * The maximum size of the resizing element. (Defaults to 2000)
37167 this.maxSize = 2000;
37170 * Whether to animate the transition to the new size
37173 this.animate = false;
37176 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37179 this.useShim = false;
37184 if(!cfg.existingProxy){
37186 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37188 this.proxy = Roo.get(cfg.existingProxy).dom;
37191 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37194 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37197 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37200 this.dragSpecs = {};
37203 * @private The adapter to use to positon and resize elements
37205 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37206 this.adapter.init(this);
37208 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37210 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37211 this.el.addClass("roo-splitbar-h");
37214 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37215 this.el.addClass("roo-splitbar-v");
37221 * Fires when the splitter is moved (alias for {@link #event-moved})
37222 * @param {Roo.bootstrap.SplitBar} this
37223 * @param {Number} newSize the new width or height
37228 * Fires when the splitter is moved
37229 * @param {Roo.bootstrap.SplitBar} this
37230 * @param {Number} newSize the new width or height
37234 * @event beforeresize
37235 * Fires before the splitter is dragged
37236 * @param {Roo.bootstrap.SplitBar} this
37238 "beforeresize" : true,
37240 "beforeapply" : true
37243 Roo.util.Observable.call(this);
37246 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37247 onStartProxyDrag : function(x, y){
37248 this.fireEvent("beforeresize", this);
37250 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37252 o.enableDisplayMode("block");
37253 // all splitbars share the same overlay
37254 Roo.bootstrap.SplitBar.prototype.overlay = o;
37256 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37257 this.overlay.show();
37258 Roo.get(this.proxy).setDisplayed("block");
37259 var size = this.adapter.getElementSize(this);
37260 this.activeMinSize = this.getMinimumSize();;
37261 this.activeMaxSize = this.getMaximumSize();;
37262 var c1 = size - this.activeMinSize;
37263 var c2 = Math.max(this.activeMaxSize - size, 0);
37264 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37265 this.dd.resetConstraints();
37266 this.dd.setXConstraint(
37267 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37268 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37270 this.dd.setYConstraint(0, 0);
37272 this.dd.resetConstraints();
37273 this.dd.setXConstraint(0, 0);
37274 this.dd.setYConstraint(
37275 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37276 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37279 this.dragSpecs.startSize = size;
37280 this.dragSpecs.startPoint = [x, y];
37281 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37285 * @private Called after the drag operation by the DDProxy
37287 onEndProxyDrag : function(e){
37288 Roo.get(this.proxy).setDisplayed(false);
37289 var endPoint = Roo.lib.Event.getXY(e);
37291 this.overlay.hide();
37294 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37295 newSize = this.dragSpecs.startSize +
37296 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37297 endPoint[0] - this.dragSpecs.startPoint[0] :
37298 this.dragSpecs.startPoint[0] - endPoint[0]
37301 newSize = this.dragSpecs.startSize +
37302 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37303 endPoint[1] - this.dragSpecs.startPoint[1] :
37304 this.dragSpecs.startPoint[1] - endPoint[1]
37307 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37308 if(newSize != this.dragSpecs.startSize){
37309 if(this.fireEvent('beforeapply', this, newSize) !== false){
37310 this.adapter.setElementSize(this, newSize);
37311 this.fireEvent("moved", this, newSize);
37312 this.fireEvent("resize", this, newSize);
37318 * Get the adapter this SplitBar uses
37319 * @return The adapter object
37321 getAdapter : function(){
37322 return this.adapter;
37326 * Set the adapter this SplitBar uses
37327 * @param {Object} adapter A SplitBar adapter object
37329 setAdapter : function(adapter){
37330 this.adapter = adapter;
37331 this.adapter.init(this);
37335 * Gets the minimum size for the resizing element
37336 * @return {Number} The minimum size
37338 getMinimumSize : function(){
37339 return this.minSize;
37343 * Sets the minimum size for the resizing element
37344 * @param {Number} minSize The minimum size
37346 setMinimumSize : function(minSize){
37347 this.minSize = minSize;
37351 * Gets the maximum size for the resizing element
37352 * @return {Number} The maximum size
37354 getMaximumSize : function(){
37355 return this.maxSize;
37359 * Sets the maximum size for the resizing element
37360 * @param {Number} maxSize The maximum size
37362 setMaximumSize : function(maxSize){
37363 this.maxSize = maxSize;
37367 * Sets the initialize size for the resizing element
37368 * @param {Number} size The initial size
37370 setCurrentSize : function(size){
37371 var oldAnimate = this.animate;
37372 this.animate = false;
37373 this.adapter.setElementSize(this, size);
37374 this.animate = oldAnimate;
37378 * Destroy this splitbar.
37379 * @param {Boolean} removeEl True to remove the element
37381 destroy : function(removeEl){
37383 this.shim.remove();
37386 this.proxy.parentNode.removeChild(this.proxy);
37394 * @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.
37396 Roo.bootstrap.SplitBar.createProxy = function(dir){
37397 var proxy = new Roo.Element(document.createElement("div"));
37398 proxy.unselectable();
37399 var cls = 'roo-splitbar-proxy';
37400 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37401 document.body.appendChild(proxy.dom);
37406 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37407 * Default Adapter. It assumes the splitter and resizing element are not positioned
37408 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37410 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37413 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37414 // do nothing for now
37415 init : function(s){
37419 * Called before drag operations to get the current size of the resizing element.
37420 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37422 getElementSize : function(s){
37423 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37424 return s.resizingEl.getWidth();
37426 return s.resizingEl.getHeight();
37431 * Called after drag operations to set the size of the resizing element.
37432 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37433 * @param {Number} newSize The new size to set
37434 * @param {Function} onComplete A function to be invoked when resizing is complete
37436 setElementSize : function(s, newSize, onComplete){
37437 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37439 s.resizingEl.setWidth(newSize);
37441 onComplete(s, newSize);
37444 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37449 s.resizingEl.setHeight(newSize);
37451 onComplete(s, newSize);
37454 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37461 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37462 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37463 * Adapter that moves the splitter element to align with the resized sizing element.
37464 * Used with an absolute positioned SplitBar.
37465 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37466 * document.body, make sure you assign an id to the body element.
37468 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37469 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37470 this.container = Roo.get(container);
37473 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37474 init : function(s){
37475 this.basic.init(s);
37478 getElementSize : function(s){
37479 return this.basic.getElementSize(s);
37482 setElementSize : function(s, newSize, onComplete){
37483 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37486 moveSplitter : function(s){
37487 var yes = Roo.bootstrap.SplitBar;
37488 switch(s.placement){
37490 s.el.setX(s.resizingEl.getRight());
37493 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37496 s.el.setY(s.resizingEl.getBottom());
37499 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37506 * Orientation constant - Create a vertical SplitBar
37510 Roo.bootstrap.SplitBar.VERTICAL = 1;
37513 * Orientation constant - Create a horizontal SplitBar
37517 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37520 * Placement constant - The resizing element is to the left of the splitter element
37524 Roo.bootstrap.SplitBar.LEFT = 1;
37527 * Placement constant - The resizing element is to the right of the splitter element
37531 Roo.bootstrap.SplitBar.RIGHT = 2;
37534 * Placement constant - The resizing element is positioned above the splitter element
37538 Roo.bootstrap.SplitBar.TOP = 3;
37541 * Placement constant - The resizing element is positioned under splitter element
37545 Roo.bootstrap.SplitBar.BOTTOM = 4;
37546 Roo.namespace("Roo.bootstrap.layout");/*
37548 * Ext JS Library 1.1.1
37549 * Copyright(c) 2006-2007, Ext JS, LLC.
37551 * Originally Released Under LGPL - original licence link has changed is not relivant.
37554 * <script type="text/javascript">
37558 * @class Roo.bootstrap.layout.Manager
37559 * @extends Roo.bootstrap.Component
37560 * Base class for layout managers.
37562 Roo.bootstrap.layout.Manager = function(config)
37564 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37570 /** false to disable window resize monitoring @type Boolean */
37571 this.monitorWindowResize = true;
37576 * Fires when a layout is performed.
37577 * @param {Roo.LayoutManager} this
37581 * @event regionresized
37582 * Fires when the user resizes a region.
37583 * @param {Roo.LayoutRegion} region The resized region
37584 * @param {Number} newSize The new size (width for east/west, height for north/south)
37586 "regionresized" : true,
37588 * @event regioncollapsed
37589 * Fires when a region is collapsed.
37590 * @param {Roo.LayoutRegion} region The collapsed region
37592 "regioncollapsed" : true,
37594 * @event regionexpanded
37595 * Fires when a region is expanded.
37596 * @param {Roo.LayoutRegion} region The expanded region
37598 "regionexpanded" : true
37600 this.updating = false;
37603 this.el = Roo.get(config.el);
37609 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37614 monitorWindowResize : true,
37620 onRender : function(ct, position)
37623 this.el = Roo.get(ct);
37626 //this.fireEvent('render',this);
37630 initEvents: function()
37634 // ie scrollbar fix
37635 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37636 document.body.scroll = "no";
37637 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37638 this.el.position('relative');
37640 this.id = this.el.id;
37641 this.el.addClass("roo-layout-container");
37642 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37643 if(this.el.dom != document.body ) {
37644 this.el.on('resize', this.layout,this);
37645 this.el.on('show', this.layout,this);
37651 * Returns true if this layout is currently being updated
37652 * @return {Boolean}
37654 isUpdating : function(){
37655 return this.updating;
37659 * Suspend the LayoutManager from doing auto-layouts while
37660 * making multiple add or remove calls
37662 beginUpdate : function(){
37663 this.updating = true;
37667 * Restore auto-layouts and optionally disable the manager from performing a layout
37668 * @param {Boolean} noLayout true to disable a layout update
37670 endUpdate : function(noLayout){
37671 this.updating = false;
37677 layout: function(){
37681 onRegionResized : function(region, newSize){
37682 this.fireEvent("regionresized", region, newSize);
37686 onRegionCollapsed : function(region){
37687 this.fireEvent("regioncollapsed", region);
37690 onRegionExpanded : function(region){
37691 this.fireEvent("regionexpanded", region);
37695 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37696 * performs box-model adjustments.
37697 * @return {Object} The size as an object {width: (the width), height: (the height)}
37699 getViewSize : function()
37702 if(this.el.dom != document.body){
37703 size = this.el.getSize();
37705 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37707 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37708 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37713 * Returns the Element this layout is bound to.
37714 * @return {Roo.Element}
37716 getEl : function(){
37721 * Returns the specified region.
37722 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37723 * @return {Roo.LayoutRegion}
37725 getRegion : function(target){
37726 return this.regions[target.toLowerCase()];
37729 onWindowResize : function(){
37730 if(this.monitorWindowResize){
37737 * Ext JS Library 1.1.1
37738 * Copyright(c) 2006-2007, Ext JS, LLC.
37740 * Originally Released Under LGPL - original licence link has changed is not relivant.
37743 * <script type="text/javascript">
37746 * @class Roo.bootstrap.layout.Border
37747 * @extends Roo.bootstrap.layout.Manager
37748 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37749 * please see: examples/bootstrap/nested.html<br><br>
37751 <b>The container the layout is rendered into can be either the body element or any other element.
37752 If it is not the body element, the container needs to either be an absolute positioned element,
37753 or you will need to add "position:relative" to the css of the container. You will also need to specify
37754 the container size if it is not the body element.</b>
37757 * Create a new Border
37758 * @param {Object} config Configuration options
37760 Roo.bootstrap.layout.Border = function(config){
37761 config = config || {};
37762 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37766 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37767 if(config[region]){
37768 config[region].region = region;
37769 this.addRegion(config[region]);
37775 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37777 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37779 parent : false, // this might point to a 'nest' or a ???
37782 * Creates and adds a new region if it doesn't already exist.
37783 * @param {String} target The target region key (north, south, east, west or center).
37784 * @param {Object} config The regions config object
37785 * @return {BorderLayoutRegion} The new region
37787 addRegion : function(config)
37789 if(!this.regions[config.region]){
37790 var r = this.factory(config);
37791 this.bindRegion(r);
37793 return this.regions[config.region];
37797 bindRegion : function(r){
37798 this.regions[r.config.region] = r;
37800 r.on("visibilitychange", this.layout, this);
37801 r.on("paneladded", this.layout, this);
37802 r.on("panelremoved", this.layout, this);
37803 r.on("invalidated", this.layout, this);
37804 r.on("resized", this.onRegionResized, this);
37805 r.on("collapsed", this.onRegionCollapsed, this);
37806 r.on("expanded", this.onRegionExpanded, this);
37810 * Performs a layout update.
37812 layout : function()
37814 if(this.updating) {
37818 // render all the rebions if they have not been done alreayd?
37819 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37820 if(this.regions[region] && !this.regions[region].bodyEl){
37821 this.regions[region].onRender(this.el)
37825 var size = this.getViewSize();
37826 var w = size.width;
37827 var h = size.height;
37832 //var x = 0, y = 0;
37834 var rs = this.regions;
37835 var north = rs["north"];
37836 var south = rs["south"];
37837 var west = rs["west"];
37838 var east = rs["east"];
37839 var center = rs["center"];
37840 //if(this.hideOnLayout){ // not supported anymore
37841 //c.el.setStyle("display", "none");
37843 if(north && north.isVisible()){
37844 var b = north.getBox();
37845 var m = north.getMargins();
37846 b.width = w - (m.left+m.right);
37849 centerY = b.height + b.y + m.bottom;
37850 centerH -= centerY;
37851 north.updateBox(this.safeBox(b));
37853 if(south && south.isVisible()){
37854 var b = south.getBox();
37855 var m = south.getMargins();
37856 b.width = w - (m.left+m.right);
37858 var totalHeight = (b.height + m.top + m.bottom);
37859 b.y = h - totalHeight + m.top;
37860 centerH -= totalHeight;
37861 south.updateBox(this.safeBox(b));
37863 if(west && west.isVisible()){
37864 var b = west.getBox();
37865 var m = west.getMargins();
37866 b.height = centerH - (m.top+m.bottom);
37868 b.y = centerY + m.top;
37869 var totalWidth = (b.width + m.left + m.right);
37870 centerX += totalWidth;
37871 centerW -= totalWidth;
37872 west.updateBox(this.safeBox(b));
37874 if(east && east.isVisible()){
37875 var b = east.getBox();
37876 var m = east.getMargins();
37877 b.height = centerH - (m.top+m.bottom);
37878 var totalWidth = (b.width + m.left + m.right);
37879 b.x = w - totalWidth + m.left;
37880 b.y = centerY + m.top;
37881 centerW -= totalWidth;
37882 east.updateBox(this.safeBox(b));
37885 var m = center.getMargins();
37887 x: centerX + m.left,
37888 y: centerY + m.top,
37889 width: centerW - (m.left+m.right),
37890 height: centerH - (m.top+m.bottom)
37892 //if(this.hideOnLayout){
37893 //center.el.setStyle("display", "block");
37895 center.updateBox(this.safeBox(centerBox));
37898 this.fireEvent("layout", this);
37902 safeBox : function(box){
37903 box.width = Math.max(0, box.width);
37904 box.height = Math.max(0, box.height);
37909 * Adds a ContentPanel (or subclass) to this layout.
37910 * @param {String} target The target region key (north, south, east, west or center).
37911 * @param {Roo.ContentPanel} panel The panel to add
37912 * @return {Roo.ContentPanel} The added panel
37914 add : function(target, panel){
37916 target = target.toLowerCase();
37917 return this.regions[target].add(panel);
37921 * Remove a ContentPanel (or subclass) to this layout.
37922 * @param {String} target The target region key (north, south, east, west or center).
37923 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37924 * @return {Roo.ContentPanel} The removed panel
37926 remove : function(target, panel){
37927 target = target.toLowerCase();
37928 return this.regions[target].remove(panel);
37932 * Searches all regions for a panel with the specified id
37933 * @param {String} panelId
37934 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37936 findPanel : function(panelId){
37937 var rs = this.regions;
37938 for(var target in rs){
37939 if(typeof rs[target] != "function"){
37940 var p = rs[target].getPanel(panelId);
37950 * Searches all regions for a panel with the specified id and activates (shows) it.
37951 * @param {String/ContentPanel} panelId The panels id or the panel itself
37952 * @return {Roo.ContentPanel} The shown panel or null
37954 showPanel : function(panelId) {
37955 var rs = this.regions;
37956 for(var target in rs){
37957 var r = rs[target];
37958 if(typeof r != "function"){
37959 if(r.hasPanel(panelId)){
37960 return r.showPanel(panelId);
37968 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37969 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37972 restoreState : function(provider){
37974 provider = Roo.state.Manager;
37976 var sm = new Roo.LayoutStateManager();
37977 sm.init(this, provider);
37983 * Adds a xtype elements to the layout.
37987 xtype : 'ContentPanel',
37994 xtype : 'NestedLayoutPanel',
38000 items : [ ... list of content panels or nested layout panels.. ]
38004 * @param {Object} cfg Xtype definition of item to add.
38006 addxtype : function(cfg)
38008 // basically accepts a pannel...
38009 // can accept a layout region..!?!?
38010 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38013 // theory? children can only be panels??
38015 //if (!cfg.xtype.match(/Panel$/)) {
38020 if (typeof(cfg.region) == 'undefined') {
38021 Roo.log("Failed to add Panel, region was not set");
38025 var region = cfg.region;
38031 xitems = cfg.items;
38036 if ( region == 'center') {
38037 Roo.log("Center: " + cfg.title);
38043 case 'Content': // ContentPanel (el, cfg)
38044 case 'Scroll': // ContentPanel (el, cfg)
38046 cfg.autoCreate = cfg.autoCreate || true;
38047 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38049 // var el = this.el.createChild();
38050 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38053 this.add(region, ret);
38057 case 'TreePanel': // our new panel!
38058 cfg.el = this.el.createChild();
38059 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38060 this.add(region, ret);
38065 // create a new Layout (which is a Border Layout...
38067 var clayout = cfg.layout;
38068 clayout.el = this.el.createChild();
38069 clayout.items = clayout.items || [];
38073 // replace this exitems with the clayout ones..
38074 xitems = clayout.items;
38076 // force background off if it's in center...
38077 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38078 cfg.background = false;
38080 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38083 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38084 //console.log('adding nested layout panel ' + cfg.toSource());
38085 this.add(region, ret);
38086 nb = {}; /// find first...
38091 // needs grid and region
38093 //var el = this.getRegion(region).el.createChild();
38095 *var el = this.el.createChild();
38096 // create the grid first...
38097 cfg.grid.container = el;
38098 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38101 if (region == 'center' && this.active ) {
38102 cfg.background = false;
38105 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38107 this.add(region, ret);
38109 if (cfg.background) {
38110 // render grid on panel activation (if panel background)
38111 ret.on('activate', function(gp) {
38112 if (!gp.grid.rendered) {
38113 // gp.grid.render(el);
38117 // cfg.grid.render(el);
38123 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38124 // it was the old xcomponent building that caused this before.
38125 // espeically if border is the top element in the tree.
38135 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38137 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38138 this.add(region, ret);
38142 throw "Can not add '" + cfg.xtype + "' to Border";
38148 this.beginUpdate();
38152 Roo.each(xitems, function(i) {
38153 region = nb && i.region ? i.region : false;
38155 var add = ret.addxtype(i);
38158 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38159 if (!i.background) {
38160 abn[region] = nb[region] ;
38167 // make the last non-background panel active..
38168 //if (nb) { Roo.log(abn); }
38171 for(var r in abn) {
38172 region = this.getRegion(r);
38174 // tried using nb[r], but it does not work..
38176 region.showPanel(abn[r]);
38187 factory : function(cfg)
38190 var validRegions = Roo.bootstrap.layout.Border.regions;
38192 var target = cfg.region;
38195 var r = Roo.bootstrap.layout;
38199 return new r.North(cfg);
38201 return new r.South(cfg);
38203 return new r.East(cfg);
38205 return new r.West(cfg);
38207 return new r.Center(cfg);
38209 throw 'Layout region "'+target+'" not supported.';
38216 * Ext JS Library 1.1.1
38217 * Copyright(c) 2006-2007, Ext JS, LLC.
38219 * Originally Released Under LGPL - original licence link has changed is not relivant.
38222 * <script type="text/javascript">
38226 * @class Roo.bootstrap.layout.Basic
38227 * @extends Roo.util.Observable
38228 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38229 * and does not have a titlebar, tabs or any other features. All it does is size and position
38230 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38231 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38232 * @cfg {string} region the region that it inhabits..
38233 * @cfg {bool} skipConfig skip config?
38237 Roo.bootstrap.layout.Basic = function(config){
38239 this.mgr = config.mgr;
38241 this.position = config.region;
38243 var skipConfig = config.skipConfig;
38247 * @scope Roo.BasicLayoutRegion
38251 * @event beforeremove
38252 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38253 * @param {Roo.LayoutRegion} this
38254 * @param {Roo.ContentPanel} panel The panel
38255 * @param {Object} e The cancel event object
38257 "beforeremove" : true,
38259 * @event invalidated
38260 * Fires when the layout for this region is changed.
38261 * @param {Roo.LayoutRegion} this
38263 "invalidated" : true,
38265 * @event visibilitychange
38266 * Fires when this region is shown or hidden
38267 * @param {Roo.LayoutRegion} this
38268 * @param {Boolean} visibility true or false
38270 "visibilitychange" : true,
38272 * @event paneladded
38273 * Fires when a panel is added.
38274 * @param {Roo.LayoutRegion} this
38275 * @param {Roo.ContentPanel} panel The panel
38277 "paneladded" : true,
38279 * @event panelremoved
38280 * Fires when a panel is removed.
38281 * @param {Roo.LayoutRegion} this
38282 * @param {Roo.ContentPanel} panel The panel
38284 "panelremoved" : true,
38286 * @event beforecollapse
38287 * Fires when this region before collapse.
38288 * @param {Roo.LayoutRegion} this
38290 "beforecollapse" : true,
38293 * Fires when this region is collapsed.
38294 * @param {Roo.LayoutRegion} this
38296 "collapsed" : true,
38299 * Fires when this region is expanded.
38300 * @param {Roo.LayoutRegion} this
38305 * Fires when this region is slid into view.
38306 * @param {Roo.LayoutRegion} this
38308 "slideshow" : true,
38311 * Fires when this region slides out of view.
38312 * @param {Roo.LayoutRegion} this
38314 "slidehide" : true,
38316 * @event panelactivated
38317 * Fires when a panel is activated.
38318 * @param {Roo.LayoutRegion} this
38319 * @param {Roo.ContentPanel} panel The activated panel
38321 "panelactivated" : true,
38324 * Fires when the user resizes this region.
38325 * @param {Roo.LayoutRegion} this
38326 * @param {Number} newSize The new size (width for east/west, height for north/south)
38330 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38331 this.panels = new Roo.util.MixedCollection();
38332 this.panels.getKey = this.getPanelId.createDelegate(this);
38334 this.activePanel = null;
38335 // ensure listeners are added...
38337 if (config.listeners || config.events) {
38338 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38339 listeners : config.listeners || {},
38340 events : config.events || {}
38344 if(skipConfig !== true){
38345 this.applyConfig(config);
38349 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38351 getPanelId : function(p){
38355 applyConfig : function(config){
38356 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38357 this.config = config;
38362 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38363 * the width, for horizontal (north, south) the height.
38364 * @param {Number} newSize The new width or height
38366 resizeTo : function(newSize){
38367 var el = this.el ? this.el :
38368 (this.activePanel ? this.activePanel.getEl() : null);
38370 switch(this.position){
38373 el.setWidth(newSize);
38374 this.fireEvent("resized", this, newSize);
38378 el.setHeight(newSize);
38379 this.fireEvent("resized", this, newSize);
38385 getBox : function(){
38386 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38389 getMargins : function(){
38390 return this.margins;
38393 updateBox : function(box){
38395 var el = this.activePanel.getEl();
38396 el.dom.style.left = box.x + "px";
38397 el.dom.style.top = box.y + "px";
38398 this.activePanel.setSize(box.width, box.height);
38402 * Returns the container element for this region.
38403 * @return {Roo.Element}
38405 getEl : function(){
38406 return this.activePanel;
38410 * Returns true if this region is currently visible.
38411 * @return {Boolean}
38413 isVisible : function(){
38414 return this.activePanel ? true : false;
38417 setActivePanel : function(panel){
38418 panel = this.getPanel(panel);
38419 if(this.activePanel && this.activePanel != panel){
38420 this.activePanel.setActiveState(false);
38421 this.activePanel.getEl().setLeftTop(-10000,-10000);
38423 this.activePanel = panel;
38424 panel.setActiveState(true);
38426 panel.setSize(this.box.width, this.box.height);
38428 this.fireEvent("panelactivated", this, panel);
38429 this.fireEvent("invalidated");
38433 * Show the specified panel.
38434 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38435 * @return {Roo.ContentPanel} The shown panel or null
38437 showPanel : function(panel){
38438 panel = this.getPanel(panel);
38440 this.setActivePanel(panel);
38446 * Get the active panel for this region.
38447 * @return {Roo.ContentPanel} The active panel or null
38449 getActivePanel : function(){
38450 return this.activePanel;
38454 * Add the passed ContentPanel(s)
38455 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38456 * @return {Roo.ContentPanel} The panel added (if only one was added)
38458 add : function(panel){
38459 if(arguments.length > 1){
38460 for(var i = 0, len = arguments.length; i < len; i++) {
38461 this.add(arguments[i]);
38465 if(this.hasPanel(panel)){
38466 this.showPanel(panel);
38469 var el = panel.getEl();
38470 if(el.dom.parentNode != this.mgr.el.dom){
38471 this.mgr.el.dom.appendChild(el.dom);
38473 if(panel.setRegion){
38474 panel.setRegion(this);
38476 this.panels.add(panel);
38477 el.setStyle("position", "absolute");
38478 if(!panel.background){
38479 this.setActivePanel(panel);
38480 if(this.config.initialSize && this.panels.getCount()==1){
38481 this.resizeTo(this.config.initialSize);
38484 this.fireEvent("paneladded", this, panel);
38489 * Returns true if the panel is in this region.
38490 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38491 * @return {Boolean}
38493 hasPanel : function(panel){
38494 if(typeof panel == "object"){ // must be panel obj
38495 panel = panel.getId();
38497 return this.getPanel(panel) ? true : false;
38501 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38502 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38503 * @param {Boolean} preservePanel Overrides the config preservePanel option
38504 * @return {Roo.ContentPanel} The panel that was removed
38506 remove : function(panel, preservePanel){
38507 panel = this.getPanel(panel);
38512 this.fireEvent("beforeremove", this, panel, e);
38513 if(e.cancel === true){
38516 var panelId = panel.getId();
38517 this.panels.removeKey(panelId);
38522 * Returns the panel specified or null if it's not in this region.
38523 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38524 * @return {Roo.ContentPanel}
38526 getPanel : function(id){
38527 if(typeof id == "object"){ // must be panel obj
38530 return this.panels.get(id);
38534 * Returns this regions position (north/south/east/west/center).
38537 getPosition: function(){
38538 return this.position;
38542 * Ext JS Library 1.1.1
38543 * Copyright(c) 2006-2007, Ext JS, LLC.
38545 * Originally Released Under LGPL - original licence link has changed is not relivant.
38548 * <script type="text/javascript">
38552 * @class Roo.bootstrap.layout.Region
38553 * @extends Roo.bootstrap.layout.Basic
38554 * This class represents a region in a layout manager.
38556 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38557 * @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})
38558 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38559 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38560 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38561 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38562 * @cfg {String} title The title for the region (overrides panel titles)
38563 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38564 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38565 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38566 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38567 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38568 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38569 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38570 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38571 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38572 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38574 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38575 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38576 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38577 * @cfg {Number} width For East/West panels
38578 * @cfg {Number} height For North/South panels
38579 * @cfg {Boolean} split To show the splitter
38580 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38582 * @cfg {string} cls Extra CSS classes to add to region
38584 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38585 * @cfg {string} region the region that it inhabits..
38588 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38589 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38591 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38592 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38593 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38595 Roo.bootstrap.layout.Region = function(config)
38597 this.applyConfig(config);
38599 var mgr = config.mgr;
38600 var pos = config.region;
38601 config.skipConfig = true;
38602 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38605 this.onRender(mgr.el);
38608 this.visible = true;
38609 this.collapsed = false;
38610 this.unrendered_panels = [];
38613 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38615 position: '', // set by wrapper (eg. north/south etc..)
38616 unrendered_panels : null, // unrendered panels.
38618 tabPosition : false,
38620 mgr: false, // points to 'Border'
38623 createBody : function(){
38624 /** This region's body element
38625 * @type Roo.Element */
38626 this.bodyEl = this.el.createChild({
38628 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38632 onRender: function(ctr, pos)
38634 var dh = Roo.DomHelper;
38635 /** This region's container element
38636 * @type Roo.Element */
38637 this.el = dh.append(ctr.dom, {
38639 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38641 /** This region's title element
38642 * @type Roo.Element */
38644 this.titleEl = dh.append(this.el.dom, {
38646 unselectable: "on",
38647 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38649 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38650 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38654 this.titleEl.enableDisplayMode();
38655 /** This region's title text element
38656 * @type HTMLElement */
38657 this.titleTextEl = this.titleEl.dom.firstChild;
38658 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38660 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38661 this.closeBtn.enableDisplayMode();
38662 this.closeBtn.on("click", this.closeClicked, this);
38663 this.closeBtn.hide();
38665 this.createBody(this.config);
38666 if(this.config.hideWhenEmpty){
38668 this.on("paneladded", this.validateVisibility, this);
38669 this.on("panelremoved", this.validateVisibility, this);
38671 if(this.autoScroll){
38672 this.bodyEl.setStyle("overflow", "auto");
38674 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38676 //if(c.titlebar !== false){
38677 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38678 this.titleEl.hide();
38680 this.titleEl.show();
38681 if(this.config.title){
38682 this.titleTextEl.innerHTML = this.config.title;
38686 if(this.config.collapsed){
38687 this.collapse(true);
38689 if(this.config.hidden){
38693 if (this.unrendered_panels && this.unrendered_panels.length) {
38694 for (var i =0;i< this.unrendered_panels.length; i++) {
38695 this.add(this.unrendered_panels[i]);
38697 this.unrendered_panels = null;
38703 applyConfig : function(c)
38706 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38707 var dh = Roo.DomHelper;
38708 if(c.titlebar !== false){
38709 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38710 this.collapseBtn.on("click", this.collapse, this);
38711 this.collapseBtn.enableDisplayMode();
38713 if(c.showPin === true || this.showPin){
38714 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38715 this.stickBtn.enableDisplayMode();
38716 this.stickBtn.on("click", this.expand, this);
38717 this.stickBtn.hide();
38722 /** This region's collapsed element
38723 * @type Roo.Element */
38726 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38727 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38730 if(c.floatable !== false){
38731 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38732 this.collapsedEl.on("click", this.collapseClick, this);
38735 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38736 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38737 id: "message", unselectable: "on", style:{"float":"left"}});
38738 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38740 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38741 this.expandBtn.on("click", this.expand, this);
38745 if(this.collapseBtn){
38746 this.collapseBtn.setVisible(c.collapsible == true);
38749 this.cmargins = c.cmargins || this.cmargins ||
38750 (this.position == "west" || this.position == "east" ?
38751 {top: 0, left: 2, right:2, bottom: 0} :
38752 {top: 2, left: 0, right:0, bottom: 2});
38754 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38757 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38759 this.autoScroll = c.autoScroll || false;
38764 this.duration = c.duration || .30;
38765 this.slideDuration = c.slideDuration || .45;
38770 * Returns true if this region is currently visible.
38771 * @return {Boolean}
38773 isVisible : function(){
38774 return this.visible;
38778 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38779 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38781 //setCollapsedTitle : function(title){
38782 // title = title || " ";
38783 // if(this.collapsedTitleTextEl){
38784 // this.collapsedTitleTextEl.innerHTML = title;
38788 getBox : function(){
38790 // if(!this.collapsed){
38791 b = this.el.getBox(false, true);
38793 // b = this.collapsedEl.getBox(false, true);
38798 getMargins : function(){
38799 return this.margins;
38800 //return this.collapsed ? this.cmargins : this.margins;
38803 highlight : function(){
38804 this.el.addClass("x-layout-panel-dragover");
38807 unhighlight : function(){
38808 this.el.removeClass("x-layout-panel-dragover");
38811 updateBox : function(box)
38813 if (!this.bodyEl) {
38814 return; // not rendered yet..
38818 if(!this.collapsed){
38819 this.el.dom.style.left = box.x + "px";
38820 this.el.dom.style.top = box.y + "px";
38821 this.updateBody(box.width, box.height);
38823 this.collapsedEl.dom.style.left = box.x + "px";
38824 this.collapsedEl.dom.style.top = box.y + "px";
38825 this.collapsedEl.setSize(box.width, box.height);
38828 this.tabs.autoSizeTabs();
38832 updateBody : function(w, h)
38835 this.el.setWidth(w);
38836 w -= this.el.getBorderWidth("rl");
38837 if(this.config.adjustments){
38838 w += this.config.adjustments[0];
38841 if(h !== null && h > 0){
38842 this.el.setHeight(h);
38843 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38844 h -= this.el.getBorderWidth("tb");
38845 if(this.config.adjustments){
38846 h += this.config.adjustments[1];
38848 this.bodyEl.setHeight(h);
38850 h = this.tabs.syncHeight(h);
38853 if(this.panelSize){
38854 w = w !== null ? w : this.panelSize.width;
38855 h = h !== null ? h : this.panelSize.height;
38857 if(this.activePanel){
38858 var el = this.activePanel.getEl();
38859 w = w !== null ? w : el.getWidth();
38860 h = h !== null ? h : el.getHeight();
38861 this.panelSize = {width: w, height: h};
38862 this.activePanel.setSize(w, h);
38864 if(Roo.isIE && this.tabs){
38865 this.tabs.el.repaint();
38870 * Returns the container element for this region.
38871 * @return {Roo.Element}
38873 getEl : function(){
38878 * Hides this region.
38881 //if(!this.collapsed){
38882 this.el.dom.style.left = "-2000px";
38885 // this.collapsedEl.dom.style.left = "-2000px";
38886 // this.collapsedEl.hide();
38888 this.visible = false;
38889 this.fireEvent("visibilitychange", this, false);
38893 * Shows this region if it was previously hidden.
38896 //if(!this.collapsed){
38899 // this.collapsedEl.show();
38901 this.visible = true;
38902 this.fireEvent("visibilitychange", this, true);
38905 closeClicked : function(){
38906 if(this.activePanel){
38907 this.remove(this.activePanel);
38911 collapseClick : function(e){
38913 e.stopPropagation();
38916 e.stopPropagation();
38922 * Collapses this region.
38923 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38926 collapse : function(skipAnim, skipCheck = false){
38927 if(this.collapsed) {
38931 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38933 this.collapsed = true;
38935 this.split.el.hide();
38937 if(this.config.animate && skipAnim !== true){
38938 this.fireEvent("invalidated", this);
38939 this.animateCollapse();
38941 this.el.setLocation(-20000,-20000);
38943 this.collapsedEl.show();
38944 this.fireEvent("collapsed", this);
38945 this.fireEvent("invalidated", this);
38951 animateCollapse : function(){
38956 * Expands this region if it was previously collapsed.
38957 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38958 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38961 expand : function(e, skipAnim){
38963 e.stopPropagation();
38965 if(!this.collapsed || this.el.hasActiveFx()) {
38969 this.afterSlideIn();
38972 this.collapsed = false;
38973 if(this.config.animate && skipAnim !== true){
38974 this.animateExpand();
38978 this.split.el.show();
38980 this.collapsedEl.setLocation(-2000,-2000);
38981 this.collapsedEl.hide();
38982 this.fireEvent("invalidated", this);
38983 this.fireEvent("expanded", this);
38987 animateExpand : function(){
38991 initTabs : function()
38993 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38995 var ts = new Roo.bootstrap.panel.Tabs({
38996 el: this.bodyEl.dom,
38998 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38999 disableTooltips: this.config.disableTabTips,
39000 toolbar : this.config.toolbar
39003 if(this.config.hideTabs){
39004 ts.stripWrap.setDisplayed(false);
39007 ts.resizeTabs = this.config.resizeTabs === true;
39008 ts.minTabWidth = this.config.minTabWidth || 40;
39009 ts.maxTabWidth = this.config.maxTabWidth || 250;
39010 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39011 ts.monitorResize = false;
39012 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39013 ts.bodyEl.addClass('roo-layout-tabs-body');
39014 this.panels.each(this.initPanelAsTab, this);
39017 initPanelAsTab : function(panel){
39018 var ti = this.tabs.addTab(
39022 this.config.closeOnTab && panel.isClosable(),
39025 if(panel.tabTip !== undefined){
39026 ti.setTooltip(panel.tabTip);
39028 ti.on("activate", function(){
39029 this.setActivePanel(panel);
39032 if(this.config.closeOnTab){
39033 ti.on("beforeclose", function(t, e){
39035 this.remove(panel);
39039 panel.tabItem = ti;
39044 updatePanelTitle : function(panel, title)
39046 if(this.activePanel == panel){
39047 this.updateTitle(title);
39050 var ti = this.tabs.getTab(panel.getEl().id);
39052 if(panel.tabTip !== undefined){
39053 ti.setTooltip(panel.tabTip);
39058 updateTitle : function(title){
39059 if(this.titleTextEl && !this.config.title){
39060 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39064 setActivePanel : function(panel)
39066 panel = this.getPanel(panel);
39067 if(this.activePanel && this.activePanel != panel){
39068 if(this.activePanel.setActiveState(false) === false){
39072 this.activePanel = panel;
39073 panel.setActiveState(true);
39074 if(this.panelSize){
39075 panel.setSize(this.panelSize.width, this.panelSize.height);
39078 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39080 this.updateTitle(panel.getTitle());
39082 this.fireEvent("invalidated", this);
39084 this.fireEvent("panelactivated", this, panel);
39088 * Shows the specified panel.
39089 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39090 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39092 showPanel : function(panel)
39094 panel = this.getPanel(panel);
39097 var tab = this.tabs.getTab(panel.getEl().id);
39098 if(tab.isHidden()){
39099 this.tabs.unhideTab(tab.id);
39103 this.setActivePanel(panel);
39110 * Get the active panel for this region.
39111 * @return {Roo.ContentPanel} The active panel or null
39113 getActivePanel : function(){
39114 return this.activePanel;
39117 validateVisibility : function(){
39118 if(this.panels.getCount() < 1){
39119 this.updateTitle(" ");
39120 this.closeBtn.hide();
39123 if(!this.isVisible()){
39130 * Adds the passed ContentPanel(s) to this region.
39131 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39132 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39134 add : function(panel)
39136 if(arguments.length > 1){
39137 for(var i = 0, len = arguments.length; i < len; i++) {
39138 this.add(arguments[i]);
39143 // if we have not been rendered yet, then we can not really do much of this..
39144 if (!this.bodyEl) {
39145 this.unrendered_panels.push(panel);
39152 if(this.hasPanel(panel)){
39153 this.showPanel(panel);
39156 panel.setRegion(this);
39157 this.panels.add(panel);
39158 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39159 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39160 // and hide them... ???
39161 this.bodyEl.dom.appendChild(panel.getEl().dom);
39162 if(panel.background !== true){
39163 this.setActivePanel(panel);
39165 this.fireEvent("paneladded", this, panel);
39172 this.initPanelAsTab(panel);
39176 if(panel.background !== true){
39177 this.tabs.activate(panel.getEl().id);
39179 this.fireEvent("paneladded", this, panel);
39184 * Hides the tab for the specified panel.
39185 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39187 hidePanel : function(panel){
39188 if(this.tabs && (panel = this.getPanel(panel))){
39189 this.tabs.hideTab(panel.getEl().id);
39194 * Unhides the tab for a previously hidden panel.
39195 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39197 unhidePanel : function(panel){
39198 if(this.tabs && (panel = this.getPanel(panel))){
39199 this.tabs.unhideTab(panel.getEl().id);
39203 clearPanels : function(){
39204 while(this.panels.getCount() > 0){
39205 this.remove(this.panels.first());
39210 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39211 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39212 * @param {Boolean} preservePanel Overrides the config preservePanel option
39213 * @return {Roo.ContentPanel} The panel that was removed
39215 remove : function(panel, preservePanel)
39217 panel = this.getPanel(panel);
39222 this.fireEvent("beforeremove", this, panel, e);
39223 if(e.cancel === true){
39226 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39227 var panelId = panel.getId();
39228 this.panels.removeKey(panelId);
39230 document.body.appendChild(panel.getEl().dom);
39233 this.tabs.removeTab(panel.getEl().id);
39234 }else if (!preservePanel){
39235 this.bodyEl.dom.removeChild(panel.getEl().dom);
39237 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39238 var p = this.panels.first();
39239 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39240 tempEl.appendChild(p.getEl().dom);
39241 this.bodyEl.update("");
39242 this.bodyEl.dom.appendChild(p.getEl().dom);
39244 this.updateTitle(p.getTitle());
39246 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39247 this.setActivePanel(p);
39249 panel.setRegion(null);
39250 if(this.activePanel == panel){
39251 this.activePanel = null;
39253 if(this.config.autoDestroy !== false && preservePanel !== true){
39254 try{panel.destroy();}catch(e){}
39256 this.fireEvent("panelremoved", this, panel);
39261 * Returns the TabPanel component used by this region
39262 * @return {Roo.TabPanel}
39264 getTabs : function(){
39268 createTool : function(parentEl, className){
39269 var btn = Roo.DomHelper.append(parentEl, {
39271 cls: "x-layout-tools-button",
39274 cls: "roo-layout-tools-button-inner " + className,
39278 btn.addClassOnOver("roo-layout-tools-button-over");
39283 * Ext JS Library 1.1.1
39284 * Copyright(c) 2006-2007, Ext JS, LLC.
39286 * Originally Released Under LGPL - original licence link has changed is not relivant.
39289 * <script type="text/javascript">
39295 * @class Roo.SplitLayoutRegion
39296 * @extends Roo.LayoutRegion
39297 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39299 Roo.bootstrap.layout.Split = function(config){
39300 this.cursor = config.cursor;
39301 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39304 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39306 splitTip : "Drag to resize.",
39307 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39308 useSplitTips : false,
39310 applyConfig : function(config){
39311 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39314 onRender : function(ctr,pos) {
39316 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39317 if(!this.config.split){
39322 var splitEl = Roo.DomHelper.append(ctr.dom, {
39324 id: this.el.id + "-split",
39325 cls: "roo-layout-split roo-layout-split-"+this.position,
39328 /** The SplitBar for this region
39329 * @type Roo.SplitBar */
39330 // does not exist yet...
39331 Roo.log([this.position, this.orientation]);
39333 this.split = new Roo.bootstrap.SplitBar({
39334 dragElement : splitEl,
39335 resizingElement: this.el,
39336 orientation : this.orientation
39339 this.split.on("moved", this.onSplitMove, this);
39340 this.split.useShim = this.config.useShim === true;
39341 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39342 if(this.useSplitTips){
39343 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39345 //if(config.collapsible){
39346 // this.split.el.on("dblclick", this.collapse, this);
39349 if(typeof this.config.minSize != "undefined"){
39350 this.split.minSize = this.config.minSize;
39352 if(typeof this.config.maxSize != "undefined"){
39353 this.split.maxSize = this.config.maxSize;
39355 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39356 this.hideSplitter();
39361 getHMaxSize : function(){
39362 var cmax = this.config.maxSize || 10000;
39363 var center = this.mgr.getRegion("center");
39364 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39367 getVMaxSize : function(){
39368 var cmax = this.config.maxSize || 10000;
39369 var center = this.mgr.getRegion("center");
39370 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39373 onSplitMove : function(split, newSize){
39374 this.fireEvent("resized", this, newSize);
39378 * Returns the {@link Roo.SplitBar} for this region.
39379 * @return {Roo.SplitBar}
39381 getSplitBar : function(){
39386 this.hideSplitter();
39387 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39390 hideSplitter : function(){
39392 this.split.el.setLocation(-2000,-2000);
39393 this.split.el.hide();
39399 this.split.el.show();
39401 Roo.bootstrap.layout.Split.superclass.show.call(this);
39404 beforeSlide: function(){
39405 if(Roo.isGecko){// firefox overflow auto bug workaround
39406 this.bodyEl.clip();
39408 this.tabs.bodyEl.clip();
39410 if(this.activePanel){
39411 this.activePanel.getEl().clip();
39413 if(this.activePanel.beforeSlide){
39414 this.activePanel.beforeSlide();
39420 afterSlide : function(){
39421 if(Roo.isGecko){// firefox overflow auto bug workaround
39422 this.bodyEl.unclip();
39424 this.tabs.bodyEl.unclip();
39426 if(this.activePanel){
39427 this.activePanel.getEl().unclip();
39428 if(this.activePanel.afterSlide){
39429 this.activePanel.afterSlide();
39435 initAutoHide : function(){
39436 if(this.autoHide !== false){
39437 if(!this.autoHideHd){
39438 var st = new Roo.util.DelayedTask(this.slideIn, this);
39439 this.autoHideHd = {
39440 "mouseout": function(e){
39441 if(!e.within(this.el, true)){
39445 "mouseover" : function(e){
39451 this.el.on(this.autoHideHd);
39455 clearAutoHide : function(){
39456 if(this.autoHide !== false){
39457 this.el.un("mouseout", this.autoHideHd.mouseout);
39458 this.el.un("mouseover", this.autoHideHd.mouseover);
39462 clearMonitor : function(){
39463 Roo.get(document).un("click", this.slideInIf, this);
39466 // these names are backwards but not changed for compat
39467 slideOut : function(){
39468 if(this.isSlid || this.el.hasActiveFx()){
39471 this.isSlid = true;
39472 if(this.collapseBtn){
39473 this.collapseBtn.hide();
39475 this.closeBtnState = this.closeBtn.getStyle('display');
39476 this.closeBtn.hide();
39478 this.stickBtn.show();
39481 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39482 this.beforeSlide();
39483 this.el.setStyle("z-index", 10001);
39484 this.el.slideIn(this.getSlideAnchor(), {
39485 callback: function(){
39487 this.initAutoHide();
39488 Roo.get(document).on("click", this.slideInIf, this);
39489 this.fireEvent("slideshow", this);
39496 afterSlideIn : function(){
39497 this.clearAutoHide();
39498 this.isSlid = false;
39499 this.clearMonitor();
39500 this.el.setStyle("z-index", "");
39501 if(this.collapseBtn){
39502 this.collapseBtn.show();
39504 this.closeBtn.setStyle('display', this.closeBtnState);
39506 this.stickBtn.hide();
39508 this.fireEvent("slidehide", this);
39511 slideIn : function(cb){
39512 if(!this.isSlid || this.el.hasActiveFx()){
39516 this.isSlid = false;
39517 this.beforeSlide();
39518 this.el.slideOut(this.getSlideAnchor(), {
39519 callback: function(){
39520 this.el.setLeftTop(-10000, -10000);
39522 this.afterSlideIn();
39530 slideInIf : function(e){
39531 if(!e.within(this.el)){
39536 animateCollapse : function(){
39537 this.beforeSlide();
39538 this.el.setStyle("z-index", 20000);
39539 var anchor = this.getSlideAnchor();
39540 this.el.slideOut(anchor, {
39541 callback : function(){
39542 this.el.setStyle("z-index", "");
39543 this.collapsedEl.slideIn(anchor, {duration:.3});
39545 this.el.setLocation(-10000,-10000);
39547 this.fireEvent("collapsed", this);
39554 animateExpand : function(){
39555 this.beforeSlide();
39556 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39557 this.el.setStyle("z-index", 20000);
39558 this.collapsedEl.hide({
39561 this.el.slideIn(this.getSlideAnchor(), {
39562 callback : function(){
39563 this.el.setStyle("z-index", "");
39566 this.split.el.show();
39568 this.fireEvent("invalidated", this);
39569 this.fireEvent("expanded", this);
39597 getAnchor : function(){
39598 return this.anchors[this.position];
39601 getCollapseAnchor : function(){
39602 return this.canchors[this.position];
39605 getSlideAnchor : function(){
39606 return this.sanchors[this.position];
39609 getAlignAdj : function(){
39610 var cm = this.cmargins;
39611 switch(this.position){
39627 getExpandAdj : function(){
39628 var c = this.collapsedEl, cm = this.cmargins;
39629 switch(this.position){
39631 return [-(cm.right+c.getWidth()+cm.left), 0];
39634 return [cm.right+c.getWidth()+cm.left, 0];
39637 return [0, -(cm.top+cm.bottom+c.getHeight())];
39640 return [0, cm.top+cm.bottom+c.getHeight()];
39646 * Ext JS Library 1.1.1
39647 * Copyright(c) 2006-2007, Ext JS, LLC.
39649 * Originally Released Under LGPL - original licence link has changed is not relivant.
39652 * <script type="text/javascript">
39655 * These classes are private internal classes
39657 Roo.bootstrap.layout.Center = function(config){
39658 config.region = "center";
39659 Roo.bootstrap.layout.Region.call(this, config);
39660 this.visible = true;
39661 this.minWidth = config.minWidth || 20;
39662 this.minHeight = config.minHeight || 20;
39665 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39667 // center panel can't be hidden
39671 // center panel can't be hidden
39674 getMinWidth: function(){
39675 return this.minWidth;
39678 getMinHeight: function(){
39679 return this.minHeight;
39693 Roo.bootstrap.layout.North = function(config)
39695 config.region = 'north';
39696 config.cursor = 'n-resize';
39698 Roo.bootstrap.layout.Split.call(this, config);
39702 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39703 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39704 this.split.el.addClass("roo-layout-split-v");
39706 //var size = config.initialSize || config.height;
39707 //if(this.el && typeof size != "undefined"){
39708 // this.el.setHeight(size);
39711 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39713 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39716 onRender : function(ctr, pos)
39718 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39719 var size = this.config.initialSize || this.config.height;
39720 if(this.el && typeof size != "undefined"){
39721 this.el.setHeight(size);
39726 getBox : function(){
39727 if(this.collapsed){
39728 return this.collapsedEl.getBox();
39730 var box = this.el.getBox();
39732 box.height += this.split.el.getHeight();
39737 updateBox : function(box){
39738 if(this.split && !this.collapsed){
39739 box.height -= this.split.el.getHeight();
39740 this.split.el.setLeft(box.x);
39741 this.split.el.setTop(box.y+box.height);
39742 this.split.el.setWidth(box.width);
39744 if(this.collapsed){
39745 this.updateBody(box.width, null);
39747 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39755 Roo.bootstrap.layout.South = function(config){
39756 config.region = 'south';
39757 config.cursor = 's-resize';
39758 Roo.bootstrap.layout.Split.call(this, config);
39760 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39761 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39762 this.split.el.addClass("roo-layout-split-v");
39767 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39768 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39770 onRender : function(ctr, pos)
39772 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39773 var size = this.config.initialSize || this.config.height;
39774 if(this.el && typeof size != "undefined"){
39775 this.el.setHeight(size);
39780 getBox : function(){
39781 if(this.collapsed){
39782 return this.collapsedEl.getBox();
39784 var box = this.el.getBox();
39786 var sh = this.split.el.getHeight();
39793 updateBox : function(box){
39794 if(this.split && !this.collapsed){
39795 var sh = this.split.el.getHeight();
39798 this.split.el.setLeft(box.x);
39799 this.split.el.setTop(box.y-sh);
39800 this.split.el.setWidth(box.width);
39802 if(this.collapsed){
39803 this.updateBody(box.width, null);
39805 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39809 Roo.bootstrap.layout.East = function(config){
39810 config.region = "east";
39811 config.cursor = "e-resize";
39812 Roo.bootstrap.layout.Split.call(this, config);
39814 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39815 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39816 this.split.el.addClass("roo-layout-split-h");
39820 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39821 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39823 onRender : function(ctr, pos)
39825 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39826 var size = this.config.initialSize || this.config.width;
39827 if(this.el && typeof size != "undefined"){
39828 this.el.setWidth(size);
39833 getBox : function(){
39834 if(this.collapsed){
39835 return this.collapsedEl.getBox();
39837 var box = this.el.getBox();
39839 var sw = this.split.el.getWidth();
39846 updateBox : function(box){
39847 if(this.split && !this.collapsed){
39848 var sw = this.split.el.getWidth();
39850 this.split.el.setLeft(box.x);
39851 this.split.el.setTop(box.y);
39852 this.split.el.setHeight(box.height);
39855 if(this.collapsed){
39856 this.updateBody(null, box.height);
39858 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39862 Roo.bootstrap.layout.West = function(config){
39863 config.region = "west";
39864 config.cursor = "w-resize";
39866 Roo.bootstrap.layout.Split.call(this, config);
39868 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39869 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39870 this.split.el.addClass("roo-layout-split-h");
39874 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39875 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39877 onRender: function(ctr, pos)
39879 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39880 var size = this.config.initialSize || this.config.width;
39881 if(typeof size != "undefined"){
39882 this.el.setWidth(size);
39886 getBox : function(){
39887 if(this.collapsed){
39888 return this.collapsedEl.getBox();
39890 var box = this.el.getBox();
39891 if (box.width == 0) {
39892 box.width = this.config.width; // kludge?
39895 box.width += this.split.el.getWidth();
39900 updateBox : function(box){
39901 if(this.split && !this.collapsed){
39902 var sw = this.split.el.getWidth();
39904 this.split.el.setLeft(box.x+box.width);
39905 this.split.el.setTop(box.y);
39906 this.split.el.setHeight(box.height);
39908 if(this.collapsed){
39909 this.updateBody(null, box.height);
39911 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39913 });Roo.namespace("Roo.bootstrap.panel");/*
39915 * Ext JS Library 1.1.1
39916 * Copyright(c) 2006-2007, Ext JS, LLC.
39918 * Originally Released Under LGPL - original licence link has changed is not relivant.
39921 * <script type="text/javascript">
39924 * @class Roo.ContentPanel
39925 * @extends Roo.util.Observable
39926 * A basic ContentPanel element.
39927 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39928 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39929 * @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
39930 * @cfg {Boolean} closable True if the panel can be closed/removed
39931 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39932 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39933 * @cfg {Toolbar} toolbar A toolbar for this panel
39934 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39935 * @cfg {String} title The title for this panel
39936 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39937 * @cfg {String} url Calls {@link #setUrl} with this value
39938 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39939 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39940 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39941 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39942 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39943 * @cfg {Boolean} badges render the badges
39944 * @cfg {String} cls extra classes to use
39945 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39948 * Create a new ContentPanel.
39949 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39950 * @param {String/Object} config A string to set only the title or a config object
39951 * @param {String} content (optional) Set the HTML content for this panel
39952 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39954 Roo.bootstrap.panel.Content = function( config){
39956 this.tpl = config.tpl || false;
39958 var el = config.el;
39959 var content = config.content;
39961 if(config.autoCreate){ // xtype is available if this is called from factory
39964 this.el = Roo.get(el);
39965 if(!this.el && config && config.autoCreate){
39966 if(typeof config.autoCreate == "object"){
39967 if(!config.autoCreate.id){
39968 config.autoCreate.id = config.id||el;
39970 this.el = Roo.DomHelper.append(document.body,
39971 config.autoCreate, true);
39975 cls: (config.cls || '') +
39976 (config.background ? ' bg-' + config.background : '') +
39977 " roo-layout-inactive-content",
39980 if (config.iframe) {
39984 style : 'border: 0px',
39985 src : 'about:blank'
39991 elcfg.html = config.html;
39995 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39996 if (config.iframe) {
39997 this.iframeEl = this.el.select('iframe',true).first();
40002 this.closable = false;
40003 this.loaded = false;
40004 this.active = false;
40007 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40009 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40011 this.wrapEl = this.el; //this.el.wrap();
40013 if (config.toolbar.items) {
40014 ti = config.toolbar.items ;
40015 delete config.toolbar.items ;
40019 this.toolbar.render(this.wrapEl, 'before');
40020 for(var i =0;i < ti.length;i++) {
40021 // Roo.log(['add child', items[i]]);
40022 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40024 this.toolbar.items = nitems;
40025 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40026 delete config.toolbar;
40030 // xtype created footer. - not sure if will work as we normally have to render first..
40031 if (this.footer && !this.footer.el && this.footer.xtype) {
40032 if (!this.wrapEl) {
40033 this.wrapEl = this.el.wrap();
40036 this.footer.container = this.wrapEl.createChild();
40038 this.footer = Roo.factory(this.footer, Roo);
40043 if(typeof config == "string"){
40044 this.title = config;
40046 Roo.apply(this, config);
40050 this.resizeEl = Roo.get(this.resizeEl, true);
40052 this.resizeEl = this.el;
40054 // handle view.xtype
40062 * Fires when this panel is activated.
40063 * @param {Roo.ContentPanel} this
40067 * @event deactivate
40068 * Fires when this panel is activated.
40069 * @param {Roo.ContentPanel} this
40071 "deactivate" : true,
40075 * Fires when this panel is resized if fitToFrame is true.
40076 * @param {Roo.ContentPanel} this
40077 * @param {Number} width The width after any component adjustments
40078 * @param {Number} height The height after any component adjustments
40084 * Fires when this tab is created
40085 * @param {Roo.ContentPanel} this
40096 if(this.autoScroll && !this.iframe){
40097 this.resizeEl.setStyle("overflow", "auto");
40099 // fix randome scrolling
40100 //this.el.on('scroll', function() {
40101 // Roo.log('fix random scolling');
40102 // this.scrollTo('top',0);
40105 content = content || this.content;
40107 this.setContent(content);
40109 if(config && config.url){
40110 this.setUrl(this.url, this.params, this.loadOnce);
40115 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40117 if (this.view && typeof(this.view.xtype) != 'undefined') {
40118 this.view.el = this.el.appendChild(document.createElement("div"));
40119 this.view = Roo.factory(this.view);
40120 this.view.render && this.view.render(false, '');
40124 this.fireEvent('render', this);
40127 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40137 setRegion : function(region){
40138 this.region = region;
40139 this.setActiveClass(region && !this.background);
40143 setActiveClass: function(state)
40146 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40147 this.el.setStyle('position','relative');
40149 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40150 this.el.setStyle('position', 'absolute');
40155 * Returns the toolbar for this Panel if one was configured.
40156 * @return {Roo.Toolbar}
40158 getToolbar : function(){
40159 return this.toolbar;
40162 setActiveState : function(active)
40164 this.active = active;
40165 this.setActiveClass(active);
40167 if(this.fireEvent("deactivate", this) === false){
40172 this.fireEvent("activate", this);
40176 * Updates this panel's element (not for iframe)
40177 * @param {String} content The new content
40178 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40180 setContent : function(content, loadScripts){
40185 this.el.update(content, loadScripts);
40188 ignoreResize : function(w, h){
40189 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40192 this.lastSize = {width: w, height: h};
40197 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40198 * @return {Roo.UpdateManager} The UpdateManager
40200 getUpdateManager : function(){
40204 return this.el.getUpdateManager();
40207 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40208 * Does not work with IFRAME contents
40209 * @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:
40212 url: "your-url.php",
40213 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40214 callback: yourFunction,
40215 scope: yourObject, //(optional scope)
40218 text: "Loading...",
40224 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40225 * 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.
40226 * @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}
40227 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40228 * @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.
40229 * @return {Roo.ContentPanel} this
40237 var um = this.el.getUpdateManager();
40238 um.update.apply(um, arguments);
40244 * 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.
40245 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40246 * @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)
40247 * @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)
40248 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40250 setUrl : function(url, params, loadOnce){
40252 this.iframeEl.dom.src = url;
40256 if(this.refreshDelegate){
40257 this.removeListener("activate", this.refreshDelegate);
40259 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40260 this.on("activate", this.refreshDelegate);
40261 return this.el.getUpdateManager();
40264 _handleRefresh : function(url, params, loadOnce){
40265 if(!loadOnce || !this.loaded){
40266 var updater = this.el.getUpdateManager();
40267 updater.update(url, params, this._setLoaded.createDelegate(this));
40271 _setLoaded : function(){
40272 this.loaded = true;
40276 * Returns this panel's id
40279 getId : function(){
40284 * Returns this panel's element - used by regiosn to add.
40285 * @return {Roo.Element}
40287 getEl : function(){
40288 return this.wrapEl || this.el;
40293 adjustForComponents : function(width, height)
40295 //Roo.log('adjustForComponents ');
40296 if(this.resizeEl != this.el){
40297 width -= this.el.getFrameWidth('lr');
40298 height -= this.el.getFrameWidth('tb');
40301 var te = this.toolbar.getEl();
40302 te.setWidth(width);
40303 height -= te.getHeight();
40306 var te = this.footer.getEl();
40307 te.setWidth(width);
40308 height -= te.getHeight();
40312 if(this.adjustments){
40313 width += this.adjustments[0];
40314 height += this.adjustments[1];
40316 return {"width": width, "height": height};
40319 setSize : function(width, height){
40320 if(this.fitToFrame && !this.ignoreResize(width, height)){
40321 if(this.fitContainer && this.resizeEl != this.el){
40322 this.el.setSize(width, height);
40324 var size = this.adjustForComponents(width, height);
40326 this.iframeEl.setSize(width,height);
40329 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40330 this.fireEvent('resize', this, size.width, size.height);
40337 * Returns this panel's title
40340 getTitle : function(){
40342 if (typeof(this.title) != 'object') {
40347 for (var k in this.title) {
40348 if (!this.title.hasOwnProperty(k)) {
40352 if (k.indexOf('-') >= 0) {
40353 var s = k.split('-');
40354 for (var i = 0; i<s.length; i++) {
40355 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40358 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40365 * Set this panel's title
40366 * @param {String} title
40368 setTitle : function(title){
40369 this.title = title;
40371 this.region.updatePanelTitle(this, title);
40376 * Returns true is this panel was configured to be closable
40377 * @return {Boolean}
40379 isClosable : function(){
40380 return this.closable;
40383 beforeSlide : function(){
40385 this.resizeEl.clip();
40388 afterSlide : function(){
40390 this.resizeEl.unclip();
40394 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40395 * Will fail silently if the {@link #setUrl} method has not been called.
40396 * This does not activate the panel, just updates its content.
40398 refresh : function(){
40399 if(this.refreshDelegate){
40400 this.loaded = false;
40401 this.refreshDelegate();
40406 * Destroys this panel
40408 destroy : function(){
40409 this.el.removeAllListeners();
40410 var tempEl = document.createElement("span");
40411 tempEl.appendChild(this.el.dom);
40412 tempEl.innerHTML = "";
40418 * form - if the content panel contains a form - this is a reference to it.
40419 * @type {Roo.form.Form}
40423 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40424 * This contains a reference to it.
40430 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40440 * @param {Object} cfg Xtype definition of item to add.
40444 getChildContainer: function () {
40445 return this.getEl();
40450 var ret = new Roo.factory(cfg);
40455 if (cfg.xtype.match(/^Form$/)) {
40458 //if (this.footer) {
40459 // el = this.footer.container.insertSibling(false, 'before');
40461 el = this.el.createChild();
40464 this.form = new Roo.form.Form(cfg);
40467 if ( this.form.allItems.length) {
40468 this.form.render(el.dom);
40472 // should only have one of theses..
40473 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40474 // views.. should not be just added - used named prop 'view''
40476 cfg.el = this.el.appendChild(document.createElement("div"));
40479 var ret = new Roo.factory(cfg);
40481 ret.render && ret.render(false, ''); // render blank..
40491 * @class Roo.bootstrap.panel.Grid
40492 * @extends Roo.bootstrap.panel.Content
40494 * Create a new GridPanel.
40495 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40496 * @param {Object} config A the config object
40502 Roo.bootstrap.panel.Grid = function(config)
40506 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40507 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40509 config.el = this.wrapper;
40510 //this.el = this.wrapper;
40512 if (config.container) {
40513 // ctor'ed from a Border/panel.grid
40516 this.wrapper.setStyle("overflow", "hidden");
40517 this.wrapper.addClass('roo-grid-container');
40522 if(config.toolbar){
40523 var tool_el = this.wrapper.createChild();
40524 this.toolbar = Roo.factory(config.toolbar);
40526 if (config.toolbar.items) {
40527 ti = config.toolbar.items ;
40528 delete config.toolbar.items ;
40532 this.toolbar.render(tool_el);
40533 for(var i =0;i < ti.length;i++) {
40534 // Roo.log(['add child', items[i]]);
40535 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40537 this.toolbar.items = nitems;
40539 delete config.toolbar;
40542 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40543 config.grid.scrollBody = true;;
40544 config.grid.monitorWindowResize = false; // turn off autosizing
40545 config.grid.autoHeight = false;
40546 config.grid.autoWidth = false;
40548 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40550 if (config.background) {
40551 // render grid on panel activation (if panel background)
40552 this.on('activate', function(gp) {
40553 if (!gp.grid.rendered) {
40554 gp.grid.render(this.wrapper);
40555 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40560 this.grid.render(this.wrapper);
40561 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40564 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40565 // ??? needed ??? config.el = this.wrapper;
40570 // xtype created footer. - not sure if will work as we normally have to render first..
40571 if (this.footer && !this.footer.el && this.footer.xtype) {
40573 var ctr = this.grid.getView().getFooterPanel(true);
40574 this.footer.dataSource = this.grid.dataSource;
40575 this.footer = Roo.factory(this.footer, Roo);
40576 this.footer.render(ctr);
40586 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40587 getId : function(){
40588 return this.grid.id;
40592 * Returns the grid for this panel
40593 * @return {Roo.bootstrap.Table}
40595 getGrid : function(){
40599 setSize : function(width, height){
40600 if(!this.ignoreResize(width, height)){
40601 var grid = this.grid;
40602 var size = this.adjustForComponents(width, height);
40603 // tfoot is not a footer?
40606 var gridel = grid.getGridEl();
40607 gridel.setSize(size.width, size.height);
40609 var tbd = grid.getGridEl().select('tbody', true).first();
40610 var thd = grid.getGridEl().select('thead',true).first();
40611 var tbf= grid.getGridEl().select('tfoot', true).first();
40614 size.height -= tbf.getHeight();
40617 size.height -= thd.getHeight();
40620 tbd.setSize(size.width, size.height );
40621 // this is for the account management tab -seems to work there.
40622 var thd = grid.getGridEl().select('thead',true).first();
40624 // tbd.setSize(size.width, size.height - thd.getHeight());
40633 beforeSlide : function(){
40634 this.grid.getView().scroller.clip();
40637 afterSlide : function(){
40638 this.grid.getView().scroller.unclip();
40641 destroy : function(){
40642 this.grid.destroy();
40644 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40649 * @class Roo.bootstrap.panel.Nest
40650 * @extends Roo.bootstrap.panel.Content
40652 * Create a new Panel, that can contain a layout.Border.
40655 * @param {Roo.BorderLayout} layout The layout for this panel
40656 * @param {String/Object} config A string to set only the title or a config object
40658 Roo.bootstrap.panel.Nest = function(config)
40660 // construct with only one argument..
40661 /* FIXME - implement nicer consturctors
40662 if (layout.layout) {
40664 layout = config.layout;
40665 delete config.layout;
40667 if (layout.xtype && !layout.getEl) {
40668 // then layout needs constructing..
40669 layout = Roo.factory(layout, Roo);
40673 config.el = config.layout.getEl();
40675 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40677 config.layout.monitorWindowResize = false; // turn off autosizing
40678 this.layout = config.layout;
40679 this.layout.getEl().addClass("roo-layout-nested-layout");
40680 this.layout.parent = this;
40687 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40689 setSize : function(width, height){
40690 if(!this.ignoreResize(width, height)){
40691 var size = this.adjustForComponents(width, height);
40692 var el = this.layout.getEl();
40693 if (size.height < 1) {
40694 el.setWidth(size.width);
40696 el.setSize(size.width, size.height);
40698 var touch = el.dom.offsetWidth;
40699 this.layout.layout();
40700 // ie requires a double layout on the first pass
40701 if(Roo.isIE && !this.initialized){
40702 this.initialized = true;
40703 this.layout.layout();
40708 // activate all subpanels if not currently active..
40710 setActiveState : function(active){
40711 this.active = active;
40712 this.setActiveClass(active);
40715 this.fireEvent("deactivate", this);
40719 this.fireEvent("activate", this);
40720 // not sure if this should happen before or after..
40721 if (!this.layout) {
40722 return; // should not happen..
40725 for (var r in this.layout.regions) {
40726 reg = this.layout.getRegion(r);
40727 if (reg.getActivePanel()) {
40728 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40729 reg.setActivePanel(reg.getActivePanel());
40732 if (!reg.panels.length) {
40735 reg.showPanel(reg.getPanel(0));
40744 * Returns the nested BorderLayout for this panel
40745 * @return {Roo.BorderLayout}
40747 getLayout : function(){
40748 return this.layout;
40752 * Adds a xtype elements to the layout of the nested panel
40756 xtype : 'ContentPanel',
40763 xtype : 'NestedLayoutPanel',
40769 items : [ ... list of content panels or nested layout panels.. ]
40773 * @param {Object} cfg Xtype definition of item to add.
40775 addxtype : function(cfg) {
40776 return this.layout.addxtype(cfg);
40781 * Ext JS Library 1.1.1
40782 * Copyright(c) 2006-2007, Ext JS, LLC.
40784 * Originally Released Under LGPL - original licence link has changed is not relivant.
40787 * <script type="text/javascript">
40790 * @class Roo.TabPanel
40791 * @extends Roo.util.Observable
40792 * A lightweight tab container.
40796 // basic tabs 1, built from existing content
40797 var tabs = new Roo.TabPanel("tabs1");
40798 tabs.addTab("script", "View Script");
40799 tabs.addTab("markup", "View Markup");
40800 tabs.activate("script");
40802 // more advanced tabs, built from javascript
40803 var jtabs = new Roo.TabPanel("jtabs");
40804 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40806 // set up the UpdateManager
40807 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40808 var updater = tab2.getUpdateManager();
40809 updater.setDefaultUrl("ajax1.htm");
40810 tab2.on('activate', updater.refresh, updater, true);
40812 // Use setUrl for Ajax loading
40813 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40814 tab3.setUrl("ajax2.htm", null, true);
40817 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40820 jtabs.activate("jtabs-1");
40823 * Create a new TabPanel.
40824 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40825 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40827 Roo.bootstrap.panel.Tabs = function(config){
40829 * The container element for this TabPanel.
40830 * @type Roo.Element
40832 this.el = Roo.get(config.el);
40835 if(typeof config == "boolean"){
40836 this.tabPosition = config ? "bottom" : "top";
40838 Roo.apply(this, config);
40842 if(this.tabPosition == "bottom"){
40843 // if tabs are at the bottom = create the body first.
40844 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40845 this.el.addClass("roo-tabs-bottom");
40847 // next create the tabs holders
40849 if (this.tabPosition == "west"){
40851 var reg = this.region; // fake it..
40853 if (!reg.mgr.parent) {
40856 reg = reg.mgr.parent.region;
40858 Roo.log("got nest?");
40860 if (reg.mgr.getRegion('west')) {
40861 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40862 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40863 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40864 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40865 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40873 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40874 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40875 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40876 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40881 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40884 // finally - if tabs are at the top, then create the body last..
40885 if(this.tabPosition != "bottom"){
40886 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40887 * @type Roo.Element
40889 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40890 this.el.addClass("roo-tabs-top");
40894 this.bodyEl.setStyle("position", "relative");
40896 this.active = null;
40897 this.activateDelegate = this.activate.createDelegate(this);
40902 * Fires when the active tab changes
40903 * @param {Roo.TabPanel} this
40904 * @param {Roo.TabPanelItem} activePanel The new active tab
40908 * @event beforetabchange
40909 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40910 * @param {Roo.TabPanel} this
40911 * @param {Object} e Set cancel to true on this object to cancel the tab change
40912 * @param {Roo.TabPanelItem} tab The tab being changed to
40914 "beforetabchange" : true
40917 Roo.EventManager.onWindowResize(this.onResize, this);
40918 this.cpad = this.el.getPadding("lr");
40919 this.hiddenCount = 0;
40922 // toolbar on the tabbar support...
40923 if (this.toolbar) {
40924 alert("no toolbar support yet");
40925 this.toolbar = false;
40927 var tcfg = this.toolbar;
40928 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40929 this.toolbar = new Roo.Toolbar(tcfg);
40930 if (Roo.isSafari) {
40931 var tbl = tcfg.container.child('table', true);
40932 tbl.setAttribute('width', '100%');
40940 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40943 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40945 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40947 tabPosition : "top",
40949 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40951 currentTabWidth : 0,
40953 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40957 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40961 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40963 preferredTabWidth : 175,
40965 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40967 resizeTabs : false,
40969 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40971 monitorResize : true,
40973 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40975 toolbar : false, // set by caller..
40977 region : false, /// set by caller
40979 disableTooltips : true, // not used yet...
40982 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40983 * @param {String} id The id of the div to use <b>or create</b>
40984 * @param {String} text The text for the tab
40985 * @param {String} content (optional) Content to put in the TabPanelItem body
40986 * @param {Boolean} closable (optional) True to create a close icon on the tab
40987 * @return {Roo.TabPanelItem} The created TabPanelItem
40989 addTab : function(id, text, content, closable, tpl)
40991 var item = new Roo.bootstrap.panel.TabItem({
40995 closable : closable,
40998 this.addTabItem(item);
41000 item.setContent(content);
41006 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41007 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41008 * @return {Roo.TabPanelItem}
41010 getTab : function(id){
41011 return this.items[id];
41015 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41016 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41018 hideTab : function(id){
41019 var t = this.items[id];
41022 this.hiddenCount++;
41023 this.autoSizeTabs();
41028 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41029 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41031 unhideTab : function(id){
41032 var t = this.items[id];
41034 t.setHidden(false);
41035 this.hiddenCount--;
41036 this.autoSizeTabs();
41041 * Adds an existing {@link Roo.TabPanelItem}.
41042 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41044 addTabItem : function(item)
41046 this.items[item.id] = item;
41047 this.items.push(item);
41048 this.autoSizeTabs();
41049 // if(this.resizeTabs){
41050 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41051 // this.autoSizeTabs();
41053 // item.autoSize();
41058 * Removes a {@link Roo.TabPanelItem}.
41059 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41061 removeTab : function(id){
41062 var items = this.items;
41063 var tab = items[id];
41064 if(!tab) { return; }
41065 var index = items.indexOf(tab);
41066 if(this.active == tab && items.length > 1){
41067 var newTab = this.getNextAvailable(index);
41072 this.stripEl.dom.removeChild(tab.pnode.dom);
41073 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41074 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41076 items.splice(index, 1);
41077 delete this.items[tab.id];
41078 tab.fireEvent("close", tab);
41079 tab.purgeListeners();
41080 this.autoSizeTabs();
41083 getNextAvailable : function(start){
41084 var items = this.items;
41086 // look for a next tab that will slide over to
41087 // replace the one being removed
41088 while(index < items.length){
41089 var item = items[++index];
41090 if(item && !item.isHidden()){
41094 // if one isn't found select the previous tab (on the left)
41097 var item = items[--index];
41098 if(item && !item.isHidden()){
41106 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41107 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41109 disableTab : function(id){
41110 var tab = this.items[id];
41111 if(tab && this.active != tab){
41117 * Enables a {@link Roo.TabPanelItem} that is disabled.
41118 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41120 enableTab : function(id){
41121 var tab = this.items[id];
41126 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41127 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41128 * @return {Roo.TabPanelItem} The TabPanelItem.
41130 activate : function(id)
41132 //Roo.log('activite:' + id);
41134 var tab = this.items[id];
41138 if(tab == this.active || tab.disabled){
41142 this.fireEvent("beforetabchange", this, e, tab);
41143 if(e.cancel !== true && !tab.disabled){
41145 this.active.hide();
41147 this.active = this.items[id];
41148 this.active.show();
41149 this.fireEvent("tabchange", this, this.active);
41155 * Gets the active {@link Roo.TabPanelItem}.
41156 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41158 getActiveTab : function(){
41159 return this.active;
41163 * Updates the tab body element to fit the height of the container element
41164 * for overflow scrolling
41165 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41167 syncHeight : function(targetHeight){
41168 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41169 var bm = this.bodyEl.getMargins();
41170 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41171 this.bodyEl.setHeight(newHeight);
41175 onResize : function(){
41176 if(this.monitorResize){
41177 this.autoSizeTabs();
41182 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41184 beginUpdate : function(){
41185 this.updating = true;
41189 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41191 endUpdate : function(){
41192 this.updating = false;
41193 this.autoSizeTabs();
41197 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41199 autoSizeTabs : function()
41201 var count = this.items.length;
41202 var vcount = count - this.hiddenCount;
41205 this.stripEl.hide();
41207 this.stripEl.show();
41210 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41215 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41216 var availWidth = Math.floor(w / vcount);
41217 var b = this.stripBody;
41218 if(b.getWidth() > w){
41219 var tabs = this.items;
41220 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41221 if(availWidth < this.minTabWidth){
41222 /*if(!this.sleft){ // incomplete scrolling code
41223 this.createScrollButtons();
41226 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41229 if(this.currentTabWidth < this.preferredTabWidth){
41230 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41236 * Returns the number of tabs in this TabPanel.
41239 getCount : function(){
41240 return this.items.length;
41244 * Resizes all the tabs to the passed width
41245 * @param {Number} The new width
41247 setTabWidth : function(width){
41248 this.currentTabWidth = width;
41249 for(var i = 0, len = this.items.length; i < len; i++) {
41250 if(!this.items[i].isHidden()) {
41251 this.items[i].setWidth(width);
41257 * Destroys this TabPanel
41258 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41260 destroy : function(removeEl){
41261 Roo.EventManager.removeResizeListener(this.onResize, this);
41262 for(var i = 0, len = this.items.length; i < len; i++){
41263 this.items[i].purgeListeners();
41265 if(removeEl === true){
41266 this.el.update("");
41271 createStrip : function(container)
41273 var strip = document.createElement("nav");
41274 strip.className = Roo.bootstrap.version == 4 ?
41275 "navbar-light bg-light" :
41276 "navbar navbar-default"; //"x-tabs-wrap";
41277 container.appendChild(strip);
41281 createStripList : function(strip)
41283 // div wrapper for retard IE
41284 // returns the "tr" element.
41285 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41286 //'<div class="x-tabs-strip-wrap">'+
41287 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41288 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41289 return strip.firstChild; //.firstChild.firstChild.firstChild;
41291 createBody : function(container)
41293 var body = document.createElement("div");
41294 Roo.id(body, "tab-body");
41295 //Roo.fly(body).addClass("x-tabs-body");
41296 Roo.fly(body).addClass("tab-content");
41297 container.appendChild(body);
41300 createItemBody :function(bodyEl, id){
41301 var body = Roo.getDom(id);
41303 body = document.createElement("div");
41306 //Roo.fly(body).addClass("x-tabs-item-body");
41307 Roo.fly(body).addClass("tab-pane");
41308 bodyEl.insertBefore(body, bodyEl.firstChild);
41312 createStripElements : function(stripEl, text, closable, tpl)
41314 var td = document.createElement("li"); // was td..
41315 td.className = 'nav-item';
41317 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41320 stripEl.appendChild(td);
41322 td.className = "x-tabs-closable";
41323 if(!this.closeTpl){
41324 this.closeTpl = new Roo.Template(
41325 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41326 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41327 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41330 var el = this.closeTpl.overwrite(td, {"text": text});
41331 var close = el.getElementsByTagName("div")[0];
41332 var inner = el.getElementsByTagName("em")[0];
41333 return {"el": el, "close": close, "inner": inner};
41336 // not sure what this is..
41337 // if(!this.tabTpl){
41338 //this.tabTpl = new Roo.Template(
41339 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41340 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41342 // this.tabTpl = new Roo.Template(
41343 // '<a href="#">' +
41344 // '<span unselectable="on"' +
41345 // (this.disableTooltips ? '' : ' title="{text}"') +
41346 // ' >{text}</span></a>'
41352 var template = tpl || this.tabTpl || false;
41355 template = new Roo.Template(
41356 Roo.bootstrap.version == 4 ?
41358 '<a class="nav-link" href="#" unselectable="on"' +
41359 (this.disableTooltips ? '' : ' title="{text}"') +
41362 '<a class="nav-link" href="#">' +
41363 '<span unselectable="on"' +
41364 (this.disableTooltips ? '' : ' title="{text}"') +
41365 ' >{text}</span></a>'
41370 switch (typeof(template)) {
41374 template = new Roo.Template(template);
41380 var el = template.overwrite(td, {"text": text});
41382 var inner = el.getElementsByTagName("span")[0];
41384 return {"el": el, "inner": inner};
41392 * @class Roo.TabPanelItem
41393 * @extends Roo.util.Observable
41394 * Represents an individual item (tab plus body) in a TabPanel.
41395 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41396 * @param {String} id The id of this TabPanelItem
41397 * @param {String} text The text for the tab of this TabPanelItem
41398 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41400 Roo.bootstrap.panel.TabItem = function(config){
41402 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41403 * @type Roo.TabPanel
41405 this.tabPanel = config.panel;
41407 * The id for this TabPanelItem
41410 this.id = config.id;
41412 this.disabled = false;
41414 this.text = config.text;
41416 this.loaded = false;
41417 this.closable = config.closable;
41420 * The body element for this TabPanelItem.
41421 * @type Roo.Element
41423 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41424 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41425 this.bodyEl.setStyle("display", "block");
41426 this.bodyEl.setStyle("zoom", "1");
41427 //this.hideAction();
41429 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41431 this.el = Roo.get(els.el);
41432 this.inner = Roo.get(els.inner, true);
41433 this.textEl = Roo.bootstrap.version == 4 ?
41434 this.el : Roo.get(this.el.dom.firstChild, true);
41436 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41437 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41440 // this.el.on("mousedown", this.onTabMouseDown, this);
41441 this.el.on("click", this.onTabClick, this);
41443 if(config.closable){
41444 var c = Roo.get(els.close, true);
41445 c.dom.title = this.closeText;
41446 c.addClassOnOver("close-over");
41447 c.on("click", this.closeClick, this);
41453 * Fires when this tab becomes the active tab.
41454 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41455 * @param {Roo.TabPanelItem} this
41459 * @event beforeclose
41460 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41461 * @param {Roo.TabPanelItem} this
41462 * @param {Object} e Set cancel to true on this object to cancel the close.
41464 "beforeclose": true,
41467 * Fires when this tab is closed.
41468 * @param {Roo.TabPanelItem} this
41472 * @event deactivate
41473 * Fires when this tab is no longer the active tab.
41474 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41475 * @param {Roo.TabPanelItem} this
41477 "deactivate" : true
41479 this.hidden = false;
41481 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41484 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41486 purgeListeners : function(){
41487 Roo.util.Observable.prototype.purgeListeners.call(this);
41488 this.el.removeAllListeners();
41491 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41494 this.status_node.addClass("active");
41497 this.tabPanel.stripWrap.repaint();
41499 this.fireEvent("activate", this.tabPanel, this);
41503 * Returns true if this tab is the active tab.
41504 * @return {Boolean}
41506 isActive : function(){
41507 return this.tabPanel.getActiveTab() == this;
41511 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41514 this.status_node.removeClass("active");
41516 this.fireEvent("deactivate", this.tabPanel, this);
41519 hideAction : function(){
41520 this.bodyEl.hide();
41521 this.bodyEl.setStyle("position", "absolute");
41522 this.bodyEl.setLeft("-20000px");
41523 this.bodyEl.setTop("-20000px");
41526 showAction : function(){
41527 this.bodyEl.setStyle("position", "relative");
41528 this.bodyEl.setTop("");
41529 this.bodyEl.setLeft("");
41530 this.bodyEl.show();
41534 * Set the tooltip for the tab.
41535 * @param {String} tooltip The tab's tooltip
41537 setTooltip : function(text){
41538 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41539 this.textEl.dom.qtip = text;
41540 this.textEl.dom.removeAttribute('title');
41542 this.textEl.dom.title = text;
41546 onTabClick : function(e){
41547 e.preventDefault();
41548 this.tabPanel.activate(this.id);
41551 onTabMouseDown : function(e){
41552 e.preventDefault();
41553 this.tabPanel.activate(this.id);
41556 getWidth : function(){
41557 return this.inner.getWidth();
41560 setWidth : function(width){
41561 var iwidth = width - this.linode.getPadding("lr");
41562 this.inner.setWidth(iwidth);
41563 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41564 this.linode.setWidth(width);
41568 * Show or hide the tab
41569 * @param {Boolean} hidden True to hide or false to show.
41571 setHidden : function(hidden){
41572 this.hidden = hidden;
41573 this.linode.setStyle("display", hidden ? "none" : "");
41577 * Returns true if this tab is "hidden"
41578 * @return {Boolean}
41580 isHidden : function(){
41581 return this.hidden;
41585 * Returns the text for this tab
41588 getText : function(){
41592 autoSize : function(){
41593 //this.el.beginMeasure();
41594 this.textEl.setWidth(1);
41596 * #2804 [new] Tabs in Roojs
41597 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41599 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41600 //this.el.endMeasure();
41604 * Sets the text for the tab (Note: this also sets the tooltip text)
41605 * @param {String} text The tab's text and tooltip
41607 setText : function(text){
41609 this.textEl.update(text);
41610 this.setTooltip(text);
41611 //if(!this.tabPanel.resizeTabs){
41612 // this.autoSize();
41616 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41618 activate : function(){
41619 this.tabPanel.activate(this.id);
41623 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41625 disable : function(){
41626 if(this.tabPanel.active != this){
41627 this.disabled = true;
41628 this.status_node.addClass("disabled");
41633 * Enables this TabPanelItem if it was previously disabled.
41635 enable : function(){
41636 this.disabled = false;
41637 this.status_node.removeClass("disabled");
41641 * Sets the content for this TabPanelItem.
41642 * @param {String} content The content
41643 * @param {Boolean} loadScripts true to look for and load scripts
41645 setContent : function(content, loadScripts){
41646 this.bodyEl.update(content, loadScripts);
41650 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41651 * @return {Roo.UpdateManager} The UpdateManager
41653 getUpdateManager : function(){
41654 return this.bodyEl.getUpdateManager();
41658 * Set a URL to be used to load the content for this TabPanelItem.
41659 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41660 * @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)
41661 * @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)
41662 * @return {Roo.UpdateManager} The UpdateManager
41664 setUrl : function(url, params, loadOnce){
41665 if(this.refreshDelegate){
41666 this.un('activate', this.refreshDelegate);
41668 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41669 this.on("activate", this.refreshDelegate);
41670 return this.bodyEl.getUpdateManager();
41674 _handleRefresh : function(url, params, loadOnce){
41675 if(!loadOnce || !this.loaded){
41676 var updater = this.bodyEl.getUpdateManager();
41677 updater.update(url, params, this._setLoaded.createDelegate(this));
41682 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41683 * Will fail silently if the setUrl method has not been called.
41684 * This does not activate the panel, just updates its content.
41686 refresh : function(){
41687 if(this.refreshDelegate){
41688 this.loaded = false;
41689 this.refreshDelegate();
41694 _setLoaded : function(){
41695 this.loaded = true;
41699 closeClick : function(e){
41702 this.fireEvent("beforeclose", this, o);
41703 if(o.cancel !== true){
41704 this.tabPanel.removeTab(this.id);
41708 * The text displayed in the tooltip for the close icon.
41711 closeText : "Close this tab"
41714 * This script refer to:
41715 * Title: International Telephone Input
41716 * Author: Jack O'Connor
41717 * Code version: v12.1.12
41718 * Availability: https://github.com/jackocnr/intl-tel-input.git
41721 Roo.bootstrap.PhoneInputData = function() {
41724 "Afghanistan (افغانستان)",
41729 "Albania (Shqipëri)",
41734 "Algeria (الجزائر)",
41759 "Antigua and Barbuda",
41769 "Armenia (Հայաստան)",
41785 "Austria (Österreich)",
41790 "Azerbaijan (Azərbaycan)",
41800 "Bahrain (البحرين)",
41805 "Bangladesh (বাংলাদেশ)",
41815 "Belarus (Беларусь)",
41820 "Belgium (België)",
41850 "Bosnia and Herzegovina (Босна и Херцеговина)",
41865 "British Indian Ocean Territory",
41870 "British Virgin Islands",
41880 "Bulgaria (България)",
41890 "Burundi (Uburundi)",
41895 "Cambodia (កម្ពុជា)",
41900 "Cameroon (Cameroun)",
41909 ["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"]
41912 "Cape Verde (Kabu Verdi)",
41917 "Caribbean Netherlands",
41928 "Central African Republic (République centrafricaine)",
41948 "Christmas Island",
41954 "Cocos (Keeling) Islands",
41965 "Comoros (جزر القمر)",
41970 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41975 "Congo (Republic) (Congo-Brazzaville)",
41995 "Croatia (Hrvatska)",
42016 "Czech Republic (Česká republika)",
42021 "Denmark (Danmark)",
42036 "Dominican Republic (República Dominicana)",
42040 ["809", "829", "849"]
42058 "Equatorial Guinea (Guinea Ecuatorial)",
42078 "Falkland Islands (Islas Malvinas)",
42083 "Faroe Islands (Føroyar)",
42104 "French Guiana (Guyane française)",
42109 "French Polynesia (Polynésie française)",
42124 "Georgia (საქართველო)",
42129 "Germany (Deutschland)",
42149 "Greenland (Kalaallit Nunaat)",
42186 "Guinea-Bissau (Guiné Bissau)",
42211 "Hungary (Magyarország)",
42216 "Iceland (Ísland)",
42236 "Iraq (العراق)",
42252 "Israel (ישראל)",
42279 "Jordan (الأردن)",
42284 "Kazakhstan (Казахстан)",
42305 "Kuwait (الكويت)",
42310 "Kyrgyzstan (Кыргызстан)",
42320 "Latvia (Latvija)",
42325 "Lebanon (لبنان)",
42340 "Libya (ليبيا)",
42350 "Lithuania (Lietuva)",
42365 "Macedonia (FYROM) (Македонија)",
42370 "Madagascar (Madagasikara)",
42400 "Marshall Islands",
42410 "Mauritania (موريتانيا)",
42415 "Mauritius (Moris)",
42436 "Moldova (Republica Moldova)",
42446 "Mongolia (Монгол)",
42451 "Montenegro (Crna Gora)",
42461 "Morocco (المغرب)",
42467 "Mozambique (Moçambique)",
42472 "Myanmar (Burma) (မြန်မာ)",
42477 "Namibia (Namibië)",
42492 "Netherlands (Nederland)",
42497 "New Caledonia (Nouvelle-Calédonie)",
42532 "North Korea (조선 민주주의 인민 공화국)",
42537 "Northern Mariana Islands",
42553 "Pakistan (پاکستان)",
42563 "Palestine (فلسطين)",
42573 "Papua New Guinea",
42615 "Réunion (La Réunion)",
42621 "Romania (România)",
42637 "Saint Barthélemy",
42648 "Saint Kitts and Nevis",
42658 "Saint Martin (Saint-Martin (partie française))",
42664 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42669 "Saint Vincent and the Grenadines",
42684 "São Tomé and Príncipe (São Tomé e Príncipe)",
42689 "Saudi Arabia (المملكة العربية السعودية)",
42694 "Senegal (Sénégal)",
42724 "Slovakia (Slovensko)",
42729 "Slovenia (Slovenija)",
42739 "Somalia (Soomaaliya)",
42749 "South Korea (대한민국)",
42754 "South Sudan (جنوب السودان)",
42764 "Sri Lanka (ශ්රී ලංකාව)",
42769 "Sudan (السودان)",
42779 "Svalbard and Jan Mayen",
42790 "Sweden (Sverige)",
42795 "Switzerland (Schweiz)",
42800 "Syria (سوريا)",
42845 "Trinidad and Tobago",
42850 "Tunisia (تونس)",
42855 "Turkey (Türkiye)",
42865 "Turks and Caicos Islands",
42875 "U.S. Virgin Islands",
42885 "Ukraine (Україна)",
42890 "United Arab Emirates (الإمارات العربية المتحدة)",
42912 "Uzbekistan (Oʻzbekiston)",
42922 "Vatican City (Città del Vaticano)",
42933 "Vietnam (Việt Nam)",
42938 "Wallis and Futuna (Wallis-et-Futuna)",
42943 "Western Sahara (الصحراء الغربية)",
42949 "Yemen (اليمن)",
42973 * This script refer to:
42974 * Title: International Telephone Input
42975 * Author: Jack O'Connor
42976 * Code version: v12.1.12
42977 * Availability: https://github.com/jackocnr/intl-tel-input.git
42981 * @class Roo.bootstrap.PhoneInput
42982 * @extends Roo.bootstrap.TriggerField
42983 * An input with International dial-code selection
42985 * @cfg {String} defaultDialCode default '+852'
42986 * @cfg {Array} preferedCountries default []
42989 * Create a new PhoneInput.
42990 * @param {Object} config Configuration options
42993 Roo.bootstrap.PhoneInput = function(config) {
42994 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42997 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42999 listWidth: undefined,
43001 selectedClass: 'active',
43003 invalidClass : "has-warning",
43005 validClass: 'has-success',
43007 allowed: '0123456789',
43012 * @cfg {String} defaultDialCode The default dial code when initializing the input
43014 defaultDialCode: '+852',
43017 * @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
43019 preferedCountries: false,
43021 getAutoCreate : function()
43023 var data = Roo.bootstrap.PhoneInputData();
43024 var align = this.labelAlign || this.parentLabelAlign();
43027 this.allCountries = [];
43028 this.dialCodeMapping = [];
43030 for (var i = 0; i < data.length; i++) {
43032 this.allCountries[i] = {
43036 priority: c[3] || 0,
43037 areaCodes: c[4] || null
43039 this.dialCodeMapping[c[2]] = {
43042 priority: c[3] || 0,
43043 areaCodes: c[4] || null
43055 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43056 maxlength: this.max_length,
43057 cls : 'form-control tel-input',
43058 autocomplete: 'new-password'
43061 var hiddenInput = {
43064 cls: 'hidden-tel-input'
43068 hiddenInput.name = this.name;
43071 if (this.disabled) {
43072 input.disabled = true;
43075 var flag_container = {
43092 cls: this.hasFeedback ? 'has-feedback' : '',
43098 cls: 'dial-code-holder',
43105 cls: 'roo-select2-container input-group',
43112 if (this.fieldLabel.length) {
43115 tooltip: 'This field is required'
43121 cls: 'control-label',
43127 html: this.fieldLabel
43130 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43136 if(this.indicatorpos == 'right') {
43137 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43144 if(align == 'left') {
43152 if(this.labelWidth > 12){
43153 label.style = "width: " + this.labelWidth + 'px';
43155 if(this.labelWidth < 13 && this.labelmd == 0){
43156 this.labelmd = this.labelWidth;
43158 if(this.labellg > 0){
43159 label.cls += ' col-lg-' + this.labellg;
43160 input.cls += ' col-lg-' + (12 - this.labellg);
43162 if(this.labelmd > 0){
43163 label.cls += ' col-md-' + this.labelmd;
43164 container.cls += ' col-md-' + (12 - this.labelmd);
43166 if(this.labelsm > 0){
43167 label.cls += ' col-sm-' + this.labelsm;
43168 container.cls += ' col-sm-' + (12 - this.labelsm);
43170 if(this.labelxs > 0){
43171 label.cls += ' col-xs-' + this.labelxs;
43172 container.cls += ' col-xs-' + (12 - this.labelxs);
43182 var settings = this;
43184 ['xs','sm','md','lg'].map(function(size){
43185 if (settings[size]) {
43186 cfg.cls += ' col-' + size + '-' + settings[size];
43190 this.store = new Roo.data.Store({
43191 proxy : new Roo.data.MemoryProxy({}),
43192 reader : new Roo.data.JsonReader({
43203 'name' : 'dialCode',
43207 'name' : 'priority',
43211 'name' : 'areaCodes',
43218 if(!this.preferedCountries) {
43219 this.preferedCountries = [
43226 var p = this.preferedCountries.reverse();
43229 for (var i = 0; i < p.length; i++) {
43230 for (var j = 0; j < this.allCountries.length; j++) {
43231 if(this.allCountries[j].iso2 == p[i]) {
43232 var t = this.allCountries[j];
43233 this.allCountries.splice(j,1);
43234 this.allCountries.unshift(t);
43240 this.store.proxy.data = {
43242 data: this.allCountries
43248 initEvents : function()
43251 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43253 this.indicator = this.indicatorEl();
43254 this.flag = this.flagEl();
43255 this.dialCodeHolder = this.dialCodeHolderEl();
43257 this.trigger = this.el.select('div.flag-box',true).first();
43258 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43263 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43264 _this.list.setWidth(lw);
43267 this.list.on('mouseover', this.onViewOver, this);
43268 this.list.on('mousemove', this.onViewMove, this);
43269 this.inputEl().on("keyup", this.onKeyUp, this);
43270 this.inputEl().on("keypress", this.onKeyPress, this);
43272 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43274 this.view = new Roo.View(this.list, this.tpl, {
43275 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43278 this.view.on('click', this.onViewClick, this);
43279 this.setValue(this.defaultDialCode);
43282 onTriggerClick : function(e)
43284 Roo.log('trigger click');
43289 if(this.isExpanded()){
43291 this.hasFocus = false;
43293 this.store.load({});
43294 this.hasFocus = true;
43299 isExpanded : function()
43301 return this.list.isVisible();
43304 collapse : function()
43306 if(!this.isExpanded()){
43310 Roo.get(document).un('mousedown', this.collapseIf, this);
43311 Roo.get(document).un('mousewheel', this.collapseIf, this);
43312 this.fireEvent('collapse', this);
43316 expand : function()
43320 if(this.isExpanded() || !this.hasFocus){
43324 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43325 this.list.setWidth(lw);
43328 this.restrictHeight();
43330 Roo.get(document).on('mousedown', this.collapseIf, this);
43331 Roo.get(document).on('mousewheel', this.collapseIf, this);
43333 this.fireEvent('expand', this);
43336 restrictHeight : function()
43338 this.list.alignTo(this.inputEl(), this.listAlign);
43339 this.list.alignTo(this.inputEl(), this.listAlign);
43342 onViewOver : function(e, t)
43344 if(this.inKeyMode){
43347 var item = this.view.findItemFromChild(t);
43350 var index = this.view.indexOf(item);
43351 this.select(index, false);
43356 onViewClick : function(view, doFocus, el, e)
43358 var index = this.view.getSelectedIndexes()[0];
43360 var r = this.store.getAt(index);
43363 this.onSelect(r, index);
43365 if(doFocus !== false && !this.blockFocus){
43366 this.inputEl().focus();
43370 onViewMove : function(e, t)
43372 this.inKeyMode = false;
43375 select : function(index, scrollIntoView)
43377 this.selectedIndex = index;
43378 this.view.select(index);
43379 if(scrollIntoView !== false){
43380 var el = this.view.getNode(index);
43382 this.list.scrollChildIntoView(el, false);
43387 createList : function()
43389 this.list = Roo.get(document.body).createChild({
43391 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43392 style: 'display:none'
43395 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43398 collapseIf : function(e)
43400 var in_combo = e.within(this.el);
43401 var in_list = e.within(this.list);
43402 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43404 if (in_combo || in_list || is_list) {
43410 onSelect : function(record, index)
43412 if(this.fireEvent('beforeselect', this, record, index) !== false){
43414 this.setFlagClass(record.data.iso2);
43415 this.setDialCode(record.data.dialCode);
43416 this.hasFocus = false;
43418 this.fireEvent('select', this, record, index);
43422 flagEl : function()
43424 var flag = this.el.select('div.flag',true).first();
43431 dialCodeHolderEl : function()
43433 var d = this.el.select('input.dial-code-holder',true).first();
43440 setDialCode : function(v)
43442 this.dialCodeHolder.dom.value = '+'+v;
43445 setFlagClass : function(n)
43447 this.flag.dom.className = 'flag '+n;
43450 getValue : function()
43452 var v = this.inputEl().getValue();
43453 if(this.dialCodeHolder) {
43454 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43459 setValue : function(v)
43461 var d = this.getDialCode(v);
43463 //invalid dial code
43464 if(v.length == 0 || !d || d.length == 0) {
43466 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43467 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43473 this.setFlagClass(this.dialCodeMapping[d].iso2);
43474 this.setDialCode(d);
43475 this.inputEl().dom.value = v.replace('+'+d,'');
43476 this.hiddenEl().dom.value = this.getValue();
43481 getDialCode : function(v)
43485 if (v.length == 0) {
43486 return this.dialCodeHolder.dom.value;
43490 if (v.charAt(0) != "+") {
43493 var numericChars = "";
43494 for (var i = 1; i < v.length; i++) {
43495 var c = v.charAt(i);
43498 if (this.dialCodeMapping[numericChars]) {
43499 dialCode = v.substr(1, i);
43501 if (numericChars.length == 4) {
43511 this.setValue(this.defaultDialCode);
43515 hiddenEl : function()
43517 return this.el.select('input.hidden-tel-input',true).first();
43520 // after setting val
43521 onKeyUp : function(e){
43522 this.setValue(this.getValue());
43525 onKeyPress : function(e){
43526 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43533 * @class Roo.bootstrap.MoneyField
43534 * @extends Roo.bootstrap.ComboBox
43535 * Bootstrap MoneyField class
43538 * Create a new MoneyField.
43539 * @param {Object} config Configuration options
43542 Roo.bootstrap.MoneyField = function(config) {
43544 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43548 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43551 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43553 allowDecimals : true,
43555 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43557 decimalSeparator : ".",
43559 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43561 decimalPrecision : 0,
43563 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43565 allowNegative : true,
43567 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43571 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43573 minValue : Number.NEGATIVE_INFINITY,
43575 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43577 maxValue : Number.MAX_VALUE,
43579 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43581 minText : "The minimum value for this field is {0}",
43583 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43585 maxText : "The maximum value for this field is {0}",
43587 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43588 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43590 nanText : "{0} is not a valid number",
43592 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43596 * @cfg {String} defaults currency of the MoneyField
43597 * value should be in lkey
43599 defaultCurrency : false,
43601 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43603 thousandsDelimiter : false,
43605 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43616 getAutoCreate : function()
43618 var align = this.labelAlign || this.parentLabelAlign();
43630 cls : 'form-control roo-money-amount-input',
43631 autocomplete: 'new-password'
43634 var hiddenInput = {
43638 cls: 'hidden-number-input'
43641 if(this.max_length) {
43642 input.maxlength = this.max_length;
43646 hiddenInput.name = this.name;
43649 if (this.disabled) {
43650 input.disabled = true;
43653 var clg = 12 - this.inputlg;
43654 var cmd = 12 - this.inputmd;
43655 var csm = 12 - this.inputsm;
43656 var cxs = 12 - this.inputxs;
43660 cls : 'row roo-money-field',
43664 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43668 cls: 'roo-select2-container input-group',
43672 cls : 'form-control roo-money-currency-input',
43673 autocomplete: 'new-password',
43675 name : this.currencyName
43679 cls : 'input-group-addon',
43693 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43697 cls: this.hasFeedback ? 'has-feedback' : '',
43708 if (this.fieldLabel.length) {
43711 tooltip: 'This field is required'
43717 cls: 'control-label',
43723 html: this.fieldLabel
43726 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43732 if(this.indicatorpos == 'right') {
43733 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43740 if(align == 'left') {
43748 if(this.labelWidth > 12){
43749 label.style = "width: " + this.labelWidth + 'px';
43751 if(this.labelWidth < 13 && this.labelmd == 0){
43752 this.labelmd = this.labelWidth;
43754 if(this.labellg > 0){
43755 label.cls += ' col-lg-' + this.labellg;
43756 input.cls += ' col-lg-' + (12 - this.labellg);
43758 if(this.labelmd > 0){
43759 label.cls += ' col-md-' + this.labelmd;
43760 container.cls += ' col-md-' + (12 - this.labelmd);
43762 if(this.labelsm > 0){
43763 label.cls += ' col-sm-' + this.labelsm;
43764 container.cls += ' col-sm-' + (12 - this.labelsm);
43766 if(this.labelxs > 0){
43767 label.cls += ' col-xs-' + this.labelxs;
43768 container.cls += ' col-xs-' + (12 - this.labelxs);
43779 var settings = this;
43781 ['xs','sm','md','lg'].map(function(size){
43782 if (settings[size]) {
43783 cfg.cls += ' col-' + size + '-' + settings[size];
43790 initEvents : function()
43792 this.indicator = this.indicatorEl();
43794 this.initCurrencyEvent();
43796 this.initNumberEvent();
43799 initCurrencyEvent : function()
43802 throw "can not find store for combo";
43805 this.store = Roo.factory(this.store, Roo.data);
43806 this.store.parent = this;
43810 this.triggerEl = this.el.select('.input-group-addon', true).first();
43812 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43817 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43818 _this.list.setWidth(lw);
43821 this.list.on('mouseover', this.onViewOver, this);
43822 this.list.on('mousemove', this.onViewMove, this);
43823 this.list.on('scroll', this.onViewScroll, this);
43826 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43829 this.view = new Roo.View(this.list, this.tpl, {
43830 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43833 this.view.on('click', this.onViewClick, this);
43835 this.store.on('beforeload', this.onBeforeLoad, this);
43836 this.store.on('load', this.onLoad, this);
43837 this.store.on('loadexception', this.onLoadException, this);
43839 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43840 "up" : function(e){
43841 this.inKeyMode = true;
43845 "down" : function(e){
43846 if(!this.isExpanded()){
43847 this.onTriggerClick();
43849 this.inKeyMode = true;
43854 "enter" : function(e){
43857 if(this.fireEvent("specialkey", this, e)){
43858 this.onViewClick(false);
43864 "esc" : function(e){
43868 "tab" : function(e){
43871 if(this.fireEvent("specialkey", this, e)){
43872 this.onViewClick(false);
43880 doRelay : function(foo, bar, hname){
43881 if(hname == 'down' || this.scope.isExpanded()){
43882 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43890 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43894 initNumberEvent : function(e)
43896 this.inputEl().on("keydown" , this.fireKey, this);
43897 this.inputEl().on("focus", this.onFocus, this);
43898 this.inputEl().on("blur", this.onBlur, this);
43900 this.inputEl().relayEvent('keyup', this);
43902 if(this.indicator){
43903 this.indicator.addClass('invisible');
43906 this.originalValue = this.getValue();
43908 if(this.validationEvent == 'keyup'){
43909 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43910 this.inputEl().on('keyup', this.filterValidation, this);
43912 else if(this.validationEvent !== false){
43913 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43916 if(this.selectOnFocus){
43917 this.on("focus", this.preFocus, this);
43920 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43921 this.inputEl().on("keypress", this.filterKeys, this);
43923 this.inputEl().relayEvent('keypress', this);
43926 var allowed = "0123456789";
43928 if(this.allowDecimals){
43929 allowed += this.decimalSeparator;
43932 if(this.allowNegative){
43936 if(this.thousandsDelimiter) {
43940 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43942 var keyPress = function(e){
43944 var k = e.getKey();
43946 var c = e.getCharCode();
43949 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43950 allowed.indexOf(String.fromCharCode(c)) === -1
43956 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43960 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43965 this.inputEl().on("keypress", keyPress, this);
43969 onTriggerClick : function(e)
43976 this.loadNext = false;
43978 if(this.isExpanded()){
43983 this.hasFocus = true;
43985 if(this.triggerAction == 'all') {
43986 this.doQuery(this.allQuery, true);
43990 this.doQuery(this.getRawValue());
43993 getCurrency : function()
43995 var v = this.currencyEl().getValue();
44000 restrictHeight : function()
44002 this.list.alignTo(this.currencyEl(), this.listAlign);
44003 this.list.alignTo(this.currencyEl(), this.listAlign);
44006 onViewClick : function(view, doFocus, el, e)
44008 var index = this.view.getSelectedIndexes()[0];
44010 var r = this.store.getAt(index);
44013 this.onSelect(r, index);
44017 onSelect : function(record, index){
44019 if(this.fireEvent('beforeselect', this, record, index) !== false){
44021 this.setFromCurrencyData(index > -1 ? record.data : false);
44025 this.fireEvent('select', this, record, index);
44029 setFromCurrencyData : function(o)
44033 this.lastCurrency = o;
44035 if (this.currencyField) {
44036 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44038 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44041 this.lastSelectionText = currency;
44043 //setting default currency
44044 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44045 this.setCurrency(this.defaultCurrency);
44049 this.setCurrency(currency);
44052 setFromData : function(o)
44056 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44058 this.setFromCurrencyData(c);
44063 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44065 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44068 this.setValue(value);
44072 setCurrency : function(v)
44074 this.currencyValue = v;
44077 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44082 setValue : function(v)
44084 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44090 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44092 this.inputEl().dom.value = (v == '') ? '' :
44093 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44095 if(!this.allowZero && v === '0') {
44096 this.hiddenEl().dom.value = '';
44097 this.inputEl().dom.value = '';
44104 getRawValue : function()
44106 var v = this.inputEl().getValue();
44111 getValue : function()
44113 return this.fixPrecision(this.parseValue(this.getRawValue()));
44116 parseValue : function(value)
44118 if(this.thousandsDelimiter) {
44120 r = new RegExp(",", "g");
44121 value = value.replace(r, "");
44124 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44125 return isNaN(value) ? '' : value;
44129 fixPrecision : function(value)
44131 if(this.thousandsDelimiter) {
44133 r = new RegExp(",", "g");
44134 value = value.replace(r, "");
44137 var nan = isNaN(value);
44139 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44140 return nan ? '' : value;
44142 return parseFloat(value).toFixed(this.decimalPrecision);
44145 decimalPrecisionFcn : function(v)
44147 return Math.floor(v);
44150 validateValue : function(value)
44152 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44156 var num = this.parseValue(value);
44159 this.markInvalid(String.format(this.nanText, value));
44163 if(num < this.minValue){
44164 this.markInvalid(String.format(this.minText, this.minValue));
44168 if(num > this.maxValue){
44169 this.markInvalid(String.format(this.maxText, this.maxValue));
44176 validate : function()
44178 if(this.disabled || this.allowBlank){
44183 var currency = this.getCurrency();
44185 if(this.validateValue(this.getRawValue()) && currency.length){
44190 this.markInvalid();
44194 getName: function()
44199 beforeBlur : function()
44205 var v = this.parseValue(this.getRawValue());
44212 onBlur : function()
44216 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44217 //this.el.removeClass(this.focusClass);
44220 this.hasFocus = false;
44222 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44226 var v = this.getValue();
44228 if(String(v) !== String(this.startValue)){
44229 this.fireEvent('change', this, v, this.startValue);
44232 this.fireEvent("blur", this);
44235 inputEl : function()
44237 return this.el.select('.roo-money-amount-input', true).first();
44240 currencyEl : function()
44242 return this.el.select('.roo-money-currency-input', true).first();
44245 hiddenEl : function()
44247 return this.el.select('input.hidden-number-input',true).first();
44251 * @class Roo.bootstrap.BezierSignature
44252 * @extends Roo.bootstrap.Component
44253 * Bootstrap BezierSignature class
44254 * This script refer to:
44255 * Title: Signature Pad
44257 * Availability: https://github.com/szimek/signature_pad
44260 * Create a new BezierSignature
44261 * @param {Object} config The config object
44264 Roo.bootstrap.BezierSignature = function(config){
44265 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44271 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44278 mouse_btn_down: true,
44281 * @cfg {int} canvas height
44283 canvas_height: '200px',
44286 * @cfg {float|function} Radius of a single dot.
44291 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44296 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44301 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44306 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44311 * @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.
44313 bg_color: 'rgba(0, 0, 0, 0)',
44316 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44318 dot_color: 'black',
44321 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44323 velocity_filter_weight: 0.7,
44326 * @cfg {function} Callback when stroke begin.
44331 * @cfg {function} Callback when stroke end.
44335 getAutoCreate : function()
44337 var cls = 'roo-signature column';
44340 cls += ' ' + this.cls;
44350 for(var i = 0; i < col_sizes.length; i++) {
44351 if(this[col_sizes[i]]) {
44352 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44362 cls: 'roo-signature-body',
44366 cls: 'roo-signature-body-canvas',
44367 height: this.canvas_height,
44368 width: this.canvas_width
44375 style: 'display: none'
44383 initEvents: function()
44385 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44387 var canvas = this.canvasEl();
44389 // mouse && touch event swapping...
44390 canvas.dom.style.touchAction = 'none';
44391 canvas.dom.style.msTouchAction = 'none';
44393 this.mouse_btn_down = false;
44394 canvas.on('mousedown', this._handleMouseDown, this);
44395 canvas.on('mousemove', this._handleMouseMove, this);
44396 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44398 if (window.PointerEvent) {
44399 canvas.on('pointerdown', this._handleMouseDown, this);
44400 canvas.on('pointermove', this._handleMouseMove, this);
44401 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44404 if ('ontouchstart' in window) {
44405 canvas.on('touchstart', this._handleTouchStart, this);
44406 canvas.on('touchmove', this._handleTouchMove, this);
44407 canvas.on('touchend', this._handleTouchEnd, this);
44410 Roo.EventManager.onWindowResize(this.resize, this, true);
44412 // file input event
44413 this.fileEl().on('change', this.uploadImage, this);
44420 resize: function(){
44422 var canvas = this.canvasEl().dom;
44423 var ctx = this.canvasElCtx();
44424 var img_data = false;
44426 if(canvas.width > 0) {
44427 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44429 // setting canvas width will clean img data
44432 var style = window.getComputedStyle ?
44433 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44435 var padding_left = parseInt(style.paddingLeft) || 0;
44436 var padding_right = parseInt(style.paddingRight) || 0;
44438 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44441 ctx.putImageData(img_data, 0, 0);
44445 _handleMouseDown: function(e)
44447 if (e.browserEvent.which === 1) {
44448 this.mouse_btn_down = true;
44449 this.strokeBegin(e);
44453 _handleMouseMove: function (e)
44455 if (this.mouse_btn_down) {
44456 this.strokeMoveUpdate(e);
44460 _handleMouseUp: function (e)
44462 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44463 this.mouse_btn_down = false;
44468 _handleTouchStart: function (e) {
44470 e.preventDefault();
44471 if (e.browserEvent.targetTouches.length === 1) {
44472 // var touch = e.browserEvent.changedTouches[0];
44473 // this.strokeBegin(touch);
44475 this.strokeBegin(e); // assume e catching the correct xy...
44479 _handleTouchMove: function (e) {
44480 e.preventDefault();
44481 // var touch = event.targetTouches[0];
44482 // _this._strokeMoveUpdate(touch);
44483 this.strokeMoveUpdate(e);
44486 _handleTouchEnd: function (e) {
44487 var wasCanvasTouched = e.target === this.canvasEl().dom;
44488 if (wasCanvasTouched) {
44489 e.preventDefault();
44490 // var touch = event.changedTouches[0];
44491 // _this._strokeEnd(touch);
44496 reset: function () {
44497 this._lastPoints = [];
44498 this._lastVelocity = 0;
44499 this._lastWidth = (this.min_width + this.max_width) / 2;
44500 this.canvasElCtx().fillStyle = this.dot_color;
44503 strokeMoveUpdate: function(e)
44505 this.strokeUpdate(e);
44507 if (this.throttle) {
44508 this.throttleStroke(this.strokeUpdate, this.throttle);
44511 this.strokeUpdate(e);
44515 strokeBegin: function(e)
44517 var newPointGroup = {
44518 color: this.dot_color,
44522 if (typeof this.onBegin === 'function') {
44526 this.curve_data.push(newPointGroup);
44528 this.strokeUpdate(e);
44531 strokeUpdate: function(e)
44533 var rect = this.canvasEl().dom.getBoundingClientRect();
44534 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44535 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44536 var lastPoints = lastPointGroup.points;
44537 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44538 var isLastPointTooClose = lastPoint
44539 ? point.distanceTo(lastPoint) <= this.min_distance
44541 var color = lastPointGroup.color;
44542 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44543 var curve = this.addPoint(point);
44545 this.drawDot({color: color, point: point});
44548 this.drawCurve({color: color, curve: curve});
44558 strokeEnd: function(e)
44560 this.strokeUpdate(e);
44561 if (typeof this.onEnd === 'function') {
44566 addPoint: function (point) {
44567 var _lastPoints = this._lastPoints;
44568 _lastPoints.push(point);
44569 if (_lastPoints.length > 2) {
44570 if (_lastPoints.length === 3) {
44571 _lastPoints.unshift(_lastPoints[0]);
44573 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44574 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44575 _lastPoints.shift();
44581 calculateCurveWidths: function (startPoint, endPoint) {
44582 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44583 (1 - this.velocity_filter_weight) * this._lastVelocity;
44585 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44588 start: this._lastWidth
44591 this._lastVelocity = velocity;
44592 this._lastWidth = newWidth;
44596 drawDot: function (_a) {
44597 var color = _a.color, point = _a.point;
44598 var ctx = this.canvasElCtx();
44599 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44601 this.drawCurveSegment(point.x, point.y, width);
44603 ctx.fillStyle = color;
44607 drawCurve: function (_a) {
44608 var color = _a.color, curve = _a.curve;
44609 var ctx = this.canvasElCtx();
44610 var widthDelta = curve.endWidth - curve.startWidth;
44611 var drawSteps = Math.floor(curve.length()) * 2;
44613 ctx.fillStyle = color;
44614 for (var i = 0; i < drawSteps; i += 1) {
44615 var t = i / drawSteps;
44621 var x = uuu * curve.startPoint.x;
44622 x += 3 * uu * t * curve.control1.x;
44623 x += 3 * u * tt * curve.control2.x;
44624 x += ttt * curve.endPoint.x;
44625 var y = uuu * curve.startPoint.y;
44626 y += 3 * uu * t * curve.control1.y;
44627 y += 3 * u * tt * curve.control2.y;
44628 y += ttt * curve.endPoint.y;
44629 var width = curve.startWidth + ttt * widthDelta;
44630 this.drawCurveSegment(x, y, width);
44636 drawCurveSegment: function (x, y, width) {
44637 var ctx = this.canvasElCtx();
44639 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44640 this.is_empty = false;
44645 var ctx = this.canvasElCtx();
44646 var canvas = this.canvasEl().dom;
44647 ctx.fillStyle = this.bg_color;
44648 ctx.clearRect(0, 0, canvas.width, canvas.height);
44649 ctx.fillRect(0, 0, canvas.width, canvas.height);
44650 this.curve_data = [];
44652 this.is_empty = true;
44657 return this.el.select('input',true).first();
44660 canvasEl: function()
44662 return this.el.select('canvas',true).first();
44665 canvasElCtx: function()
44667 return this.el.select('canvas',true).first().dom.getContext('2d');
44670 getImage: function(type)
44672 if(this.is_empty) {
44677 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44680 drawFromImage: function(img_src)
44682 var img = new Image();
44684 img.onload = function(){
44685 this.canvasElCtx().drawImage(img, 0, 0);
44690 this.is_empty = false;
44693 selectImage: function()
44695 this.fileEl().dom.click();
44698 uploadImage: function(e)
44700 var reader = new FileReader();
44702 reader.onload = function(e){
44703 var img = new Image();
44704 img.onload = function(){
44706 this.canvasElCtx().drawImage(img, 0, 0);
44708 img.src = e.target.result;
44711 reader.readAsDataURL(e.target.files[0]);
44714 // Bezier Point Constructor
44715 Point: (function () {
44716 function Point(x, y, time) {
44719 this.time = time || Date.now();
44721 Point.prototype.distanceTo = function (start) {
44722 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44724 Point.prototype.equals = function (other) {
44725 return this.x === other.x && this.y === other.y && this.time === other.time;
44727 Point.prototype.velocityFrom = function (start) {
44728 return this.time !== start.time
44729 ? this.distanceTo(start) / (this.time - start.time)
44736 // Bezier Constructor
44737 Bezier: (function () {
44738 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44739 this.startPoint = startPoint;
44740 this.control2 = control2;
44741 this.control1 = control1;
44742 this.endPoint = endPoint;
44743 this.startWidth = startWidth;
44744 this.endWidth = endWidth;
44746 Bezier.fromPoints = function (points, widths, scope) {
44747 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44748 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44749 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44751 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44752 var dx1 = s1.x - s2.x;
44753 var dy1 = s1.y - s2.y;
44754 var dx2 = s2.x - s3.x;
44755 var dy2 = s2.y - s3.y;
44756 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44757 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44758 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44759 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44760 var dxm = m1.x - m2.x;
44761 var dym = m1.y - m2.y;
44762 var k = l2 / (l1 + l2);
44763 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44764 var tx = s2.x - cm.x;
44765 var ty = s2.y - cm.y;
44767 c1: new scope.Point(m1.x + tx, m1.y + ty),
44768 c2: new scope.Point(m2.x + tx, m2.y + ty)
44771 Bezier.prototype.length = function () {
44776 for (var i = 0; i <= steps; i += 1) {
44778 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44779 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44781 var xdiff = cx - px;
44782 var ydiff = cy - py;
44783 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44790 Bezier.prototype.point = function (t, start, c1, c2, end) {
44791 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44792 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44793 + (3.0 * c2 * (1.0 - t) * t * t)
44794 + (end * t * t * t);
44799 throttleStroke: function(fn, wait) {
44800 if (wait === void 0) { wait = 250; }
44802 var timeout = null;
44806 var later = function () {
44807 previous = Date.now();
44809 result = fn.apply(storedContext, storedArgs);
44811 storedContext = null;
44815 return function wrapper() {
44817 for (var _i = 0; _i < arguments.length; _i++) {
44818 args[_i] = arguments[_i];
44820 var now = Date.now();
44821 var remaining = wait - (now - previous);
44822 storedContext = this;
44824 if (remaining <= 0 || remaining > wait) {
44826 clearTimeout(timeout);
44830 result = fn.apply(storedContext, storedArgs);
44832 storedContext = null;
44836 else if (!timeout) {
44837 timeout = window.setTimeout(later, remaining);