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 on_el = 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 if (!el.attr('tooltip')) {
29086 pel = el.findParent("[tooltip]");
29088 bindEl = Roo.get(pel);
29094 // you can not look for children, as if el is the body.. then everythign is the child..
29095 if (!pel && !el.attr('tooltip')) { //
29096 if (!el.select("[tooltip]").elements.length) {
29099 // is the mouse over this child...?
29100 bindEl = el.select("[tooltip]").first();
29101 var xy = ev.getXY();
29102 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29103 //Roo.log("not in region.");
29106 //Roo.log("child element over..");
29109 this.currentEl = el;
29110 this.currentTip.bind(bindEl);
29111 this.currentRegion = Roo.lib.Region.getRegion(dom);
29112 this.currentTip.enter();
29115 leave : function(ev)
29117 var dom = ev.getTarget();
29118 //Roo.log(['leave',dom]);
29119 if (!this.currentEl) {
29124 if (dom != this.currentEl.dom) {
29127 var xy = ev.getXY();
29128 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29131 // only activate leave if mouse cursor is outside... bounding box..
29136 if (this.currentTip) {
29137 this.currentTip.leave();
29139 //Roo.log('clear currentEl');
29140 this.currentEl = false;
29145 'left' : ['r-l', [-2,0], 'right'],
29146 'right' : ['l-r', [2,0], 'left'],
29147 'bottom' : ['t-b', [0,2], 'top'],
29148 'top' : [ 'b-t', [0,-2], 'bottom']
29154 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29159 delay : null, // can be { show : 300 , hide: 500}
29163 hoverState : null, //???
29165 placement : 'bottom',
29169 getAutoCreate : function(){
29176 cls : 'tooltip-arrow arrow'
29179 cls : 'tooltip-inner'
29186 bind : function(el)
29191 initEvents : function()
29193 this.arrowEl = this.el.select('.arrow', true).first();
29194 this.innerEl = this.el.select('.tooltip-inner', true).first();
29197 enter : function () {
29199 if (this.timeout != null) {
29200 clearTimeout(this.timeout);
29203 this.hoverState = 'in';
29204 //Roo.log("enter - show");
29205 if (!this.delay || !this.delay.show) {
29210 this.timeout = setTimeout(function () {
29211 if (_t.hoverState == 'in') {
29214 }, this.delay.show);
29218 clearTimeout(this.timeout);
29220 this.hoverState = 'out';
29221 if (!this.delay || !this.delay.hide) {
29227 this.timeout = setTimeout(function () {
29228 //Roo.log("leave - timeout");
29230 if (_t.hoverState == 'out') {
29232 Roo.bootstrap.Tooltip.currentEl = false;
29237 show : function (msg)
29240 this.render(document.body);
29243 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29245 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29247 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29249 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29250 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29252 var placement = typeof this.placement == 'function' ?
29253 this.placement.call(this, this.el, on_el) :
29256 var autoToken = /\s?auto?\s?/i;
29257 var autoPlace = autoToken.test(placement);
29259 placement = placement.replace(autoToken, '') || 'top';
29263 //this.el.setXY([0,0]);
29265 //this.el.dom.style.display='block';
29267 //this.el.appendTo(on_el);
29269 var p = this.getPosition();
29270 var box = this.el.getBox();
29276 var align = this.alignment[placement];
29278 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29280 if(placement == 'top' || placement == 'bottom'){
29282 placement = 'right';
29285 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29286 placement = 'left';
29289 var scroll = Roo.select('body', true).first().getScroll();
29291 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29295 align = this.alignment[placement];
29297 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29301 var elems = document.getElementsByTagName('div');
29302 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29303 for (var i = 0; i < elems.length; i++) {
29304 var zindex = Number.parseInt(
29305 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29308 if (zindex > highest) {
29315 this.el.dom.style.zIndex = highest;
29317 this.el.alignTo(this.bindEl, align[0],align[1]);
29318 //var arrow = this.el.select('.arrow',true).first();
29319 //arrow.set(align[2],
29321 this.el.addClass(placement);
29322 this.el.addClass("bs-tooltip-"+ placement);
29324 this.el.addClass('in fade show');
29326 this.hoverState = null;
29328 if (this.el.hasClass('fade')) {
29343 //this.el.setXY([0,0]);
29344 this.el.removeClass(['show', 'in']);
29360 * @class Roo.bootstrap.LocationPicker
29361 * @extends Roo.bootstrap.Component
29362 * Bootstrap LocationPicker class
29363 * @cfg {Number} latitude Position when init default 0
29364 * @cfg {Number} longitude Position when init default 0
29365 * @cfg {Number} zoom default 15
29366 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29367 * @cfg {Boolean} mapTypeControl default false
29368 * @cfg {Boolean} disableDoubleClickZoom default false
29369 * @cfg {Boolean} scrollwheel default true
29370 * @cfg {Boolean} streetViewControl default false
29371 * @cfg {Number} radius default 0
29372 * @cfg {String} locationName
29373 * @cfg {Boolean} draggable default true
29374 * @cfg {Boolean} enableAutocomplete default false
29375 * @cfg {Boolean} enableReverseGeocode default true
29376 * @cfg {String} markerTitle
29379 * Create a new LocationPicker
29380 * @param {Object} config The config object
29384 Roo.bootstrap.LocationPicker = function(config){
29386 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29391 * Fires when the picker initialized.
29392 * @param {Roo.bootstrap.LocationPicker} this
29393 * @param {Google Location} location
29397 * @event positionchanged
29398 * Fires when the picker position changed.
29399 * @param {Roo.bootstrap.LocationPicker} this
29400 * @param {Google Location} location
29402 positionchanged : true,
29405 * Fires when the map resize.
29406 * @param {Roo.bootstrap.LocationPicker} this
29411 * Fires when the map show.
29412 * @param {Roo.bootstrap.LocationPicker} this
29417 * Fires when the map hide.
29418 * @param {Roo.bootstrap.LocationPicker} this
29423 * Fires when click the map.
29424 * @param {Roo.bootstrap.LocationPicker} this
29425 * @param {Map event} e
29429 * @event mapRightClick
29430 * Fires when right click the map.
29431 * @param {Roo.bootstrap.LocationPicker} this
29432 * @param {Map event} e
29434 mapRightClick : true,
29436 * @event markerClick
29437 * Fires when click the marker.
29438 * @param {Roo.bootstrap.LocationPicker} this
29439 * @param {Map event} e
29441 markerClick : true,
29443 * @event markerRightClick
29444 * Fires when right click the marker.
29445 * @param {Roo.bootstrap.LocationPicker} this
29446 * @param {Map event} e
29448 markerRightClick : true,
29450 * @event OverlayViewDraw
29451 * Fires when OverlayView Draw
29452 * @param {Roo.bootstrap.LocationPicker} this
29454 OverlayViewDraw : true,
29456 * @event OverlayViewOnAdd
29457 * Fires when OverlayView Draw
29458 * @param {Roo.bootstrap.LocationPicker} this
29460 OverlayViewOnAdd : true,
29462 * @event OverlayViewOnRemove
29463 * Fires when OverlayView Draw
29464 * @param {Roo.bootstrap.LocationPicker} this
29466 OverlayViewOnRemove : true,
29468 * @event OverlayViewShow
29469 * Fires when OverlayView Draw
29470 * @param {Roo.bootstrap.LocationPicker} this
29471 * @param {Pixel} cpx
29473 OverlayViewShow : true,
29475 * @event OverlayViewHide
29476 * Fires when OverlayView Draw
29477 * @param {Roo.bootstrap.LocationPicker} this
29479 OverlayViewHide : true,
29481 * @event loadexception
29482 * Fires when load google lib failed.
29483 * @param {Roo.bootstrap.LocationPicker} this
29485 loadexception : true
29490 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29492 gMapContext: false,
29498 mapTypeControl: false,
29499 disableDoubleClickZoom: false,
29501 streetViewControl: false,
29505 enableAutocomplete: false,
29506 enableReverseGeocode: true,
29509 getAutoCreate: function()
29514 cls: 'roo-location-picker'
29520 initEvents: function(ct, position)
29522 if(!this.el.getWidth() || this.isApplied()){
29526 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29531 initial: function()
29533 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29534 this.fireEvent('loadexception', this);
29538 if(!this.mapTypeId){
29539 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29542 this.gMapContext = this.GMapContext();
29544 this.initOverlayView();
29546 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29550 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29551 _this.setPosition(_this.gMapContext.marker.position);
29554 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29555 _this.fireEvent('mapClick', this, event);
29559 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29560 _this.fireEvent('mapRightClick', this, event);
29564 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29565 _this.fireEvent('markerClick', this, event);
29569 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29570 _this.fireEvent('markerRightClick', this, event);
29574 this.setPosition(this.gMapContext.location);
29576 this.fireEvent('initial', this, this.gMapContext.location);
29579 initOverlayView: function()
29583 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29587 _this.fireEvent('OverlayViewDraw', _this);
29592 _this.fireEvent('OverlayViewOnAdd', _this);
29595 onRemove: function()
29597 _this.fireEvent('OverlayViewOnRemove', _this);
29600 show: function(cpx)
29602 _this.fireEvent('OverlayViewShow', _this, cpx);
29607 _this.fireEvent('OverlayViewHide', _this);
29613 fromLatLngToContainerPixel: function(event)
29615 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29618 isApplied: function()
29620 return this.getGmapContext() == false ? false : true;
29623 getGmapContext: function()
29625 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29628 GMapContext: function()
29630 var position = new google.maps.LatLng(this.latitude, this.longitude);
29632 var _map = new google.maps.Map(this.el.dom, {
29635 mapTypeId: this.mapTypeId,
29636 mapTypeControl: this.mapTypeControl,
29637 disableDoubleClickZoom: this.disableDoubleClickZoom,
29638 scrollwheel: this.scrollwheel,
29639 streetViewControl: this.streetViewControl,
29640 locationName: this.locationName,
29641 draggable: this.draggable,
29642 enableAutocomplete: this.enableAutocomplete,
29643 enableReverseGeocode: this.enableReverseGeocode
29646 var _marker = new google.maps.Marker({
29647 position: position,
29649 title: this.markerTitle,
29650 draggable: this.draggable
29657 location: position,
29658 radius: this.radius,
29659 locationName: this.locationName,
29660 addressComponents: {
29661 formatted_address: null,
29662 addressLine1: null,
29663 addressLine2: null,
29665 streetNumber: null,
29669 stateOrProvince: null
29672 domContainer: this.el.dom,
29673 geodecoder: new google.maps.Geocoder()
29677 drawCircle: function(center, radius, options)
29679 if (this.gMapContext.circle != null) {
29680 this.gMapContext.circle.setMap(null);
29684 options = Roo.apply({}, options, {
29685 strokeColor: "#0000FF",
29686 strokeOpacity: .35,
29688 fillColor: "#0000FF",
29692 options.map = this.gMapContext.map;
29693 options.radius = radius;
29694 options.center = center;
29695 this.gMapContext.circle = new google.maps.Circle(options);
29696 return this.gMapContext.circle;
29702 setPosition: function(location)
29704 this.gMapContext.location = location;
29705 this.gMapContext.marker.setPosition(location);
29706 this.gMapContext.map.panTo(location);
29707 this.drawCircle(location, this.gMapContext.radius, {});
29711 if (this.gMapContext.settings.enableReverseGeocode) {
29712 this.gMapContext.geodecoder.geocode({
29713 latLng: this.gMapContext.location
29714 }, function(results, status) {
29716 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29717 _this.gMapContext.locationName = results[0].formatted_address;
29718 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29720 _this.fireEvent('positionchanged', this, location);
29727 this.fireEvent('positionchanged', this, location);
29732 google.maps.event.trigger(this.gMapContext.map, "resize");
29734 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29736 this.fireEvent('resize', this);
29739 setPositionByLatLng: function(latitude, longitude)
29741 this.setPosition(new google.maps.LatLng(latitude, longitude));
29744 getCurrentPosition: function()
29747 latitude: this.gMapContext.location.lat(),
29748 longitude: this.gMapContext.location.lng()
29752 getAddressName: function()
29754 return this.gMapContext.locationName;
29757 getAddressComponents: function()
29759 return this.gMapContext.addressComponents;
29762 address_component_from_google_geocode: function(address_components)
29766 for (var i = 0; i < address_components.length; i++) {
29767 var component = address_components[i];
29768 if (component.types.indexOf("postal_code") >= 0) {
29769 result.postalCode = component.short_name;
29770 } else if (component.types.indexOf("street_number") >= 0) {
29771 result.streetNumber = component.short_name;
29772 } else if (component.types.indexOf("route") >= 0) {
29773 result.streetName = component.short_name;
29774 } else if (component.types.indexOf("neighborhood") >= 0) {
29775 result.city = component.short_name;
29776 } else if (component.types.indexOf("locality") >= 0) {
29777 result.city = component.short_name;
29778 } else if (component.types.indexOf("sublocality") >= 0) {
29779 result.district = component.short_name;
29780 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29781 result.stateOrProvince = component.short_name;
29782 } else if (component.types.indexOf("country") >= 0) {
29783 result.country = component.short_name;
29787 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29788 result.addressLine2 = "";
29792 setZoomLevel: function(zoom)
29794 this.gMapContext.map.setZoom(zoom);
29807 this.fireEvent('show', this);
29818 this.fireEvent('hide', this);
29823 Roo.apply(Roo.bootstrap.LocationPicker, {
29825 OverlayView : function(map, options)
29827 options = options || {};
29834 * @class Roo.bootstrap.Alert
29835 * @extends Roo.bootstrap.Component
29836 * Bootstrap Alert class - shows an alert area box
29838 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29839 Enter a valid email address
29842 * @cfg {String} title The title of alert
29843 * @cfg {String} html The content of alert
29844 * @cfg {String} weight ( success | info | warning | danger )
29845 * @cfg {String} fa font-awesomeicon
29846 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29847 * @cfg {Boolean} close true to show a x closer
29851 * Create a new alert
29852 * @param {Object} config The config object
29856 Roo.bootstrap.Alert = function(config){
29857 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29861 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29867 faicon: false, // BC
29871 getAutoCreate : function()
29883 style : this.close ? '' : 'display:none'
29887 cls : 'roo-alert-icon'
29892 cls : 'roo-alert-title',
29897 cls : 'roo-alert-text',
29904 cfg.cn[0].cls += ' fa ' + this.faicon;
29907 cfg.cn[0].cls += ' fa ' + this.fa;
29911 cfg.cls += ' alert-' + this.weight;
29917 initEvents: function()
29919 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29920 this.titleEl = this.el.select('.roo-alert-title',true).first();
29921 this.iconEl = this.el.select('.roo-alert-icon',true).first();
29922 if (this.seconds > 0) {
29923 this.hide.defer(this.seconds, this);
29927 setTitle : function(str)
29929 this.titleEl.dom.innerHTML = str;
29932 setText : function(str)
29934 this.titleEl.dom.innerHTML = str;
29937 setWeight : function(weight)
29940 this.el.removeClass('alert-' + this.weight);
29943 this.weight = weight;
29945 this.el.addClass('alert-' + this.weight);
29948 setIcon : function(icon)
29951 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29954 this.faicon = icon;
29956 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
29977 * @class Roo.bootstrap.UploadCropbox
29978 * @extends Roo.bootstrap.Component
29979 * Bootstrap UploadCropbox class
29980 * @cfg {String} emptyText show when image has been loaded
29981 * @cfg {String} rotateNotify show when image too small to rotate
29982 * @cfg {Number} errorTimeout default 3000
29983 * @cfg {Number} minWidth default 300
29984 * @cfg {Number} minHeight default 300
29985 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29986 * @cfg {Boolean} isDocument (true|false) default false
29987 * @cfg {String} url action url
29988 * @cfg {String} paramName default 'imageUpload'
29989 * @cfg {String} method default POST
29990 * @cfg {Boolean} loadMask (true|false) default true
29991 * @cfg {Boolean} loadingText default 'Loading...'
29994 * Create a new UploadCropbox
29995 * @param {Object} config The config object
29998 Roo.bootstrap.UploadCropbox = function(config){
29999 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30003 * @event beforeselectfile
30004 * Fire before select file
30005 * @param {Roo.bootstrap.UploadCropbox} this
30007 "beforeselectfile" : true,
30010 * Fire after initEvent
30011 * @param {Roo.bootstrap.UploadCropbox} this
30016 * Fire after initEvent
30017 * @param {Roo.bootstrap.UploadCropbox} this
30018 * @param {String} data
30023 * Fire when preparing the file data
30024 * @param {Roo.bootstrap.UploadCropbox} this
30025 * @param {Object} file
30030 * Fire when get exception
30031 * @param {Roo.bootstrap.UploadCropbox} this
30032 * @param {XMLHttpRequest} xhr
30034 "exception" : true,
30036 * @event beforeloadcanvas
30037 * Fire before load the canvas
30038 * @param {Roo.bootstrap.UploadCropbox} this
30039 * @param {String} src
30041 "beforeloadcanvas" : true,
30044 * Fire when trash image
30045 * @param {Roo.bootstrap.UploadCropbox} this
30050 * Fire when download the image
30051 * @param {Roo.bootstrap.UploadCropbox} this
30055 * @event footerbuttonclick
30056 * Fire when footerbuttonclick
30057 * @param {Roo.bootstrap.UploadCropbox} this
30058 * @param {String} type
30060 "footerbuttonclick" : true,
30064 * @param {Roo.bootstrap.UploadCropbox} this
30069 * Fire when rotate the image
30070 * @param {Roo.bootstrap.UploadCropbox} this
30071 * @param {String} pos
30076 * Fire when inspect the file
30077 * @param {Roo.bootstrap.UploadCropbox} this
30078 * @param {Object} file
30083 * Fire when xhr upload the file
30084 * @param {Roo.bootstrap.UploadCropbox} this
30085 * @param {Object} data
30090 * Fire when arrange the file data
30091 * @param {Roo.bootstrap.UploadCropbox} this
30092 * @param {Object} formData
30097 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30100 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30102 emptyText : 'Click to upload image',
30103 rotateNotify : 'Image is too small to rotate',
30104 errorTimeout : 3000,
30118 cropType : 'image/jpeg',
30120 canvasLoaded : false,
30121 isDocument : false,
30123 paramName : 'imageUpload',
30125 loadingText : 'Loading...',
30128 getAutoCreate : function()
30132 cls : 'roo-upload-cropbox',
30136 cls : 'roo-upload-cropbox-selector',
30141 cls : 'roo-upload-cropbox-body',
30142 style : 'cursor:pointer',
30146 cls : 'roo-upload-cropbox-preview'
30150 cls : 'roo-upload-cropbox-thumb'
30154 cls : 'roo-upload-cropbox-empty-notify',
30155 html : this.emptyText
30159 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30160 html : this.rotateNotify
30166 cls : 'roo-upload-cropbox-footer',
30169 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30179 onRender : function(ct, position)
30181 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30183 if (this.buttons.length) {
30185 Roo.each(this.buttons, function(bb) {
30187 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30189 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30195 this.maskEl = this.el;
30199 initEvents : function()
30201 this.urlAPI = (window.createObjectURL && window) ||
30202 (window.URL && URL.revokeObjectURL && URL) ||
30203 (window.webkitURL && webkitURL);
30205 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30206 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30208 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30209 this.selectorEl.hide();
30211 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30212 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30214 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30215 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30216 this.thumbEl.hide();
30218 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30219 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30221 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30222 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30223 this.errorEl.hide();
30225 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30226 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30227 this.footerEl.hide();
30229 this.setThumbBoxSize();
30235 this.fireEvent('initial', this);
30242 window.addEventListener("resize", function() { _this.resize(); } );
30244 this.bodyEl.on('click', this.beforeSelectFile, this);
30247 this.bodyEl.on('touchstart', this.onTouchStart, this);
30248 this.bodyEl.on('touchmove', this.onTouchMove, this);
30249 this.bodyEl.on('touchend', this.onTouchEnd, this);
30253 this.bodyEl.on('mousedown', this.onMouseDown, this);
30254 this.bodyEl.on('mousemove', this.onMouseMove, this);
30255 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30256 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30257 Roo.get(document).on('mouseup', this.onMouseUp, this);
30260 this.selectorEl.on('change', this.onFileSelected, this);
30266 this.baseScale = 1;
30268 this.baseRotate = 1;
30269 this.dragable = false;
30270 this.pinching = false;
30273 this.cropData = false;
30274 this.notifyEl.dom.innerHTML = this.emptyText;
30276 this.selectorEl.dom.value = '';
30280 resize : function()
30282 if(this.fireEvent('resize', this) != false){
30283 this.setThumbBoxPosition();
30284 this.setCanvasPosition();
30288 onFooterButtonClick : function(e, el, o, type)
30291 case 'rotate-left' :
30292 this.onRotateLeft(e);
30294 case 'rotate-right' :
30295 this.onRotateRight(e);
30298 this.beforeSelectFile(e);
30313 this.fireEvent('footerbuttonclick', this, type);
30316 beforeSelectFile : function(e)
30318 e.preventDefault();
30320 if(this.fireEvent('beforeselectfile', this) != false){
30321 this.selectorEl.dom.click();
30325 onFileSelected : function(e)
30327 e.preventDefault();
30329 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30333 var file = this.selectorEl.dom.files[0];
30335 if(this.fireEvent('inspect', this, file) != false){
30336 this.prepare(file);
30341 trash : function(e)
30343 this.fireEvent('trash', this);
30346 download : function(e)
30348 this.fireEvent('download', this);
30351 loadCanvas : function(src)
30353 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30357 this.imageEl = document.createElement('img');
30361 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30363 this.imageEl.src = src;
30367 onLoadCanvas : function()
30369 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30370 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30372 this.bodyEl.un('click', this.beforeSelectFile, this);
30374 this.notifyEl.hide();
30375 this.thumbEl.show();
30376 this.footerEl.show();
30378 this.baseRotateLevel();
30380 if(this.isDocument){
30381 this.setThumbBoxSize();
30384 this.setThumbBoxPosition();
30386 this.baseScaleLevel();
30392 this.canvasLoaded = true;
30395 this.maskEl.unmask();
30400 setCanvasPosition : function()
30402 if(!this.canvasEl){
30406 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30407 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30409 this.previewEl.setLeft(pw);
30410 this.previewEl.setTop(ph);
30414 onMouseDown : function(e)
30418 this.dragable = true;
30419 this.pinching = false;
30421 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30422 this.dragable = false;
30426 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30427 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30431 onMouseMove : function(e)
30435 if(!this.canvasLoaded){
30439 if (!this.dragable){
30443 var minX = Math.ceil(this.thumbEl.getLeft(true));
30444 var minY = Math.ceil(this.thumbEl.getTop(true));
30446 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30447 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30449 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30450 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30452 x = x - this.mouseX;
30453 y = y - this.mouseY;
30455 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30456 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30458 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30459 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30461 this.previewEl.setLeft(bgX);
30462 this.previewEl.setTop(bgY);
30464 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30465 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30468 onMouseUp : function(e)
30472 this.dragable = false;
30475 onMouseWheel : function(e)
30479 this.startScale = this.scale;
30481 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30483 if(!this.zoomable()){
30484 this.scale = this.startScale;
30493 zoomable : function()
30495 var minScale = this.thumbEl.getWidth() / this.minWidth;
30497 if(this.minWidth < this.minHeight){
30498 minScale = this.thumbEl.getHeight() / this.minHeight;
30501 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30502 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30506 (this.rotate == 0 || this.rotate == 180) &&
30508 width > this.imageEl.OriginWidth ||
30509 height > this.imageEl.OriginHeight ||
30510 (width < this.minWidth && height < this.minHeight)
30518 (this.rotate == 90 || this.rotate == 270) &&
30520 width > this.imageEl.OriginWidth ||
30521 height > this.imageEl.OriginHeight ||
30522 (width < this.minHeight && height < this.minWidth)
30529 !this.isDocument &&
30530 (this.rotate == 0 || this.rotate == 180) &&
30532 width < this.minWidth ||
30533 width > this.imageEl.OriginWidth ||
30534 height < this.minHeight ||
30535 height > this.imageEl.OriginHeight
30542 !this.isDocument &&
30543 (this.rotate == 90 || this.rotate == 270) &&
30545 width < this.minHeight ||
30546 width > this.imageEl.OriginWidth ||
30547 height < this.minWidth ||
30548 height > this.imageEl.OriginHeight
30558 onRotateLeft : function(e)
30560 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30562 var minScale = this.thumbEl.getWidth() / this.minWidth;
30564 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30565 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30567 this.startScale = this.scale;
30569 while (this.getScaleLevel() < minScale){
30571 this.scale = this.scale + 1;
30573 if(!this.zoomable()){
30578 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30579 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30584 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30591 this.scale = this.startScale;
30593 this.onRotateFail();
30598 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30600 if(this.isDocument){
30601 this.setThumbBoxSize();
30602 this.setThumbBoxPosition();
30603 this.setCanvasPosition();
30608 this.fireEvent('rotate', this, 'left');
30612 onRotateRight : function(e)
30614 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30616 var minScale = this.thumbEl.getWidth() / this.minWidth;
30618 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30619 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30621 this.startScale = this.scale;
30623 while (this.getScaleLevel() < minScale){
30625 this.scale = this.scale + 1;
30627 if(!this.zoomable()){
30632 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30633 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30638 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30645 this.scale = this.startScale;
30647 this.onRotateFail();
30652 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30654 if(this.isDocument){
30655 this.setThumbBoxSize();
30656 this.setThumbBoxPosition();
30657 this.setCanvasPosition();
30662 this.fireEvent('rotate', this, 'right');
30665 onRotateFail : function()
30667 this.errorEl.show(true);
30671 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30676 this.previewEl.dom.innerHTML = '';
30678 var canvasEl = document.createElement("canvas");
30680 var contextEl = canvasEl.getContext("2d");
30682 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30683 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30684 var center = this.imageEl.OriginWidth / 2;
30686 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30687 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30688 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30689 center = this.imageEl.OriginHeight / 2;
30692 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30694 contextEl.translate(center, center);
30695 contextEl.rotate(this.rotate * Math.PI / 180);
30697 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30699 this.canvasEl = document.createElement("canvas");
30701 this.contextEl = this.canvasEl.getContext("2d");
30703 switch (this.rotate) {
30706 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30707 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30709 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30714 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30715 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30717 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30718 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);
30722 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30727 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30728 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30730 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30731 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);
30735 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);
30740 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30741 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30743 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30744 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30748 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);
30755 this.previewEl.appendChild(this.canvasEl);
30757 this.setCanvasPosition();
30762 if(!this.canvasLoaded){
30766 var imageCanvas = document.createElement("canvas");
30768 var imageContext = imageCanvas.getContext("2d");
30770 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30771 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30773 var center = imageCanvas.width / 2;
30775 imageContext.translate(center, center);
30777 imageContext.rotate(this.rotate * Math.PI / 180);
30779 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30781 var canvas = document.createElement("canvas");
30783 var context = canvas.getContext("2d");
30785 canvas.width = this.minWidth;
30786 canvas.height = this.minHeight;
30788 switch (this.rotate) {
30791 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30792 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30794 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30795 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30797 var targetWidth = this.minWidth - 2 * x;
30798 var targetHeight = this.minHeight - 2 * y;
30802 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30803 scale = targetWidth / width;
30806 if(x > 0 && y == 0){
30807 scale = targetHeight / height;
30810 if(x > 0 && y > 0){
30811 scale = targetWidth / width;
30813 if(width < height){
30814 scale = targetHeight / height;
30818 context.scale(scale, scale);
30820 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30821 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30823 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30824 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30826 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30831 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30832 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30834 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30835 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30837 var targetWidth = this.minWidth - 2 * x;
30838 var targetHeight = this.minHeight - 2 * y;
30842 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30843 scale = targetWidth / width;
30846 if(x > 0 && y == 0){
30847 scale = targetHeight / height;
30850 if(x > 0 && y > 0){
30851 scale = targetWidth / width;
30853 if(width < height){
30854 scale = targetHeight / height;
30858 context.scale(scale, scale);
30860 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30861 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30863 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30864 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30866 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30868 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30873 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30874 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30876 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30877 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30879 var targetWidth = this.minWidth - 2 * x;
30880 var targetHeight = this.minHeight - 2 * y;
30884 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30885 scale = targetWidth / width;
30888 if(x > 0 && y == 0){
30889 scale = targetHeight / height;
30892 if(x > 0 && y > 0){
30893 scale = targetWidth / width;
30895 if(width < height){
30896 scale = targetHeight / height;
30900 context.scale(scale, scale);
30902 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30903 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30905 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30906 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30908 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30909 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30911 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30916 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30917 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30919 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30920 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30922 var targetWidth = this.minWidth - 2 * x;
30923 var targetHeight = this.minHeight - 2 * y;
30927 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30928 scale = targetWidth / width;
30931 if(x > 0 && y == 0){
30932 scale = targetHeight / height;
30935 if(x > 0 && y > 0){
30936 scale = targetWidth / width;
30938 if(width < height){
30939 scale = targetHeight / height;
30943 context.scale(scale, scale);
30945 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30946 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30948 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30949 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30951 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30953 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30960 this.cropData = canvas.toDataURL(this.cropType);
30962 if(this.fireEvent('crop', this, this.cropData) !== false){
30963 this.process(this.file, this.cropData);
30970 setThumbBoxSize : function()
30974 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30975 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30976 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30978 this.minWidth = width;
30979 this.minHeight = height;
30981 if(this.rotate == 90 || this.rotate == 270){
30982 this.minWidth = height;
30983 this.minHeight = width;
30988 width = Math.ceil(this.minWidth * height / this.minHeight);
30990 if(this.minWidth > this.minHeight){
30992 height = Math.ceil(this.minHeight * width / this.minWidth);
30995 this.thumbEl.setStyle({
30996 width : width + 'px',
30997 height : height + 'px'
31004 setThumbBoxPosition : function()
31006 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31007 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31009 this.thumbEl.setLeft(x);
31010 this.thumbEl.setTop(y);
31014 baseRotateLevel : function()
31016 this.baseRotate = 1;
31019 typeof(this.exif) != 'undefined' &&
31020 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31021 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31023 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31026 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31030 baseScaleLevel : function()
31034 if(this.isDocument){
31036 if(this.baseRotate == 6 || this.baseRotate == 8){
31038 height = this.thumbEl.getHeight();
31039 this.baseScale = height / this.imageEl.OriginWidth;
31041 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31042 width = this.thumbEl.getWidth();
31043 this.baseScale = width / this.imageEl.OriginHeight;
31049 height = this.thumbEl.getHeight();
31050 this.baseScale = height / this.imageEl.OriginHeight;
31052 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31053 width = this.thumbEl.getWidth();
31054 this.baseScale = width / this.imageEl.OriginWidth;
31060 if(this.baseRotate == 6 || this.baseRotate == 8){
31062 width = this.thumbEl.getHeight();
31063 this.baseScale = width / this.imageEl.OriginHeight;
31065 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31066 height = this.thumbEl.getWidth();
31067 this.baseScale = height / this.imageEl.OriginHeight;
31070 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31071 height = this.thumbEl.getWidth();
31072 this.baseScale = height / this.imageEl.OriginHeight;
31074 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31075 width = this.thumbEl.getHeight();
31076 this.baseScale = width / this.imageEl.OriginWidth;
31083 width = this.thumbEl.getWidth();
31084 this.baseScale = width / this.imageEl.OriginWidth;
31086 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31087 height = this.thumbEl.getHeight();
31088 this.baseScale = height / this.imageEl.OriginHeight;
31091 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31093 height = this.thumbEl.getHeight();
31094 this.baseScale = height / this.imageEl.OriginHeight;
31096 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31097 width = this.thumbEl.getWidth();
31098 this.baseScale = width / this.imageEl.OriginWidth;
31106 getScaleLevel : function()
31108 return this.baseScale * Math.pow(1.1, this.scale);
31111 onTouchStart : function(e)
31113 if(!this.canvasLoaded){
31114 this.beforeSelectFile(e);
31118 var touches = e.browserEvent.touches;
31124 if(touches.length == 1){
31125 this.onMouseDown(e);
31129 if(touches.length != 2){
31135 for(var i = 0, finger; finger = touches[i]; i++){
31136 coords.push(finger.pageX, finger.pageY);
31139 var x = Math.pow(coords[0] - coords[2], 2);
31140 var y = Math.pow(coords[1] - coords[3], 2);
31142 this.startDistance = Math.sqrt(x + y);
31144 this.startScale = this.scale;
31146 this.pinching = true;
31147 this.dragable = false;
31151 onTouchMove : function(e)
31153 if(!this.pinching && !this.dragable){
31157 var touches = e.browserEvent.touches;
31164 this.onMouseMove(e);
31170 for(var i = 0, finger; finger = touches[i]; i++){
31171 coords.push(finger.pageX, finger.pageY);
31174 var x = Math.pow(coords[0] - coords[2], 2);
31175 var y = Math.pow(coords[1] - coords[3], 2);
31177 this.endDistance = Math.sqrt(x + y);
31179 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31181 if(!this.zoomable()){
31182 this.scale = this.startScale;
31190 onTouchEnd : function(e)
31192 this.pinching = false;
31193 this.dragable = false;
31197 process : function(file, crop)
31200 this.maskEl.mask(this.loadingText);
31203 this.xhr = new XMLHttpRequest();
31205 file.xhr = this.xhr;
31207 this.xhr.open(this.method, this.url, true);
31210 "Accept": "application/json",
31211 "Cache-Control": "no-cache",
31212 "X-Requested-With": "XMLHttpRequest"
31215 for (var headerName in headers) {
31216 var headerValue = headers[headerName];
31218 this.xhr.setRequestHeader(headerName, headerValue);
31224 this.xhr.onload = function()
31226 _this.xhrOnLoad(_this.xhr);
31229 this.xhr.onerror = function()
31231 _this.xhrOnError(_this.xhr);
31234 var formData = new FormData();
31236 formData.append('returnHTML', 'NO');
31239 formData.append('crop', crop);
31242 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31243 formData.append(this.paramName, file, file.name);
31246 if(typeof(file.filename) != 'undefined'){
31247 formData.append('filename', file.filename);
31250 if(typeof(file.mimetype) != 'undefined'){
31251 formData.append('mimetype', file.mimetype);
31254 if(this.fireEvent('arrange', this, formData) != false){
31255 this.xhr.send(formData);
31259 xhrOnLoad : function(xhr)
31262 this.maskEl.unmask();
31265 if (xhr.readyState !== 4) {
31266 this.fireEvent('exception', this, xhr);
31270 var response = Roo.decode(xhr.responseText);
31272 if(!response.success){
31273 this.fireEvent('exception', this, xhr);
31277 var response = Roo.decode(xhr.responseText);
31279 this.fireEvent('upload', this, response);
31283 xhrOnError : function()
31286 this.maskEl.unmask();
31289 Roo.log('xhr on error');
31291 var response = Roo.decode(xhr.responseText);
31297 prepare : function(file)
31300 this.maskEl.mask(this.loadingText);
31306 if(typeof(file) === 'string'){
31307 this.loadCanvas(file);
31311 if(!file || !this.urlAPI){
31316 this.cropType = file.type;
31320 if(this.fireEvent('prepare', this, this.file) != false){
31322 var reader = new FileReader();
31324 reader.onload = function (e) {
31325 if (e.target.error) {
31326 Roo.log(e.target.error);
31330 var buffer = e.target.result,
31331 dataView = new DataView(buffer),
31333 maxOffset = dataView.byteLength - 4,
31337 if (dataView.getUint16(0) === 0xffd8) {
31338 while (offset < maxOffset) {
31339 markerBytes = dataView.getUint16(offset);
31341 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31342 markerLength = dataView.getUint16(offset + 2) + 2;
31343 if (offset + markerLength > dataView.byteLength) {
31344 Roo.log('Invalid meta data: Invalid segment size.');
31348 if(markerBytes == 0xffe1){
31349 _this.parseExifData(
31356 offset += markerLength;
31366 var url = _this.urlAPI.createObjectURL(_this.file);
31368 _this.loadCanvas(url);
31373 reader.readAsArrayBuffer(this.file);
31379 parseExifData : function(dataView, offset, length)
31381 var tiffOffset = offset + 10,
31385 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31386 // No Exif data, might be XMP data instead
31390 // Check for the ASCII code for "Exif" (0x45786966):
31391 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31392 // No Exif data, might be XMP data instead
31395 if (tiffOffset + 8 > dataView.byteLength) {
31396 Roo.log('Invalid Exif data: Invalid segment size.');
31399 // Check for the two null bytes:
31400 if (dataView.getUint16(offset + 8) !== 0x0000) {
31401 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31404 // Check the byte alignment:
31405 switch (dataView.getUint16(tiffOffset)) {
31407 littleEndian = true;
31410 littleEndian = false;
31413 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31416 // Check for the TIFF tag marker (0x002A):
31417 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31418 Roo.log('Invalid Exif data: Missing TIFF marker.');
31421 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31422 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31424 this.parseExifTags(
31427 tiffOffset + dirOffset,
31432 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31437 if (dirOffset + 6 > dataView.byteLength) {
31438 Roo.log('Invalid Exif data: Invalid directory offset.');
31441 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31442 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31443 if (dirEndOffset + 4 > dataView.byteLength) {
31444 Roo.log('Invalid Exif data: Invalid directory size.');
31447 for (i = 0; i < tagsNumber; i += 1) {
31451 dirOffset + 2 + 12 * i, // tag offset
31455 // Return the offset to the next directory:
31456 return dataView.getUint32(dirEndOffset, littleEndian);
31459 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31461 var tag = dataView.getUint16(offset, littleEndian);
31463 this.exif[tag] = this.getExifValue(
31467 dataView.getUint16(offset + 2, littleEndian), // tag type
31468 dataView.getUint32(offset + 4, littleEndian), // tag length
31473 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31475 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31484 Roo.log('Invalid Exif data: Invalid tag type.');
31488 tagSize = tagType.size * length;
31489 // Determine if the value is contained in the dataOffset bytes,
31490 // or if the value at the dataOffset is a pointer to the actual data:
31491 dataOffset = tagSize > 4 ?
31492 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31493 if (dataOffset + tagSize > dataView.byteLength) {
31494 Roo.log('Invalid Exif data: Invalid data offset.');
31497 if (length === 1) {
31498 return tagType.getValue(dataView, dataOffset, littleEndian);
31501 for (i = 0; i < length; i += 1) {
31502 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31505 if (tagType.ascii) {
31507 // Concatenate the chars:
31508 for (i = 0; i < values.length; i += 1) {
31510 // Ignore the terminating NULL byte(s):
31511 if (c === '\u0000') {
31523 Roo.apply(Roo.bootstrap.UploadCropbox, {
31525 'Orientation': 0x0112
31529 1: 0, //'top-left',
31531 3: 180, //'bottom-right',
31532 // 4: 'bottom-left',
31534 6: 90, //'right-top',
31535 // 7: 'right-bottom',
31536 8: 270 //'left-bottom'
31540 // byte, 8-bit unsigned int:
31542 getValue: function (dataView, dataOffset) {
31543 return dataView.getUint8(dataOffset);
31547 // ascii, 8-bit byte:
31549 getValue: function (dataView, dataOffset) {
31550 return String.fromCharCode(dataView.getUint8(dataOffset));
31555 // short, 16 bit int:
31557 getValue: function (dataView, dataOffset, littleEndian) {
31558 return dataView.getUint16(dataOffset, littleEndian);
31562 // long, 32 bit int:
31564 getValue: function (dataView, dataOffset, littleEndian) {
31565 return dataView.getUint32(dataOffset, littleEndian);
31569 // rational = two long values, first is numerator, second is denominator:
31571 getValue: function (dataView, dataOffset, littleEndian) {
31572 return dataView.getUint32(dataOffset, littleEndian) /
31573 dataView.getUint32(dataOffset + 4, littleEndian);
31577 // slong, 32 bit signed int:
31579 getValue: function (dataView, dataOffset, littleEndian) {
31580 return dataView.getInt32(dataOffset, littleEndian);
31584 // srational, two slongs, first is numerator, second is denominator:
31586 getValue: function (dataView, dataOffset, littleEndian) {
31587 return dataView.getInt32(dataOffset, littleEndian) /
31588 dataView.getInt32(dataOffset + 4, littleEndian);
31598 cls : 'btn-group roo-upload-cropbox-rotate-left',
31599 action : 'rotate-left',
31603 cls : 'btn btn-default',
31604 html : '<i class="fa fa-undo"></i>'
31610 cls : 'btn-group roo-upload-cropbox-picture',
31611 action : 'picture',
31615 cls : 'btn btn-default',
31616 html : '<i class="fa fa-picture-o"></i>'
31622 cls : 'btn-group roo-upload-cropbox-rotate-right',
31623 action : 'rotate-right',
31627 cls : 'btn btn-default',
31628 html : '<i class="fa fa-repeat"></i>'
31636 cls : 'btn-group roo-upload-cropbox-rotate-left',
31637 action : 'rotate-left',
31641 cls : 'btn btn-default',
31642 html : '<i class="fa fa-undo"></i>'
31648 cls : 'btn-group roo-upload-cropbox-download',
31649 action : 'download',
31653 cls : 'btn btn-default',
31654 html : '<i class="fa fa-download"></i>'
31660 cls : 'btn-group roo-upload-cropbox-crop',
31665 cls : 'btn btn-default',
31666 html : '<i class="fa fa-crop"></i>'
31672 cls : 'btn-group roo-upload-cropbox-trash',
31677 cls : 'btn btn-default',
31678 html : '<i class="fa fa-trash"></i>'
31684 cls : 'btn-group roo-upload-cropbox-rotate-right',
31685 action : 'rotate-right',
31689 cls : 'btn btn-default',
31690 html : '<i class="fa fa-repeat"></i>'
31698 cls : 'btn-group roo-upload-cropbox-rotate-left',
31699 action : 'rotate-left',
31703 cls : 'btn btn-default',
31704 html : '<i class="fa fa-undo"></i>'
31710 cls : 'btn-group roo-upload-cropbox-rotate-right',
31711 action : 'rotate-right',
31715 cls : 'btn btn-default',
31716 html : '<i class="fa fa-repeat"></i>'
31729 * @class Roo.bootstrap.DocumentManager
31730 * @extends Roo.bootstrap.Component
31731 * Bootstrap DocumentManager class
31732 * @cfg {String} paramName default 'imageUpload'
31733 * @cfg {String} toolTipName default 'filename'
31734 * @cfg {String} method default POST
31735 * @cfg {String} url action url
31736 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31737 * @cfg {Boolean} multiple multiple upload default true
31738 * @cfg {Number} thumbSize default 300
31739 * @cfg {String} fieldLabel
31740 * @cfg {Number} labelWidth default 4
31741 * @cfg {String} labelAlign (left|top) default left
31742 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31743 * @cfg {Number} labellg set the width of label (1-12)
31744 * @cfg {Number} labelmd set the width of label (1-12)
31745 * @cfg {Number} labelsm set the width of label (1-12)
31746 * @cfg {Number} labelxs set the width of label (1-12)
31749 * Create a new DocumentManager
31750 * @param {Object} config The config object
31753 Roo.bootstrap.DocumentManager = function(config){
31754 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31757 this.delegates = [];
31762 * Fire when initial the DocumentManager
31763 * @param {Roo.bootstrap.DocumentManager} this
31768 * inspect selected file
31769 * @param {Roo.bootstrap.DocumentManager} this
31770 * @param {File} file
31775 * Fire when xhr load exception
31776 * @param {Roo.bootstrap.DocumentManager} this
31777 * @param {XMLHttpRequest} xhr
31779 "exception" : true,
31781 * @event afterupload
31782 * Fire when xhr load exception
31783 * @param {Roo.bootstrap.DocumentManager} this
31784 * @param {XMLHttpRequest} xhr
31786 "afterupload" : true,
31789 * prepare the form data
31790 * @param {Roo.bootstrap.DocumentManager} this
31791 * @param {Object} formData
31796 * Fire when remove the file
31797 * @param {Roo.bootstrap.DocumentManager} this
31798 * @param {Object} file
31803 * Fire after refresh the file
31804 * @param {Roo.bootstrap.DocumentManager} this
31809 * Fire after click the image
31810 * @param {Roo.bootstrap.DocumentManager} this
31811 * @param {Object} file
31816 * Fire when upload a image and editable set to true
31817 * @param {Roo.bootstrap.DocumentManager} this
31818 * @param {Object} file
31822 * @event beforeselectfile
31823 * Fire before select file
31824 * @param {Roo.bootstrap.DocumentManager} this
31826 "beforeselectfile" : true,
31829 * Fire before process file
31830 * @param {Roo.bootstrap.DocumentManager} this
31831 * @param {Object} file
31835 * @event previewrendered
31836 * Fire when preview rendered
31837 * @param {Roo.bootstrap.DocumentManager} this
31838 * @param {Object} file
31840 "previewrendered" : true,
31843 "previewResize" : true
31848 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31857 paramName : 'imageUpload',
31858 toolTipName : 'filename',
31861 labelAlign : 'left',
31871 getAutoCreate : function()
31873 var managerWidget = {
31875 cls : 'roo-document-manager',
31879 cls : 'roo-document-manager-selector',
31884 cls : 'roo-document-manager-uploader',
31888 cls : 'roo-document-manager-upload-btn',
31889 html : '<i class="fa fa-plus"></i>'
31900 cls : 'column col-md-12',
31905 if(this.fieldLabel.length){
31910 cls : 'column col-md-12',
31911 html : this.fieldLabel
31915 cls : 'column col-md-12',
31920 if(this.labelAlign == 'left'){
31925 html : this.fieldLabel
31934 if(this.labelWidth > 12){
31935 content[0].style = "width: " + this.labelWidth + 'px';
31938 if(this.labelWidth < 13 && this.labelmd == 0){
31939 this.labelmd = this.labelWidth;
31942 if(this.labellg > 0){
31943 content[0].cls += ' col-lg-' + this.labellg;
31944 content[1].cls += ' col-lg-' + (12 - this.labellg);
31947 if(this.labelmd > 0){
31948 content[0].cls += ' col-md-' + this.labelmd;
31949 content[1].cls += ' col-md-' + (12 - this.labelmd);
31952 if(this.labelsm > 0){
31953 content[0].cls += ' col-sm-' + this.labelsm;
31954 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31957 if(this.labelxs > 0){
31958 content[0].cls += ' col-xs-' + this.labelxs;
31959 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31967 cls : 'row clearfix',
31975 initEvents : function()
31977 this.managerEl = this.el.select('.roo-document-manager', true).first();
31978 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31980 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31981 this.selectorEl.hide();
31984 this.selectorEl.attr('multiple', 'multiple');
31987 this.selectorEl.on('change', this.onFileSelected, this);
31989 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31990 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31992 this.uploader.on('click', this.onUploaderClick, this);
31994 this.renderProgressDialog();
31998 window.addEventListener("resize", function() { _this.refresh(); } );
32000 this.fireEvent('initial', this);
32003 renderProgressDialog : function()
32007 this.progressDialog = new Roo.bootstrap.Modal({
32008 cls : 'roo-document-manager-progress-dialog',
32009 allow_close : false,
32020 btnclick : function() {
32021 _this.uploadCancel();
32027 this.progressDialog.render(Roo.get(document.body));
32029 this.progress = new Roo.bootstrap.Progress({
32030 cls : 'roo-document-manager-progress',
32035 this.progress.render(this.progressDialog.getChildContainer());
32037 this.progressBar = new Roo.bootstrap.ProgressBar({
32038 cls : 'roo-document-manager-progress-bar',
32041 aria_valuemax : 12,
32045 this.progressBar.render(this.progress.getChildContainer());
32048 onUploaderClick : function(e)
32050 e.preventDefault();
32052 if(this.fireEvent('beforeselectfile', this) != false){
32053 this.selectorEl.dom.click();
32058 onFileSelected : function(e)
32060 e.preventDefault();
32062 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32066 Roo.each(this.selectorEl.dom.files, function(file){
32067 if(this.fireEvent('inspect', this, file) != false){
32068 this.files.push(file);
32078 this.selectorEl.dom.value = '';
32080 if(!this.files || !this.files.length){
32084 if(this.boxes > 0 && this.files.length > this.boxes){
32085 this.files = this.files.slice(0, this.boxes);
32088 this.uploader.show();
32090 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32091 this.uploader.hide();
32100 Roo.each(this.files, function(file){
32102 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32103 var f = this.renderPreview(file);
32108 if(file.type.indexOf('image') != -1){
32109 this.delegates.push(
32111 _this.process(file);
32112 }).createDelegate(this)
32120 _this.process(file);
32121 }).createDelegate(this)
32126 this.files = files;
32128 this.delegates = this.delegates.concat(docs);
32130 if(!this.delegates.length){
32135 this.progressBar.aria_valuemax = this.delegates.length;
32142 arrange : function()
32144 if(!this.delegates.length){
32145 this.progressDialog.hide();
32150 var delegate = this.delegates.shift();
32152 this.progressDialog.show();
32154 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32156 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32161 refresh : function()
32163 this.uploader.show();
32165 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32166 this.uploader.hide();
32169 Roo.isTouch ? this.closable(false) : this.closable(true);
32171 this.fireEvent('refresh', this);
32174 onRemove : function(e, el, o)
32176 e.preventDefault();
32178 this.fireEvent('remove', this, o);
32182 remove : function(o)
32186 Roo.each(this.files, function(file){
32187 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32196 this.files = files;
32203 Roo.each(this.files, function(file){
32208 file.target.remove();
32217 onClick : function(e, el, o)
32219 e.preventDefault();
32221 this.fireEvent('click', this, o);
32225 closable : function(closable)
32227 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32229 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32241 xhrOnLoad : function(xhr)
32243 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32247 if (xhr.readyState !== 4) {
32249 this.fireEvent('exception', this, xhr);
32253 var response = Roo.decode(xhr.responseText);
32255 if(!response.success){
32257 this.fireEvent('exception', this, xhr);
32261 var file = this.renderPreview(response.data);
32263 this.files.push(file);
32267 this.fireEvent('afterupload', this, xhr);
32271 xhrOnError : function(xhr)
32273 Roo.log('xhr on error');
32275 var response = Roo.decode(xhr.responseText);
32282 process : function(file)
32284 if(this.fireEvent('process', this, file) !== false){
32285 if(this.editable && file.type.indexOf('image') != -1){
32286 this.fireEvent('edit', this, file);
32290 this.uploadStart(file, false);
32297 uploadStart : function(file, crop)
32299 this.xhr = new XMLHttpRequest();
32301 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32306 file.xhr = this.xhr;
32308 this.managerEl.createChild({
32310 cls : 'roo-document-manager-loading',
32314 tooltip : file.name,
32315 cls : 'roo-document-manager-thumb',
32316 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32322 this.xhr.open(this.method, this.url, true);
32325 "Accept": "application/json",
32326 "Cache-Control": "no-cache",
32327 "X-Requested-With": "XMLHttpRequest"
32330 for (var headerName in headers) {
32331 var headerValue = headers[headerName];
32333 this.xhr.setRequestHeader(headerName, headerValue);
32339 this.xhr.onload = function()
32341 _this.xhrOnLoad(_this.xhr);
32344 this.xhr.onerror = function()
32346 _this.xhrOnError(_this.xhr);
32349 var formData = new FormData();
32351 formData.append('returnHTML', 'NO');
32354 formData.append('crop', crop);
32357 formData.append(this.paramName, file, file.name);
32364 if(this.fireEvent('prepare', this, formData, options) != false){
32366 if(options.manually){
32370 this.xhr.send(formData);
32374 this.uploadCancel();
32377 uploadCancel : function()
32383 this.delegates = [];
32385 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32392 renderPreview : function(file)
32394 if(typeof(file.target) != 'undefined' && file.target){
32398 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32400 var previewEl = this.managerEl.createChild({
32402 cls : 'roo-document-manager-preview',
32406 tooltip : file[this.toolTipName],
32407 cls : 'roo-document-manager-thumb',
32408 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32413 html : '<i class="fa fa-times-circle"></i>'
32418 var close = previewEl.select('button.close', true).first();
32420 close.on('click', this.onRemove, this, file);
32422 file.target = previewEl;
32424 var image = previewEl.select('img', true).first();
32428 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32430 image.on('click', this.onClick, this, file);
32432 this.fireEvent('previewrendered', this, file);
32438 onPreviewLoad : function(file, image)
32440 if(typeof(file.target) == 'undefined' || !file.target){
32444 var width = image.dom.naturalWidth || image.dom.width;
32445 var height = image.dom.naturalHeight || image.dom.height;
32447 if(!this.previewResize) {
32451 if(width > height){
32452 file.target.addClass('wide');
32456 file.target.addClass('tall');
32461 uploadFromSource : function(file, crop)
32463 this.xhr = new XMLHttpRequest();
32465 this.managerEl.createChild({
32467 cls : 'roo-document-manager-loading',
32471 tooltip : file.name,
32472 cls : 'roo-document-manager-thumb',
32473 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32479 this.xhr.open(this.method, this.url, true);
32482 "Accept": "application/json",
32483 "Cache-Control": "no-cache",
32484 "X-Requested-With": "XMLHttpRequest"
32487 for (var headerName in headers) {
32488 var headerValue = headers[headerName];
32490 this.xhr.setRequestHeader(headerName, headerValue);
32496 this.xhr.onload = function()
32498 _this.xhrOnLoad(_this.xhr);
32501 this.xhr.onerror = function()
32503 _this.xhrOnError(_this.xhr);
32506 var formData = new FormData();
32508 formData.append('returnHTML', 'NO');
32510 formData.append('crop', crop);
32512 if(typeof(file.filename) != 'undefined'){
32513 formData.append('filename', file.filename);
32516 if(typeof(file.mimetype) != 'undefined'){
32517 formData.append('mimetype', file.mimetype);
32522 if(this.fireEvent('prepare', this, formData) != false){
32523 this.xhr.send(formData);
32533 * @class Roo.bootstrap.DocumentViewer
32534 * @extends Roo.bootstrap.Component
32535 * Bootstrap DocumentViewer class
32536 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32537 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32540 * Create a new DocumentViewer
32541 * @param {Object} config The config object
32544 Roo.bootstrap.DocumentViewer = function(config){
32545 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32550 * Fire after initEvent
32551 * @param {Roo.bootstrap.DocumentViewer} this
32557 * @param {Roo.bootstrap.DocumentViewer} this
32562 * Fire after download button
32563 * @param {Roo.bootstrap.DocumentViewer} this
32568 * Fire after trash button
32569 * @param {Roo.bootstrap.DocumentViewer} this
32576 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32578 showDownload : true,
32582 getAutoCreate : function()
32586 cls : 'roo-document-viewer',
32590 cls : 'roo-document-viewer-body',
32594 cls : 'roo-document-viewer-thumb',
32598 cls : 'roo-document-viewer-image'
32606 cls : 'roo-document-viewer-footer',
32609 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32613 cls : 'btn-group roo-document-viewer-download',
32617 cls : 'btn btn-default',
32618 html : '<i class="fa fa-download"></i>'
32624 cls : 'btn-group roo-document-viewer-trash',
32628 cls : 'btn btn-default',
32629 html : '<i class="fa fa-trash"></i>'
32642 initEvents : function()
32644 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32645 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32647 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32648 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32650 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32651 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32653 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32654 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32656 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32657 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32659 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32660 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32662 this.bodyEl.on('click', this.onClick, this);
32663 this.downloadBtn.on('click', this.onDownload, this);
32664 this.trashBtn.on('click', this.onTrash, this);
32666 this.downloadBtn.hide();
32667 this.trashBtn.hide();
32669 if(this.showDownload){
32670 this.downloadBtn.show();
32673 if(this.showTrash){
32674 this.trashBtn.show();
32677 if(!this.showDownload && !this.showTrash) {
32678 this.footerEl.hide();
32683 initial : function()
32685 this.fireEvent('initial', this);
32689 onClick : function(e)
32691 e.preventDefault();
32693 this.fireEvent('click', this);
32696 onDownload : function(e)
32698 e.preventDefault();
32700 this.fireEvent('download', this);
32703 onTrash : function(e)
32705 e.preventDefault();
32707 this.fireEvent('trash', this);
32719 * @class Roo.bootstrap.NavProgressBar
32720 * @extends Roo.bootstrap.Component
32721 * Bootstrap NavProgressBar class
32724 * Create a new nav progress bar
32725 * @param {Object} config The config object
32728 Roo.bootstrap.NavProgressBar = function(config){
32729 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32731 this.bullets = this.bullets || [];
32733 // Roo.bootstrap.NavProgressBar.register(this);
32737 * Fires when the active item changes
32738 * @param {Roo.bootstrap.NavProgressBar} this
32739 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32740 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32747 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32752 getAutoCreate : function()
32754 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32758 cls : 'roo-navigation-bar-group',
32762 cls : 'roo-navigation-top-bar'
32766 cls : 'roo-navigation-bullets-bar',
32770 cls : 'roo-navigation-bar'
32777 cls : 'roo-navigation-bottom-bar'
32787 initEvents: function()
32792 onRender : function(ct, position)
32794 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32796 if(this.bullets.length){
32797 Roo.each(this.bullets, function(b){
32806 addItem : function(cfg)
32808 var item = new Roo.bootstrap.NavProgressItem(cfg);
32810 item.parentId = this.id;
32811 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32814 var top = new Roo.bootstrap.Element({
32816 cls : 'roo-navigation-bar-text'
32819 var bottom = new Roo.bootstrap.Element({
32821 cls : 'roo-navigation-bar-text'
32824 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32825 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32827 var topText = new Roo.bootstrap.Element({
32829 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32832 var bottomText = new Roo.bootstrap.Element({
32834 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32837 topText.onRender(top.el, null);
32838 bottomText.onRender(bottom.el, null);
32841 item.bottomEl = bottom;
32844 this.barItems.push(item);
32849 getActive : function()
32851 var active = false;
32853 Roo.each(this.barItems, function(v){
32855 if (!v.isActive()) {
32867 setActiveItem : function(item)
32871 Roo.each(this.barItems, function(v){
32872 if (v.rid == item.rid) {
32876 if (v.isActive()) {
32877 v.setActive(false);
32882 item.setActive(true);
32884 this.fireEvent('changed', this, item, prev);
32887 getBarItem: function(rid)
32891 Roo.each(this.barItems, function(e) {
32892 if (e.rid != rid) {
32903 indexOfItem : function(item)
32907 Roo.each(this.barItems, function(v, i){
32909 if (v.rid != item.rid) {
32920 setActiveNext : function()
32922 var i = this.indexOfItem(this.getActive());
32924 if (i > this.barItems.length) {
32928 this.setActiveItem(this.barItems[i+1]);
32931 setActivePrev : function()
32933 var i = this.indexOfItem(this.getActive());
32939 this.setActiveItem(this.barItems[i-1]);
32942 format : function()
32944 if(!this.barItems.length){
32948 var width = 100 / this.barItems.length;
32950 Roo.each(this.barItems, function(i){
32951 i.el.setStyle('width', width + '%');
32952 i.topEl.el.setStyle('width', width + '%');
32953 i.bottomEl.el.setStyle('width', width + '%');
32962 * Nav Progress Item
32967 * @class Roo.bootstrap.NavProgressItem
32968 * @extends Roo.bootstrap.Component
32969 * Bootstrap NavProgressItem class
32970 * @cfg {String} rid the reference id
32971 * @cfg {Boolean} active (true|false) Is item active default false
32972 * @cfg {Boolean} disabled (true|false) Is item active default false
32973 * @cfg {String} html
32974 * @cfg {String} position (top|bottom) text position default bottom
32975 * @cfg {String} icon show icon instead of number
32978 * Create a new NavProgressItem
32979 * @param {Object} config The config object
32981 Roo.bootstrap.NavProgressItem = function(config){
32982 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32987 * The raw click event for the entire grid.
32988 * @param {Roo.bootstrap.NavProgressItem} this
32989 * @param {Roo.EventObject} e
32996 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33002 position : 'bottom',
33005 getAutoCreate : function()
33007 var iconCls = 'roo-navigation-bar-item-icon';
33009 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33013 cls: 'roo-navigation-bar-item',
33023 cfg.cls += ' active';
33026 cfg.cls += ' disabled';
33032 disable : function()
33034 this.setDisabled(true);
33037 enable : function()
33039 this.setDisabled(false);
33042 initEvents: function()
33044 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33046 this.iconEl.on('click', this.onClick, this);
33049 onClick : function(e)
33051 e.preventDefault();
33057 if(this.fireEvent('click', this, e) === false){
33061 this.parent().setActiveItem(this);
33064 isActive: function ()
33066 return this.active;
33069 setActive : function(state)
33071 if(this.active == state){
33075 this.active = state;
33078 this.el.addClass('active');
33082 this.el.removeClass('active');
33087 setDisabled : function(state)
33089 if(this.disabled == state){
33093 this.disabled = state;
33096 this.el.addClass('disabled');
33100 this.el.removeClass('disabled');
33103 tooltipEl : function()
33105 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33118 * @class Roo.bootstrap.FieldLabel
33119 * @extends Roo.bootstrap.Component
33120 * Bootstrap FieldLabel class
33121 * @cfg {String} html contents of the element
33122 * @cfg {String} tag tag of the element default label
33123 * @cfg {String} cls class of the element
33124 * @cfg {String} target label target
33125 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33126 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33127 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33128 * @cfg {String} iconTooltip default "This field is required"
33129 * @cfg {String} indicatorpos (left|right) default left
33132 * Create a new FieldLabel
33133 * @param {Object} config The config object
33136 Roo.bootstrap.FieldLabel = function(config){
33137 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33142 * Fires after the field has been marked as invalid.
33143 * @param {Roo.form.FieldLabel} this
33144 * @param {String} msg The validation message
33149 * Fires after the field has been validated with no errors.
33150 * @param {Roo.form.FieldLabel} this
33156 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33163 invalidClass : 'has-warning',
33164 validClass : 'has-success',
33165 iconTooltip : 'This field is required',
33166 indicatorpos : 'left',
33168 getAutoCreate : function(){
33171 if (!this.allowBlank) {
33177 cls : 'roo-bootstrap-field-label ' + this.cls,
33182 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33183 tooltip : this.iconTooltip
33192 if(this.indicatorpos == 'right'){
33195 cls : 'roo-bootstrap-field-label ' + this.cls,
33204 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33205 tooltip : this.iconTooltip
33214 initEvents: function()
33216 Roo.bootstrap.Element.superclass.initEvents.call(this);
33218 this.indicator = this.indicatorEl();
33220 if(this.indicator){
33221 this.indicator.removeClass('visible');
33222 this.indicator.addClass('invisible');
33225 Roo.bootstrap.FieldLabel.register(this);
33228 indicatorEl : function()
33230 var indicator = this.el.select('i.roo-required-indicator',true).first();
33241 * Mark this field as valid
33243 markValid : function()
33245 if(this.indicator){
33246 this.indicator.removeClass('visible');
33247 this.indicator.addClass('invisible');
33249 if (Roo.bootstrap.version == 3) {
33250 this.el.removeClass(this.invalidClass);
33251 this.el.addClass(this.validClass);
33253 this.el.removeClass('is-invalid');
33254 this.el.addClass('is-valid');
33258 this.fireEvent('valid', this);
33262 * Mark this field as invalid
33263 * @param {String} msg The validation message
33265 markInvalid : function(msg)
33267 if(this.indicator){
33268 this.indicator.removeClass('invisible');
33269 this.indicator.addClass('visible');
33271 if (Roo.bootstrap.version == 3) {
33272 this.el.removeClass(this.validClass);
33273 this.el.addClass(this.invalidClass);
33275 this.el.removeClass('is-valid');
33276 this.el.addClass('is-invalid');
33280 this.fireEvent('invalid', this, msg);
33286 Roo.apply(Roo.bootstrap.FieldLabel, {
33291 * register a FieldLabel Group
33292 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33294 register : function(label)
33296 if(this.groups.hasOwnProperty(label.target)){
33300 this.groups[label.target] = label;
33304 * fetch a FieldLabel Group based on the target
33305 * @param {string} target
33306 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33308 get: function(target) {
33309 if (typeof(this.groups[target]) == 'undefined') {
33313 return this.groups[target] ;
33322 * page DateSplitField.
33328 * @class Roo.bootstrap.DateSplitField
33329 * @extends Roo.bootstrap.Component
33330 * Bootstrap DateSplitField class
33331 * @cfg {string} fieldLabel - the label associated
33332 * @cfg {Number} labelWidth set the width of label (0-12)
33333 * @cfg {String} labelAlign (top|left)
33334 * @cfg {Boolean} dayAllowBlank (true|false) default false
33335 * @cfg {Boolean} monthAllowBlank (true|false) default false
33336 * @cfg {Boolean} yearAllowBlank (true|false) default false
33337 * @cfg {string} dayPlaceholder
33338 * @cfg {string} monthPlaceholder
33339 * @cfg {string} yearPlaceholder
33340 * @cfg {string} dayFormat default 'd'
33341 * @cfg {string} monthFormat default 'm'
33342 * @cfg {string} yearFormat default 'Y'
33343 * @cfg {Number} labellg set the width of label (1-12)
33344 * @cfg {Number} labelmd set the width of label (1-12)
33345 * @cfg {Number} labelsm set the width of label (1-12)
33346 * @cfg {Number} labelxs set the width of label (1-12)
33350 * Create a new DateSplitField
33351 * @param {Object} config The config object
33354 Roo.bootstrap.DateSplitField = function(config){
33355 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33361 * getting the data of years
33362 * @param {Roo.bootstrap.DateSplitField} this
33363 * @param {Object} years
33368 * getting the data of days
33369 * @param {Roo.bootstrap.DateSplitField} this
33370 * @param {Object} days
33375 * Fires after the field has been marked as invalid.
33376 * @param {Roo.form.Field} this
33377 * @param {String} msg The validation message
33382 * Fires after the field has been validated with no errors.
33383 * @param {Roo.form.Field} this
33389 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33392 labelAlign : 'top',
33394 dayAllowBlank : false,
33395 monthAllowBlank : false,
33396 yearAllowBlank : false,
33397 dayPlaceholder : '',
33398 monthPlaceholder : '',
33399 yearPlaceholder : '',
33403 isFormField : true,
33409 getAutoCreate : function()
33413 cls : 'row roo-date-split-field-group',
33418 cls : 'form-hidden-field roo-date-split-field-group-value',
33424 var labelCls = 'col-md-12';
33425 var contentCls = 'col-md-4';
33427 if(this.fieldLabel){
33431 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33435 html : this.fieldLabel
33440 if(this.labelAlign == 'left'){
33442 if(this.labelWidth > 12){
33443 label.style = "width: " + this.labelWidth + 'px';
33446 if(this.labelWidth < 13 && this.labelmd == 0){
33447 this.labelmd = this.labelWidth;
33450 if(this.labellg > 0){
33451 labelCls = ' col-lg-' + this.labellg;
33452 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33455 if(this.labelmd > 0){
33456 labelCls = ' col-md-' + this.labelmd;
33457 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33460 if(this.labelsm > 0){
33461 labelCls = ' col-sm-' + this.labelsm;
33462 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33465 if(this.labelxs > 0){
33466 labelCls = ' col-xs-' + this.labelxs;
33467 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33471 label.cls += ' ' + labelCls;
33473 cfg.cn.push(label);
33476 Roo.each(['day', 'month', 'year'], function(t){
33479 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33486 inputEl: function ()
33488 return this.el.select('.roo-date-split-field-group-value', true).first();
33491 onRender : function(ct, position)
33495 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33497 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33499 this.dayField = new Roo.bootstrap.ComboBox({
33500 allowBlank : this.dayAllowBlank,
33501 alwaysQuery : true,
33502 displayField : 'value',
33505 forceSelection : true,
33507 placeholder : this.dayPlaceholder,
33508 selectOnFocus : true,
33509 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33510 triggerAction : 'all',
33512 valueField : 'value',
33513 store : new Roo.data.SimpleStore({
33514 data : (function() {
33516 _this.fireEvent('days', _this, days);
33519 fields : [ 'value' ]
33522 select : function (_self, record, index)
33524 _this.setValue(_this.getValue());
33529 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33531 this.monthField = new Roo.bootstrap.MonthField({
33532 after : '<i class=\"fa fa-calendar\"></i>',
33533 allowBlank : this.monthAllowBlank,
33534 placeholder : this.monthPlaceholder,
33537 render : function (_self)
33539 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33540 e.preventDefault();
33544 select : function (_self, oldvalue, newvalue)
33546 _this.setValue(_this.getValue());
33551 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33553 this.yearField = new Roo.bootstrap.ComboBox({
33554 allowBlank : this.yearAllowBlank,
33555 alwaysQuery : true,
33556 displayField : 'value',
33559 forceSelection : true,
33561 placeholder : this.yearPlaceholder,
33562 selectOnFocus : true,
33563 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33564 triggerAction : 'all',
33566 valueField : 'value',
33567 store : new Roo.data.SimpleStore({
33568 data : (function() {
33570 _this.fireEvent('years', _this, years);
33573 fields : [ 'value' ]
33576 select : function (_self, record, index)
33578 _this.setValue(_this.getValue());
33583 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33586 setValue : function(v, format)
33588 this.inputEl.dom.value = v;
33590 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33592 var d = Date.parseDate(v, f);
33599 this.setDay(d.format(this.dayFormat));
33600 this.setMonth(d.format(this.monthFormat));
33601 this.setYear(d.format(this.yearFormat));
33608 setDay : function(v)
33610 this.dayField.setValue(v);
33611 this.inputEl.dom.value = this.getValue();
33616 setMonth : function(v)
33618 this.monthField.setValue(v, true);
33619 this.inputEl.dom.value = this.getValue();
33624 setYear : function(v)
33626 this.yearField.setValue(v);
33627 this.inputEl.dom.value = this.getValue();
33632 getDay : function()
33634 return this.dayField.getValue();
33637 getMonth : function()
33639 return this.monthField.getValue();
33642 getYear : function()
33644 return this.yearField.getValue();
33647 getValue : function()
33649 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33651 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33661 this.inputEl.dom.value = '';
33666 validate : function()
33668 var d = this.dayField.validate();
33669 var m = this.monthField.validate();
33670 var y = this.yearField.validate();
33675 (!this.dayAllowBlank && !d) ||
33676 (!this.monthAllowBlank && !m) ||
33677 (!this.yearAllowBlank && !y)
33682 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33691 this.markInvalid();
33696 markValid : function()
33699 var label = this.el.select('label', true).first();
33700 var icon = this.el.select('i.fa-star', true).first();
33706 this.fireEvent('valid', this);
33710 * Mark this field as invalid
33711 * @param {String} msg The validation message
33713 markInvalid : function(msg)
33716 var label = this.el.select('label', true).first();
33717 var icon = this.el.select('i.fa-star', true).first();
33719 if(label && !icon){
33720 this.el.select('.roo-date-split-field-label', true).createChild({
33722 cls : 'text-danger fa fa-lg fa-star',
33723 tooltip : 'This field is required',
33724 style : 'margin-right:5px;'
33728 this.fireEvent('invalid', this, msg);
33731 clearInvalid : function()
33733 var label = this.el.select('label', true).first();
33734 var icon = this.el.select('i.fa-star', true).first();
33740 this.fireEvent('valid', this);
33743 getName: function()
33753 * http://masonry.desandro.com
33755 * The idea is to render all the bricks based on vertical width...
33757 * The original code extends 'outlayer' - we might need to use that....
33763 * @class Roo.bootstrap.LayoutMasonry
33764 * @extends Roo.bootstrap.Component
33765 * Bootstrap Layout Masonry class
33768 * Create a new Element
33769 * @param {Object} config The config object
33772 Roo.bootstrap.LayoutMasonry = function(config){
33774 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33778 Roo.bootstrap.LayoutMasonry.register(this);
33784 * Fire after layout the items
33785 * @param {Roo.bootstrap.LayoutMasonry} this
33786 * @param {Roo.EventObject} e
33793 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33796 * @cfg {Boolean} isLayoutInstant = no animation?
33798 isLayoutInstant : false, // needed?
33801 * @cfg {Number} boxWidth width of the columns
33806 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33811 * @cfg {Number} padWidth padding below box..
33816 * @cfg {Number} gutter gutter width..
33821 * @cfg {Number} maxCols maximum number of columns
33827 * @cfg {Boolean} isAutoInitial defalut true
33829 isAutoInitial : true,
33834 * @cfg {Boolean} isHorizontal defalut false
33836 isHorizontal : false,
33838 currentSize : null,
33844 bricks: null, //CompositeElement
33848 _isLayoutInited : false,
33850 // isAlternative : false, // only use for vertical layout...
33853 * @cfg {Number} alternativePadWidth padding below box..
33855 alternativePadWidth : 50,
33857 selectedBrick : [],
33859 getAutoCreate : function(){
33861 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33865 cls: 'blog-masonary-wrapper ' + this.cls,
33867 cls : 'mas-boxes masonary'
33874 getChildContainer: function( )
33876 if (this.boxesEl) {
33877 return this.boxesEl;
33880 this.boxesEl = this.el.select('.mas-boxes').first();
33882 return this.boxesEl;
33886 initEvents : function()
33890 if(this.isAutoInitial){
33891 Roo.log('hook children rendered');
33892 this.on('childrenrendered', function() {
33893 Roo.log('children rendered');
33899 initial : function()
33901 this.selectedBrick = [];
33903 this.currentSize = this.el.getBox(true);
33905 Roo.EventManager.onWindowResize(this.resize, this);
33907 if(!this.isAutoInitial){
33915 //this.layout.defer(500,this);
33919 resize : function()
33921 var cs = this.el.getBox(true);
33924 this.currentSize.width == cs.width &&
33925 this.currentSize.x == cs.x &&
33926 this.currentSize.height == cs.height &&
33927 this.currentSize.y == cs.y
33929 Roo.log("no change in with or X or Y");
33933 this.currentSize = cs;
33939 layout : function()
33941 this._resetLayout();
33943 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33945 this.layoutItems( isInstant );
33947 this._isLayoutInited = true;
33949 this.fireEvent('layout', this);
33953 _resetLayout : function()
33955 if(this.isHorizontal){
33956 this.horizontalMeasureColumns();
33960 this.verticalMeasureColumns();
33964 verticalMeasureColumns : function()
33966 this.getContainerWidth();
33968 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33969 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33973 var boxWidth = this.boxWidth + this.padWidth;
33975 if(this.containerWidth < this.boxWidth){
33976 boxWidth = this.containerWidth
33979 var containerWidth = this.containerWidth;
33981 var cols = Math.floor(containerWidth / boxWidth);
33983 this.cols = Math.max( cols, 1 );
33985 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33987 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33989 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33991 this.colWidth = boxWidth + avail - this.padWidth;
33993 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33994 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33997 horizontalMeasureColumns : function()
33999 this.getContainerWidth();
34001 var boxWidth = this.boxWidth;
34003 if(this.containerWidth < boxWidth){
34004 boxWidth = this.containerWidth;
34007 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34009 this.el.setHeight(boxWidth);
34013 getContainerWidth : function()
34015 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34018 layoutItems : function( isInstant )
34020 Roo.log(this.bricks);
34022 var items = Roo.apply([], this.bricks);
34024 if(this.isHorizontal){
34025 this._horizontalLayoutItems( items , isInstant );
34029 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34030 // this._verticalAlternativeLayoutItems( items , isInstant );
34034 this._verticalLayoutItems( items , isInstant );
34038 _verticalLayoutItems : function ( items , isInstant)
34040 if ( !items || !items.length ) {
34045 ['xs', 'xs', 'xs', 'tall'],
34046 ['xs', 'xs', 'tall'],
34047 ['xs', 'xs', 'sm'],
34048 ['xs', 'xs', 'xs'],
34054 ['sm', 'xs', 'xs'],
34058 ['tall', 'xs', 'xs', 'xs'],
34059 ['tall', 'xs', 'xs'],
34071 Roo.each(items, function(item, k){
34073 switch (item.size) {
34074 // these layouts take up a full box,
34085 boxes.push([item]);
34108 var filterPattern = function(box, length)
34116 var pattern = box.slice(0, length);
34120 Roo.each(pattern, function(i){
34121 format.push(i.size);
34124 Roo.each(standard, function(s){
34126 if(String(s) != String(format)){
34135 if(!match && length == 1){
34140 filterPattern(box, length - 1);
34144 queue.push(pattern);
34146 box = box.slice(length, box.length);
34148 filterPattern(box, 4);
34154 Roo.each(boxes, function(box, k){
34160 if(box.length == 1){
34165 filterPattern(box, 4);
34169 this._processVerticalLayoutQueue( queue, isInstant );
34173 // _verticalAlternativeLayoutItems : function( items , isInstant )
34175 // if ( !items || !items.length ) {
34179 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34183 _horizontalLayoutItems : function ( items , isInstant)
34185 if ( !items || !items.length || items.length < 3) {
34191 var eItems = items.slice(0, 3);
34193 items = items.slice(3, items.length);
34196 ['xs', 'xs', 'xs', 'wide'],
34197 ['xs', 'xs', 'wide'],
34198 ['xs', 'xs', 'sm'],
34199 ['xs', 'xs', 'xs'],
34205 ['sm', 'xs', 'xs'],
34209 ['wide', 'xs', 'xs', 'xs'],
34210 ['wide', 'xs', 'xs'],
34223 Roo.each(items, function(item, k){
34225 switch (item.size) {
34236 boxes.push([item]);
34260 var filterPattern = function(box, length)
34268 var pattern = box.slice(0, length);
34272 Roo.each(pattern, function(i){
34273 format.push(i.size);
34276 Roo.each(standard, function(s){
34278 if(String(s) != String(format)){
34287 if(!match && length == 1){
34292 filterPattern(box, length - 1);
34296 queue.push(pattern);
34298 box = box.slice(length, box.length);
34300 filterPattern(box, 4);
34306 Roo.each(boxes, function(box, k){
34312 if(box.length == 1){
34317 filterPattern(box, 4);
34324 var pos = this.el.getBox(true);
34328 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34330 var hit_end = false;
34332 Roo.each(queue, function(box){
34336 Roo.each(box, function(b){
34338 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34348 Roo.each(box, function(b){
34350 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34353 mx = Math.max(mx, b.x);
34357 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34361 Roo.each(box, function(b){
34363 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34377 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34380 /** Sets position of item in DOM
34381 * @param {Element} item
34382 * @param {Number} x - horizontal position
34383 * @param {Number} y - vertical position
34384 * @param {Boolean} isInstant - disables transitions
34386 _processVerticalLayoutQueue : function( queue, isInstant )
34388 var pos = this.el.getBox(true);
34393 for (var i = 0; i < this.cols; i++){
34397 Roo.each(queue, function(box, k){
34399 var col = k % this.cols;
34401 Roo.each(box, function(b,kk){
34403 b.el.position('absolute');
34405 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34406 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34408 if(b.size == 'md-left' || b.size == 'md-right'){
34409 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34410 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34413 b.el.setWidth(width);
34414 b.el.setHeight(height);
34416 b.el.select('iframe',true).setSize(width,height);
34420 for (var i = 0; i < this.cols; i++){
34422 if(maxY[i] < maxY[col]){
34427 col = Math.min(col, i);
34431 x = pos.x + col * (this.colWidth + this.padWidth);
34435 var positions = [];
34437 switch (box.length){
34439 positions = this.getVerticalOneBoxColPositions(x, y, box);
34442 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34445 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34448 positions = this.getVerticalFourBoxColPositions(x, y, box);
34454 Roo.each(box, function(b,kk){
34456 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34458 var sz = b.el.getSize();
34460 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34468 for (var i = 0; i < this.cols; i++){
34469 mY = Math.max(mY, maxY[i]);
34472 this.el.setHeight(mY - pos.y);
34476 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34478 // var pos = this.el.getBox(true);
34481 // var maxX = pos.right;
34483 // var maxHeight = 0;
34485 // Roo.each(items, function(item, k){
34489 // item.el.position('absolute');
34491 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34493 // item.el.setWidth(width);
34495 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34497 // item.el.setHeight(height);
34500 // item.el.setXY([x, y], isInstant ? false : true);
34502 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34505 // y = y + height + this.alternativePadWidth;
34507 // maxHeight = maxHeight + height + this.alternativePadWidth;
34511 // this.el.setHeight(maxHeight);
34515 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34517 var pos = this.el.getBox(true);
34522 var maxX = pos.right;
34524 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34526 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34528 Roo.each(queue, function(box, k){
34530 Roo.each(box, function(b, kk){
34532 b.el.position('absolute');
34534 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34535 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34537 if(b.size == 'md-left' || b.size == 'md-right'){
34538 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34539 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34542 b.el.setWidth(width);
34543 b.el.setHeight(height);
34551 var positions = [];
34553 switch (box.length){
34555 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34558 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34561 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34564 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34570 Roo.each(box, function(b,kk){
34572 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34574 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34582 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34584 Roo.each(eItems, function(b,k){
34586 b.size = (k == 0) ? 'sm' : 'xs';
34587 b.x = (k == 0) ? 2 : 1;
34588 b.y = (k == 0) ? 2 : 1;
34590 b.el.position('absolute');
34592 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34594 b.el.setWidth(width);
34596 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34598 b.el.setHeight(height);
34602 var positions = [];
34605 x : maxX - this.unitWidth * 2 - this.gutter,
34610 x : maxX - this.unitWidth,
34611 y : minY + (this.unitWidth + this.gutter) * 2
34615 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34619 Roo.each(eItems, function(b,k){
34621 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34627 getVerticalOneBoxColPositions : function(x, y, box)
34631 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34633 if(box[0].size == 'md-left'){
34637 if(box[0].size == 'md-right'){
34642 x : x + (this.unitWidth + this.gutter) * rand,
34649 getVerticalTwoBoxColPositions : function(x, y, box)
34653 if(box[0].size == 'xs'){
34657 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34661 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34675 x : x + (this.unitWidth + this.gutter) * 2,
34676 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34683 getVerticalThreeBoxColPositions : function(x, y, box)
34687 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34695 x : x + (this.unitWidth + this.gutter) * 1,
34700 x : x + (this.unitWidth + this.gutter) * 2,
34708 if(box[0].size == 'xs' && box[1].size == 'xs'){
34717 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34721 x : x + (this.unitWidth + this.gutter) * 1,
34735 x : x + (this.unitWidth + this.gutter) * 2,
34740 x : x + (this.unitWidth + this.gutter) * 2,
34741 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34748 getVerticalFourBoxColPositions : function(x, y, box)
34752 if(box[0].size == 'xs'){
34761 y : y + (this.unitHeight + this.gutter) * 1
34766 y : y + (this.unitHeight + this.gutter) * 2
34770 x : x + (this.unitWidth + this.gutter) * 1,
34784 x : x + (this.unitWidth + this.gutter) * 2,
34789 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34790 y : y + (this.unitHeight + this.gutter) * 1
34794 x : x + (this.unitWidth + this.gutter) * 2,
34795 y : y + (this.unitWidth + this.gutter) * 2
34802 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34806 if(box[0].size == 'md-left'){
34808 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34815 if(box[0].size == 'md-right'){
34817 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34818 y : minY + (this.unitWidth + this.gutter) * 1
34824 var rand = Math.floor(Math.random() * (4 - box[0].y));
34827 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34828 y : minY + (this.unitWidth + this.gutter) * rand
34835 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34839 if(box[0].size == 'xs'){
34842 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34847 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34848 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34856 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34861 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34862 y : minY + (this.unitWidth + this.gutter) * 2
34869 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34873 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34876 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34881 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34882 y : minY + (this.unitWidth + this.gutter) * 1
34886 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34887 y : minY + (this.unitWidth + this.gutter) * 2
34894 if(box[0].size == 'xs' && box[1].size == 'xs'){
34897 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34902 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34907 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34908 y : minY + (this.unitWidth + this.gutter) * 1
34916 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34921 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34922 y : minY + (this.unitWidth + this.gutter) * 2
34926 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34927 y : minY + (this.unitWidth + this.gutter) * 2
34934 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34938 if(box[0].size == 'xs'){
34941 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34946 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34951 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),
34956 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34957 y : minY + (this.unitWidth + this.gutter) * 1
34965 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34970 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34971 y : minY + (this.unitWidth + this.gutter) * 2
34975 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34976 y : minY + (this.unitWidth + this.gutter) * 2
34980 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),
34981 y : minY + (this.unitWidth + this.gutter) * 2
34989 * remove a Masonry Brick
34990 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34992 removeBrick : function(brick_id)
34998 for (var i = 0; i<this.bricks.length; i++) {
34999 if (this.bricks[i].id == brick_id) {
35000 this.bricks.splice(i,1);
35001 this.el.dom.removeChild(Roo.get(brick_id).dom);
35008 * adds a Masonry Brick
35009 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35011 addBrick : function(cfg)
35013 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35014 //this.register(cn);
35015 cn.parentId = this.id;
35016 cn.render(this.el);
35021 * register a Masonry Brick
35022 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35025 register : function(brick)
35027 this.bricks.push(brick);
35028 brick.masonryId = this.id;
35032 * clear all the Masonry Brick
35034 clearAll : function()
35037 //this.getChildContainer().dom.innerHTML = "";
35038 this.el.dom.innerHTML = '';
35041 getSelected : function()
35043 if (!this.selectedBrick) {
35047 return this.selectedBrick;
35051 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35055 * register a Masonry Layout
35056 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35059 register : function(layout)
35061 this.groups[layout.id] = layout;
35064 * fetch a Masonry Layout based on the masonry layout ID
35065 * @param {string} the masonry layout to add
35066 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35069 get: function(layout_id) {
35070 if (typeof(this.groups[layout_id]) == 'undefined') {
35073 return this.groups[layout_id] ;
35085 * http://masonry.desandro.com
35087 * The idea is to render all the bricks based on vertical width...
35089 * The original code extends 'outlayer' - we might need to use that....
35095 * @class Roo.bootstrap.LayoutMasonryAuto
35096 * @extends Roo.bootstrap.Component
35097 * Bootstrap Layout Masonry class
35100 * Create a new Element
35101 * @param {Object} config The config object
35104 Roo.bootstrap.LayoutMasonryAuto = function(config){
35105 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35108 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35111 * @cfg {Boolean} isFitWidth - resize the width..
35113 isFitWidth : false, // options..
35115 * @cfg {Boolean} isOriginLeft = left align?
35117 isOriginLeft : true,
35119 * @cfg {Boolean} isOriginTop = top align?
35121 isOriginTop : false,
35123 * @cfg {Boolean} isLayoutInstant = no animation?
35125 isLayoutInstant : false, // needed?
35127 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35129 isResizingContainer : true,
35131 * @cfg {Number} columnWidth width of the columns
35137 * @cfg {Number} maxCols maximum number of columns
35142 * @cfg {Number} padHeight padding below box..
35148 * @cfg {Boolean} isAutoInitial defalut true
35151 isAutoInitial : true,
35157 initialColumnWidth : 0,
35158 currentSize : null,
35160 colYs : null, // array.
35167 bricks: null, //CompositeElement
35168 cols : 0, // array?
35169 // element : null, // wrapped now this.el
35170 _isLayoutInited : null,
35173 getAutoCreate : function(){
35177 cls: 'blog-masonary-wrapper ' + this.cls,
35179 cls : 'mas-boxes masonary'
35186 getChildContainer: function( )
35188 if (this.boxesEl) {
35189 return this.boxesEl;
35192 this.boxesEl = this.el.select('.mas-boxes').first();
35194 return this.boxesEl;
35198 initEvents : function()
35202 if(this.isAutoInitial){
35203 Roo.log('hook children rendered');
35204 this.on('childrenrendered', function() {
35205 Roo.log('children rendered');
35212 initial : function()
35214 this.reloadItems();
35216 this.currentSize = this.el.getBox(true);
35218 /// was window resize... - let's see if this works..
35219 Roo.EventManager.onWindowResize(this.resize, this);
35221 if(!this.isAutoInitial){
35226 this.layout.defer(500,this);
35229 reloadItems: function()
35231 this.bricks = this.el.select('.masonry-brick', true);
35233 this.bricks.each(function(b) {
35234 //Roo.log(b.getSize());
35235 if (!b.attr('originalwidth')) {
35236 b.attr('originalwidth', b.getSize().width);
35241 Roo.log(this.bricks.elements.length);
35244 resize : function()
35247 var cs = this.el.getBox(true);
35249 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35250 Roo.log("no change in with or X");
35253 this.currentSize = cs;
35257 layout : function()
35260 this._resetLayout();
35261 //this._manageStamps();
35263 // don't animate first layout
35264 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35265 this.layoutItems( isInstant );
35267 // flag for initalized
35268 this._isLayoutInited = true;
35271 layoutItems : function( isInstant )
35273 //var items = this._getItemsForLayout( this.items );
35274 // original code supports filtering layout items.. we just ignore it..
35276 this._layoutItems( this.bricks , isInstant );
35278 this._postLayout();
35280 _layoutItems : function ( items , isInstant)
35282 //this.fireEvent( 'layout', this, items );
35285 if ( !items || !items.elements.length ) {
35286 // no items, emit event with empty array
35291 items.each(function(item) {
35292 Roo.log("layout item");
35294 // get x/y object from method
35295 var position = this._getItemLayoutPosition( item );
35297 position.item = item;
35298 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35299 queue.push( position );
35302 this._processLayoutQueue( queue );
35304 /** Sets position of item in DOM
35305 * @param {Element} item
35306 * @param {Number} x - horizontal position
35307 * @param {Number} y - vertical position
35308 * @param {Boolean} isInstant - disables transitions
35310 _processLayoutQueue : function( queue )
35312 for ( var i=0, len = queue.length; i < len; i++ ) {
35313 var obj = queue[i];
35314 obj.item.position('absolute');
35315 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35321 * Any logic you want to do after each layout,
35322 * i.e. size the container
35324 _postLayout : function()
35326 this.resizeContainer();
35329 resizeContainer : function()
35331 if ( !this.isResizingContainer ) {
35334 var size = this._getContainerSize();
35336 this.el.setSize(size.width,size.height);
35337 this.boxesEl.setSize(size.width,size.height);
35343 _resetLayout : function()
35345 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35346 this.colWidth = this.el.getWidth();
35347 //this.gutter = this.el.getWidth();
35349 this.measureColumns();
35355 this.colYs.push( 0 );
35361 measureColumns : function()
35363 this.getContainerWidth();
35364 // if columnWidth is 0, default to outerWidth of first item
35365 if ( !this.columnWidth ) {
35366 var firstItem = this.bricks.first();
35367 Roo.log(firstItem);
35368 this.columnWidth = this.containerWidth;
35369 if (firstItem && firstItem.attr('originalwidth') ) {
35370 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35372 // columnWidth fall back to item of first element
35373 Roo.log("set column width?");
35374 this.initialColumnWidth = this.columnWidth ;
35376 // if first elem has no width, default to size of container
35381 if (this.initialColumnWidth) {
35382 this.columnWidth = this.initialColumnWidth;
35387 // column width is fixed at the top - however if container width get's smaller we should
35390 // this bit calcs how man columns..
35392 var columnWidth = this.columnWidth += this.gutter;
35394 // calculate columns
35395 var containerWidth = this.containerWidth + this.gutter;
35397 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35398 // fix rounding errors, typically with gutters
35399 var excess = columnWidth - containerWidth % columnWidth;
35402 // if overshoot is less than a pixel, round up, otherwise floor it
35403 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35404 cols = Math[ mathMethod ]( cols );
35405 this.cols = Math.max( cols, 1 );
35406 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35408 // padding positioning..
35409 var totalColWidth = this.cols * this.columnWidth;
35410 var padavail = this.containerWidth - totalColWidth;
35411 // so for 2 columns - we need 3 'pads'
35413 var padNeeded = (1+this.cols) * this.padWidth;
35415 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35417 this.columnWidth += padExtra
35418 //this.padWidth = Math.floor(padavail / ( this.cols));
35420 // adjust colum width so that padding is fixed??
35422 // we have 3 columns ... total = width * 3
35423 // we have X left over... that should be used by
35425 //if (this.expandC) {
35433 getContainerWidth : function()
35435 /* // container is parent if fit width
35436 var container = this.isFitWidth ? this.element.parentNode : this.element;
35437 // check that this.size and size are there
35438 // IE8 triggers resize on body size change, so they might not be
35440 var size = getSize( container ); //FIXME
35441 this.containerWidth = size && size.innerWidth; //FIXME
35444 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35448 _getItemLayoutPosition : function( item ) // what is item?
35450 // we resize the item to our columnWidth..
35452 item.setWidth(this.columnWidth);
35453 item.autoBoxAdjust = false;
35455 var sz = item.getSize();
35457 // how many columns does this brick span
35458 var remainder = this.containerWidth % this.columnWidth;
35460 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35461 // round if off by 1 pixel, otherwise use ceil
35462 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35463 colSpan = Math.min( colSpan, this.cols );
35465 // normally this should be '1' as we dont' currently allow multi width columns..
35467 var colGroup = this._getColGroup( colSpan );
35468 // get the minimum Y value from the columns
35469 var minimumY = Math.min.apply( Math, colGroup );
35470 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35472 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35474 // position the brick
35476 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35477 y: this.currentSize.y + minimumY + this.padHeight
35481 // apply setHeight to necessary columns
35482 var setHeight = minimumY + sz.height + this.padHeight;
35483 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35485 var setSpan = this.cols + 1 - colGroup.length;
35486 for ( var i = 0; i < setSpan; i++ ) {
35487 this.colYs[ shortColIndex + i ] = setHeight ;
35494 * @param {Number} colSpan - number of columns the element spans
35495 * @returns {Array} colGroup
35497 _getColGroup : function( colSpan )
35499 if ( colSpan < 2 ) {
35500 // if brick spans only one column, use all the column Ys
35505 // how many different places could this brick fit horizontally
35506 var groupCount = this.cols + 1 - colSpan;
35507 // for each group potential horizontal position
35508 for ( var i = 0; i < groupCount; i++ ) {
35509 // make an array of colY values for that one group
35510 var groupColYs = this.colYs.slice( i, i + colSpan );
35511 // and get the max value of the array
35512 colGroup[i] = Math.max.apply( Math, groupColYs );
35517 _manageStamp : function( stamp )
35519 var stampSize = stamp.getSize();
35520 var offset = stamp.getBox();
35521 // get the columns that this stamp affects
35522 var firstX = this.isOriginLeft ? offset.x : offset.right;
35523 var lastX = firstX + stampSize.width;
35524 var firstCol = Math.floor( firstX / this.columnWidth );
35525 firstCol = Math.max( 0, firstCol );
35527 var lastCol = Math.floor( lastX / this.columnWidth );
35528 // lastCol should not go over if multiple of columnWidth #425
35529 lastCol -= lastX % this.columnWidth ? 0 : 1;
35530 lastCol = Math.min( this.cols - 1, lastCol );
35532 // set colYs to bottom of the stamp
35533 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35536 for ( var i = firstCol; i <= lastCol; i++ ) {
35537 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35542 _getContainerSize : function()
35544 this.maxY = Math.max.apply( Math, this.colYs );
35549 if ( this.isFitWidth ) {
35550 size.width = this._getContainerFitWidth();
35556 _getContainerFitWidth : function()
35558 var unusedCols = 0;
35559 // count unused columns
35562 if ( this.colYs[i] !== 0 ) {
35567 // fit container to columns that have been used
35568 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35571 needsResizeLayout : function()
35573 var previousWidth = this.containerWidth;
35574 this.getContainerWidth();
35575 return previousWidth !== this.containerWidth;
35590 * @class Roo.bootstrap.MasonryBrick
35591 * @extends Roo.bootstrap.Component
35592 * Bootstrap MasonryBrick class
35595 * Create a new MasonryBrick
35596 * @param {Object} config The config object
35599 Roo.bootstrap.MasonryBrick = function(config){
35601 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35603 Roo.bootstrap.MasonryBrick.register(this);
35609 * When a MasonryBrick is clcik
35610 * @param {Roo.bootstrap.MasonryBrick} this
35611 * @param {Roo.EventObject} e
35617 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35620 * @cfg {String} title
35624 * @cfg {String} html
35628 * @cfg {String} bgimage
35632 * @cfg {String} videourl
35636 * @cfg {String} cls
35640 * @cfg {String} href
35644 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35649 * @cfg {String} placetitle (center|bottom)
35654 * @cfg {Boolean} isFitContainer defalut true
35656 isFitContainer : true,
35659 * @cfg {Boolean} preventDefault defalut false
35661 preventDefault : false,
35664 * @cfg {Boolean} inverse defalut false
35666 maskInverse : false,
35668 getAutoCreate : function()
35670 if(!this.isFitContainer){
35671 return this.getSplitAutoCreate();
35674 var cls = 'masonry-brick masonry-brick-full';
35676 if(this.href.length){
35677 cls += ' masonry-brick-link';
35680 if(this.bgimage.length){
35681 cls += ' masonry-brick-image';
35684 if(this.maskInverse){
35685 cls += ' mask-inverse';
35688 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35689 cls += ' enable-mask';
35693 cls += ' masonry-' + this.size + '-brick';
35696 if(this.placetitle.length){
35698 switch (this.placetitle) {
35700 cls += ' masonry-center-title';
35703 cls += ' masonry-bottom-title';
35710 if(!this.html.length && !this.bgimage.length){
35711 cls += ' masonry-center-title';
35714 if(!this.html.length && this.bgimage.length){
35715 cls += ' masonry-bottom-title';
35720 cls += ' ' + this.cls;
35724 tag: (this.href.length) ? 'a' : 'div',
35729 cls: 'masonry-brick-mask'
35733 cls: 'masonry-brick-paragraph',
35739 if(this.href.length){
35740 cfg.href = this.href;
35743 var cn = cfg.cn[1].cn;
35745 if(this.title.length){
35748 cls: 'masonry-brick-title',
35753 if(this.html.length){
35756 cls: 'masonry-brick-text',
35761 if (!this.title.length && !this.html.length) {
35762 cfg.cn[1].cls += ' hide';
35765 if(this.bgimage.length){
35768 cls: 'masonry-brick-image-view',
35773 if(this.videourl.length){
35774 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35775 // youtube support only?
35778 cls: 'masonry-brick-image-view',
35781 allowfullscreen : true
35789 getSplitAutoCreate : function()
35791 var cls = 'masonry-brick masonry-brick-split';
35793 if(this.href.length){
35794 cls += ' masonry-brick-link';
35797 if(this.bgimage.length){
35798 cls += ' masonry-brick-image';
35802 cls += ' masonry-' + this.size + '-brick';
35805 switch (this.placetitle) {
35807 cls += ' masonry-center-title';
35810 cls += ' masonry-bottom-title';
35813 if(!this.bgimage.length){
35814 cls += ' masonry-center-title';
35817 if(this.bgimage.length){
35818 cls += ' masonry-bottom-title';
35824 cls += ' ' + this.cls;
35828 tag: (this.href.length) ? 'a' : 'div',
35833 cls: 'masonry-brick-split-head',
35837 cls: 'masonry-brick-paragraph',
35844 cls: 'masonry-brick-split-body',
35850 if(this.href.length){
35851 cfg.href = this.href;
35854 if(this.title.length){
35855 cfg.cn[0].cn[0].cn.push({
35857 cls: 'masonry-brick-title',
35862 if(this.html.length){
35863 cfg.cn[1].cn.push({
35865 cls: 'masonry-brick-text',
35870 if(this.bgimage.length){
35871 cfg.cn[0].cn.push({
35873 cls: 'masonry-brick-image-view',
35878 if(this.videourl.length){
35879 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35880 // youtube support only?
35881 cfg.cn[0].cn.cn.push({
35883 cls: 'masonry-brick-image-view',
35886 allowfullscreen : true
35893 initEvents: function()
35895 switch (this.size) {
35928 this.el.on('touchstart', this.onTouchStart, this);
35929 this.el.on('touchmove', this.onTouchMove, this);
35930 this.el.on('touchend', this.onTouchEnd, this);
35931 this.el.on('contextmenu', this.onContextMenu, this);
35933 this.el.on('mouseenter' ,this.enter, this);
35934 this.el.on('mouseleave', this.leave, this);
35935 this.el.on('click', this.onClick, this);
35938 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35939 this.parent().bricks.push(this);
35944 onClick: function(e, el)
35946 var time = this.endTimer - this.startTimer;
35947 // Roo.log(e.preventDefault());
35950 e.preventDefault();
35955 if(!this.preventDefault){
35959 e.preventDefault();
35961 if (this.activeClass != '') {
35962 this.selectBrick();
35965 this.fireEvent('click', this, e);
35968 enter: function(e, el)
35970 e.preventDefault();
35972 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35976 if(this.bgimage.length && this.html.length){
35977 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35981 leave: function(e, el)
35983 e.preventDefault();
35985 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35989 if(this.bgimage.length && this.html.length){
35990 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35994 onTouchStart: function(e, el)
35996 // e.preventDefault();
35998 this.touchmoved = false;
36000 if(!this.isFitContainer){
36004 if(!this.bgimage.length || !this.html.length){
36008 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36010 this.timer = new Date().getTime();
36014 onTouchMove: function(e, el)
36016 this.touchmoved = true;
36019 onContextMenu : function(e,el)
36021 e.preventDefault();
36022 e.stopPropagation();
36026 onTouchEnd: function(e, el)
36028 // e.preventDefault();
36030 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36037 if(!this.bgimage.length || !this.html.length){
36039 if(this.href.length){
36040 window.location.href = this.href;
36046 if(!this.isFitContainer){
36050 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36052 window.location.href = this.href;
36055 //selection on single brick only
36056 selectBrick : function() {
36058 if (!this.parentId) {
36062 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36063 var index = m.selectedBrick.indexOf(this.id);
36066 m.selectedBrick.splice(index,1);
36067 this.el.removeClass(this.activeClass);
36071 for(var i = 0; i < m.selectedBrick.length; i++) {
36072 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36073 b.el.removeClass(b.activeClass);
36076 m.selectedBrick = [];
36078 m.selectedBrick.push(this.id);
36079 this.el.addClass(this.activeClass);
36083 isSelected : function(){
36084 return this.el.hasClass(this.activeClass);
36089 Roo.apply(Roo.bootstrap.MasonryBrick, {
36092 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36094 * register a Masonry Brick
36095 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36098 register : function(brick)
36100 //this.groups[brick.id] = brick;
36101 this.groups.add(brick.id, brick);
36104 * fetch a masonry brick based on the masonry brick ID
36105 * @param {string} the masonry brick to add
36106 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36109 get: function(brick_id)
36111 // if (typeof(this.groups[brick_id]) == 'undefined') {
36114 // return this.groups[brick_id] ;
36116 if(this.groups.key(brick_id)) {
36117 return this.groups.key(brick_id);
36135 * @class Roo.bootstrap.Brick
36136 * @extends Roo.bootstrap.Component
36137 * Bootstrap Brick class
36140 * Create a new Brick
36141 * @param {Object} config The config object
36144 Roo.bootstrap.Brick = function(config){
36145 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36151 * When a Brick is click
36152 * @param {Roo.bootstrap.Brick} this
36153 * @param {Roo.EventObject} e
36159 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36162 * @cfg {String} title
36166 * @cfg {String} html
36170 * @cfg {String} bgimage
36174 * @cfg {String} cls
36178 * @cfg {String} href
36182 * @cfg {String} video
36186 * @cfg {Boolean} square
36190 getAutoCreate : function()
36192 var cls = 'roo-brick';
36194 if(this.href.length){
36195 cls += ' roo-brick-link';
36198 if(this.bgimage.length){
36199 cls += ' roo-brick-image';
36202 if(!this.html.length && !this.bgimage.length){
36203 cls += ' roo-brick-center-title';
36206 if(!this.html.length && this.bgimage.length){
36207 cls += ' roo-brick-bottom-title';
36211 cls += ' ' + this.cls;
36215 tag: (this.href.length) ? 'a' : 'div',
36220 cls: 'roo-brick-paragraph',
36226 if(this.href.length){
36227 cfg.href = this.href;
36230 var cn = cfg.cn[0].cn;
36232 if(this.title.length){
36235 cls: 'roo-brick-title',
36240 if(this.html.length){
36243 cls: 'roo-brick-text',
36250 if(this.bgimage.length){
36253 cls: 'roo-brick-image-view',
36261 initEvents: function()
36263 if(this.title.length || this.html.length){
36264 this.el.on('mouseenter' ,this.enter, this);
36265 this.el.on('mouseleave', this.leave, this);
36268 Roo.EventManager.onWindowResize(this.resize, this);
36270 if(this.bgimage.length){
36271 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36272 this.imageEl.on('load', this.onImageLoad, this);
36279 onImageLoad : function()
36284 resize : function()
36286 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36288 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36290 if(this.bgimage.length){
36291 var image = this.el.select('.roo-brick-image-view', true).first();
36293 image.setWidth(paragraph.getWidth());
36296 image.setHeight(paragraph.getWidth());
36299 this.el.setHeight(image.getHeight());
36300 paragraph.setHeight(image.getHeight());
36306 enter: function(e, el)
36308 e.preventDefault();
36310 if(this.bgimage.length){
36311 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36312 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36316 leave: function(e, el)
36318 e.preventDefault();
36320 if(this.bgimage.length){
36321 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36322 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36337 * @class Roo.bootstrap.NumberField
36338 * @extends Roo.bootstrap.Input
36339 * Bootstrap NumberField class
36345 * Create a new NumberField
36346 * @param {Object} config The config object
36349 Roo.bootstrap.NumberField = function(config){
36350 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36353 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36356 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36358 allowDecimals : true,
36360 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36362 decimalSeparator : ".",
36364 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36366 decimalPrecision : 2,
36368 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36370 allowNegative : true,
36373 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36377 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36379 minValue : Number.NEGATIVE_INFINITY,
36381 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36383 maxValue : Number.MAX_VALUE,
36385 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36387 minText : "The minimum value for this field is {0}",
36389 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36391 maxText : "The maximum value for this field is {0}",
36393 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36394 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36396 nanText : "{0} is not a valid number",
36398 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36400 thousandsDelimiter : false,
36402 * @cfg {String} valueAlign alignment of value
36404 valueAlign : "left",
36406 getAutoCreate : function()
36408 var hiddenInput = {
36412 cls: 'hidden-number-input'
36416 hiddenInput.name = this.name;
36421 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36423 this.name = hiddenInput.name;
36425 if(cfg.cn.length > 0) {
36426 cfg.cn.push(hiddenInput);
36433 initEvents : function()
36435 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36437 var allowed = "0123456789";
36439 if(this.allowDecimals){
36440 allowed += this.decimalSeparator;
36443 if(this.allowNegative){
36447 if(this.thousandsDelimiter) {
36451 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36453 var keyPress = function(e){
36455 var k = e.getKey();
36457 var c = e.getCharCode();
36460 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36461 allowed.indexOf(String.fromCharCode(c)) === -1
36467 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36471 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36476 this.el.on("keypress", keyPress, this);
36479 validateValue : function(value)
36482 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36486 var num = this.parseValue(value);
36489 this.markInvalid(String.format(this.nanText, value));
36493 if(num < this.minValue){
36494 this.markInvalid(String.format(this.minText, this.minValue));
36498 if(num > this.maxValue){
36499 this.markInvalid(String.format(this.maxText, this.maxValue));
36506 getValue : function()
36508 var v = this.hiddenEl().getValue();
36510 return this.fixPrecision(this.parseValue(v));
36513 parseValue : function(value)
36515 if(this.thousandsDelimiter) {
36517 r = new RegExp(",", "g");
36518 value = value.replace(r, "");
36521 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36522 return isNaN(value) ? '' : value;
36525 fixPrecision : function(value)
36527 if(this.thousandsDelimiter) {
36529 r = new RegExp(",", "g");
36530 value = value.replace(r, "");
36533 var nan = isNaN(value);
36535 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36536 return nan ? '' : value;
36538 return parseFloat(value).toFixed(this.decimalPrecision);
36541 setValue : function(v)
36543 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36549 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36551 this.inputEl().dom.value = (v == '') ? '' :
36552 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36554 if(!this.allowZero && v === '0') {
36555 this.hiddenEl().dom.value = '';
36556 this.inputEl().dom.value = '';
36563 decimalPrecisionFcn : function(v)
36565 return Math.floor(v);
36568 beforeBlur : function()
36570 var v = this.parseValue(this.getRawValue());
36572 if(v || v === 0 || v === ''){
36577 hiddenEl : function()
36579 return this.el.select('input.hidden-number-input',true).first();
36591 * @class Roo.bootstrap.DocumentSlider
36592 * @extends Roo.bootstrap.Component
36593 * Bootstrap DocumentSlider class
36596 * Create a new DocumentViewer
36597 * @param {Object} config The config object
36600 Roo.bootstrap.DocumentSlider = function(config){
36601 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36608 * Fire after initEvent
36609 * @param {Roo.bootstrap.DocumentSlider} this
36614 * Fire after update
36615 * @param {Roo.bootstrap.DocumentSlider} this
36621 * @param {Roo.bootstrap.DocumentSlider} this
36627 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36633 getAutoCreate : function()
36637 cls : 'roo-document-slider',
36641 cls : 'roo-document-slider-header',
36645 cls : 'roo-document-slider-header-title'
36651 cls : 'roo-document-slider-body',
36655 cls : 'roo-document-slider-prev',
36659 cls : 'fa fa-chevron-left'
36665 cls : 'roo-document-slider-thumb',
36669 cls : 'roo-document-slider-image'
36675 cls : 'roo-document-slider-next',
36679 cls : 'fa fa-chevron-right'
36691 initEvents : function()
36693 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36694 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36696 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36697 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36699 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36700 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36702 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36703 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36705 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36706 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36708 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36709 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36711 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36712 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36714 this.thumbEl.on('click', this.onClick, this);
36716 this.prevIndicator.on('click', this.prev, this);
36718 this.nextIndicator.on('click', this.next, this);
36722 initial : function()
36724 if(this.files.length){
36725 this.indicator = 1;
36729 this.fireEvent('initial', this);
36732 update : function()
36734 this.imageEl.attr('src', this.files[this.indicator - 1]);
36736 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36738 this.prevIndicator.show();
36740 if(this.indicator == 1){
36741 this.prevIndicator.hide();
36744 this.nextIndicator.show();
36746 if(this.indicator == this.files.length){
36747 this.nextIndicator.hide();
36750 this.thumbEl.scrollTo('top');
36752 this.fireEvent('update', this);
36755 onClick : function(e)
36757 e.preventDefault();
36759 this.fireEvent('click', this);
36764 e.preventDefault();
36766 this.indicator = Math.max(1, this.indicator - 1);
36773 e.preventDefault();
36775 this.indicator = Math.min(this.files.length, this.indicator + 1);
36789 * @class Roo.bootstrap.RadioSet
36790 * @extends Roo.bootstrap.Input
36791 * Bootstrap RadioSet class
36792 * @cfg {String} indicatorpos (left|right) default left
36793 * @cfg {Boolean} inline (true|false) inline the element (default true)
36794 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36796 * Create a new RadioSet
36797 * @param {Object} config The config object
36800 Roo.bootstrap.RadioSet = function(config){
36802 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36806 Roo.bootstrap.RadioSet.register(this);
36811 * Fires when the element is checked or unchecked.
36812 * @param {Roo.bootstrap.RadioSet} this This radio
36813 * @param {Roo.bootstrap.Radio} item The checked item
36818 * Fires when the element is click.
36819 * @param {Roo.bootstrap.RadioSet} this This radio set
36820 * @param {Roo.bootstrap.Radio} item The checked item
36821 * @param {Roo.EventObject} e The event object
36828 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36836 indicatorpos : 'left',
36838 getAutoCreate : function()
36842 cls : 'roo-radio-set-label',
36846 html : this.fieldLabel
36850 if (Roo.bootstrap.version == 3) {
36853 if(this.indicatorpos == 'left'){
36856 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36857 tooltip : 'This field is required'
36862 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36863 tooltip : 'This field is required'
36869 cls : 'roo-radio-set-items'
36872 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36874 if (align === 'left' && this.fieldLabel.length) {
36877 cls : "roo-radio-set-right",
36883 if(this.labelWidth > 12){
36884 label.style = "width: " + this.labelWidth + 'px';
36887 if(this.labelWidth < 13 && this.labelmd == 0){
36888 this.labelmd = this.labelWidth;
36891 if(this.labellg > 0){
36892 label.cls += ' col-lg-' + this.labellg;
36893 items.cls += ' col-lg-' + (12 - this.labellg);
36896 if(this.labelmd > 0){
36897 label.cls += ' col-md-' + this.labelmd;
36898 items.cls += ' col-md-' + (12 - this.labelmd);
36901 if(this.labelsm > 0){
36902 label.cls += ' col-sm-' + this.labelsm;
36903 items.cls += ' col-sm-' + (12 - this.labelsm);
36906 if(this.labelxs > 0){
36907 label.cls += ' col-xs-' + this.labelxs;
36908 items.cls += ' col-xs-' + (12 - this.labelxs);
36914 cls : 'roo-radio-set',
36918 cls : 'roo-radio-set-input',
36921 value : this.value ? this.value : ''
36928 if(this.weight.length){
36929 cfg.cls += ' roo-radio-' + this.weight;
36933 cfg.cls += ' roo-radio-set-inline';
36937 ['xs','sm','md','lg'].map(function(size){
36938 if (settings[size]) {
36939 cfg.cls += ' col-' + size + '-' + settings[size];
36947 initEvents : function()
36949 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36950 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36952 if(!this.fieldLabel.length){
36953 this.labelEl.hide();
36956 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36957 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36959 this.indicator = this.indicatorEl();
36961 if(this.indicator){
36962 this.indicator.addClass('invisible');
36965 this.originalValue = this.getValue();
36969 inputEl: function ()
36971 return this.el.select('.roo-radio-set-input', true).first();
36974 getChildContainer : function()
36976 return this.itemsEl;
36979 register : function(item)
36981 this.radioes.push(item);
36985 validate : function()
36987 if(this.getVisibilityEl().hasClass('hidden')){
36993 Roo.each(this.radioes, function(i){
37002 if(this.allowBlank) {
37006 if(this.disabled || valid){
37011 this.markInvalid();
37016 markValid : function()
37018 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37019 this.indicatorEl().removeClass('visible');
37020 this.indicatorEl().addClass('invisible');
37024 if (Roo.bootstrap.version == 3) {
37025 this.el.removeClass([this.invalidClass, this.validClass]);
37026 this.el.addClass(this.validClass);
37028 this.el.removeClass(['is-invalid','is-valid']);
37029 this.el.addClass(['is-valid']);
37031 this.fireEvent('valid', this);
37034 markInvalid : function(msg)
37036 if(this.allowBlank || this.disabled){
37040 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37041 this.indicatorEl().removeClass('invisible');
37042 this.indicatorEl().addClass('visible');
37044 if (Roo.bootstrap.version == 3) {
37045 this.el.removeClass([this.invalidClass, this.validClass]);
37046 this.el.addClass(this.invalidClass);
37048 this.el.removeClass(['is-invalid','is-valid']);
37049 this.el.addClass(['is-invalid']);
37052 this.fireEvent('invalid', this, msg);
37056 setValue : function(v, suppressEvent)
37058 if(this.value === v){
37065 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37068 Roo.each(this.radioes, function(i){
37070 i.el.removeClass('checked');
37073 Roo.each(this.radioes, function(i){
37075 if(i.value === v || i.value.toString() === v.toString()){
37077 i.el.addClass('checked');
37079 if(suppressEvent !== true){
37080 this.fireEvent('check', this, i);
37091 clearInvalid : function(){
37093 if(!this.el || this.preventMark){
37097 this.el.removeClass([this.invalidClass]);
37099 this.fireEvent('valid', this);
37104 Roo.apply(Roo.bootstrap.RadioSet, {
37108 register : function(set)
37110 this.groups[set.name] = set;
37113 get: function(name)
37115 if (typeof(this.groups[name]) == 'undefined') {
37119 return this.groups[name] ;
37125 * Ext JS Library 1.1.1
37126 * Copyright(c) 2006-2007, Ext JS, LLC.
37128 * Originally Released Under LGPL - original licence link has changed is not relivant.
37131 * <script type="text/javascript">
37136 * @class Roo.bootstrap.SplitBar
37137 * @extends Roo.util.Observable
37138 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37142 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37143 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37144 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37145 split.minSize = 100;
37146 split.maxSize = 600;
37147 split.animate = true;
37148 split.on('moved', splitterMoved);
37151 * Create a new SplitBar
37152 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37153 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37154 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37155 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37156 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37157 position of the SplitBar).
37159 Roo.bootstrap.SplitBar = function(cfg){
37164 // dragElement : elm
37165 // resizingElement: el,
37167 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37168 // placement : Roo.bootstrap.SplitBar.LEFT ,
37169 // existingProxy ???
37172 this.el = Roo.get(cfg.dragElement, true);
37173 this.el.dom.unselectable = "on";
37175 this.resizingEl = Roo.get(cfg.resizingElement, true);
37179 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37180 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37183 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37186 * The minimum size of the resizing element. (Defaults to 0)
37192 * The maximum size of the resizing element. (Defaults to 2000)
37195 this.maxSize = 2000;
37198 * Whether to animate the transition to the new size
37201 this.animate = false;
37204 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37207 this.useShim = false;
37212 if(!cfg.existingProxy){
37214 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37216 this.proxy = Roo.get(cfg.existingProxy).dom;
37219 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37222 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37225 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37228 this.dragSpecs = {};
37231 * @private The adapter to use to positon and resize elements
37233 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37234 this.adapter.init(this);
37236 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37238 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37239 this.el.addClass("roo-splitbar-h");
37242 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37243 this.el.addClass("roo-splitbar-v");
37249 * Fires when the splitter is moved (alias for {@link #event-moved})
37250 * @param {Roo.bootstrap.SplitBar} this
37251 * @param {Number} newSize the new width or height
37256 * Fires when the splitter is moved
37257 * @param {Roo.bootstrap.SplitBar} this
37258 * @param {Number} newSize the new width or height
37262 * @event beforeresize
37263 * Fires before the splitter is dragged
37264 * @param {Roo.bootstrap.SplitBar} this
37266 "beforeresize" : true,
37268 "beforeapply" : true
37271 Roo.util.Observable.call(this);
37274 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37275 onStartProxyDrag : function(x, y){
37276 this.fireEvent("beforeresize", this);
37278 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37280 o.enableDisplayMode("block");
37281 // all splitbars share the same overlay
37282 Roo.bootstrap.SplitBar.prototype.overlay = o;
37284 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37285 this.overlay.show();
37286 Roo.get(this.proxy).setDisplayed("block");
37287 var size = this.adapter.getElementSize(this);
37288 this.activeMinSize = this.getMinimumSize();;
37289 this.activeMaxSize = this.getMaximumSize();;
37290 var c1 = size - this.activeMinSize;
37291 var c2 = Math.max(this.activeMaxSize - size, 0);
37292 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37293 this.dd.resetConstraints();
37294 this.dd.setXConstraint(
37295 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37296 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37298 this.dd.setYConstraint(0, 0);
37300 this.dd.resetConstraints();
37301 this.dd.setXConstraint(0, 0);
37302 this.dd.setYConstraint(
37303 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37304 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37307 this.dragSpecs.startSize = size;
37308 this.dragSpecs.startPoint = [x, y];
37309 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37313 * @private Called after the drag operation by the DDProxy
37315 onEndProxyDrag : function(e){
37316 Roo.get(this.proxy).setDisplayed(false);
37317 var endPoint = Roo.lib.Event.getXY(e);
37319 this.overlay.hide();
37322 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37323 newSize = this.dragSpecs.startSize +
37324 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37325 endPoint[0] - this.dragSpecs.startPoint[0] :
37326 this.dragSpecs.startPoint[0] - endPoint[0]
37329 newSize = this.dragSpecs.startSize +
37330 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37331 endPoint[1] - this.dragSpecs.startPoint[1] :
37332 this.dragSpecs.startPoint[1] - endPoint[1]
37335 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37336 if(newSize != this.dragSpecs.startSize){
37337 if(this.fireEvent('beforeapply', this, newSize) !== false){
37338 this.adapter.setElementSize(this, newSize);
37339 this.fireEvent("moved", this, newSize);
37340 this.fireEvent("resize", this, newSize);
37346 * Get the adapter this SplitBar uses
37347 * @return The adapter object
37349 getAdapter : function(){
37350 return this.adapter;
37354 * Set the adapter this SplitBar uses
37355 * @param {Object} adapter A SplitBar adapter object
37357 setAdapter : function(adapter){
37358 this.adapter = adapter;
37359 this.adapter.init(this);
37363 * Gets the minimum size for the resizing element
37364 * @return {Number} The minimum size
37366 getMinimumSize : function(){
37367 return this.minSize;
37371 * Sets the minimum size for the resizing element
37372 * @param {Number} minSize The minimum size
37374 setMinimumSize : function(minSize){
37375 this.minSize = minSize;
37379 * Gets the maximum size for the resizing element
37380 * @return {Number} The maximum size
37382 getMaximumSize : function(){
37383 return this.maxSize;
37387 * Sets the maximum size for the resizing element
37388 * @param {Number} maxSize The maximum size
37390 setMaximumSize : function(maxSize){
37391 this.maxSize = maxSize;
37395 * Sets the initialize size for the resizing element
37396 * @param {Number} size The initial size
37398 setCurrentSize : function(size){
37399 var oldAnimate = this.animate;
37400 this.animate = false;
37401 this.adapter.setElementSize(this, size);
37402 this.animate = oldAnimate;
37406 * Destroy this splitbar.
37407 * @param {Boolean} removeEl True to remove the element
37409 destroy : function(removeEl){
37411 this.shim.remove();
37414 this.proxy.parentNode.removeChild(this.proxy);
37422 * @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.
37424 Roo.bootstrap.SplitBar.createProxy = function(dir){
37425 var proxy = new Roo.Element(document.createElement("div"));
37426 proxy.unselectable();
37427 var cls = 'roo-splitbar-proxy';
37428 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37429 document.body.appendChild(proxy.dom);
37434 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37435 * Default Adapter. It assumes the splitter and resizing element are not positioned
37436 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37438 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37441 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37442 // do nothing for now
37443 init : function(s){
37447 * Called before drag operations to get the current size of the resizing element.
37448 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37450 getElementSize : function(s){
37451 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37452 return s.resizingEl.getWidth();
37454 return s.resizingEl.getHeight();
37459 * Called after drag operations to set the size of the resizing element.
37460 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37461 * @param {Number} newSize The new size to set
37462 * @param {Function} onComplete A function to be invoked when resizing is complete
37464 setElementSize : function(s, newSize, onComplete){
37465 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37467 s.resizingEl.setWidth(newSize);
37469 onComplete(s, newSize);
37472 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37477 s.resizingEl.setHeight(newSize);
37479 onComplete(s, newSize);
37482 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37489 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37490 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37491 * Adapter that moves the splitter element to align with the resized sizing element.
37492 * Used with an absolute positioned SplitBar.
37493 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37494 * document.body, make sure you assign an id to the body element.
37496 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37497 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37498 this.container = Roo.get(container);
37501 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37502 init : function(s){
37503 this.basic.init(s);
37506 getElementSize : function(s){
37507 return this.basic.getElementSize(s);
37510 setElementSize : function(s, newSize, onComplete){
37511 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37514 moveSplitter : function(s){
37515 var yes = Roo.bootstrap.SplitBar;
37516 switch(s.placement){
37518 s.el.setX(s.resizingEl.getRight());
37521 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37524 s.el.setY(s.resizingEl.getBottom());
37527 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37534 * Orientation constant - Create a vertical SplitBar
37538 Roo.bootstrap.SplitBar.VERTICAL = 1;
37541 * Orientation constant - Create a horizontal SplitBar
37545 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37548 * Placement constant - The resizing element is to the left of the splitter element
37552 Roo.bootstrap.SplitBar.LEFT = 1;
37555 * Placement constant - The resizing element is to the right of the splitter element
37559 Roo.bootstrap.SplitBar.RIGHT = 2;
37562 * Placement constant - The resizing element is positioned above the splitter element
37566 Roo.bootstrap.SplitBar.TOP = 3;
37569 * Placement constant - The resizing element is positioned under splitter element
37573 Roo.bootstrap.SplitBar.BOTTOM = 4;
37574 Roo.namespace("Roo.bootstrap.layout");/*
37576 * Ext JS Library 1.1.1
37577 * Copyright(c) 2006-2007, Ext JS, LLC.
37579 * Originally Released Under LGPL - original licence link has changed is not relivant.
37582 * <script type="text/javascript">
37586 * @class Roo.bootstrap.layout.Manager
37587 * @extends Roo.bootstrap.Component
37588 * Base class for layout managers.
37590 Roo.bootstrap.layout.Manager = function(config)
37592 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37598 /** false to disable window resize monitoring @type Boolean */
37599 this.monitorWindowResize = true;
37604 * Fires when a layout is performed.
37605 * @param {Roo.LayoutManager} this
37609 * @event regionresized
37610 * Fires when the user resizes a region.
37611 * @param {Roo.LayoutRegion} region The resized region
37612 * @param {Number} newSize The new size (width for east/west, height for north/south)
37614 "regionresized" : true,
37616 * @event regioncollapsed
37617 * Fires when a region is collapsed.
37618 * @param {Roo.LayoutRegion} region The collapsed region
37620 "regioncollapsed" : true,
37622 * @event regionexpanded
37623 * Fires when a region is expanded.
37624 * @param {Roo.LayoutRegion} region The expanded region
37626 "regionexpanded" : true
37628 this.updating = false;
37631 this.el = Roo.get(config.el);
37637 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37642 monitorWindowResize : true,
37648 onRender : function(ct, position)
37651 this.el = Roo.get(ct);
37654 //this.fireEvent('render',this);
37658 initEvents: function()
37662 // ie scrollbar fix
37663 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37664 document.body.scroll = "no";
37665 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37666 this.el.position('relative');
37668 this.id = this.el.id;
37669 this.el.addClass("roo-layout-container");
37670 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37671 if(this.el.dom != document.body ) {
37672 this.el.on('resize', this.layout,this);
37673 this.el.on('show', this.layout,this);
37679 * Returns true if this layout is currently being updated
37680 * @return {Boolean}
37682 isUpdating : function(){
37683 return this.updating;
37687 * Suspend the LayoutManager from doing auto-layouts while
37688 * making multiple add or remove calls
37690 beginUpdate : function(){
37691 this.updating = true;
37695 * Restore auto-layouts and optionally disable the manager from performing a layout
37696 * @param {Boolean} noLayout true to disable a layout update
37698 endUpdate : function(noLayout){
37699 this.updating = false;
37705 layout: function(){
37709 onRegionResized : function(region, newSize){
37710 this.fireEvent("regionresized", region, newSize);
37714 onRegionCollapsed : function(region){
37715 this.fireEvent("regioncollapsed", region);
37718 onRegionExpanded : function(region){
37719 this.fireEvent("regionexpanded", region);
37723 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37724 * performs box-model adjustments.
37725 * @return {Object} The size as an object {width: (the width), height: (the height)}
37727 getViewSize : function()
37730 if(this.el.dom != document.body){
37731 size = this.el.getSize();
37733 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37735 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37736 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37741 * Returns the Element this layout is bound to.
37742 * @return {Roo.Element}
37744 getEl : function(){
37749 * Returns the specified region.
37750 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37751 * @return {Roo.LayoutRegion}
37753 getRegion : function(target){
37754 return this.regions[target.toLowerCase()];
37757 onWindowResize : function(){
37758 if(this.monitorWindowResize){
37765 * Ext JS Library 1.1.1
37766 * Copyright(c) 2006-2007, Ext JS, LLC.
37768 * Originally Released Under LGPL - original licence link has changed is not relivant.
37771 * <script type="text/javascript">
37774 * @class Roo.bootstrap.layout.Border
37775 * @extends Roo.bootstrap.layout.Manager
37776 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37777 * please see: examples/bootstrap/nested.html<br><br>
37779 <b>The container the layout is rendered into can be either the body element or any other element.
37780 If it is not the body element, the container needs to either be an absolute positioned element,
37781 or you will need to add "position:relative" to the css of the container. You will also need to specify
37782 the container size if it is not the body element.</b>
37785 * Create a new Border
37786 * @param {Object} config Configuration options
37788 Roo.bootstrap.layout.Border = function(config){
37789 config = config || {};
37790 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37794 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37795 if(config[region]){
37796 config[region].region = region;
37797 this.addRegion(config[region]);
37803 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37805 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37807 parent : false, // this might point to a 'nest' or a ???
37810 * Creates and adds a new region if it doesn't already exist.
37811 * @param {String} target The target region key (north, south, east, west or center).
37812 * @param {Object} config The regions config object
37813 * @return {BorderLayoutRegion} The new region
37815 addRegion : function(config)
37817 if(!this.regions[config.region]){
37818 var r = this.factory(config);
37819 this.bindRegion(r);
37821 return this.regions[config.region];
37825 bindRegion : function(r){
37826 this.regions[r.config.region] = r;
37828 r.on("visibilitychange", this.layout, this);
37829 r.on("paneladded", this.layout, this);
37830 r.on("panelremoved", this.layout, this);
37831 r.on("invalidated", this.layout, this);
37832 r.on("resized", this.onRegionResized, this);
37833 r.on("collapsed", this.onRegionCollapsed, this);
37834 r.on("expanded", this.onRegionExpanded, this);
37838 * Performs a layout update.
37840 layout : function()
37842 if(this.updating) {
37846 // render all the rebions if they have not been done alreayd?
37847 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37848 if(this.regions[region] && !this.regions[region].bodyEl){
37849 this.regions[region].onRender(this.el)
37853 var size = this.getViewSize();
37854 var w = size.width;
37855 var h = size.height;
37860 //var x = 0, y = 0;
37862 var rs = this.regions;
37863 var north = rs["north"];
37864 var south = rs["south"];
37865 var west = rs["west"];
37866 var east = rs["east"];
37867 var center = rs["center"];
37868 //if(this.hideOnLayout){ // not supported anymore
37869 //c.el.setStyle("display", "none");
37871 if(north && north.isVisible()){
37872 var b = north.getBox();
37873 var m = north.getMargins();
37874 b.width = w - (m.left+m.right);
37877 centerY = b.height + b.y + m.bottom;
37878 centerH -= centerY;
37879 north.updateBox(this.safeBox(b));
37881 if(south && south.isVisible()){
37882 var b = south.getBox();
37883 var m = south.getMargins();
37884 b.width = w - (m.left+m.right);
37886 var totalHeight = (b.height + m.top + m.bottom);
37887 b.y = h - totalHeight + m.top;
37888 centerH -= totalHeight;
37889 south.updateBox(this.safeBox(b));
37891 if(west && west.isVisible()){
37892 var b = west.getBox();
37893 var m = west.getMargins();
37894 b.height = centerH - (m.top+m.bottom);
37896 b.y = centerY + m.top;
37897 var totalWidth = (b.width + m.left + m.right);
37898 centerX += totalWidth;
37899 centerW -= totalWidth;
37900 west.updateBox(this.safeBox(b));
37902 if(east && east.isVisible()){
37903 var b = east.getBox();
37904 var m = east.getMargins();
37905 b.height = centerH - (m.top+m.bottom);
37906 var totalWidth = (b.width + m.left + m.right);
37907 b.x = w - totalWidth + m.left;
37908 b.y = centerY + m.top;
37909 centerW -= totalWidth;
37910 east.updateBox(this.safeBox(b));
37913 var m = center.getMargins();
37915 x: centerX + m.left,
37916 y: centerY + m.top,
37917 width: centerW - (m.left+m.right),
37918 height: centerH - (m.top+m.bottom)
37920 //if(this.hideOnLayout){
37921 //center.el.setStyle("display", "block");
37923 center.updateBox(this.safeBox(centerBox));
37926 this.fireEvent("layout", this);
37930 safeBox : function(box){
37931 box.width = Math.max(0, box.width);
37932 box.height = Math.max(0, box.height);
37937 * Adds a ContentPanel (or subclass) to this layout.
37938 * @param {String} target The target region key (north, south, east, west or center).
37939 * @param {Roo.ContentPanel} panel The panel to add
37940 * @return {Roo.ContentPanel} The added panel
37942 add : function(target, panel){
37944 target = target.toLowerCase();
37945 return this.regions[target].add(panel);
37949 * Remove a ContentPanel (or subclass) to this layout.
37950 * @param {String} target The target region key (north, south, east, west or center).
37951 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37952 * @return {Roo.ContentPanel} The removed panel
37954 remove : function(target, panel){
37955 target = target.toLowerCase();
37956 return this.regions[target].remove(panel);
37960 * Searches all regions for a panel with the specified id
37961 * @param {String} panelId
37962 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37964 findPanel : function(panelId){
37965 var rs = this.regions;
37966 for(var target in rs){
37967 if(typeof rs[target] != "function"){
37968 var p = rs[target].getPanel(panelId);
37978 * Searches all regions for a panel with the specified id and activates (shows) it.
37979 * @param {String/ContentPanel} panelId The panels id or the panel itself
37980 * @return {Roo.ContentPanel} The shown panel or null
37982 showPanel : function(panelId) {
37983 var rs = this.regions;
37984 for(var target in rs){
37985 var r = rs[target];
37986 if(typeof r != "function"){
37987 if(r.hasPanel(panelId)){
37988 return r.showPanel(panelId);
37996 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37997 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38000 restoreState : function(provider){
38002 provider = Roo.state.Manager;
38004 var sm = new Roo.LayoutStateManager();
38005 sm.init(this, provider);
38011 * Adds a xtype elements to the layout.
38015 xtype : 'ContentPanel',
38022 xtype : 'NestedLayoutPanel',
38028 items : [ ... list of content panels or nested layout panels.. ]
38032 * @param {Object} cfg Xtype definition of item to add.
38034 addxtype : function(cfg)
38036 // basically accepts a pannel...
38037 // can accept a layout region..!?!?
38038 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38041 // theory? children can only be panels??
38043 //if (!cfg.xtype.match(/Panel$/)) {
38048 if (typeof(cfg.region) == 'undefined') {
38049 Roo.log("Failed to add Panel, region was not set");
38053 var region = cfg.region;
38059 xitems = cfg.items;
38064 if ( region == 'center') {
38065 Roo.log("Center: " + cfg.title);
38071 case 'Content': // ContentPanel (el, cfg)
38072 case 'Scroll': // ContentPanel (el, cfg)
38074 cfg.autoCreate = cfg.autoCreate || true;
38075 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38077 // var el = this.el.createChild();
38078 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38081 this.add(region, ret);
38085 case 'TreePanel': // our new panel!
38086 cfg.el = this.el.createChild();
38087 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38088 this.add(region, ret);
38093 // create a new Layout (which is a Border Layout...
38095 var clayout = cfg.layout;
38096 clayout.el = this.el.createChild();
38097 clayout.items = clayout.items || [];
38101 // replace this exitems with the clayout ones..
38102 xitems = clayout.items;
38104 // force background off if it's in center...
38105 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38106 cfg.background = false;
38108 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38111 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38112 //console.log('adding nested layout panel ' + cfg.toSource());
38113 this.add(region, ret);
38114 nb = {}; /// find first...
38119 // needs grid and region
38121 //var el = this.getRegion(region).el.createChild();
38123 *var el = this.el.createChild();
38124 // create the grid first...
38125 cfg.grid.container = el;
38126 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38129 if (region == 'center' && this.active ) {
38130 cfg.background = false;
38133 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38135 this.add(region, ret);
38137 if (cfg.background) {
38138 // render grid on panel activation (if panel background)
38139 ret.on('activate', function(gp) {
38140 if (!gp.grid.rendered) {
38141 // gp.grid.render(el);
38145 // cfg.grid.render(el);
38151 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38152 // it was the old xcomponent building that caused this before.
38153 // espeically if border is the top element in the tree.
38163 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38165 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38166 this.add(region, ret);
38170 throw "Can not add '" + cfg.xtype + "' to Border";
38176 this.beginUpdate();
38180 Roo.each(xitems, function(i) {
38181 region = nb && i.region ? i.region : false;
38183 var add = ret.addxtype(i);
38186 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38187 if (!i.background) {
38188 abn[region] = nb[region] ;
38195 // make the last non-background panel active..
38196 //if (nb) { Roo.log(abn); }
38199 for(var r in abn) {
38200 region = this.getRegion(r);
38202 // tried using nb[r], but it does not work..
38204 region.showPanel(abn[r]);
38215 factory : function(cfg)
38218 var validRegions = Roo.bootstrap.layout.Border.regions;
38220 var target = cfg.region;
38223 var r = Roo.bootstrap.layout;
38227 return new r.North(cfg);
38229 return new r.South(cfg);
38231 return new r.East(cfg);
38233 return new r.West(cfg);
38235 return new r.Center(cfg);
38237 throw 'Layout region "'+target+'" not supported.';
38244 * Ext JS Library 1.1.1
38245 * Copyright(c) 2006-2007, Ext JS, LLC.
38247 * Originally Released Under LGPL - original licence link has changed is not relivant.
38250 * <script type="text/javascript">
38254 * @class Roo.bootstrap.layout.Basic
38255 * @extends Roo.util.Observable
38256 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38257 * and does not have a titlebar, tabs or any other features. All it does is size and position
38258 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38259 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38260 * @cfg {string} region the region that it inhabits..
38261 * @cfg {bool} skipConfig skip config?
38265 Roo.bootstrap.layout.Basic = function(config){
38267 this.mgr = config.mgr;
38269 this.position = config.region;
38271 var skipConfig = config.skipConfig;
38275 * @scope Roo.BasicLayoutRegion
38279 * @event beforeremove
38280 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38281 * @param {Roo.LayoutRegion} this
38282 * @param {Roo.ContentPanel} panel The panel
38283 * @param {Object} e The cancel event object
38285 "beforeremove" : true,
38287 * @event invalidated
38288 * Fires when the layout for this region is changed.
38289 * @param {Roo.LayoutRegion} this
38291 "invalidated" : true,
38293 * @event visibilitychange
38294 * Fires when this region is shown or hidden
38295 * @param {Roo.LayoutRegion} this
38296 * @param {Boolean} visibility true or false
38298 "visibilitychange" : true,
38300 * @event paneladded
38301 * Fires when a panel is added.
38302 * @param {Roo.LayoutRegion} this
38303 * @param {Roo.ContentPanel} panel The panel
38305 "paneladded" : true,
38307 * @event panelremoved
38308 * Fires when a panel is removed.
38309 * @param {Roo.LayoutRegion} this
38310 * @param {Roo.ContentPanel} panel The panel
38312 "panelremoved" : true,
38314 * @event beforecollapse
38315 * Fires when this region before collapse.
38316 * @param {Roo.LayoutRegion} this
38318 "beforecollapse" : true,
38321 * Fires when this region is collapsed.
38322 * @param {Roo.LayoutRegion} this
38324 "collapsed" : true,
38327 * Fires when this region is expanded.
38328 * @param {Roo.LayoutRegion} this
38333 * Fires when this region is slid into view.
38334 * @param {Roo.LayoutRegion} this
38336 "slideshow" : true,
38339 * Fires when this region slides out of view.
38340 * @param {Roo.LayoutRegion} this
38342 "slidehide" : true,
38344 * @event panelactivated
38345 * Fires when a panel is activated.
38346 * @param {Roo.LayoutRegion} this
38347 * @param {Roo.ContentPanel} panel The activated panel
38349 "panelactivated" : true,
38352 * Fires when the user resizes this region.
38353 * @param {Roo.LayoutRegion} this
38354 * @param {Number} newSize The new size (width for east/west, height for north/south)
38358 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38359 this.panels = new Roo.util.MixedCollection();
38360 this.panels.getKey = this.getPanelId.createDelegate(this);
38362 this.activePanel = null;
38363 // ensure listeners are added...
38365 if (config.listeners || config.events) {
38366 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38367 listeners : config.listeners || {},
38368 events : config.events || {}
38372 if(skipConfig !== true){
38373 this.applyConfig(config);
38377 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38379 getPanelId : function(p){
38383 applyConfig : function(config){
38384 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38385 this.config = config;
38390 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38391 * the width, for horizontal (north, south) the height.
38392 * @param {Number} newSize The new width or height
38394 resizeTo : function(newSize){
38395 var el = this.el ? this.el :
38396 (this.activePanel ? this.activePanel.getEl() : null);
38398 switch(this.position){
38401 el.setWidth(newSize);
38402 this.fireEvent("resized", this, newSize);
38406 el.setHeight(newSize);
38407 this.fireEvent("resized", this, newSize);
38413 getBox : function(){
38414 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38417 getMargins : function(){
38418 return this.margins;
38421 updateBox : function(box){
38423 var el = this.activePanel.getEl();
38424 el.dom.style.left = box.x + "px";
38425 el.dom.style.top = box.y + "px";
38426 this.activePanel.setSize(box.width, box.height);
38430 * Returns the container element for this region.
38431 * @return {Roo.Element}
38433 getEl : function(){
38434 return this.activePanel;
38438 * Returns true if this region is currently visible.
38439 * @return {Boolean}
38441 isVisible : function(){
38442 return this.activePanel ? true : false;
38445 setActivePanel : function(panel){
38446 panel = this.getPanel(panel);
38447 if(this.activePanel && this.activePanel != panel){
38448 this.activePanel.setActiveState(false);
38449 this.activePanel.getEl().setLeftTop(-10000,-10000);
38451 this.activePanel = panel;
38452 panel.setActiveState(true);
38454 panel.setSize(this.box.width, this.box.height);
38456 this.fireEvent("panelactivated", this, panel);
38457 this.fireEvent("invalidated");
38461 * Show the specified panel.
38462 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38463 * @return {Roo.ContentPanel} The shown panel or null
38465 showPanel : function(panel){
38466 panel = this.getPanel(panel);
38468 this.setActivePanel(panel);
38474 * Get the active panel for this region.
38475 * @return {Roo.ContentPanel} The active panel or null
38477 getActivePanel : function(){
38478 return this.activePanel;
38482 * Add the passed ContentPanel(s)
38483 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38484 * @return {Roo.ContentPanel} The panel added (if only one was added)
38486 add : function(panel){
38487 if(arguments.length > 1){
38488 for(var i = 0, len = arguments.length; i < len; i++) {
38489 this.add(arguments[i]);
38493 if(this.hasPanel(panel)){
38494 this.showPanel(panel);
38497 var el = panel.getEl();
38498 if(el.dom.parentNode != this.mgr.el.dom){
38499 this.mgr.el.dom.appendChild(el.dom);
38501 if(panel.setRegion){
38502 panel.setRegion(this);
38504 this.panels.add(panel);
38505 el.setStyle("position", "absolute");
38506 if(!panel.background){
38507 this.setActivePanel(panel);
38508 if(this.config.initialSize && this.panels.getCount()==1){
38509 this.resizeTo(this.config.initialSize);
38512 this.fireEvent("paneladded", this, panel);
38517 * Returns true if the panel is in this region.
38518 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38519 * @return {Boolean}
38521 hasPanel : function(panel){
38522 if(typeof panel == "object"){ // must be panel obj
38523 panel = panel.getId();
38525 return this.getPanel(panel) ? true : false;
38529 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38530 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38531 * @param {Boolean} preservePanel Overrides the config preservePanel option
38532 * @return {Roo.ContentPanel} The panel that was removed
38534 remove : function(panel, preservePanel){
38535 panel = this.getPanel(panel);
38540 this.fireEvent("beforeremove", this, panel, e);
38541 if(e.cancel === true){
38544 var panelId = panel.getId();
38545 this.panels.removeKey(panelId);
38550 * Returns the panel specified or null if it's not in this region.
38551 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38552 * @return {Roo.ContentPanel}
38554 getPanel : function(id){
38555 if(typeof id == "object"){ // must be panel obj
38558 return this.panels.get(id);
38562 * Returns this regions position (north/south/east/west/center).
38565 getPosition: function(){
38566 return this.position;
38570 * Ext JS Library 1.1.1
38571 * Copyright(c) 2006-2007, Ext JS, LLC.
38573 * Originally Released Under LGPL - original licence link has changed is not relivant.
38576 * <script type="text/javascript">
38580 * @class Roo.bootstrap.layout.Region
38581 * @extends Roo.bootstrap.layout.Basic
38582 * This class represents a region in a layout manager.
38584 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38585 * @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})
38586 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38587 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38588 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38589 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38590 * @cfg {String} title The title for the region (overrides panel titles)
38591 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38592 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38593 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38594 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38595 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38596 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38597 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38598 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38599 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38600 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38602 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38603 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38604 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38605 * @cfg {Number} width For East/West panels
38606 * @cfg {Number} height For North/South panels
38607 * @cfg {Boolean} split To show the splitter
38608 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38610 * @cfg {string} cls Extra CSS classes to add to region
38612 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38613 * @cfg {string} region the region that it inhabits..
38616 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38617 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38619 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38620 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38621 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38623 Roo.bootstrap.layout.Region = function(config)
38625 this.applyConfig(config);
38627 var mgr = config.mgr;
38628 var pos = config.region;
38629 config.skipConfig = true;
38630 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38633 this.onRender(mgr.el);
38636 this.visible = true;
38637 this.collapsed = false;
38638 this.unrendered_panels = [];
38641 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38643 position: '', // set by wrapper (eg. north/south etc..)
38644 unrendered_panels : null, // unrendered panels.
38646 tabPosition : false,
38648 mgr: false, // points to 'Border'
38651 createBody : function(){
38652 /** This region's body element
38653 * @type Roo.Element */
38654 this.bodyEl = this.el.createChild({
38656 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38660 onRender: function(ctr, pos)
38662 var dh = Roo.DomHelper;
38663 /** This region's container element
38664 * @type Roo.Element */
38665 this.el = dh.append(ctr.dom, {
38667 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38669 /** This region's title element
38670 * @type Roo.Element */
38672 this.titleEl = dh.append(this.el.dom, {
38674 unselectable: "on",
38675 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38677 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38678 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38682 this.titleEl.enableDisplayMode();
38683 /** This region's title text element
38684 * @type HTMLElement */
38685 this.titleTextEl = this.titleEl.dom.firstChild;
38686 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38688 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38689 this.closeBtn.enableDisplayMode();
38690 this.closeBtn.on("click", this.closeClicked, this);
38691 this.closeBtn.hide();
38693 this.createBody(this.config);
38694 if(this.config.hideWhenEmpty){
38696 this.on("paneladded", this.validateVisibility, this);
38697 this.on("panelremoved", this.validateVisibility, this);
38699 if(this.autoScroll){
38700 this.bodyEl.setStyle("overflow", "auto");
38702 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38704 //if(c.titlebar !== false){
38705 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38706 this.titleEl.hide();
38708 this.titleEl.show();
38709 if(this.config.title){
38710 this.titleTextEl.innerHTML = this.config.title;
38714 if(this.config.collapsed){
38715 this.collapse(true);
38717 if(this.config.hidden){
38721 if (this.unrendered_panels && this.unrendered_panels.length) {
38722 for (var i =0;i< this.unrendered_panels.length; i++) {
38723 this.add(this.unrendered_panels[i]);
38725 this.unrendered_panels = null;
38731 applyConfig : function(c)
38734 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38735 var dh = Roo.DomHelper;
38736 if(c.titlebar !== false){
38737 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38738 this.collapseBtn.on("click", this.collapse, this);
38739 this.collapseBtn.enableDisplayMode();
38741 if(c.showPin === true || this.showPin){
38742 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38743 this.stickBtn.enableDisplayMode();
38744 this.stickBtn.on("click", this.expand, this);
38745 this.stickBtn.hide();
38750 /** This region's collapsed element
38751 * @type Roo.Element */
38754 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38755 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38758 if(c.floatable !== false){
38759 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38760 this.collapsedEl.on("click", this.collapseClick, this);
38763 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38764 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38765 id: "message", unselectable: "on", style:{"float":"left"}});
38766 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38768 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38769 this.expandBtn.on("click", this.expand, this);
38773 if(this.collapseBtn){
38774 this.collapseBtn.setVisible(c.collapsible == true);
38777 this.cmargins = c.cmargins || this.cmargins ||
38778 (this.position == "west" || this.position == "east" ?
38779 {top: 0, left: 2, right:2, bottom: 0} :
38780 {top: 2, left: 0, right:0, bottom: 2});
38782 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38785 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38787 this.autoScroll = c.autoScroll || false;
38792 this.duration = c.duration || .30;
38793 this.slideDuration = c.slideDuration || .45;
38798 * Returns true if this region is currently visible.
38799 * @return {Boolean}
38801 isVisible : function(){
38802 return this.visible;
38806 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38807 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38809 //setCollapsedTitle : function(title){
38810 // title = title || " ";
38811 // if(this.collapsedTitleTextEl){
38812 // this.collapsedTitleTextEl.innerHTML = title;
38816 getBox : function(){
38818 // if(!this.collapsed){
38819 b = this.el.getBox(false, true);
38821 // b = this.collapsedEl.getBox(false, true);
38826 getMargins : function(){
38827 return this.margins;
38828 //return this.collapsed ? this.cmargins : this.margins;
38831 highlight : function(){
38832 this.el.addClass("x-layout-panel-dragover");
38835 unhighlight : function(){
38836 this.el.removeClass("x-layout-panel-dragover");
38839 updateBox : function(box)
38841 if (!this.bodyEl) {
38842 return; // not rendered yet..
38846 if(!this.collapsed){
38847 this.el.dom.style.left = box.x + "px";
38848 this.el.dom.style.top = box.y + "px";
38849 this.updateBody(box.width, box.height);
38851 this.collapsedEl.dom.style.left = box.x + "px";
38852 this.collapsedEl.dom.style.top = box.y + "px";
38853 this.collapsedEl.setSize(box.width, box.height);
38856 this.tabs.autoSizeTabs();
38860 updateBody : function(w, h)
38863 this.el.setWidth(w);
38864 w -= this.el.getBorderWidth("rl");
38865 if(this.config.adjustments){
38866 w += this.config.adjustments[0];
38869 if(h !== null && h > 0){
38870 this.el.setHeight(h);
38871 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38872 h -= this.el.getBorderWidth("tb");
38873 if(this.config.adjustments){
38874 h += this.config.adjustments[1];
38876 this.bodyEl.setHeight(h);
38878 h = this.tabs.syncHeight(h);
38881 if(this.panelSize){
38882 w = w !== null ? w : this.panelSize.width;
38883 h = h !== null ? h : this.panelSize.height;
38885 if(this.activePanel){
38886 var el = this.activePanel.getEl();
38887 w = w !== null ? w : el.getWidth();
38888 h = h !== null ? h : el.getHeight();
38889 this.panelSize = {width: w, height: h};
38890 this.activePanel.setSize(w, h);
38892 if(Roo.isIE && this.tabs){
38893 this.tabs.el.repaint();
38898 * Returns the container element for this region.
38899 * @return {Roo.Element}
38901 getEl : function(){
38906 * Hides this region.
38909 //if(!this.collapsed){
38910 this.el.dom.style.left = "-2000px";
38913 // this.collapsedEl.dom.style.left = "-2000px";
38914 // this.collapsedEl.hide();
38916 this.visible = false;
38917 this.fireEvent("visibilitychange", this, false);
38921 * Shows this region if it was previously hidden.
38924 //if(!this.collapsed){
38927 // this.collapsedEl.show();
38929 this.visible = true;
38930 this.fireEvent("visibilitychange", this, true);
38933 closeClicked : function(){
38934 if(this.activePanel){
38935 this.remove(this.activePanel);
38939 collapseClick : function(e){
38941 e.stopPropagation();
38944 e.stopPropagation();
38950 * Collapses this region.
38951 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38954 collapse : function(skipAnim, skipCheck = false){
38955 if(this.collapsed) {
38959 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38961 this.collapsed = true;
38963 this.split.el.hide();
38965 if(this.config.animate && skipAnim !== true){
38966 this.fireEvent("invalidated", this);
38967 this.animateCollapse();
38969 this.el.setLocation(-20000,-20000);
38971 this.collapsedEl.show();
38972 this.fireEvent("collapsed", this);
38973 this.fireEvent("invalidated", this);
38979 animateCollapse : function(){
38984 * Expands this region if it was previously collapsed.
38985 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38986 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38989 expand : function(e, skipAnim){
38991 e.stopPropagation();
38993 if(!this.collapsed || this.el.hasActiveFx()) {
38997 this.afterSlideIn();
39000 this.collapsed = false;
39001 if(this.config.animate && skipAnim !== true){
39002 this.animateExpand();
39006 this.split.el.show();
39008 this.collapsedEl.setLocation(-2000,-2000);
39009 this.collapsedEl.hide();
39010 this.fireEvent("invalidated", this);
39011 this.fireEvent("expanded", this);
39015 animateExpand : function(){
39019 initTabs : function()
39021 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39023 var ts = new Roo.bootstrap.panel.Tabs({
39024 el: this.bodyEl.dom,
39026 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39027 disableTooltips: this.config.disableTabTips,
39028 toolbar : this.config.toolbar
39031 if(this.config.hideTabs){
39032 ts.stripWrap.setDisplayed(false);
39035 ts.resizeTabs = this.config.resizeTabs === true;
39036 ts.minTabWidth = this.config.minTabWidth || 40;
39037 ts.maxTabWidth = this.config.maxTabWidth || 250;
39038 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39039 ts.monitorResize = false;
39040 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39041 ts.bodyEl.addClass('roo-layout-tabs-body');
39042 this.panels.each(this.initPanelAsTab, this);
39045 initPanelAsTab : function(panel){
39046 var ti = this.tabs.addTab(
39050 this.config.closeOnTab && panel.isClosable(),
39053 if(panel.tabTip !== undefined){
39054 ti.setTooltip(panel.tabTip);
39056 ti.on("activate", function(){
39057 this.setActivePanel(panel);
39060 if(this.config.closeOnTab){
39061 ti.on("beforeclose", function(t, e){
39063 this.remove(panel);
39067 panel.tabItem = ti;
39072 updatePanelTitle : function(panel, title)
39074 if(this.activePanel == panel){
39075 this.updateTitle(title);
39078 var ti = this.tabs.getTab(panel.getEl().id);
39080 if(panel.tabTip !== undefined){
39081 ti.setTooltip(panel.tabTip);
39086 updateTitle : function(title){
39087 if(this.titleTextEl && !this.config.title){
39088 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39092 setActivePanel : function(panel)
39094 panel = this.getPanel(panel);
39095 if(this.activePanel && this.activePanel != panel){
39096 if(this.activePanel.setActiveState(false) === false){
39100 this.activePanel = panel;
39101 panel.setActiveState(true);
39102 if(this.panelSize){
39103 panel.setSize(this.panelSize.width, this.panelSize.height);
39106 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39108 this.updateTitle(panel.getTitle());
39110 this.fireEvent("invalidated", this);
39112 this.fireEvent("panelactivated", this, panel);
39116 * Shows the specified panel.
39117 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39118 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39120 showPanel : function(panel)
39122 panel = this.getPanel(panel);
39125 var tab = this.tabs.getTab(panel.getEl().id);
39126 if(tab.isHidden()){
39127 this.tabs.unhideTab(tab.id);
39131 this.setActivePanel(panel);
39138 * Get the active panel for this region.
39139 * @return {Roo.ContentPanel} The active panel or null
39141 getActivePanel : function(){
39142 return this.activePanel;
39145 validateVisibility : function(){
39146 if(this.panels.getCount() < 1){
39147 this.updateTitle(" ");
39148 this.closeBtn.hide();
39151 if(!this.isVisible()){
39158 * Adds the passed ContentPanel(s) to this region.
39159 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39160 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39162 add : function(panel)
39164 if(arguments.length > 1){
39165 for(var i = 0, len = arguments.length; i < len; i++) {
39166 this.add(arguments[i]);
39171 // if we have not been rendered yet, then we can not really do much of this..
39172 if (!this.bodyEl) {
39173 this.unrendered_panels.push(panel);
39180 if(this.hasPanel(panel)){
39181 this.showPanel(panel);
39184 panel.setRegion(this);
39185 this.panels.add(panel);
39186 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39187 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39188 // and hide them... ???
39189 this.bodyEl.dom.appendChild(panel.getEl().dom);
39190 if(panel.background !== true){
39191 this.setActivePanel(panel);
39193 this.fireEvent("paneladded", this, panel);
39200 this.initPanelAsTab(panel);
39204 if(panel.background !== true){
39205 this.tabs.activate(panel.getEl().id);
39207 this.fireEvent("paneladded", this, panel);
39212 * Hides the tab for the specified panel.
39213 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39215 hidePanel : function(panel){
39216 if(this.tabs && (panel = this.getPanel(panel))){
39217 this.tabs.hideTab(panel.getEl().id);
39222 * Unhides the tab for a previously hidden panel.
39223 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39225 unhidePanel : function(panel){
39226 if(this.tabs && (panel = this.getPanel(panel))){
39227 this.tabs.unhideTab(panel.getEl().id);
39231 clearPanels : function(){
39232 while(this.panels.getCount() > 0){
39233 this.remove(this.panels.first());
39238 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39239 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39240 * @param {Boolean} preservePanel Overrides the config preservePanel option
39241 * @return {Roo.ContentPanel} The panel that was removed
39243 remove : function(panel, preservePanel)
39245 panel = this.getPanel(panel);
39250 this.fireEvent("beforeremove", this, panel, e);
39251 if(e.cancel === true){
39254 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39255 var panelId = panel.getId();
39256 this.panels.removeKey(panelId);
39258 document.body.appendChild(panel.getEl().dom);
39261 this.tabs.removeTab(panel.getEl().id);
39262 }else if (!preservePanel){
39263 this.bodyEl.dom.removeChild(panel.getEl().dom);
39265 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39266 var p = this.panels.first();
39267 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39268 tempEl.appendChild(p.getEl().dom);
39269 this.bodyEl.update("");
39270 this.bodyEl.dom.appendChild(p.getEl().dom);
39272 this.updateTitle(p.getTitle());
39274 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39275 this.setActivePanel(p);
39277 panel.setRegion(null);
39278 if(this.activePanel == panel){
39279 this.activePanel = null;
39281 if(this.config.autoDestroy !== false && preservePanel !== true){
39282 try{panel.destroy();}catch(e){}
39284 this.fireEvent("panelremoved", this, panel);
39289 * Returns the TabPanel component used by this region
39290 * @return {Roo.TabPanel}
39292 getTabs : function(){
39296 createTool : function(parentEl, className){
39297 var btn = Roo.DomHelper.append(parentEl, {
39299 cls: "x-layout-tools-button",
39302 cls: "roo-layout-tools-button-inner " + className,
39306 btn.addClassOnOver("roo-layout-tools-button-over");
39311 * Ext JS Library 1.1.1
39312 * Copyright(c) 2006-2007, Ext JS, LLC.
39314 * Originally Released Under LGPL - original licence link has changed is not relivant.
39317 * <script type="text/javascript">
39323 * @class Roo.SplitLayoutRegion
39324 * @extends Roo.LayoutRegion
39325 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39327 Roo.bootstrap.layout.Split = function(config){
39328 this.cursor = config.cursor;
39329 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39332 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39334 splitTip : "Drag to resize.",
39335 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39336 useSplitTips : false,
39338 applyConfig : function(config){
39339 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39342 onRender : function(ctr,pos) {
39344 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39345 if(!this.config.split){
39350 var splitEl = Roo.DomHelper.append(ctr.dom, {
39352 id: this.el.id + "-split",
39353 cls: "roo-layout-split roo-layout-split-"+this.position,
39356 /** The SplitBar for this region
39357 * @type Roo.SplitBar */
39358 // does not exist yet...
39359 Roo.log([this.position, this.orientation]);
39361 this.split = new Roo.bootstrap.SplitBar({
39362 dragElement : splitEl,
39363 resizingElement: this.el,
39364 orientation : this.orientation
39367 this.split.on("moved", this.onSplitMove, this);
39368 this.split.useShim = this.config.useShim === true;
39369 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39370 if(this.useSplitTips){
39371 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39373 //if(config.collapsible){
39374 // this.split.el.on("dblclick", this.collapse, this);
39377 if(typeof this.config.minSize != "undefined"){
39378 this.split.minSize = this.config.minSize;
39380 if(typeof this.config.maxSize != "undefined"){
39381 this.split.maxSize = this.config.maxSize;
39383 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39384 this.hideSplitter();
39389 getHMaxSize : function(){
39390 var cmax = this.config.maxSize || 10000;
39391 var center = this.mgr.getRegion("center");
39392 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39395 getVMaxSize : function(){
39396 var cmax = this.config.maxSize || 10000;
39397 var center = this.mgr.getRegion("center");
39398 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39401 onSplitMove : function(split, newSize){
39402 this.fireEvent("resized", this, newSize);
39406 * Returns the {@link Roo.SplitBar} for this region.
39407 * @return {Roo.SplitBar}
39409 getSplitBar : function(){
39414 this.hideSplitter();
39415 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39418 hideSplitter : function(){
39420 this.split.el.setLocation(-2000,-2000);
39421 this.split.el.hide();
39427 this.split.el.show();
39429 Roo.bootstrap.layout.Split.superclass.show.call(this);
39432 beforeSlide: function(){
39433 if(Roo.isGecko){// firefox overflow auto bug workaround
39434 this.bodyEl.clip();
39436 this.tabs.bodyEl.clip();
39438 if(this.activePanel){
39439 this.activePanel.getEl().clip();
39441 if(this.activePanel.beforeSlide){
39442 this.activePanel.beforeSlide();
39448 afterSlide : function(){
39449 if(Roo.isGecko){// firefox overflow auto bug workaround
39450 this.bodyEl.unclip();
39452 this.tabs.bodyEl.unclip();
39454 if(this.activePanel){
39455 this.activePanel.getEl().unclip();
39456 if(this.activePanel.afterSlide){
39457 this.activePanel.afterSlide();
39463 initAutoHide : function(){
39464 if(this.autoHide !== false){
39465 if(!this.autoHideHd){
39466 var st = new Roo.util.DelayedTask(this.slideIn, this);
39467 this.autoHideHd = {
39468 "mouseout": function(e){
39469 if(!e.within(this.el, true)){
39473 "mouseover" : function(e){
39479 this.el.on(this.autoHideHd);
39483 clearAutoHide : function(){
39484 if(this.autoHide !== false){
39485 this.el.un("mouseout", this.autoHideHd.mouseout);
39486 this.el.un("mouseover", this.autoHideHd.mouseover);
39490 clearMonitor : function(){
39491 Roo.get(document).un("click", this.slideInIf, this);
39494 // these names are backwards but not changed for compat
39495 slideOut : function(){
39496 if(this.isSlid || this.el.hasActiveFx()){
39499 this.isSlid = true;
39500 if(this.collapseBtn){
39501 this.collapseBtn.hide();
39503 this.closeBtnState = this.closeBtn.getStyle('display');
39504 this.closeBtn.hide();
39506 this.stickBtn.show();
39509 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39510 this.beforeSlide();
39511 this.el.setStyle("z-index", 10001);
39512 this.el.slideIn(this.getSlideAnchor(), {
39513 callback: function(){
39515 this.initAutoHide();
39516 Roo.get(document).on("click", this.slideInIf, this);
39517 this.fireEvent("slideshow", this);
39524 afterSlideIn : function(){
39525 this.clearAutoHide();
39526 this.isSlid = false;
39527 this.clearMonitor();
39528 this.el.setStyle("z-index", "");
39529 if(this.collapseBtn){
39530 this.collapseBtn.show();
39532 this.closeBtn.setStyle('display', this.closeBtnState);
39534 this.stickBtn.hide();
39536 this.fireEvent("slidehide", this);
39539 slideIn : function(cb){
39540 if(!this.isSlid || this.el.hasActiveFx()){
39544 this.isSlid = false;
39545 this.beforeSlide();
39546 this.el.slideOut(this.getSlideAnchor(), {
39547 callback: function(){
39548 this.el.setLeftTop(-10000, -10000);
39550 this.afterSlideIn();
39558 slideInIf : function(e){
39559 if(!e.within(this.el)){
39564 animateCollapse : function(){
39565 this.beforeSlide();
39566 this.el.setStyle("z-index", 20000);
39567 var anchor = this.getSlideAnchor();
39568 this.el.slideOut(anchor, {
39569 callback : function(){
39570 this.el.setStyle("z-index", "");
39571 this.collapsedEl.slideIn(anchor, {duration:.3});
39573 this.el.setLocation(-10000,-10000);
39575 this.fireEvent("collapsed", this);
39582 animateExpand : function(){
39583 this.beforeSlide();
39584 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39585 this.el.setStyle("z-index", 20000);
39586 this.collapsedEl.hide({
39589 this.el.slideIn(this.getSlideAnchor(), {
39590 callback : function(){
39591 this.el.setStyle("z-index", "");
39594 this.split.el.show();
39596 this.fireEvent("invalidated", this);
39597 this.fireEvent("expanded", this);
39625 getAnchor : function(){
39626 return this.anchors[this.position];
39629 getCollapseAnchor : function(){
39630 return this.canchors[this.position];
39633 getSlideAnchor : function(){
39634 return this.sanchors[this.position];
39637 getAlignAdj : function(){
39638 var cm = this.cmargins;
39639 switch(this.position){
39655 getExpandAdj : function(){
39656 var c = this.collapsedEl, cm = this.cmargins;
39657 switch(this.position){
39659 return [-(cm.right+c.getWidth()+cm.left), 0];
39662 return [cm.right+c.getWidth()+cm.left, 0];
39665 return [0, -(cm.top+cm.bottom+c.getHeight())];
39668 return [0, cm.top+cm.bottom+c.getHeight()];
39674 * Ext JS Library 1.1.1
39675 * Copyright(c) 2006-2007, Ext JS, LLC.
39677 * Originally Released Under LGPL - original licence link has changed is not relivant.
39680 * <script type="text/javascript">
39683 * These classes are private internal classes
39685 Roo.bootstrap.layout.Center = function(config){
39686 config.region = "center";
39687 Roo.bootstrap.layout.Region.call(this, config);
39688 this.visible = true;
39689 this.minWidth = config.minWidth || 20;
39690 this.minHeight = config.minHeight || 20;
39693 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39695 // center panel can't be hidden
39699 // center panel can't be hidden
39702 getMinWidth: function(){
39703 return this.minWidth;
39706 getMinHeight: function(){
39707 return this.minHeight;
39721 Roo.bootstrap.layout.North = function(config)
39723 config.region = 'north';
39724 config.cursor = 'n-resize';
39726 Roo.bootstrap.layout.Split.call(this, config);
39730 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39731 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39732 this.split.el.addClass("roo-layout-split-v");
39734 //var size = config.initialSize || config.height;
39735 //if(this.el && typeof size != "undefined"){
39736 // this.el.setHeight(size);
39739 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39741 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39744 onRender : function(ctr, pos)
39746 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39747 var size = this.config.initialSize || this.config.height;
39748 if(this.el && typeof size != "undefined"){
39749 this.el.setHeight(size);
39754 getBox : function(){
39755 if(this.collapsed){
39756 return this.collapsedEl.getBox();
39758 var box = this.el.getBox();
39760 box.height += this.split.el.getHeight();
39765 updateBox : function(box){
39766 if(this.split && !this.collapsed){
39767 box.height -= this.split.el.getHeight();
39768 this.split.el.setLeft(box.x);
39769 this.split.el.setTop(box.y+box.height);
39770 this.split.el.setWidth(box.width);
39772 if(this.collapsed){
39773 this.updateBody(box.width, null);
39775 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39783 Roo.bootstrap.layout.South = function(config){
39784 config.region = 'south';
39785 config.cursor = 's-resize';
39786 Roo.bootstrap.layout.Split.call(this, config);
39788 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39789 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39790 this.split.el.addClass("roo-layout-split-v");
39795 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39796 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39798 onRender : function(ctr, pos)
39800 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39801 var size = this.config.initialSize || this.config.height;
39802 if(this.el && typeof size != "undefined"){
39803 this.el.setHeight(size);
39808 getBox : function(){
39809 if(this.collapsed){
39810 return this.collapsedEl.getBox();
39812 var box = this.el.getBox();
39814 var sh = this.split.el.getHeight();
39821 updateBox : function(box){
39822 if(this.split && !this.collapsed){
39823 var sh = this.split.el.getHeight();
39826 this.split.el.setLeft(box.x);
39827 this.split.el.setTop(box.y-sh);
39828 this.split.el.setWidth(box.width);
39830 if(this.collapsed){
39831 this.updateBody(box.width, null);
39833 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39837 Roo.bootstrap.layout.East = function(config){
39838 config.region = "east";
39839 config.cursor = "e-resize";
39840 Roo.bootstrap.layout.Split.call(this, config);
39842 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39843 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39844 this.split.el.addClass("roo-layout-split-h");
39848 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39849 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39851 onRender : function(ctr, pos)
39853 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39854 var size = this.config.initialSize || this.config.width;
39855 if(this.el && typeof size != "undefined"){
39856 this.el.setWidth(size);
39861 getBox : function(){
39862 if(this.collapsed){
39863 return this.collapsedEl.getBox();
39865 var box = this.el.getBox();
39867 var sw = this.split.el.getWidth();
39874 updateBox : function(box){
39875 if(this.split && !this.collapsed){
39876 var sw = this.split.el.getWidth();
39878 this.split.el.setLeft(box.x);
39879 this.split.el.setTop(box.y);
39880 this.split.el.setHeight(box.height);
39883 if(this.collapsed){
39884 this.updateBody(null, box.height);
39886 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39890 Roo.bootstrap.layout.West = function(config){
39891 config.region = "west";
39892 config.cursor = "w-resize";
39894 Roo.bootstrap.layout.Split.call(this, config);
39896 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39897 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39898 this.split.el.addClass("roo-layout-split-h");
39902 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39903 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39905 onRender: function(ctr, pos)
39907 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39908 var size = this.config.initialSize || this.config.width;
39909 if(typeof size != "undefined"){
39910 this.el.setWidth(size);
39914 getBox : function(){
39915 if(this.collapsed){
39916 return this.collapsedEl.getBox();
39918 var box = this.el.getBox();
39919 if (box.width == 0) {
39920 box.width = this.config.width; // kludge?
39923 box.width += this.split.el.getWidth();
39928 updateBox : function(box){
39929 if(this.split && !this.collapsed){
39930 var sw = this.split.el.getWidth();
39932 this.split.el.setLeft(box.x+box.width);
39933 this.split.el.setTop(box.y);
39934 this.split.el.setHeight(box.height);
39936 if(this.collapsed){
39937 this.updateBody(null, box.height);
39939 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39941 });Roo.namespace("Roo.bootstrap.panel");/*
39943 * Ext JS Library 1.1.1
39944 * Copyright(c) 2006-2007, Ext JS, LLC.
39946 * Originally Released Under LGPL - original licence link has changed is not relivant.
39949 * <script type="text/javascript">
39952 * @class Roo.ContentPanel
39953 * @extends Roo.util.Observable
39954 * A basic ContentPanel element.
39955 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39956 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39957 * @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
39958 * @cfg {Boolean} closable True if the panel can be closed/removed
39959 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39960 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39961 * @cfg {Toolbar} toolbar A toolbar for this panel
39962 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39963 * @cfg {String} title The title for this panel
39964 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39965 * @cfg {String} url Calls {@link #setUrl} with this value
39966 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39967 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39968 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39969 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39970 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39971 * @cfg {Boolean} badges render the badges
39972 * @cfg {String} cls extra classes to use
39973 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39976 * Create a new ContentPanel.
39977 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39978 * @param {String/Object} config A string to set only the title or a config object
39979 * @param {String} content (optional) Set the HTML content for this panel
39980 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39982 Roo.bootstrap.panel.Content = function( config){
39984 this.tpl = config.tpl || false;
39986 var el = config.el;
39987 var content = config.content;
39989 if(config.autoCreate){ // xtype is available if this is called from factory
39992 this.el = Roo.get(el);
39993 if(!this.el && config && config.autoCreate){
39994 if(typeof config.autoCreate == "object"){
39995 if(!config.autoCreate.id){
39996 config.autoCreate.id = config.id||el;
39998 this.el = Roo.DomHelper.append(document.body,
39999 config.autoCreate, true);
40003 cls: (config.cls || '') +
40004 (config.background ? ' bg-' + config.background : '') +
40005 " roo-layout-inactive-content",
40008 if (config.iframe) {
40012 style : 'border: 0px',
40013 src : 'about:blank'
40019 elcfg.html = config.html;
40023 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40024 if (config.iframe) {
40025 this.iframeEl = this.el.select('iframe',true).first();
40030 this.closable = false;
40031 this.loaded = false;
40032 this.active = false;
40035 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40037 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40039 this.wrapEl = this.el; //this.el.wrap();
40041 if (config.toolbar.items) {
40042 ti = config.toolbar.items ;
40043 delete config.toolbar.items ;
40047 this.toolbar.render(this.wrapEl, 'before');
40048 for(var i =0;i < ti.length;i++) {
40049 // Roo.log(['add child', items[i]]);
40050 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40052 this.toolbar.items = nitems;
40053 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40054 delete config.toolbar;
40058 // xtype created footer. - not sure if will work as we normally have to render first..
40059 if (this.footer && !this.footer.el && this.footer.xtype) {
40060 if (!this.wrapEl) {
40061 this.wrapEl = this.el.wrap();
40064 this.footer.container = this.wrapEl.createChild();
40066 this.footer = Roo.factory(this.footer, Roo);
40071 if(typeof config == "string"){
40072 this.title = config;
40074 Roo.apply(this, config);
40078 this.resizeEl = Roo.get(this.resizeEl, true);
40080 this.resizeEl = this.el;
40082 // handle view.xtype
40090 * Fires when this panel is activated.
40091 * @param {Roo.ContentPanel} this
40095 * @event deactivate
40096 * Fires when this panel is activated.
40097 * @param {Roo.ContentPanel} this
40099 "deactivate" : true,
40103 * Fires when this panel is resized if fitToFrame is true.
40104 * @param {Roo.ContentPanel} this
40105 * @param {Number} width The width after any component adjustments
40106 * @param {Number} height The height after any component adjustments
40112 * Fires when this tab is created
40113 * @param {Roo.ContentPanel} this
40124 if(this.autoScroll && !this.iframe){
40125 this.resizeEl.setStyle("overflow", "auto");
40127 // fix randome scrolling
40128 //this.el.on('scroll', function() {
40129 // Roo.log('fix random scolling');
40130 // this.scrollTo('top',0);
40133 content = content || this.content;
40135 this.setContent(content);
40137 if(config && config.url){
40138 this.setUrl(this.url, this.params, this.loadOnce);
40143 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40145 if (this.view && typeof(this.view.xtype) != 'undefined') {
40146 this.view.el = this.el.appendChild(document.createElement("div"));
40147 this.view = Roo.factory(this.view);
40148 this.view.render && this.view.render(false, '');
40152 this.fireEvent('render', this);
40155 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40165 setRegion : function(region){
40166 this.region = region;
40167 this.setActiveClass(region && !this.background);
40171 setActiveClass: function(state)
40174 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40175 this.el.setStyle('position','relative');
40177 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40178 this.el.setStyle('position', 'absolute');
40183 * Returns the toolbar for this Panel if one was configured.
40184 * @return {Roo.Toolbar}
40186 getToolbar : function(){
40187 return this.toolbar;
40190 setActiveState : function(active)
40192 this.active = active;
40193 this.setActiveClass(active);
40195 if(this.fireEvent("deactivate", this) === false){
40200 this.fireEvent("activate", this);
40204 * Updates this panel's element (not for iframe)
40205 * @param {String} content The new content
40206 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40208 setContent : function(content, loadScripts){
40213 this.el.update(content, loadScripts);
40216 ignoreResize : function(w, h){
40217 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40220 this.lastSize = {width: w, height: h};
40225 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40226 * @return {Roo.UpdateManager} The UpdateManager
40228 getUpdateManager : function(){
40232 return this.el.getUpdateManager();
40235 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40236 * Does not work with IFRAME contents
40237 * @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:
40240 url: "your-url.php",
40241 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40242 callback: yourFunction,
40243 scope: yourObject, //(optional scope)
40246 text: "Loading...",
40252 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40253 * 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.
40254 * @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}
40255 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40256 * @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.
40257 * @return {Roo.ContentPanel} this
40265 var um = this.el.getUpdateManager();
40266 um.update.apply(um, arguments);
40272 * 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.
40273 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40274 * @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)
40275 * @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)
40276 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40278 setUrl : function(url, params, loadOnce){
40280 this.iframeEl.dom.src = url;
40284 if(this.refreshDelegate){
40285 this.removeListener("activate", this.refreshDelegate);
40287 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40288 this.on("activate", this.refreshDelegate);
40289 return this.el.getUpdateManager();
40292 _handleRefresh : function(url, params, loadOnce){
40293 if(!loadOnce || !this.loaded){
40294 var updater = this.el.getUpdateManager();
40295 updater.update(url, params, this._setLoaded.createDelegate(this));
40299 _setLoaded : function(){
40300 this.loaded = true;
40304 * Returns this panel's id
40307 getId : function(){
40312 * Returns this panel's element - used by regiosn to add.
40313 * @return {Roo.Element}
40315 getEl : function(){
40316 return this.wrapEl || this.el;
40321 adjustForComponents : function(width, height)
40323 //Roo.log('adjustForComponents ');
40324 if(this.resizeEl != this.el){
40325 width -= this.el.getFrameWidth('lr');
40326 height -= this.el.getFrameWidth('tb');
40329 var te = this.toolbar.getEl();
40330 te.setWidth(width);
40331 height -= te.getHeight();
40334 var te = this.footer.getEl();
40335 te.setWidth(width);
40336 height -= te.getHeight();
40340 if(this.adjustments){
40341 width += this.adjustments[0];
40342 height += this.adjustments[1];
40344 return {"width": width, "height": height};
40347 setSize : function(width, height){
40348 if(this.fitToFrame && !this.ignoreResize(width, height)){
40349 if(this.fitContainer && this.resizeEl != this.el){
40350 this.el.setSize(width, height);
40352 var size = this.adjustForComponents(width, height);
40354 this.iframeEl.setSize(width,height);
40357 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40358 this.fireEvent('resize', this, size.width, size.height);
40365 * Returns this panel's title
40368 getTitle : function(){
40370 if (typeof(this.title) != 'object') {
40375 for (var k in this.title) {
40376 if (!this.title.hasOwnProperty(k)) {
40380 if (k.indexOf('-') >= 0) {
40381 var s = k.split('-');
40382 for (var i = 0; i<s.length; i++) {
40383 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40386 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40393 * Set this panel's title
40394 * @param {String} title
40396 setTitle : function(title){
40397 this.title = title;
40399 this.region.updatePanelTitle(this, title);
40404 * Returns true is this panel was configured to be closable
40405 * @return {Boolean}
40407 isClosable : function(){
40408 return this.closable;
40411 beforeSlide : function(){
40413 this.resizeEl.clip();
40416 afterSlide : function(){
40418 this.resizeEl.unclip();
40422 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40423 * Will fail silently if the {@link #setUrl} method has not been called.
40424 * This does not activate the panel, just updates its content.
40426 refresh : function(){
40427 if(this.refreshDelegate){
40428 this.loaded = false;
40429 this.refreshDelegate();
40434 * Destroys this panel
40436 destroy : function(){
40437 this.el.removeAllListeners();
40438 var tempEl = document.createElement("span");
40439 tempEl.appendChild(this.el.dom);
40440 tempEl.innerHTML = "";
40446 * form - if the content panel contains a form - this is a reference to it.
40447 * @type {Roo.form.Form}
40451 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40452 * This contains a reference to it.
40458 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40468 * @param {Object} cfg Xtype definition of item to add.
40472 getChildContainer: function () {
40473 return this.getEl();
40478 var ret = new Roo.factory(cfg);
40483 if (cfg.xtype.match(/^Form$/)) {
40486 //if (this.footer) {
40487 // el = this.footer.container.insertSibling(false, 'before');
40489 el = this.el.createChild();
40492 this.form = new Roo.form.Form(cfg);
40495 if ( this.form.allItems.length) {
40496 this.form.render(el.dom);
40500 // should only have one of theses..
40501 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40502 // views.. should not be just added - used named prop 'view''
40504 cfg.el = this.el.appendChild(document.createElement("div"));
40507 var ret = new Roo.factory(cfg);
40509 ret.render && ret.render(false, ''); // render blank..
40519 * @class Roo.bootstrap.panel.Grid
40520 * @extends Roo.bootstrap.panel.Content
40522 * Create a new GridPanel.
40523 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40524 * @param {Object} config A the config object
40530 Roo.bootstrap.panel.Grid = function(config)
40534 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40535 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40537 config.el = this.wrapper;
40538 //this.el = this.wrapper;
40540 if (config.container) {
40541 // ctor'ed from a Border/panel.grid
40544 this.wrapper.setStyle("overflow", "hidden");
40545 this.wrapper.addClass('roo-grid-container');
40550 if(config.toolbar){
40551 var tool_el = this.wrapper.createChild();
40552 this.toolbar = Roo.factory(config.toolbar);
40554 if (config.toolbar.items) {
40555 ti = config.toolbar.items ;
40556 delete config.toolbar.items ;
40560 this.toolbar.render(tool_el);
40561 for(var i =0;i < ti.length;i++) {
40562 // Roo.log(['add child', items[i]]);
40563 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40565 this.toolbar.items = nitems;
40567 delete config.toolbar;
40570 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40571 config.grid.scrollBody = true;;
40572 config.grid.monitorWindowResize = false; // turn off autosizing
40573 config.grid.autoHeight = false;
40574 config.grid.autoWidth = false;
40576 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40578 if (config.background) {
40579 // render grid on panel activation (if panel background)
40580 this.on('activate', function(gp) {
40581 if (!gp.grid.rendered) {
40582 gp.grid.render(this.wrapper);
40583 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40588 this.grid.render(this.wrapper);
40589 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40592 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40593 // ??? needed ??? config.el = this.wrapper;
40598 // xtype created footer. - not sure if will work as we normally have to render first..
40599 if (this.footer && !this.footer.el && this.footer.xtype) {
40601 var ctr = this.grid.getView().getFooterPanel(true);
40602 this.footer.dataSource = this.grid.dataSource;
40603 this.footer = Roo.factory(this.footer, Roo);
40604 this.footer.render(ctr);
40614 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40615 getId : function(){
40616 return this.grid.id;
40620 * Returns the grid for this panel
40621 * @return {Roo.bootstrap.Table}
40623 getGrid : function(){
40627 setSize : function(width, height){
40628 if(!this.ignoreResize(width, height)){
40629 var grid = this.grid;
40630 var size = this.adjustForComponents(width, height);
40631 // tfoot is not a footer?
40634 var gridel = grid.getGridEl();
40635 gridel.setSize(size.width, size.height);
40637 var tbd = grid.getGridEl().select('tbody', true).first();
40638 var thd = grid.getGridEl().select('thead',true).first();
40639 var tbf= grid.getGridEl().select('tfoot', true).first();
40642 size.height -= tbf.getHeight();
40645 size.height -= thd.getHeight();
40648 tbd.setSize(size.width, size.height );
40649 // this is for the account management tab -seems to work there.
40650 var thd = grid.getGridEl().select('thead',true).first();
40652 // tbd.setSize(size.width, size.height - thd.getHeight());
40661 beforeSlide : function(){
40662 this.grid.getView().scroller.clip();
40665 afterSlide : function(){
40666 this.grid.getView().scroller.unclip();
40669 destroy : function(){
40670 this.grid.destroy();
40672 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40677 * @class Roo.bootstrap.panel.Nest
40678 * @extends Roo.bootstrap.panel.Content
40680 * Create a new Panel, that can contain a layout.Border.
40683 * @param {Roo.BorderLayout} layout The layout for this panel
40684 * @param {String/Object} config A string to set only the title or a config object
40686 Roo.bootstrap.panel.Nest = function(config)
40688 // construct with only one argument..
40689 /* FIXME - implement nicer consturctors
40690 if (layout.layout) {
40692 layout = config.layout;
40693 delete config.layout;
40695 if (layout.xtype && !layout.getEl) {
40696 // then layout needs constructing..
40697 layout = Roo.factory(layout, Roo);
40701 config.el = config.layout.getEl();
40703 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40705 config.layout.monitorWindowResize = false; // turn off autosizing
40706 this.layout = config.layout;
40707 this.layout.getEl().addClass("roo-layout-nested-layout");
40708 this.layout.parent = this;
40715 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40717 setSize : function(width, height){
40718 if(!this.ignoreResize(width, height)){
40719 var size = this.adjustForComponents(width, height);
40720 var el = this.layout.getEl();
40721 if (size.height < 1) {
40722 el.setWidth(size.width);
40724 el.setSize(size.width, size.height);
40726 var touch = el.dom.offsetWidth;
40727 this.layout.layout();
40728 // ie requires a double layout on the first pass
40729 if(Roo.isIE && !this.initialized){
40730 this.initialized = true;
40731 this.layout.layout();
40736 // activate all subpanels if not currently active..
40738 setActiveState : function(active){
40739 this.active = active;
40740 this.setActiveClass(active);
40743 this.fireEvent("deactivate", this);
40747 this.fireEvent("activate", this);
40748 // not sure if this should happen before or after..
40749 if (!this.layout) {
40750 return; // should not happen..
40753 for (var r in this.layout.regions) {
40754 reg = this.layout.getRegion(r);
40755 if (reg.getActivePanel()) {
40756 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40757 reg.setActivePanel(reg.getActivePanel());
40760 if (!reg.panels.length) {
40763 reg.showPanel(reg.getPanel(0));
40772 * Returns the nested BorderLayout for this panel
40773 * @return {Roo.BorderLayout}
40775 getLayout : function(){
40776 return this.layout;
40780 * Adds a xtype elements to the layout of the nested panel
40784 xtype : 'ContentPanel',
40791 xtype : 'NestedLayoutPanel',
40797 items : [ ... list of content panels or nested layout panels.. ]
40801 * @param {Object} cfg Xtype definition of item to add.
40803 addxtype : function(cfg) {
40804 return this.layout.addxtype(cfg);
40809 * Ext JS Library 1.1.1
40810 * Copyright(c) 2006-2007, Ext JS, LLC.
40812 * Originally Released Under LGPL - original licence link has changed is not relivant.
40815 * <script type="text/javascript">
40818 * @class Roo.TabPanel
40819 * @extends Roo.util.Observable
40820 * A lightweight tab container.
40824 // basic tabs 1, built from existing content
40825 var tabs = new Roo.TabPanel("tabs1");
40826 tabs.addTab("script", "View Script");
40827 tabs.addTab("markup", "View Markup");
40828 tabs.activate("script");
40830 // more advanced tabs, built from javascript
40831 var jtabs = new Roo.TabPanel("jtabs");
40832 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40834 // set up the UpdateManager
40835 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40836 var updater = tab2.getUpdateManager();
40837 updater.setDefaultUrl("ajax1.htm");
40838 tab2.on('activate', updater.refresh, updater, true);
40840 // Use setUrl for Ajax loading
40841 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40842 tab3.setUrl("ajax2.htm", null, true);
40845 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40848 jtabs.activate("jtabs-1");
40851 * Create a new TabPanel.
40852 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40853 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40855 Roo.bootstrap.panel.Tabs = function(config){
40857 * The container element for this TabPanel.
40858 * @type Roo.Element
40860 this.el = Roo.get(config.el);
40863 if(typeof config == "boolean"){
40864 this.tabPosition = config ? "bottom" : "top";
40866 Roo.apply(this, config);
40870 if(this.tabPosition == "bottom"){
40871 // if tabs are at the bottom = create the body first.
40872 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40873 this.el.addClass("roo-tabs-bottom");
40875 // next create the tabs holders
40877 if (this.tabPosition == "west"){
40879 var reg = this.region; // fake it..
40881 if (!reg.mgr.parent) {
40884 reg = reg.mgr.parent.region;
40886 Roo.log("got nest?");
40888 if (reg.mgr.getRegion('west')) {
40889 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40890 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40891 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40892 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40893 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40901 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40902 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40903 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40904 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40909 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40912 // finally - if tabs are at the top, then create the body last..
40913 if(this.tabPosition != "bottom"){
40914 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40915 * @type Roo.Element
40917 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40918 this.el.addClass("roo-tabs-top");
40922 this.bodyEl.setStyle("position", "relative");
40924 this.active = null;
40925 this.activateDelegate = this.activate.createDelegate(this);
40930 * Fires when the active tab changes
40931 * @param {Roo.TabPanel} this
40932 * @param {Roo.TabPanelItem} activePanel The new active tab
40936 * @event beforetabchange
40937 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40938 * @param {Roo.TabPanel} this
40939 * @param {Object} e Set cancel to true on this object to cancel the tab change
40940 * @param {Roo.TabPanelItem} tab The tab being changed to
40942 "beforetabchange" : true
40945 Roo.EventManager.onWindowResize(this.onResize, this);
40946 this.cpad = this.el.getPadding("lr");
40947 this.hiddenCount = 0;
40950 // toolbar on the tabbar support...
40951 if (this.toolbar) {
40952 alert("no toolbar support yet");
40953 this.toolbar = false;
40955 var tcfg = this.toolbar;
40956 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40957 this.toolbar = new Roo.Toolbar(tcfg);
40958 if (Roo.isSafari) {
40959 var tbl = tcfg.container.child('table', true);
40960 tbl.setAttribute('width', '100%');
40968 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40971 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40973 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40975 tabPosition : "top",
40977 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40979 currentTabWidth : 0,
40981 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40985 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40989 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40991 preferredTabWidth : 175,
40993 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40995 resizeTabs : false,
40997 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40999 monitorResize : true,
41001 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41003 toolbar : false, // set by caller..
41005 region : false, /// set by caller
41007 disableTooltips : true, // not used yet...
41010 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41011 * @param {String} id The id of the div to use <b>or create</b>
41012 * @param {String} text The text for the tab
41013 * @param {String} content (optional) Content to put in the TabPanelItem body
41014 * @param {Boolean} closable (optional) True to create a close icon on the tab
41015 * @return {Roo.TabPanelItem} The created TabPanelItem
41017 addTab : function(id, text, content, closable, tpl)
41019 var item = new Roo.bootstrap.panel.TabItem({
41023 closable : closable,
41026 this.addTabItem(item);
41028 item.setContent(content);
41034 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41035 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41036 * @return {Roo.TabPanelItem}
41038 getTab : function(id){
41039 return this.items[id];
41043 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41044 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41046 hideTab : function(id){
41047 var t = this.items[id];
41050 this.hiddenCount++;
41051 this.autoSizeTabs();
41056 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41057 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41059 unhideTab : function(id){
41060 var t = this.items[id];
41062 t.setHidden(false);
41063 this.hiddenCount--;
41064 this.autoSizeTabs();
41069 * Adds an existing {@link Roo.TabPanelItem}.
41070 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41072 addTabItem : function(item)
41074 this.items[item.id] = item;
41075 this.items.push(item);
41076 this.autoSizeTabs();
41077 // if(this.resizeTabs){
41078 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41079 // this.autoSizeTabs();
41081 // item.autoSize();
41086 * Removes a {@link Roo.TabPanelItem}.
41087 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41089 removeTab : function(id){
41090 var items = this.items;
41091 var tab = items[id];
41092 if(!tab) { return; }
41093 var index = items.indexOf(tab);
41094 if(this.active == tab && items.length > 1){
41095 var newTab = this.getNextAvailable(index);
41100 this.stripEl.dom.removeChild(tab.pnode.dom);
41101 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41102 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41104 items.splice(index, 1);
41105 delete this.items[tab.id];
41106 tab.fireEvent("close", tab);
41107 tab.purgeListeners();
41108 this.autoSizeTabs();
41111 getNextAvailable : function(start){
41112 var items = this.items;
41114 // look for a next tab that will slide over to
41115 // replace the one being removed
41116 while(index < items.length){
41117 var item = items[++index];
41118 if(item && !item.isHidden()){
41122 // if one isn't found select the previous tab (on the left)
41125 var item = items[--index];
41126 if(item && !item.isHidden()){
41134 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41135 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41137 disableTab : function(id){
41138 var tab = this.items[id];
41139 if(tab && this.active != tab){
41145 * Enables a {@link Roo.TabPanelItem} that is disabled.
41146 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41148 enableTab : function(id){
41149 var tab = this.items[id];
41154 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41155 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41156 * @return {Roo.TabPanelItem} The TabPanelItem.
41158 activate : function(id)
41160 //Roo.log('activite:' + id);
41162 var tab = this.items[id];
41166 if(tab == this.active || tab.disabled){
41170 this.fireEvent("beforetabchange", this, e, tab);
41171 if(e.cancel !== true && !tab.disabled){
41173 this.active.hide();
41175 this.active = this.items[id];
41176 this.active.show();
41177 this.fireEvent("tabchange", this, this.active);
41183 * Gets the active {@link Roo.TabPanelItem}.
41184 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41186 getActiveTab : function(){
41187 return this.active;
41191 * Updates the tab body element to fit the height of the container element
41192 * for overflow scrolling
41193 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41195 syncHeight : function(targetHeight){
41196 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41197 var bm = this.bodyEl.getMargins();
41198 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41199 this.bodyEl.setHeight(newHeight);
41203 onResize : function(){
41204 if(this.monitorResize){
41205 this.autoSizeTabs();
41210 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41212 beginUpdate : function(){
41213 this.updating = true;
41217 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41219 endUpdate : function(){
41220 this.updating = false;
41221 this.autoSizeTabs();
41225 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41227 autoSizeTabs : function()
41229 var count = this.items.length;
41230 var vcount = count - this.hiddenCount;
41233 this.stripEl.hide();
41235 this.stripEl.show();
41238 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41243 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41244 var availWidth = Math.floor(w / vcount);
41245 var b = this.stripBody;
41246 if(b.getWidth() > w){
41247 var tabs = this.items;
41248 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41249 if(availWidth < this.minTabWidth){
41250 /*if(!this.sleft){ // incomplete scrolling code
41251 this.createScrollButtons();
41254 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41257 if(this.currentTabWidth < this.preferredTabWidth){
41258 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41264 * Returns the number of tabs in this TabPanel.
41267 getCount : function(){
41268 return this.items.length;
41272 * Resizes all the tabs to the passed width
41273 * @param {Number} The new width
41275 setTabWidth : function(width){
41276 this.currentTabWidth = width;
41277 for(var i = 0, len = this.items.length; i < len; i++) {
41278 if(!this.items[i].isHidden()) {
41279 this.items[i].setWidth(width);
41285 * Destroys this TabPanel
41286 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41288 destroy : function(removeEl){
41289 Roo.EventManager.removeResizeListener(this.onResize, this);
41290 for(var i = 0, len = this.items.length; i < len; i++){
41291 this.items[i].purgeListeners();
41293 if(removeEl === true){
41294 this.el.update("");
41299 createStrip : function(container)
41301 var strip = document.createElement("nav");
41302 strip.className = Roo.bootstrap.version == 4 ?
41303 "navbar-light bg-light" :
41304 "navbar navbar-default"; //"x-tabs-wrap";
41305 container.appendChild(strip);
41309 createStripList : function(strip)
41311 // div wrapper for retard IE
41312 // returns the "tr" element.
41313 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41314 //'<div class="x-tabs-strip-wrap">'+
41315 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41316 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41317 return strip.firstChild; //.firstChild.firstChild.firstChild;
41319 createBody : function(container)
41321 var body = document.createElement("div");
41322 Roo.id(body, "tab-body");
41323 //Roo.fly(body).addClass("x-tabs-body");
41324 Roo.fly(body).addClass("tab-content");
41325 container.appendChild(body);
41328 createItemBody :function(bodyEl, id){
41329 var body = Roo.getDom(id);
41331 body = document.createElement("div");
41334 //Roo.fly(body).addClass("x-tabs-item-body");
41335 Roo.fly(body).addClass("tab-pane");
41336 bodyEl.insertBefore(body, bodyEl.firstChild);
41340 createStripElements : function(stripEl, text, closable, tpl)
41342 var td = document.createElement("li"); // was td..
41343 td.className = 'nav-item';
41345 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41348 stripEl.appendChild(td);
41350 td.className = "x-tabs-closable";
41351 if(!this.closeTpl){
41352 this.closeTpl = new Roo.Template(
41353 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41354 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41355 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41358 var el = this.closeTpl.overwrite(td, {"text": text});
41359 var close = el.getElementsByTagName("div")[0];
41360 var inner = el.getElementsByTagName("em")[0];
41361 return {"el": el, "close": close, "inner": inner};
41364 // not sure what this is..
41365 // if(!this.tabTpl){
41366 //this.tabTpl = new Roo.Template(
41367 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41368 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41370 // this.tabTpl = new Roo.Template(
41371 // '<a href="#">' +
41372 // '<span unselectable="on"' +
41373 // (this.disableTooltips ? '' : ' title="{text}"') +
41374 // ' >{text}</span></a>'
41380 var template = tpl || this.tabTpl || false;
41383 template = new Roo.Template(
41384 Roo.bootstrap.version == 4 ?
41386 '<a class="nav-link" href="#" unselectable="on"' +
41387 (this.disableTooltips ? '' : ' title="{text}"') +
41390 '<a class="nav-link" href="#">' +
41391 '<span unselectable="on"' +
41392 (this.disableTooltips ? '' : ' title="{text}"') +
41393 ' >{text}</span></a>'
41398 switch (typeof(template)) {
41402 template = new Roo.Template(template);
41408 var el = template.overwrite(td, {"text": text});
41410 var inner = el.getElementsByTagName("span")[0];
41412 return {"el": el, "inner": inner};
41420 * @class Roo.TabPanelItem
41421 * @extends Roo.util.Observable
41422 * Represents an individual item (tab plus body) in a TabPanel.
41423 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41424 * @param {String} id The id of this TabPanelItem
41425 * @param {String} text The text for the tab of this TabPanelItem
41426 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41428 Roo.bootstrap.panel.TabItem = function(config){
41430 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41431 * @type Roo.TabPanel
41433 this.tabPanel = config.panel;
41435 * The id for this TabPanelItem
41438 this.id = config.id;
41440 this.disabled = false;
41442 this.text = config.text;
41444 this.loaded = false;
41445 this.closable = config.closable;
41448 * The body element for this TabPanelItem.
41449 * @type Roo.Element
41451 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41452 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41453 this.bodyEl.setStyle("display", "block");
41454 this.bodyEl.setStyle("zoom", "1");
41455 //this.hideAction();
41457 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41459 this.el = Roo.get(els.el);
41460 this.inner = Roo.get(els.inner, true);
41461 this.textEl = Roo.bootstrap.version == 4 ?
41462 this.el : Roo.get(this.el.dom.firstChild, true);
41464 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41465 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41468 // this.el.on("mousedown", this.onTabMouseDown, this);
41469 this.el.on("click", this.onTabClick, this);
41471 if(config.closable){
41472 var c = Roo.get(els.close, true);
41473 c.dom.title = this.closeText;
41474 c.addClassOnOver("close-over");
41475 c.on("click", this.closeClick, this);
41481 * Fires when this tab becomes the active tab.
41482 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41483 * @param {Roo.TabPanelItem} this
41487 * @event beforeclose
41488 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41489 * @param {Roo.TabPanelItem} this
41490 * @param {Object} e Set cancel to true on this object to cancel the close.
41492 "beforeclose": true,
41495 * Fires when this tab is closed.
41496 * @param {Roo.TabPanelItem} this
41500 * @event deactivate
41501 * Fires when this tab is no longer the active tab.
41502 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41503 * @param {Roo.TabPanelItem} this
41505 "deactivate" : true
41507 this.hidden = false;
41509 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41512 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41514 purgeListeners : function(){
41515 Roo.util.Observable.prototype.purgeListeners.call(this);
41516 this.el.removeAllListeners();
41519 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41522 this.status_node.addClass("active");
41525 this.tabPanel.stripWrap.repaint();
41527 this.fireEvent("activate", this.tabPanel, this);
41531 * Returns true if this tab is the active tab.
41532 * @return {Boolean}
41534 isActive : function(){
41535 return this.tabPanel.getActiveTab() == this;
41539 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41542 this.status_node.removeClass("active");
41544 this.fireEvent("deactivate", this.tabPanel, this);
41547 hideAction : function(){
41548 this.bodyEl.hide();
41549 this.bodyEl.setStyle("position", "absolute");
41550 this.bodyEl.setLeft("-20000px");
41551 this.bodyEl.setTop("-20000px");
41554 showAction : function(){
41555 this.bodyEl.setStyle("position", "relative");
41556 this.bodyEl.setTop("");
41557 this.bodyEl.setLeft("");
41558 this.bodyEl.show();
41562 * Set the tooltip for the tab.
41563 * @param {String} tooltip The tab's tooltip
41565 setTooltip : function(text){
41566 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41567 this.textEl.dom.qtip = text;
41568 this.textEl.dom.removeAttribute('title');
41570 this.textEl.dom.title = text;
41574 onTabClick : function(e){
41575 e.preventDefault();
41576 this.tabPanel.activate(this.id);
41579 onTabMouseDown : function(e){
41580 e.preventDefault();
41581 this.tabPanel.activate(this.id);
41584 getWidth : function(){
41585 return this.inner.getWidth();
41588 setWidth : function(width){
41589 var iwidth = width - this.linode.getPadding("lr");
41590 this.inner.setWidth(iwidth);
41591 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41592 this.linode.setWidth(width);
41596 * Show or hide the tab
41597 * @param {Boolean} hidden True to hide or false to show.
41599 setHidden : function(hidden){
41600 this.hidden = hidden;
41601 this.linode.setStyle("display", hidden ? "none" : "");
41605 * Returns true if this tab is "hidden"
41606 * @return {Boolean}
41608 isHidden : function(){
41609 return this.hidden;
41613 * Returns the text for this tab
41616 getText : function(){
41620 autoSize : function(){
41621 //this.el.beginMeasure();
41622 this.textEl.setWidth(1);
41624 * #2804 [new] Tabs in Roojs
41625 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41627 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41628 //this.el.endMeasure();
41632 * Sets the text for the tab (Note: this also sets the tooltip text)
41633 * @param {String} text The tab's text and tooltip
41635 setText : function(text){
41637 this.textEl.update(text);
41638 this.setTooltip(text);
41639 //if(!this.tabPanel.resizeTabs){
41640 // this.autoSize();
41644 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41646 activate : function(){
41647 this.tabPanel.activate(this.id);
41651 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41653 disable : function(){
41654 if(this.tabPanel.active != this){
41655 this.disabled = true;
41656 this.status_node.addClass("disabled");
41661 * Enables this TabPanelItem if it was previously disabled.
41663 enable : function(){
41664 this.disabled = false;
41665 this.status_node.removeClass("disabled");
41669 * Sets the content for this TabPanelItem.
41670 * @param {String} content The content
41671 * @param {Boolean} loadScripts true to look for and load scripts
41673 setContent : function(content, loadScripts){
41674 this.bodyEl.update(content, loadScripts);
41678 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41679 * @return {Roo.UpdateManager} The UpdateManager
41681 getUpdateManager : function(){
41682 return this.bodyEl.getUpdateManager();
41686 * Set a URL to be used to load the content for this TabPanelItem.
41687 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41688 * @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)
41689 * @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)
41690 * @return {Roo.UpdateManager} The UpdateManager
41692 setUrl : function(url, params, loadOnce){
41693 if(this.refreshDelegate){
41694 this.un('activate', this.refreshDelegate);
41696 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41697 this.on("activate", this.refreshDelegate);
41698 return this.bodyEl.getUpdateManager();
41702 _handleRefresh : function(url, params, loadOnce){
41703 if(!loadOnce || !this.loaded){
41704 var updater = this.bodyEl.getUpdateManager();
41705 updater.update(url, params, this._setLoaded.createDelegate(this));
41710 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41711 * Will fail silently if the setUrl method has not been called.
41712 * This does not activate the panel, just updates its content.
41714 refresh : function(){
41715 if(this.refreshDelegate){
41716 this.loaded = false;
41717 this.refreshDelegate();
41722 _setLoaded : function(){
41723 this.loaded = true;
41727 closeClick : function(e){
41730 this.fireEvent("beforeclose", this, o);
41731 if(o.cancel !== true){
41732 this.tabPanel.removeTab(this.id);
41736 * The text displayed in the tooltip for the close icon.
41739 closeText : "Close this tab"
41742 * This script refer to:
41743 * Title: International Telephone Input
41744 * Author: Jack O'Connor
41745 * Code version: v12.1.12
41746 * Availability: https://github.com/jackocnr/intl-tel-input.git
41749 Roo.bootstrap.PhoneInputData = function() {
41752 "Afghanistan (افغانستان)",
41757 "Albania (Shqipëri)",
41762 "Algeria (الجزائر)",
41787 "Antigua and Barbuda",
41797 "Armenia (Հայաստան)",
41813 "Austria (Österreich)",
41818 "Azerbaijan (Azərbaycan)",
41828 "Bahrain (البحرين)",
41833 "Bangladesh (বাংলাদেশ)",
41843 "Belarus (Беларусь)",
41848 "Belgium (België)",
41878 "Bosnia and Herzegovina (Босна и Херцеговина)",
41893 "British Indian Ocean Territory",
41898 "British Virgin Islands",
41908 "Bulgaria (България)",
41918 "Burundi (Uburundi)",
41923 "Cambodia (កម្ពុជា)",
41928 "Cameroon (Cameroun)",
41937 ["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"]
41940 "Cape Verde (Kabu Verdi)",
41945 "Caribbean Netherlands",
41956 "Central African Republic (République centrafricaine)",
41976 "Christmas Island",
41982 "Cocos (Keeling) Islands",
41993 "Comoros (جزر القمر)",
41998 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42003 "Congo (Republic) (Congo-Brazzaville)",
42023 "Croatia (Hrvatska)",
42044 "Czech Republic (Česká republika)",
42049 "Denmark (Danmark)",
42064 "Dominican Republic (República Dominicana)",
42068 ["809", "829", "849"]
42086 "Equatorial Guinea (Guinea Ecuatorial)",
42106 "Falkland Islands (Islas Malvinas)",
42111 "Faroe Islands (Føroyar)",
42132 "French Guiana (Guyane française)",
42137 "French Polynesia (Polynésie française)",
42152 "Georgia (საქართველო)",
42157 "Germany (Deutschland)",
42177 "Greenland (Kalaallit Nunaat)",
42214 "Guinea-Bissau (Guiné Bissau)",
42239 "Hungary (Magyarország)",
42244 "Iceland (Ísland)",
42264 "Iraq (العراق)",
42280 "Israel (ישראל)",
42307 "Jordan (الأردن)",
42312 "Kazakhstan (Казахстан)",
42333 "Kuwait (الكويت)",
42338 "Kyrgyzstan (Кыргызстан)",
42348 "Latvia (Latvija)",
42353 "Lebanon (لبنان)",
42368 "Libya (ليبيا)",
42378 "Lithuania (Lietuva)",
42393 "Macedonia (FYROM) (Македонија)",
42398 "Madagascar (Madagasikara)",
42428 "Marshall Islands",
42438 "Mauritania (موريتانيا)",
42443 "Mauritius (Moris)",
42464 "Moldova (Republica Moldova)",
42474 "Mongolia (Монгол)",
42479 "Montenegro (Crna Gora)",
42489 "Morocco (المغرب)",
42495 "Mozambique (Moçambique)",
42500 "Myanmar (Burma) (မြန်မာ)",
42505 "Namibia (Namibië)",
42520 "Netherlands (Nederland)",
42525 "New Caledonia (Nouvelle-Calédonie)",
42560 "North Korea (조선 민주주의 인민 공화국)",
42565 "Northern Mariana Islands",
42581 "Pakistan (پاکستان)",
42591 "Palestine (فلسطين)",
42601 "Papua New Guinea",
42643 "Réunion (La Réunion)",
42649 "Romania (România)",
42665 "Saint Barthélemy",
42676 "Saint Kitts and Nevis",
42686 "Saint Martin (Saint-Martin (partie française))",
42692 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42697 "Saint Vincent and the Grenadines",
42712 "São Tomé and Príncipe (São Tomé e Príncipe)",
42717 "Saudi Arabia (المملكة العربية السعودية)",
42722 "Senegal (Sénégal)",
42752 "Slovakia (Slovensko)",
42757 "Slovenia (Slovenija)",
42767 "Somalia (Soomaaliya)",
42777 "South Korea (대한민국)",
42782 "South Sudan (جنوب السودان)",
42792 "Sri Lanka (ශ්රී ලංකාව)",
42797 "Sudan (السودان)",
42807 "Svalbard and Jan Mayen",
42818 "Sweden (Sverige)",
42823 "Switzerland (Schweiz)",
42828 "Syria (سوريا)",
42873 "Trinidad and Tobago",
42878 "Tunisia (تونس)",
42883 "Turkey (Türkiye)",
42893 "Turks and Caicos Islands",
42903 "U.S. Virgin Islands",
42913 "Ukraine (Україна)",
42918 "United Arab Emirates (الإمارات العربية المتحدة)",
42940 "Uzbekistan (Oʻzbekiston)",
42950 "Vatican City (Città del Vaticano)",
42961 "Vietnam (Việt Nam)",
42966 "Wallis and Futuna (Wallis-et-Futuna)",
42971 "Western Sahara (الصحراء الغربية)",
42977 "Yemen (اليمن)",
43001 * This script refer to:
43002 * Title: International Telephone Input
43003 * Author: Jack O'Connor
43004 * Code version: v12.1.12
43005 * Availability: https://github.com/jackocnr/intl-tel-input.git
43009 * @class Roo.bootstrap.PhoneInput
43010 * @extends Roo.bootstrap.TriggerField
43011 * An input with International dial-code selection
43013 * @cfg {String} defaultDialCode default '+852'
43014 * @cfg {Array} preferedCountries default []
43017 * Create a new PhoneInput.
43018 * @param {Object} config Configuration options
43021 Roo.bootstrap.PhoneInput = function(config) {
43022 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43025 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43027 listWidth: undefined,
43029 selectedClass: 'active',
43031 invalidClass : "has-warning",
43033 validClass: 'has-success',
43035 allowed: '0123456789',
43040 * @cfg {String} defaultDialCode The default dial code when initializing the input
43042 defaultDialCode: '+852',
43045 * @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
43047 preferedCountries: false,
43049 getAutoCreate : function()
43051 var data = Roo.bootstrap.PhoneInputData();
43052 var align = this.labelAlign || this.parentLabelAlign();
43055 this.allCountries = [];
43056 this.dialCodeMapping = [];
43058 for (var i = 0; i < data.length; i++) {
43060 this.allCountries[i] = {
43064 priority: c[3] || 0,
43065 areaCodes: c[4] || null
43067 this.dialCodeMapping[c[2]] = {
43070 priority: c[3] || 0,
43071 areaCodes: c[4] || null
43083 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43084 maxlength: this.max_length,
43085 cls : 'form-control tel-input',
43086 autocomplete: 'new-password'
43089 var hiddenInput = {
43092 cls: 'hidden-tel-input'
43096 hiddenInput.name = this.name;
43099 if (this.disabled) {
43100 input.disabled = true;
43103 var flag_container = {
43120 cls: this.hasFeedback ? 'has-feedback' : '',
43126 cls: 'dial-code-holder',
43133 cls: 'roo-select2-container input-group',
43140 if (this.fieldLabel.length) {
43143 tooltip: 'This field is required'
43149 cls: 'control-label',
43155 html: this.fieldLabel
43158 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43164 if(this.indicatorpos == 'right') {
43165 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43172 if(align == 'left') {
43180 if(this.labelWidth > 12){
43181 label.style = "width: " + this.labelWidth + 'px';
43183 if(this.labelWidth < 13 && this.labelmd == 0){
43184 this.labelmd = this.labelWidth;
43186 if(this.labellg > 0){
43187 label.cls += ' col-lg-' + this.labellg;
43188 input.cls += ' col-lg-' + (12 - this.labellg);
43190 if(this.labelmd > 0){
43191 label.cls += ' col-md-' + this.labelmd;
43192 container.cls += ' col-md-' + (12 - this.labelmd);
43194 if(this.labelsm > 0){
43195 label.cls += ' col-sm-' + this.labelsm;
43196 container.cls += ' col-sm-' + (12 - this.labelsm);
43198 if(this.labelxs > 0){
43199 label.cls += ' col-xs-' + this.labelxs;
43200 container.cls += ' col-xs-' + (12 - this.labelxs);
43210 var settings = this;
43212 ['xs','sm','md','lg'].map(function(size){
43213 if (settings[size]) {
43214 cfg.cls += ' col-' + size + '-' + settings[size];
43218 this.store = new Roo.data.Store({
43219 proxy : new Roo.data.MemoryProxy({}),
43220 reader : new Roo.data.JsonReader({
43231 'name' : 'dialCode',
43235 'name' : 'priority',
43239 'name' : 'areaCodes',
43246 if(!this.preferedCountries) {
43247 this.preferedCountries = [
43254 var p = this.preferedCountries.reverse();
43257 for (var i = 0; i < p.length; i++) {
43258 for (var j = 0; j < this.allCountries.length; j++) {
43259 if(this.allCountries[j].iso2 == p[i]) {
43260 var t = this.allCountries[j];
43261 this.allCountries.splice(j,1);
43262 this.allCountries.unshift(t);
43268 this.store.proxy.data = {
43270 data: this.allCountries
43276 initEvents : function()
43279 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43281 this.indicator = this.indicatorEl();
43282 this.flag = this.flagEl();
43283 this.dialCodeHolder = this.dialCodeHolderEl();
43285 this.trigger = this.el.select('div.flag-box',true).first();
43286 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43291 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43292 _this.list.setWidth(lw);
43295 this.list.on('mouseover', this.onViewOver, this);
43296 this.list.on('mousemove', this.onViewMove, this);
43297 this.inputEl().on("keyup", this.onKeyUp, this);
43298 this.inputEl().on("keypress", this.onKeyPress, this);
43300 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43302 this.view = new Roo.View(this.list, this.tpl, {
43303 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43306 this.view.on('click', this.onViewClick, this);
43307 this.setValue(this.defaultDialCode);
43310 onTriggerClick : function(e)
43312 Roo.log('trigger click');
43317 if(this.isExpanded()){
43319 this.hasFocus = false;
43321 this.store.load({});
43322 this.hasFocus = true;
43327 isExpanded : function()
43329 return this.list.isVisible();
43332 collapse : function()
43334 if(!this.isExpanded()){
43338 Roo.get(document).un('mousedown', this.collapseIf, this);
43339 Roo.get(document).un('mousewheel', this.collapseIf, this);
43340 this.fireEvent('collapse', this);
43344 expand : function()
43348 if(this.isExpanded() || !this.hasFocus){
43352 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43353 this.list.setWidth(lw);
43356 this.restrictHeight();
43358 Roo.get(document).on('mousedown', this.collapseIf, this);
43359 Roo.get(document).on('mousewheel', this.collapseIf, this);
43361 this.fireEvent('expand', this);
43364 restrictHeight : function()
43366 this.list.alignTo(this.inputEl(), this.listAlign);
43367 this.list.alignTo(this.inputEl(), this.listAlign);
43370 onViewOver : function(e, t)
43372 if(this.inKeyMode){
43375 var item = this.view.findItemFromChild(t);
43378 var index = this.view.indexOf(item);
43379 this.select(index, false);
43384 onViewClick : function(view, doFocus, el, e)
43386 var index = this.view.getSelectedIndexes()[0];
43388 var r = this.store.getAt(index);
43391 this.onSelect(r, index);
43393 if(doFocus !== false && !this.blockFocus){
43394 this.inputEl().focus();
43398 onViewMove : function(e, t)
43400 this.inKeyMode = false;
43403 select : function(index, scrollIntoView)
43405 this.selectedIndex = index;
43406 this.view.select(index);
43407 if(scrollIntoView !== false){
43408 var el = this.view.getNode(index);
43410 this.list.scrollChildIntoView(el, false);
43415 createList : function()
43417 this.list = Roo.get(document.body).createChild({
43419 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43420 style: 'display:none'
43423 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43426 collapseIf : function(e)
43428 var in_combo = e.within(this.el);
43429 var in_list = e.within(this.list);
43430 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43432 if (in_combo || in_list || is_list) {
43438 onSelect : function(record, index)
43440 if(this.fireEvent('beforeselect', this, record, index) !== false){
43442 this.setFlagClass(record.data.iso2);
43443 this.setDialCode(record.data.dialCode);
43444 this.hasFocus = false;
43446 this.fireEvent('select', this, record, index);
43450 flagEl : function()
43452 var flag = this.el.select('div.flag',true).first();
43459 dialCodeHolderEl : function()
43461 var d = this.el.select('input.dial-code-holder',true).first();
43468 setDialCode : function(v)
43470 this.dialCodeHolder.dom.value = '+'+v;
43473 setFlagClass : function(n)
43475 this.flag.dom.className = 'flag '+n;
43478 getValue : function()
43480 var v = this.inputEl().getValue();
43481 if(this.dialCodeHolder) {
43482 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43487 setValue : function(v)
43489 var d = this.getDialCode(v);
43491 //invalid dial code
43492 if(v.length == 0 || !d || d.length == 0) {
43494 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43495 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43501 this.setFlagClass(this.dialCodeMapping[d].iso2);
43502 this.setDialCode(d);
43503 this.inputEl().dom.value = v.replace('+'+d,'');
43504 this.hiddenEl().dom.value = this.getValue();
43509 getDialCode : function(v)
43513 if (v.length == 0) {
43514 return this.dialCodeHolder.dom.value;
43518 if (v.charAt(0) != "+") {
43521 var numericChars = "";
43522 for (var i = 1; i < v.length; i++) {
43523 var c = v.charAt(i);
43526 if (this.dialCodeMapping[numericChars]) {
43527 dialCode = v.substr(1, i);
43529 if (numericChars.length == 4) {
43539 this.setValue(this.defaultDialCode);
43543 hiddenEl : function()
43545 return this.el.select('input.hidden-tel-input',true).first();
43548 // after setting val
43549 onKeyUp : function(e){
43550 this.setValue(this.getValue());
43553 onKeyPress : function(e){
43554 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43561 * @class Roo.bootstrap.MoneyField
43562 * @extends Roo.bootstrap.ComboBox
43563 * Bootstrap MoneyField class
43566 * Create a new MoneyField.
43567 * @param {Object} config Configuration options
43570 Roo.bootstrap.MoneyField = function(config) {
43572 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43576 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43579 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43581 allowDecimals : true,
43583 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43585 decimalSeparator : ".",
43587 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43589 decimalPrecision : 0,
43591 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43593 allowNegative : true,
43595 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43599 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43601 minValue : Number.NEGATIVE_INFINITY,
43603 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43605 maxValue : Number.MAX_VALUE,
43607 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43609 minText : "The minimum value for this field is {0}",
43611 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43613 maxText : "The maximum value for this field is {0}",
43615 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43616 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43618 nanText : "{0} is not a valid number",
43620 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43624 * @cfg {String} defaults currency of the MoneyField
43625 * value should be in lkey
43627 defaultCurrency : false,
43629 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43631 thousandsDelimiter : false,
43633 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43644 getAutoCreate : function()
43646 var align = this.labelAlign || this.parentLabelAlign();
43658 cls : 'form-control roo-money-amount-input',
43659 autocomplete: 'new-password'
43662 var hiddenInput = {
43666 cls: 'hidden-number-input'
43669 if(this.max_length) {
43670 input.maxlength = this.max_length;
43674 hiddenInput.name = this.name;
43677 if (this.disabled) {
43678 input.disabled = true;
43681 var clg = 12 - this.inputlg;
43682 var cmd = 12 - this.inputmd;
43683 var csm = 12 - this.inputsm;
43684 var cxs = 12 - this.inputxs;
43688 cls : 'row roo-money-field',
43692 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43696 cls: 'roo-select2-container input-group',
43700 cls : 'form-control roo-money-currency-input',
43701 autocomplete: 'new-password',
43703 name : this.currencyName
43707 cls : 'input-group-addon',
43721 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43725 cls: this.hasFeedback ? 'has-feedback' : '',
43736 if (this.fieldLabel.length) {
43739 tooltip: 'This field is required'
43745 cls: 'control-label',
43751 html: this.fieldLabel
43754 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43760 if(this.indicatorpos == 'right') {
43761 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43768 if(align == 'left') {
43776 if(this.labelWidth > 12){
43777 label.style = "width: " + this.labelWidth + 'px';
43779 if(this.labelWidth < 13 && this.labelmd == 0){
43780 this.labelmd = this.labelWidth;
43782 if(this.labellg > 0){
43783 label.cls += ' col-lg-' + this.labellg;
43784 input.cls += ' col-lg-' + (12 - this.labellg);
43786 if(this.labelmd > 0){
43787 label.cls += ' col-md-' + this.labelmd;
43788 container.cls += ' col-md-' + (12 - this.labelmd);
43790 if(this.labelsm > 0){
43791 label.cls += ' col-sm-' + this.labelsm;
43792 container.cls += ' col-sm-' + (12 - this.labelsm);
43794 if(this.labelxs > 0){
43795 label.cls += ' col-xs-' + this.labelxs;
43796 container.cls += ' col-xs-' + (12 - this.labelxs);
43807 var settings = this;
43809 ['xs','sm','md','lg'].map(function(size){
43810 if (settings[size]) {
43811 cfg.cls += ' col-' + size + '-' + settings[size];
43818 initEvents : function()
43820 this.indicator = this.indicatorEl();
43822 this.initCurrencyEvent();
43824 this.initNumberEvent();
43827 initCurrencyEvent : function()
43830 throw "can not find store for combo";
43833 this.store = Roo.factory(this.store, Roo.data);
43834 this.store.parent = this;
43838 this.triggerEl = this.el.select('.input-group-addon', true).first();
43840 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43845 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43846 _this.list.setWidth(lw);
43849 this.list.on('mouseover', this.onViewOver, this);
43850 this.list.on('mousemove', this.onViewMove, this);
43851 this.list.on('scroll', this.onViewScroll, this);
43854 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43857 this.view = new Roo.View(this.list, this.tpl, {
43858 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43861 this.view.on('click', this.onViewClick, this);
43863 this.store.on('beforeload', this.onBeforeLoad, this);
43864 this.store.on('load', this.onLoad, this);
43865 this.store.on('loadexception', this.onLoadException, this);
43867 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43868 "up" : function(e){
43869 this.inKeyMode = true;
43873 "down" : function(e){
43874 if(!this.isExpanded()){
43875 this.onTriggerClick();
43877 this.inKeyMode = true;
43882 "enter" : function(e){
43885 if(this.fireEvent("specialkey", this, e)){
43886 this.onViewClick(false);
43892 "esc" : function(e){
43896 "tab" : function(e){
43899 if(this.fireEvent("specialkey", this, e)){
43900 this.onViewClick(false);
43908 doRelay : function(foo, bar, hname){
43909 if(hname == 'down' || this.scope.isExpanded()){
43910 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43918 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43922 initNumberEvent : function(e)
43924 this.inputEl().on("keydown" , this.fireKey, this);
43925 this.inputEl().on("focus", this.onFocus, this);
43926 this.inputEl().on("blur", this.onBlur, this);
43928 this.inputEl().relayEvent('keyup', this);
43930 if(this.indicator){
43931 this.indicator.addClass('invisible');
43934 this.originalValue = this.getValue();
43936 if(this.validationEvent == 'keyup'){
43937 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43938 this.inputEl().on('keyup', this.filterValidation, this);
43940 else if(this.validationEvent !== false){
43941 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43944 if(this.selectOnFocus){
43945 this.on("focus", this.preFocus, this);
43948 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43949 this.inputEl().on("keypress", this.filterKeys, this);
43951 this.inputEl().relayEvent('keypress', this);
43954 var allowed = "0123456789";
43956 if(this.allowDecimals){
43957 allowed += this.decimalSeparator;
43960 if(this.allowNegative){
43964 if(this.thousandsDelimiter) {
43968 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43970 var keyPress = function(e){
43972 var k = e.getKey();
43974 var c = e.getCharCode();
43977 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43978 allowed.indexOf(String.fromCharCode(c)) === -1
43984 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43988 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43993 this.inputEl().on("keypress", keyPress, this);
43997 onTriggerClick : function(e)
44004 this.loadNext = false;
44006 if(this.isExpanded()){
44011 this.hasFocus = true;
44013 if(this.triggerAction == 'all') {
44014 this.doQuery(this.allQuery, true);
44018 this.doQuery(this.getRawValue());
44021 getCurrency : function()
44023 var v = this.currencyEl().getValue();
44028 restrictHeight : function()
44030 this.list.alignTo(this.currencyEl(), this.listAlign);
44031 this.list.alignTo(this.currencyEl(), this.listAlign);
44034 onViewClick : function(view, doFocus, el, e)
44036 var index = this.view.getSelectedIndexes()[0];
44038 var r = this.store.getAt(index);
44041 this.onSelect(r, index);
44045 onSelect : function(record, index){
44047 if(this.fireEvent('beforeselect', this, record, index) !== false){
44049 this.setFromCurrencyData(index > -1 ? record.data : false);
44053 this.fireEvent('select', this, record, index);
44057 setFromCurrencyData : function(o)
44061 this.lastCurrency = o;
44063 if (this.currencyField) {
44064 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44066 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44069 this.lastSelectionText = currency;
44071 //setting default currency
44072 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44073 this.setCurrency(this.defaultCurrency);
44077 this.setCurrency(currency);
44080 setFromData : function(o)
44084 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44086 this.setFromCurrencyData(c);
44091 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44093 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44096 this.setValue(value);
44100 setCurrency : function(v)
44102 this.currencyValue = v;
44105 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44110 setValue : function(v)
44112 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44118 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44120 this.inputEl().dom.value = (v == '') ? '' :
44121 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44123 if(!this.allowZero && v === '0') {
44124 this.hiddenEl().dom.value = '';
44125 this.inputEl().dom.value = '';
44132 getRawValue : function()
44134 var v = this.inputEl().getValue();
44139 getValue : function()
44141 return this.fixPrecision(this.parseValue(this.getRawValue()));
44144 parseValue : function(value)
44146 if(this.thousandsDelimiter) {
44148 r = new RegExp(",", "g");
44149 value = value.replace(r, "");
44152 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44153 return isNaN(value) ? '' : value;
44157 fixPrecision : function(value)
44159 if(this.thousandsDelimiter) {
44161 r = new RegExp(",", "g");
44162 value = value.replace(r, "");
44165 var nan = isNaN(value);
44167 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44168 return nan ? '' : value;
44170 return parseFloat(value).toFixed(this.decimalPrecision);
44173 decimalPrecisionFcn : function(v)
44175 return Math.floor(v);
44178 validateValue : function(value)
44180 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44184 var num = this.parseValue(value);
44187 this.markInvalid(String.format(this.nanText, value));
44191 if(num < this.minValue){
44192 this.markInvalid(String.format(this.minText, this.minValue));
44196 if(num > this.maxValue){
44197 this.markInvalid(String.format(this.maxText, this.maxValue));
44204 validate : function()
44206 if(this.disabled || this.allowBlank){
44211 var currency = this.getCurrency();
44213 if(this.validateValue(this.getRawValue()) && currency.length){
44218 this.markInvalid();
44222 getName: function()
44227 beforeBlur : function()
44233 var v = this.parseValue(this.getRawValue());
44240 onBlur : function()
44244 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44245 //this.el.removeClass(this.focusClass);
44248 this.hasFocus = false;
44250 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44254 var v = this.getValue();
44256 if(String(v) !== String(this.startValue)){
44257 this.fireEvent('change', this, v, this.startValue);
44260 this.fireEvent("blur", this);
44263 inputEl : function()
44265 return this.el.select('.roo-money-amount-input', true).first();
44268 currencyEl : function()
44270 return this.el.select('.roo-money-currency-input', true).first();
44273 hiddenEl : function()
44275 return this.el.select('input.hidden-number-input',true).first();
44279 * @class Roo.bootstrap.BezierSignature
44280 * @extends Roo.bootstrap.Component
44281 * Bootstrap BezierSignature class
44282 * This script refer to:
44283 * Title: Signature Pad
44285 * Availability: https://github.com/szimek/signature_pad
44288 * Create a new BezierSignature
44289 * @param {Object} config The config object
44292 Roo.bootstrap.BezierSignature = function(config){
44293 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44299 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44306 mouse_btn_down: true,
44309 * @cfg {int} canvas height
44311 canvas_height: '200px',
44314 * @cfg {float|function} Radius of a single dot.
44319 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44324 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44329 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44334 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44339 * @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.
44341 bg_color: 'rgba(0, 0, 0, 0)',
44344 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44346 dot_color: 'black',
44349 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44351 velocity_filter_weight: 0.7,
44354 * @cfg {function} Callback when stroke begin.
44359 * @cfg {function} Callback when stroke end.
44363 getAutoCreate : function()
44365 var cls = 'roo-signature column';
44368 cls += ' ' + this.cls;
44378 for(var i = 0; i < col_sizes.length; i++) {
44379 if(this[col_sizes[i]]) {
44380 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44390 cls: 'roo-signature-body',
44394 cls: 'roo-signature-body-canvas',
44395 height: this.canvas_height,
44396 width: this.canvas_width
44403 style: 'display: none'
44411 initEvents: function()
44413 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44415 var canvas = this.canvasEl();
44417 // mouse && touch event swapping...
44418 canvas.dom.style.touchAction = 'none';
44419 canvas.dom.style.msTouchAction = 'none';
44421 this.mouse_btn_down = false;
44422 canvas.on('mousedown', this._handleMouseDown, this);
44423 canvas.on('mousemove', this._handleMouseMove, this);
44424 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44426 if (window.PointerEvent) {
44427 canvas.on('pointerdown', this._handleMouseDown, this);
44428 canvas.on('pointermove', this._handleMouseMove, this);
44429 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44432 if ('ontouchstart' in window) {
44433 canvas.on('touchstart', this._handleTouchStart, this);
44434 canvas.on('touchmove', this._handleTouchMove, this);
44435 canvas.on('touchend', this._handleTouchEnd, this);
44438 Roo.EventManager.onWindowResize(this.resize, this, true);
44440 // file input event
44441 this.fileEl().on('change', this.uploadImage, this);
44448 resize: function(){
44450 var canvas = this.canvasEl().dom;
44451 var ctx = this.canvasElCtx();
44452 var img_data = false;
44454 if(canvas.width > 0) {
44455 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44457 // setting canvas width will clean img data
44460 var style = window.getComputedStyle ?
44461 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44463 var padding_left = parseInt(style.paddingLeft) || 0;
44464 var padding_right = parseInt(style.paddingRight) || 0;
44466 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44469 ctx.putImageData(img_data, 0, 0);
44473 _handleMouseDown: function(e)
44475 if (e.browserEvent.which === 1) {
44476 this.mouse_btn_down = true;
44477 this.strokeBegin(e);
44481 _handleMouseMove: function (e)
44483 if (this.mouse_btn_down) {
44484 this.strokeMoveUpdate(e);
44488 _handleMouseUp: function (e)
44490 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44491 this.mouse_btn_down = false;
44496 _handleTouchStart: function (e) {
44498 e.preventDefault();
44499 if (e.browserEvent.targetTouches.length === 1) {
44500 // var touch = e.browserEvent.changedTouches[0];
44501 // this.strokeBegin(touch);
44503 this.strokeBegin(e); // assume e catching the correct xy...
44507 _handleTouchMove: function (e) {
44508 e.preventDefault();
44509 // var touch = event.targetTouches[0];
44510 // _this._strokeMoveUpdate(touch);
44511 this.strokeMoveUpdate(e);
44514 _handleTouchEnd: function (e) {
44515 var wasCanvasTouched = e.target === this.canvasEl().dom;
44516 if (wasCanvasTouched) {
44517 e.preventDefault();
44518 // var touch = event.changedTouches[0];
44519 // _this._strokeEnd(touch);
44524 reset: function () {
44525 this._lastPoints = [];
44526 this._lastVelocity = 0;
44527 this._lastWidth = (this.min_width + this.max_width) / 2;
44528 this.canvasElCtx().fillStyle = this.dot_color;
44531 strokeMoveUpdate: function(e)
44533 this.strokeUpdate(e);
44535 if (this.throttle) {
44536 this.throttleStroke(this.strokeUpdate, this.throttle);
44539 this.strokeUpdate(e);
44543 strokeBegin: function(e)
44545 var newPointGroup = {
44546 color: this.dot_color,
44550 if (typeof this.onBegin === 'function') {
44554 this.curve_data.push(newPointGroup);
44556 this.strokeUpdate(e);
44559 strokeUpdate: function(e)
44561 var rect = this.canvasEl().dom.getBoundingClientRect();
44562 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44563 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44564 var lastPoints = lastPointGroup.points;
44565 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44566 var isLastPointTooClose = lastPoint
44567 ? point.distanceTo(lastPoint) <= this.min_distance
44569 var color = lastPointGroup.color;
44570 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44571 var curve = this.addPoint(point);
44573 this.drawDot({color: color, point: point});
44576 this.drawCurve({color: color, curve: curve});
44586 strokeEnd: function(e)
44588 this.strokeUpdate(e);
44589 if (typeof this.onEnd === 'function') {
44594 addPoint: function (point) {
44595 var _lastPoints = this._lastPoints;
44596 _lastPoints.push(point);
44597 if (_lastPoints.length > 2) {
44598 if (_lastPoints.length === 3) {
44599 _lastPoints.unshift(_lastPoints[0]);
44601 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44602 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44603 _lastPoints.shift();
44609 calculateCurveWidths: function (startPoint, endPoint) {
44610 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44611 (1 - this.velocity_filter_weight) * this._lastVelocity;
44613 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44616 start: this._lastWidth
44619 this._lastVelocity = velocity;
44620 this._lastWidth = newWidth;
44624 drawDot: function (_a) {
44625 var color = _a.color, point = _a.point;
44626 var ctx = this.canvasElCtx();
44627 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44629 this.drawCurveSegment(point.x, point.y, width);
44631 ctx.fillStyle = color;
44635 drawCurve: function (_a) {
44636 var color = _a.color, curve = _a.curve;
44637 var ctx = this.canvasElCtx();
44638 var widthDelta = curve.endWidth - curve.startWidth;
44639 var drawSteps = Math.floor(curve.length()) * 2;
44641 ctx.fillStyle = color;
44642 for (var i = 0; i < drawSteps; i += 1) {
44643 var t = i / drawSteps;
44649 var x = uuu * curve.startPoint.x;
44650 x += 3 * uu * t * curve.control1.x;
44651 x += 3 * u * tt * curve.control2.x;
44652 x += ttt * curve.endPoint.x;
44653 var y = uuu * curve.startPoint.y;
44654 y += 3 * uu * t * curve.control1.y;
44655 y += 3 * u * tt * curve.control2.y;
44656 y += ttt * curve.endPoint.y;
44657 var width = curve.startWidth + ttt * widthDelta;
44658 this.drawCurveSegment(x, y, width);
44664 drawCurveSegment: function (x, y, width) {
44665 var ctx = this.canvasElCtx();
44667 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44668 this.is_empty = false;
44673 var ctx = this.canvasElCtx();
44674 var canvas = this.canvasEl().dom;
44675 ctx.fillStyle = this.bg_color;
44676 ctx.clearRect(0, 0, canvas.width, canvas.height);
44677 ctx.fillRect(0, 0, canvas.width, canvas.height);
44678 this.curve_data = [];
44680 this.is_empty = true;
44685 return this.el.select('input',true).first();
44688 canvasEl: function()
44690 return this.el.select('canvas',true).first();
44693 canvasElCtx: function()
44695 return this.el.select('canvas',true).first().dom.getContext('2d');
44698 getImage: function(type)
44700 if(this.is_empty) {
44705 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44708 drawFromImage: function(img_src)
44710 var img = new Image();
44712 img.onload = function(){
44713 this.canvasElCtx().drawImage(img, 0, 0);
44718 this.is_empty = false;
44721 selectImage: function()
44723 this.fileEl().dom.click();
44726 uploadImage: function(e)
44728 var reader = new FileReader();
44730 reader.onload = function(e){
44731 var img = new Image();
44732 img.onload = function(){
44734 this.canvasElCtx().drawImage(img, 0, 0);
44736 img.src = e.target.result;
44739 reader.readAsDataURL(e.target.files[0]);
44742 // Bezier Point Constructor
44743 Point: (function () {
44744 function Point(x, y, time) {
44747 this.time = time || Date.now();
44749 Point.prototype.distanceTo = function (start) {
44750 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44752 Point.prototype.equals = function (other) {
44753 return this.x === other.x && this.y === other.y && this.time === other.time;
44755 Point.prototype.velocityFrom = function (start) {
44756 return this.time !== start.time
44757 ? this.distanceTo(start) / (this.time - start.time)
44764 // Bezier Constructor
44765 Bezier: (function () {
44766 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44767 this.startPoint = startPoint;
44768 this.control2 = control2;
44769 this.control1 = control1;
44770 this.endPoint = endPoint;
44771 this.startWidth = startWidth;
44772 this.endWidth = endWidth;
44774 Bezier.fromPoints = function (points, widths, scope) {
44775 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44776 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44777 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44779 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44780 var dx1 = s1.x - s2.x;
44781 var dy1 = s1.y - s2.y;
44782 var dx2 = s2.x - s3.x;
44783 var dy2 = s2.y - s3.y;
44784 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44785 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44786 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44787 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44788 var dxm = m1.x - m2.x;
44789 var dym = m1.y - m2.y;
44790 var k = l2 / (l1 + l2);
44791 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44792 var tx = s2.x - cm.x;
44793 var ty = s2.y - cm.y;
44795 c1: new scope.Point(m1.x + tx, m1.y + ty),
44796 c2: new scope.Point(m2.x + tx, m2.y + ty)
44799 Bezier.prototype.length = function () {
44804 for (var i = 0; i <= steps; i += 1) {
44806 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44807 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44809 var xdiff = cx - px;
44810 var ydiff = cy - py;
44811 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44818 Bezier.prototype.point = function (t, start, c1, c2, end) {
44819 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44820 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44821 + (3.0 * c2 * (1.0 - t) * t * t)
44822 + (end * t * t * t);
44827 throttleStroke: function(fn, wait) {
44828 if (wait === void 0) { wait = 250; }
44830 var timeout = null;
44834 var later = function () {
44835 previous = Date.now();
44837 result = fn.apply(storedContext, storedArgs);
44839 storedContext = null;
44843 return function wrapper() {
44845 for (var _i = 0; _i < arguments.length; _i++) {
44846 args[_i] = arguments[_i];
44848 var now = Date.now();
44849 var remaining = wait - (now - previous);
44850 storedContext = this;
44852 if (remaining <= 0 || remaining > wait) {
44854 clearTimeout(timeout);
44858 result = fn.apply(storedContext, storedArgs);
44860 storedContext = null;
44864 else if (!timeout) {
44865 timeout = window.setTimeout(later, remaining);