2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
365 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367 return Roo.get(document.body);
371 * Fetch the element to display the tooltip on.
372 * @return {Roo.Element} defaults to this.el
374 tooltipEl : function()
379 addxtype : function(tree,cntr)
383 cn = Roo.factory(tree);
384 //Roo.log(['addxtype', cn]);
386 cn.parentType = this.xtype; //??
387 cn.parentId = this.id;
389 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390 if (typeof(cn.container_method) == 'string') {
391 cntr = cn.container_method;
395 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
397 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
399 var build_from_html = Roo.XComponent.build_from_html;
401 var is_body = (tree.xtype == 'Body') ;
403 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405 var self_cntr_el = Roo.get(this[cntr](false));
407 // do not try and build conditional elements
408 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414 return this.addxtypeChild(tree,cntr, is_body);
417 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
420 return this.addxtypeChild(Roo.apply({}, tree),cntr);
423 Roo.log('skipping render');
429 if (!build_from_html) {
433 // this i think handles overlaying multiple children of the same type
434 // with the sam eelement.. - which might be buggy..
436 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
442 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453 addxtypeChild : function (tree, cntr, is_body)
455 Roo.debug && Roo.log('addxtypeChild:' + cntr);
457 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
460 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461 (typeof(tree['flexy:foreach']) != 'undefined');
465 skip_children = false;
466 // render the element if it's not BODY.
469 // if parent was disabled, then do not try and create the children..
470 if(!this[cntr](true)){
475 cn = Roo.factory(tree);
477 cn.parentType = this.xtype; //??
478 cn.parentId = this.id;
480 var build_from_html = Roo.XComponent.build_from_html;
483 // does the container contain child eleemnts with 'xtype' attributes.
484 // that match this xtype..
485 // note - when we render we create these as well..
486 // so we should check to see if body has xtype set.
487 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489 var self_cntr_el = Roo.get(this[cntr](false));
490 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492 //Roo.log(Roo.XComponent.build_from_html);
493 //Roo.log("got echild:");
496 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497 // and are not displayed -this causes this to use up the wrong element when matching.
498 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
501 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
508 //echild.dom.removeAttribute('xtype');
510 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511 Roo.debug && Roo.log(self_cntr_el);
512 Roo.debug && Roo.log(echild);
513 Roo.debug && Roo.log(cn);
519 // if object has flexy:if - then it may or may not be rendered.
520 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
521 // skip a flexy if element.
522 Roo.debug && Roo.log('skipping render');
523 Roo.debug && Roo.log(tree);
525 Roo.debug && Roo.log('skipping all children');
526 skip_children = true;
531 // actually if flexy:foreach is found, we really want to create
532 // multiple copies here...
534 //Roo.log(this[cntr]());
535 // some elements do not have render methods.. like the layouts...
537 if(this[cntr](true) === false){
542 cn.render && cn.render(this[cntr](true));
545 // then add the element..
552 if (typeof (tree.menu) != 'undefined') {
553 tree.menu.parentType = cn.xtype;
554 tree.menu.triggerEl = cn.el;
555 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559 if (!tree.items || !tree.items.length) {
561 //Roo.log(["no children", this]);
566 var items = tree.items;
569 //Roo.log(items.length);
571 if (!skip_children) {
572 for(var i =0;i < items.length;i++) {
573 // Roo.log(['add child', items[i]]);
574 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
580 //Roo.log("fire childrenrendered");
582 cn.fireEvent('childrenrendered', this);
588 * Set the element that will be used to show or hide
590 setVisibilityEl : function(el)
592 this.visibilityEl = el;
596 * Get the element that will be used to show or hide
598 getVisibilityEl : function()
600 if (typeof(this.visibilityEl) == 'object') {
601 return this.visibilityEl;
604 if (typeof(this.visibilityEl) == 'string') {
605 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612 * Show a component - removes 'hidden' class
616 if(!this.getVisibilityEl()){
620 this.getVisibilityEl().removeClass(['hidden','d-none']);
622 this.fireEvent('show', this);
627 * Hide a component - adds 'hidden' class
631 if(!this.getVisibilityEl()){
635 this.getVisibilityEl().addClass(['hidden','d-none']);
637 this.fireEvent('hide', this);
650 * @class Roo.bootstrap.Element
651 * @extends Roo.bootstrap.Component
652 * Bootstrap Element class
653 * @cfg {String} html contents of the element
654 * @cfg {String} tag tag of the element
655 * @cfg {String} cls class of the element
656 * @cfg {Boolean} preventDefault (true|false) default false
657 * @cfg {Boolean} clickable (true|false) default false
658 * @cfg {String} role default blank - set to button to force cursor pointer
662 * Create a new Element
663 * @param {Object} config The config object
666 Roo.bootstrap.Element = function(config){
667 Roo.bootstrap.Element.superclass.constructor.call(this, config);
673 * When a element is chick
674 * @param {Roo.bootstrap.Element} this
675 * @param {Roo.EventObject} e
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
688 preventDefault: false,
693 getAutoCreate : function(){
697 // cls: this.cls, double assign in parent class Component.js :: onRender
700 if (this.role !== false) {
701 cfg.role = this.role;
707 initEvents: function()
709 Roo.bootstrap.Element.superclass.initEvents.call(this);
712 this.el.on('click', this.onClick, this);
718 onClick : function(e)
720 if(this.preventDefault){
724 this.fireEvent('click', this, e); // why was this double click before?
732 getValue : function()
734 return this.el.dom.innerHTML;
737 setValue : function(value)
739 this.el.dom.innerHTML = value;
754 * @class Roo.bootstrap.DropTarget
755 * @extends Roo.bootstrap.Element
756 * Bootstrap DropTarget class
758 * @cfg {string} name dropable name
761 * Create a new Dropable Area
762 * @param {Object} config The config object
765 Roo.bootstrap.DropTarget = function(config){
766 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
772 * When a element is chick
773 * @param {Roo.bootstrap.Element} this
774 * @param {Roo.EventObject} e
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
783 getAutoCreate : function(){
788 initEvents: function()
790 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
794 drop : this.dragDrop.createDelegate(this),
795 enter : this.dragEnter.createDelegate(this),
796 out : this.dragOut.createDelegate(this),
797 over : this.dragOver.createDelegate(this)
801 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
804 dragDrop : function(source,e,data)
806 // user has to decide how to impliment this.
809 //this.fireEvent('drop', this, source, e ,data);
813 dragEnter : function(n, dd, e, data)
815 // probably want to resize the element to match the dropped element..
817 this.originalSize = this.el.getSize();
818 this.el.setSize( n.el.getSize());
819 this.dropZone.DDM.refreshCache(this.name);
820 Roo.log([n, dd, e, data]);
823 dragOut : function(value)
825 // resize back to normal
827 this.el.setSize(this.originalSize);
828 this.dropZone.resetConstraints();
831 dragOver : function()
848 * @class Roo.bootstrap.Body
849 * @extends Roo.bootstrap.Component
850 * Bootstrap Body class
854 * @param {Object} config The config object
857 Roo.bootstrap.Body = function(config){
859 config = config || {};
861 Roo.bootstrap.Body.superclass.constructor.call(this, config);
862 this.el = Roo.get(config.el ? config.el : document.body );
863 if (this.cls && this.cls.length) {
864 Roo.get(document.body).addClass(this.cls);
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
870 is_body : true,// just to make sure it's constructed?
875 onRender : function(ct, position)
877 /* Roo.log("Roo.bootstrap.Body - onRender");
878 if (this.cls && this.cls.length) {
879 Roo.get(document.body).addClass(this.cls);
898 * @class Roo.bootstrap.ButtonGroup
899 * @extends Roo.bootstrap.Component
900 * Bootstrap ButtonGroup class
901 * @cfg {String} size lg | sm | xs (default empty normal)
902 * @cfg {String} align vertical | justified (default none)
903 * @cfg {String} direction up | down (default down)
904 * @cfg {Boolean} toolbar false | true
905 * @cfg {Boolean} btn true | false
910 * @param {Object} config The config object
913 Roo.bootstrap.ButtonGroup = function(config){
914 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
925 getAutoCreate : function(){
931 cfg.html = this.html || cfg.html;
942 if (['vertical','justified'].indexOf(this.align)!==-1) {
943 cfg.cls = 'btn-group-' + this.align;
945 if (this.align == 'justified') {
946 console.log(this.items);
950 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951 cfg.cls += ' btn-group-' + this.size;
954 if (this.direction == 'up') {
955 cfg.cls += ' dropup' ;
961 * Add a button to the group (similar to NavItem API.)
963 addItem : function(cfg)
965 var cn = new Roo.bootstrap.Button(cfg);
967 cn.parentId = this.id;
968 cn.onRender(this.el, null);
982 * @class Roo.bootstrap.Button
983 * @extends Roo.bootstrap.Component
984 * Bootstrap Button class
985 * @cfg {String} html The button content
986 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989 * @cfg {String} size (lg|sm|xs)
990 * @cfg {String} tag (a|input|submit)
991 * @cfg {String} href empty or href
992 * @cfg {Boolean} disabled default false;
993 * @cfg {Boolean} isClose default false;
994 * @cfg {String} glyphicon depricated - use fa
995 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996 * @cfg {String} badge text for badge
997 * @cfg {String} theme (default|glow)
998 * @cfg {Boolean} inverse dark themed version
999 * @cfg {Boolean} toggle is it a slidy toggle button
1000 * @cfg {Boolean} pressed default null - if the button ahs active state
1001 * @cfg {String} ontext text for on slidy toggle state
1002 * @cfg {String} offtext text for off slidy toggle state
1003 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1004 * @cfg {Boolean} removeClass remove the standard class..
1005 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1006 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1009 * Create a new button
1010 * @param {Object} config The config object
1014 Roo.bootstrap.Button = function(config){
1015 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1021 * When a button is pressed
1022 * @param {Roo.bootstrap.Button} btn
1023 * @param {Roo.EventObject} e
1028 * When a button is double clicked
1029 * @param {Roo.bootstrap.Button} btn
1030 * @param {Roo.EventObject} e
1035 * After the button has been toggles
1036 * @param {Roo.bootstrap.Button} btn
1037 * @param {Roo.EventObject} e
1038 * @param {boolean} pressed (also available as button.pressed)
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1065 preventDefault: true,
1074 getAutoCreate : function(){
1082 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084 this.tag = 'button';
1088 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090 if (this.toggle == true) {
1093 cls: 'slider-frame roo-button',
1097 'data-on-text':'ON',
1098 'data-off-text':'OFF',
1099 cls: 'slider-button',
1104 // why are we validating the weights?
1105 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106 cfg.cls += ' ' + this.weight;
1113 cfg.cls += ' close';
1115 cfg["aria-hidden"] = true;
1117 cfg.html = "×";
1123 if (this.theme==='default') {
1124 cfg.cls = 'btn roo-button';
1126 //if (this.parentType != 'Navbar') {
1127 this.weight = this.weight.length ? this.weight : 'default';
1129 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133 cfg.cls += ' btn-' + outline + weight;
1134 if (this.weight == 'default') {
1136 cfg.cls += ' btn-' + this.weight;
1139 } else if (this.theme==='glow') {
1142 cfg.cls = 'btn-glow roo-button';
1144 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146 cfg.cls += ' ' + this.weight;
1152 this.cls += ' inverse';
1156 if (this.active || this.pressed === true) {
1157 cfg.cls += ' active';
1160 if (this.disabled) {
1161 cfg.disabled = 'disabled';
1165 Roo.log('changing to ul' );
1167 this.glyphicon = 'caret';
1168 if (Roo.bootstrap.version == 4) {
1169 this.fa = 'caret-down';
1174 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176 //gsRoo.log(this.parentType);
1177 if (this.parentType === 'Navbar' && !this.parent().bar) {
1178 Roo.log('changing to li?');
1187 href : this.href || '#'
1190 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1191 cfg.cls += ' dropdown';
1198 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1200 if (this.glyphicon) {
1201 cfg.html = ' ' + cfg.html;
1206 cls: 'glyphicon glyphicon-' + this.glyphicon
1211 cfg.html = ' ' + cfg.html;
1216 cls: 'fa fas fa-' + this.fa
1226 // cfg.cls='btn roo-button';
1230 var value = cfg.html;
1235 cls: 'glyphicon glyphicon-' + this.glyphicon,
1242 cls: 'fa fas fa-' + this.fa,
1247 var bw = this.badge_weight.length ? this.badge_weight :
1248 (this.weight.length ? this.weight : 'secondary');
1249 bw = bw == 'default' ? 'secondary' : bw;
1255 cls: 'badge badge-' + bw,
1264 cfg.cls += ' dropdown';
1265 cfg.html = typeof(cfg.html) != 'undefined' ?
1266 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1269 if (cfg.tag !== 'a' && this.href !== '') {
1270 throw "Tag must be a to set href.";
1271 } else if (this.href.length > 0) {
1272 cfg.href = this.href;
1275 if(this.removeClass){
1280 cfg.target = this.target;
1285 initEvents: function() {
1286 // Roo.log('init events?');
1287 // Roo.log(this.el.dom);
1290 if (typeof (this.menu) != 'undefined') {
1291 this.menu.parentType = this.xtype;
1292 this.menu.triggerEl = this.el;
1293 this.addxtype(Roo.apply({}, this.menu));
1297 if (this.el.hasClass('roo-button')) {
1298 this.el.on('click', this.onClick, this);
1299 this.el.on('dblclick', this.onDblClick, this);
1301 this.el.select('.roo-button').on('click', this.onClick, this);
1302 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1306 if(this.removeClass){
1307 this.el.on('click', this.onClick, this);
1310 if (this.group === true) {
1311 if (this.pressed === false || this.pressed === true) {
1314 this.pressed = false;
1315 this.setActive(this.pressed);
1320 this.el.enableDisplayMode();
1323 onClick : function(e)
1325 if (this.disabled) {
1329 Roo.log('button on click ');
1330 if(this.preventDefault){
1339 this.setActive(true);
1340 var pi = this.parent().items;
1341 for (var i = 0;i < pi.length;i++) {
1342 if (this == pi[i]) {
1345 if (pi[i].el.hasClass('roo-button')) {
1346 pi[i].setActive(false);
1349 this.fireEvent('click', this, e);
1353 if (this.pressed === true || this.pressed === false) {
1354 this.toggleActive(e);
1358 this.fireEvent('click', this, e);
1360 onDblClick: function(e)
1362 if (this.disabled) {
1365 if(this.preventDefault){
1368 this.fireEvent('dblclick', this, e);
1371 * Enables this button
1375 this.disabled = false;
1376 this.el.removeClass('disabled');
1377 this.el.dom.removeAttribute("disabled");
1381 * Disable this button
1383 disable : function()
1385 this.disabled = true;
1386 this.el.addClass('disabled');
1387 this.el.attr("disabled", "disabled")
1390 * sets the active state on/off,
1391 * @param {Boolean} state (optional) Force a particular state
1393 setActive : function(v) {
1395 this.el[v ? 'addClass' : 'removeClass']('active');
1399 * toggles the current active state
1401 toggleActive : function(e)
1403 this.setActive(!this.pressed); // this modifies pressed...
1404 this.fireEvent('toggle', this, e, this.pressed);
1407 * get the current active state
1408 * @return {boolean} true if it's active
1410 isActive : function()
1412 return this.el.hasClass('active');
1415 * set the text of the first selected button
1417 setText : function(str)
1419 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1422 * get the text of the first selected button
1424 getText : function()
1426 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1429 setWeight : function(str)
1431 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434 var outline = this.outline ? 'outline-' : '';
1435 if (str == 'default') {
1436 this.el.addClass('btn-default btn-outline-secondary');
1439 this.el.addClass('btn-' + outline + str);
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446 Roo.bootstrap.Button.weights = [
1466 * @class Roo.bootstrap.Column
1467 * @extends Roo.bootstrap.Component
1468 * Bootstrap Column class
1469 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1479 * @cfg {Boolean} hidden (true|false) hide the element
1480 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481 * @cfg {String} fa (ban|check|...) font awesome icon
1482 * @cfg {Number} fasize (1|2|....) font awsome size
1484 * @cfg {String} icon (info-sign|check|...) glyphicon name
1486 * @cfg {String} html content of column.
1489 * Create a new Column
1490 * @param {Object} config The config object
1493 Roo.bootstrap.Column = function(config){
1494 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1515 getAutoCreate : function(){
1516 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1524 var sizes = ['xs','sm','md','lg'];
1525 sizes.map(function(size ,ix){
1526 //Roo.log( size + ':' + settings[size]);
1528 if (settings[size+'off'] !== false) {
1529 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1532 if (settings[size] === false) {
1536 if (!settings[size]) { // 0 = hidden
1537 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539 for (var i = ix; i > -1; i--) {
1540 cfg.cls += ' d-' + sizes[i] + '-none';
1546 cfg.cls += ' col-' + size + '-' + settings[size] + (
1547 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1553 cfg.cls += ' hidden';
1556 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557 cfg.cls +=' alert alert-' + this.alert;
1561 if (this.html.length) {
1562 cfg.html = this.html;
1566 if (this.fasize > 1) {
1567 fasize = ' fa-' + this.fasize + 'x';
1569 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1574 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1593 * @class Roo.bootstrap.Container
1594 * @extends Roo.bootstrap.Component
1595 * Bootstrap Container class
1596 * @cfg {Boolean} jumbotron is it a jumbotron element
1597 * @cfg {String} html content of element
1598 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1600 * @cfg {String} header content of header (for panel)
1601 * @cfg {String} footer content of footer (for panel)
1602 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603 * @cfg {String} tag (header|aside|section) type of HTML tag.
1604 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605 * @cfg {String} fa font awesome icon
1606 * @cfg {String} icon (info-sign|check|...) glyphicon name
1607 * @cfg {Boolean} hidden (true|false) hide the element
1608 * @cfg {Boolean} expandable (true|false) default false
1609 * @cfg {Boolean} expanded (true|false) default true
1610 * @cfg {String} rheader contet on the right of header
1611 * @cfg {Boolean} clickable (true|false) default false
1615 * Create a new Container
1616 * @param {Object} config The config object
1619 Roo.bootstrap.Container = function(config){
1620 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1626 * After the panel has been expand
1628 * @param {Roo.bootstrap.Container} this
1633 * After the panel has been collapsed
1635 * @param {Roo.bootstrap.Container} this
1640 * When a element is chick
1641 * @param {Roo.bootstrap.Container} this
1642 * @param {Roo.EventObject} e
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1666 getChildContainer : function() {
1672 if (this.panel.length) {
1673 return this.el.select('.panel-body',true).first();
1680 getAutoCreate : function(){
1683 tag : this.tag || 'div',
1687 if (this.jumbotron) {
1688 cfg.cls = 'jumbotron';
1693 // - this is applied by the parent..
1695 // cfg.cls = this.cls + '';
1698 if (this.sticky.length) {
1700 var bd = Roo.get(document.body);
1701 if (!bd.hasClass('bootstrap-sticky')) {
1702 bd.addClass('bootstrap-sticky');
1703 Roo.select('html',true).setStyle('height', '100%');
1706 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1710 if (this.well.length) {
1711 switch (this.well) {
1714 cfg.cls +=' well well-' +this.well;
1723 cfg.cls += ' hidden';
1727 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728 cfg.cls +=' alert alert-' + this.alert;
1733 if (this.panel.length) {
1734 cfg.cls += ' panel panel-' + this.panel;
1736 if (this.header.length) {
1740 if(this.expandable){
1742 cfg.cls = cfg.cls + ' expandable';
1746 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1754 cls : 'panel-title',
1755 html : (this.expandable ? ' ' : '') + this.header
1759 cls: 'panel-header-right',
1765 cls : 'panel-heading',
1766 style : this.expandable ? 'cursor: pointer' : '',
1774 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1779 if (this.footer.length) {
1781 cls : 'panel-footer',
1790 body.html = this.html || cfg.html;
1791 // prefix with the icons..
1793 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1796 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1801 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802 cfg.cls = 'container';
1808 initEvents: function()
1810 if(this.expandable){
1811 var headerEl = this.headerEl();
1814 headerEl.on('click', this.onToggleClick, this);
1819 this.el.on('click', this.onClick, this);
1824 onToggleClick : function()
1826 var headerEl = this.headerEl();
1842 if(this.fireEvent('expand', this)) {
1844 this.expanded = true;
1846 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848 this.el.select('.panel-body',true).first().removeClass('hide');
1850 var toggleEl = this.toggleEl();
1856 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1861 collapse : function()
1863 if(this.fireEvent('collapse', this)) {
1865 this.expanded = false;
1867 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868 this.el.select('.panel-body',true).first().addClass('hide');
1870 var toggleEl = this.toggleEl();
1876 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1880 toggleEl : function()
1882 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1886 return this.el.select('.panel-heading .fa',true).first();
1889 headerEl : function()
1891 if(!this.el || !this.panel.length || !this.header.length){
1895 return this.el.select('.panel-heading',true).first()
1900 if(!this.el || !this.panel.length){
1904 return this.el.select('.panel-body',true).first()
1907 titleEl : function()
1909 if(!this.el || !this.panel.length || !this.header.length){
1913 return this.el.select('.panel-title',true).first();
1916 setTitle : function(v)
1918 var titleEl = this.titleEl();
1924 titleEl.dom.innerHTML = v;
1927 getTitle : function()
1930 var titleEl = this.titleEl();
1936 return titleEl.dom.innerHTML;
1939 setRightTitle : function(v)
1941 var t = this.el.select('.panel-header-right',true).first();
1947 t.dom.innerHTML = v;
1950 onClick : function(e)
1954 this.fireEvent('click', this, e);
1961 * This is BS4's Card element.. - similar to our containers probably..
1965 * @class Roo.bootstrap.Card
1966 * @extends Roo.bootstrap.Component
1967 * Bootstrap Card class
1970 * possible... may not be implemented..
1971 * @cfg {String} header_image src url of image.
1972 * @cfg {String|Object} header
1973 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1976 * @cfg {String} title
1977 * @cfg {String} subtitle
1978 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979 * @cfg {String} footer
1981 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983 * @cfg {String} margin (0|1|2|3|4|5|auto)
1984 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991 * @cfg {String} padding (0|1|2|3|4|5)
1992 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994 * @cfg {String} padding_left (0|1|2|3|4|5)
1995 * @cfg {String} padding_right (0|1|2|3|4|5)
1996 * @cfg {String} padding_x (0|1|2|3|4|5)
1997 * @cfg {String} padding_y (0|1|2|3|4|5)
1999 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005 * @config {Boolean} dragable if this card can be dragged.
2006 * @config {String} drag_group group for drag
2007 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2008 * @config {String} drop_group group for drag
2010 * @config {Boolean} collapsable can the body be collapsed.
2011 * @config {Boolean} collapsed is the body collapsed when rendered...
2012 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013 * @config {Boolean} rotated is the body rotated when rendered...
2016 * Create a new Container
2017 * @param {Object} config The config object
2020 Roo.bootstrap.Card = function(config){
2021 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2027 * When a element a card is dropped
2028 * @param {Roo.bootstrap.Card} this
2031 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032 * @param {String} position 'above' or 'below'
2033 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2039 * When a element a card is rotate
2040 * @param {Roo.bootstrap.Card} this
2041 * @param {Roo.Element} n the node being dropped?
2042 * @param {Boolean} rotate status
2047 * When a card element is dragged over ready to drop (return false to block dropable)
2048 * @param {Roo.bootstrap.Card} this
2049 * @param {Object} data from dragdrop
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2062 margin: '', /// may be better in component?
2092 collapsable : false,
2101 childContainer : false,
2102 dropEl : false, /// the dom placeholde element that indicates drop location.
2103 containerEl: false, // body container
2104 bodyEl: false, // card-body
2105 headerContainerEl : false, //
2107 header_imageEl : false,
2110 layoutCls : function()
2114 Roo.log(this.margin_bottom.length);
2115 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2121 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2126 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2132 // more generic support?
2140 // Roo.log("Call onRender: " + this.xtype);
2141 /* We are looking at something like this.
2143 <img src="..." class="card-img-top" alt="...">
2144 <div class="card-body">
2145 <h5 class="card-title">Card title</h5>
2146 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148 >> this bit is really the body...
2149 <div> << we will ad dthis in hopefully it will not break shit.
2151 ** card text does not actually have any styling...
2153 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2156 <a href="#" class="card-link">Card link</a>
2159 <div class="card-footer">
2160 <small class="text-muted">Last updated 3 mins ago</small>
2164 getAutoCreate : function(){
2172 if (this.weight.length && this.weight != 'light') {
2173 cfg.cls += ' text-white';
2175 cfg.cls += ' text-dark'; // need as it's nested..
2177 if (this.weight.length) {
2178 cfg.cls += ' bg-' + this.weight;
2181 cfg.cls += ' ' + this.layoutCls();
2184 var hdr_ctr = false;
2185 if (this.header.length) {
2187 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2196 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2202 if (this.collapsable) {
2205 cls : 'd-block user-select-none',
2209 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2214 hdr.cn.push(hdr_ctr);
2219 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2224 if (this.header_image.length) {
2227 cls : 'card-img-top',
2228 src: this.header_image // escape?
2233 cls : 'card-img-top d-none'
2239 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2243 if (this.collapsable || this.rotateable) {
2246 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2253 if (this.title.length) {
2257 src: this.title // escape?
2261 if (this.subtitle.length) {
2265 src: this.subtitle // escape?
2271 cls : 'roo-card-body-ctr'
2274 if (this.html.length) {
2280 // fixme ? handle objects?
2282 if (this.footer.length) {
2285 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2290 cfg.cn.push({cls : 'card-footer d-none'});
2299 getCardHeader : function()
2301 var ret = this.el.select('.card-header',true).first();
2302 if (ret.hasClass('d-none')) {
2303 ret.removeClass('d-none');
2308 getCardFooter : function()
2310 var ret = this.el.select('.card-footer',true).first();
2311 if (ret.hasClass('d-none')) {
2312 ret.removeClass('d-none');
2317 getCardImageTop : function()
2319 var ret = this.header_imageEl;
2320 if (ret.hasClass('d-none')) {
2321 ret.removeClass('d-none');
2327 getChildContainer : function()
2333 return this.el.select('.roo-card-body-ctr',true).first();
2336 initEvents: function()
2338 this.bodyEl = this.el.select('.card-body',true).first();
2339 this.containerEl = this.getChildContainer();
2341 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342 containerScroll: true,
2343 ddGroup: this.drag_group || 'default_card_drag_group'
2345 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347 if (this.dropable) {
2348 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349 containerScroll: true,
2350 ddGroup: this.drop_group || 'default_card_drag_group'
2352 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2359 if (this.collapsable) {
2360 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362 if (this.rotateable) {
2363 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367 this.footerEl = this.el.select('.card-footer',true).first();
2368 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370 this.headerEl = this.el.select('.card-header',true).first();
2373 this.el.addClass('roo-card-rotated');
2374 this.fireEvent('rotate', this, true);
2376 this.header_imageEl = this.el.select('.card-img-top',true).first();
2377 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2380 getDragData : function(e)
2382 var target = this.getEl();
2384 //this.handleSelection(e);
2389 nodes: this.getEl(),
2394 dragData.ddel = target.dom ; // the div element
2395 Roo.log(target.getWidth( ));
2396 dragData.ddel.style.width = target.getWidth() + 'px';
2403 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2404 * whole Element becomes the target, and this causes the drop gesture to append.
2406 * Returns an object:
2409 position : 'below' or 'above'
2410 card : relateive to card OBJECT (or true for no cards listed)
2411 items_n : relative to nth item in list
2412 card_n : relative to nth card in list
2417 getTargetFromEvent : function(e, dragged_card_el)
2419 var target = e.getTarget();
2420 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421 target = target.parentNode;
2432 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433 // see if target is one of the 'cards'...
2436 //Roo.log(this.items.length);
2439 var last_card_n = 0;
2441 for (var i = 0;i< this.items.length;i++) {
2443 if (!this.items[i].el.hasClass('card')) {
2446 pos = this.getDropPoint(e, this.items[i].el.dom);
2448 cards_len = ret.cards.length;
2449 //Roo.log(this.items[i].el.dom.id);
2450 ret.cards.push(this.items[i]);
2452 if (ret.card_n < 0 && pos == 'above') {
2453 ret.position = cards_len > 0 ? 'below' : pos;
2454 ret.items_n = i > 0 ? i - 1 : 0;
2455 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2456 ret.card = ret.cards[ret.card_n];
2459 if (!ret.cards.length) {
2461 ret.position = 'below';
2465 // could not find a card.. stick it at the end..
2466 if (ret.card_n < 0) {
2467 ret.card_n = last_card_n;
2468 ret.card = ret.cards[last_card_n];
2469 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470 ret.position = 'below';
2473 if (this.items[ret.items_n].el == dragged_card_el) {
2477 if (ret.position == 'below') {
2478 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480 if (card_after && card_after.el == dragged_card_el) {
2487 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489 if (card_before && card_before.el == dragged_card_el) {
2496 onNodeEnter : function(n, dd, e, data){
2499 onNodeOver : function(n, dd, e, data)
2502 var target_info = this.getTargetFromEvent(e,data.source.el);
2503 if (target_info === false) {
2504 this.dropPlaceHolder('hide');
2507 Roo.log(['getTargetFromEvent', target_info ]);
2510 if (this.fireEvent('cardover', this, [ data ]) === false) {
2514 this.dropPlaceHolder('show', target_info,data);
2518 onNodeOut : function(n, dd, e, data){
2519 this.dropPlaceHolder('hide');
2522 onNodeDrop : function(n, dd, e, data)
2525 // call drop - return false if
2527 // this could actually fail - if the Network drops..
2528 // we will ignore this at present..- client should probably reload
2529 // the whole set of cards if stuff like that fails.
2532 var info = this.getTargetFromEvent(e,data.source.el);
2533 if (info === false) {
2536 this.dropPlaceHolder('hide');
2540 this.acceptCard(data.source, info.position, info.card, info.items_n);
2544 firstChildCard : function()
2546 for (var i = 0;i< this.items.length;i++) {
2548 if (!this.items[i].el.hasClass('card')) {
2551 return this.items[i];
2553 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2558 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2560 acceptCard : function(move_card, position, next_to_card )
2562 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2566 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568 move_card.parent().removeCard(move_card);
2571 var dom = move_card.el.dom;
2572 dom.style.width = ''; // clear with - which is set by drag.
2574 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575 var cardel = next_to_card.el.dom;
2577 if (position == 'above' ) {
2578 cardel.parentNode.insertBefore(dom, cardel);
2579 } else if (cardel.nextSibling) {
2580 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582 cardel.parentNode.append(dom);
2585 // card container???
2586 this.containerEl.dom.append(dom);
2589 //FIXME HANDLE card = true
2591 // add this to the correct place in items.
2593 // remove Card from items.
2596 if (this.items.length) {
2598 //Roo.log([info.items_n, info.position, this.items.length]);
2599 for (var i =0; i < this.items.length; i++) {
2600 if (i == to_items_n && position == 'above') {
2601 nitems.push(move_card);
2603 nitems.push(this.items[i]);
2604 if (i == to_items_n && position == 'below') {
2605 nitems.push(move_card);
2608 this.items = nitems;
2609 Roo.log(this.items);
2611 this.items.push(move_card);
2614 move_card.parentId = this.id;
2620 removeCard : function(c)
2622 this.items = this.items.filter(function(e) { return e != c });
2625 dom.parentNode.removeChild(dom);
2626 dom.style.width = ''; // clear with - which is set by drag.
2631 /** Decide whether to drop above or below a View node. */
2632 getDropPoint : function(e, n, dd)
2637 if (n == this.containerEl.dom) {
2640 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641 var c = t + (b - t) / 2;
2642 var y = Roo.lib.Event.getPageY(e);
2649 onToggleCollapse : function(e)
2651 if (this.collapsed) {
2652 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653 this.collapsableEl.addClass('show');
2654 this.collapsed = false;
2657 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658 this.collapsableEl.removeClass('show');
2659 this.collapsed = true;
2664 onToggleRotate : function(e)
2666 this.collapsableEl.removeClass('show');
2667 this.footerEl.removeClass('d-none');
2668 this.el.removeClass('roo-card-rotated');
2669 this.el.removeClass('d-none');
2672 this.collapsableEl.addClass('show');
2673 this.rotated = false;
2674 this.fireEvent('rotate', this, this.rotated);
2677 this.el.addClass('roo-card-rotated');
2678 this.footerEl.addClass('d-none');
2679 this.el.select('.roo-collapsable').removeClass('show');
2681 this.rotated = true;
2682 this.fireEvent('rotate', this, this.rotated);
2686 dropPlaceHolder: function (action, info, data)
2688 if (this.dropEl === false) {
2689 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2693 this.dropEl.removeClass(['d-none', 'd-block']);
2694 if (action == 'hide') {
2696 this.dropEl.addClass('d-none');
2699 // FIXME - info.card == true!!!
2700 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702 if (info.card !== true) {
2703 var cardel = info.card.el.dom;
2705 if (info.position == 'above') {
2706 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707 } else if (cardel.nextSibling) {
2708 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710 cardel.parentNode.append(this.dropEl.dom);
2713 // card container???
2714 this.containerEl.dom.append(this.dropEl.dom);
2717 this.dropEl.addClass('d-block roo-card-dropzone');
2719 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2726 setHeaderText: function(html)
2729 if (this.headerContainerEl) {
2730 this.headerContainerEl.dom.innerHTML = html;
2733 onHeaderImageLoad : function(ev, he)
2735 if (!this.header_image_fit_square) {
2739 var hw = he.naturalHeight / he.naturalWidth;
2742 //var w = he.dom.naturalWidth;
2745 he.style.position = 'relative';
2747 var nw = (ww * (1/hw));
2748 Roo.get(he).setSize( ww * (1/hw), ww);
2749 he.style.left = ((ww - nw)/ 2) + 'px';
2750 he.style.position = 'relative';
2761 * Card header - holder for the card header elements.
2766 * @class Roo.bootstrap.CardHeader
2767 * @extends Roo.bootstrap.Element
2768 * Bootstrap CardHeader class
2770 * Create a new Card Header - that you can embed children into
2771 * @param {Object} config The config object
2774 Roo.bootstrap.CardHeader = function(config){
2775 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2781 container_method : 'getCardHeader'
2794 * Card footer - holder for the card footer elements.
2799 * @class Roo.bootstrap.CardFooter
2800 * @extends Roo.bootstrap.Element
2801 * Bootstrap CardFooter class
2803 * Create a new Card Footer - that you can embed children into
2804 * @param {Object} config The config object
2807 Roo.bootstrap.CardFooter = function(config){
2808 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2814 container_method : 'getCardFooter'
2827 * Card header - holder for the card header elements.
2832 * @class Roo.bootstrap.CardImageTop
2833 * @extends Roo.bootstrap.Element
2834 * Bootstrap CardImageTop class
2836 * Create a new Card Image Top container
2837 * @param {Object} config The config object
2840 Roo.bootstrap.CardImageTop = function(config){
2841 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2847 container_method : 'getCardImageTop'
2862 * @class Roo.bootstrap.ButtonUploader
2863 * @extends Roo.bootstrap.Button
2864 * Bootstrap Button Uploader class - it's a button which when you add files to it
2867 * @cfg {Number} errorTimeout default 3000
2868 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2869 * @cfg {Array} html The button text.
2870 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2873 * Create a new CardUploader
2874 * @param {Object} config The config object
2877 Roo.bootstrap.ButtonUploader = function(config){
2881 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2887 * @event beforeselect
2888 * When button is pressed, before show upload files dialog is shown
2889 * @param {Roo.bootstrap.UploaderButton} this
2892 'beforeselect' : true,
2894 * @event fired when files have been selected,
2895 * When a the download link is clicked
2896 * @param {Roo.bootstrap.UploaderButton} this
2897 * @param {Array} Array of files that have been uploaded
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2907 errorTimeout : 3000,
2911 fileCollection : false,
2916 getAutoCreate : function()
2921 cls : 'd-none roo-card-upload-selector'
2924 if (this.multiple) {
2925 im.multiple = 'multiple';
2931 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2941 initEvents : function()
2944 Roo.bootstrap.Button.prototype.initEvents.call(this);
2950 this.urlAPI = (window.createObjectURL && window) ||
2951 (window.URL && URL.revokeObjectURL && URL) ||
2952 (window.webkitURL && webkitURL);
2957 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959 this.selectorEl.on('change', this.onFileSelected, this);
2966 onClick : function(e)
2970 if ( this.fireEvent('beforeselect', this) === false) {
2974 this.selectorEl.dom.click();
2978 onFileSelected : function(e)
2982 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2985 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986 this.selectorEl.dom.value = '';// hopefully reset..
2988 this.fireEvent('uploaded', this, files );
2996 * addCard - add an Attachment to the uploader
2997 * @param data - the data about the image to upload
3001 title : "Title of file",
3002 is_uploaded : false,
3003 src : "http://.....",
3004 srcfile : { the File upload object },
3005 mimetype : file.type,
3008 .. any other data...
3033 * @class Roo.bootstrap.Img
3034 * @extends Roo.bootstrap.Component
3035 * Bootstrap Img class
3036 * @cfg {Boolean} imgResponsive false | true
3037 * @cfg {String} border rounded | circle | thumbnail
3038 * @cfg {String} src image source
3039 * @cfg {String} alt image alternative text
3040 * @cfg {String} href a tag href
3041 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042 * @cfg {String} xsUrl xs image source
3043 * @cfg {String} smUrl sm image source
3044 * @cfg {String} mdUrl md image source
3045 * @cfg {String} lgUrl lg image source
3048 * Create a new Input
3049 * @param {Object} config The config object
3052 Roo.bootstrap.Img = function(config){
3053 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3059 * The img click event for the img.
3060 * @param {Roo.EventObject} e
3066 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3068 imgResponsive: true,
3078 getAutoCreate : function()
3080 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3081 return this.createSingleImg();
3086 cls: 'roo-image-responsive-group',
3091 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3093 if(!_this[size + 'Url']){
3099 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3100 html: _this.html || cfg.html,
3101 src: _this[size + 'Url']
3104 img.cls += ' roo-image-responsive-' + size;
3106 var s = ['xs', 'sm', 'md', 'lg'];
3108 s.splice(s.indexOf(size), 1);
3110 Roo.each(s, function(ss){
3111 img.cls += ' hidden-' + ss;
3114 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3115 cfg.cls += ' img-' + _this.border;
3119 cfg.alt = _this.alt;
3132 a.target = _this.target;
3136 cfg.cn.push((_this.href) ? a : img);
3143 createSingleImg : function()
3147 cls: (this.imgResponsive) ? 'img-responsive' : '',
3149 src : 'about:blank' // just incase src get's set to undefined?!?
3152 cfg.html = this.html || cfg.html;
3154 cfg.src = this.src || cfg.src;
3156 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3157 cfg.cls += ' img-' + this.border;
3174 a.target = this.target;
3179 return (this.href) ? a : cfg;
3182 initEvents: function()
3185 this.el.on('click', this.onClick, this);
3190 onClick : function(e)
3192 Roo.log('img onclick');
3193 this.fireEvent('click', this, e);
3196 * Sets the url of the image - used to update it
3197 * @param {String} url the url of the image
3200 setSrc : function(url)
3204 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205 this.el.dom.src = url;
3209 this.el.select('img', true).first().dom.src = url;
3225 * @class Roo.bootstrap.Link
3226 * @extends Roo.bootstrap.Component
3227 * Bootstrap Link Class
3228 * @cfg {String} alt image alternative text
3229 * @cfg {String} href a tag href
3230 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3231 * @cfg {String} html the content of the link.
3232 * @cfg {String} anchor name for the anchor link
3233 * @cfg {String} fa - favicon
3235 * @cfg {Boolean} preventDefault (true | false) default false
3239 * Create a new Input
3240 * @param {Object} config The config object
3243 Roo.bootstrap.Link = function(config){
3244 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3250 * The img click event for the img.
3251 * @param {Roo.EventObject} e
3257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3261 preventDefault: false,
3267 getAutoCreate : function()
3269 var html = this.html || '';
3271 if (this.fa !== false) {
3272 html = '<i class="fa fa-' + this.fa + '"></i>';
3277 // anchor's do not require html/href...
3278 if (this.anchor === false) {
3280 cfg.href = this.href || '#';
3282 cfg.name = this.anchor;
3283 if (this.html !== false || this.fa !== false) {
3286 if (this.href !== false) {
3287 cfg.href = this.href;
3291 if(this.alt !== false){
3296 if(this.target !== false) {
3297 cfg.target = this.target;
3303 initEvents: function() {
3305 if(!this.href || this.preventDefault){
3306 this.el.on('click', this.onClick, this);
3310 onClick : function(e)
3312 if(this.preventDefault){
3315 //Roo.log('img onclick');
3316 this.fireEvent('click', this, e);
3329 * @class Roo.bootstrap.Header
3330 * @extends Roo.bootstrap.Component
3331 * Bootstrap Header class
3332 * @cfg {String} html content of header
3333 * @cfg {Number} level (1|2|3|4|5|6) default 1
3336 * Create a new Header
3337 * @param {Object} config The config object
3341 Roo.bootstrap.Header = function(config){
3342 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3345 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3353 getAutoCreate : function(){
3358 tag: 'h' + (1 *this.level),
3359 html: this.html || ''
3371 * Ext JS Library 1.1.1
3372 * Copyright(c) 2006-2007, Ext JS, LLC.
3374 * Originally Released Under LGPL - original licence link has changed is not relivant.
3377 * <script type="text/javascript">
3381 * @class Roo.bootstrap.MenuMgr
3382 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3385 Roo.bootstrap.MenuMgr = function(){
3386 var menus, active, groups = {}, attached = false, lastShow = new Date();
3388 // private - called when first menu is created
3391 active = new Roo.util.MixedCollection();
3392 Roo.get(document).addKeyListener(27, function(){
3393 if(active.length > 0){
3401 if(active && active.length > 0){
3402 var c = active.clone();
3412 if(active.length < 1){
3413 Roo.get(document).un("mouseup", onMouseDown);
3421 var last = active.last();
3422 lastShow = new Date();
3425 Roo.get(document).on("mouseup", onMouseDown);
3430 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3431 m.parentMenu.activeChild = m;
3432 }else if(last && last.isVisible()){
3433 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3438 function onBeforeHide(m){
3440 m.activeChild.hide();
3442 if(m.autoHideTimer){
3443 clearTimeout(m.autoHideTimer);
3444 delete m.autoHideTimer;
3449 function onBeforeShow(m){
3450 var pm = m.parentMenu;
3451 if(!pm && !m.allowOtherMenus){
3453 }else if(pm && pm.activeChild && active != m){
3454 pm.activeChild.hide();
3458 // private this should really trigger on mouseup..
3459 function onMouseDown(e){
3460 Roo.log("on Mouse Up");
3462 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3463 Roo.log("MenuManager hideAll");
3472 function onBeforeCheck(mi, state){
3474 var g = groups[mi.group];
3475 for(var i = 0, l = g.length; i < l; i++){
3477 g[i].setChecked(false);
3486 * Hides all menus that are currently visible
3488 hideAll : function(){
3493 register : function(menu){
3497 menus[menu.id] = menu;
3498 menu.on("beforehide", onBeforeHide);
3499 menu.on("hide", onHide);
3500 menu.on("beforeshow", onBeforeShow);
3501 menu.on("show", onShow);
3503 if(g && menu.events["checkchange"]){
3507 groups[g].push(menu);
3508 menu.on("checkchange", onCheck);
3513 * Returns a {@link Roo.menu.Menu} object
3514 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3515 * be used to generate and return a new Menu instance.
3517 get : function(menu){
3518 if(typeof menu == "string"){ // menu id
3520 }else if(menu.events){ // menu instance
3523 /*else if(typeof menu.length == 'number'){ // array of menu items?
3524 return new Roo.bootstrap.Menu({items:menu});
3525 }else{ // otherwise, must be a config
3526 return new Roo.bootstrap.Menu(menu);
3533 unregister : function(menu){
3534 delete menus[menu.id];
3535 menu.un("beforehide", onBeforeHide);
3536 menu.un("hide", onHide);
3537 menu.un("beforeshow", onBeforeShow);
3538 menu.un("show", onShow);
3540 if(g && menu.events["checkchange"]){
3541 groups[g].remove(menu);
3542 menu.un("checkchange", onCheck);
3547 registerCheckable : function(menuItem){
3548 var g = menuItem.group;
3553 groups[g].push(menuItem);
3554 menuItem.on("beforecheckchange", onBeforeCheck);
3559 unregisterCheckable : function(menuItem){
3560 var g = menuItem.group;
3562 groups[g].remove(menuItem);
3563 menuItem.un("beforecheckchange", onBeforeCheck);
3575 * @class Roo.bootstrap.Menu
3576 * @extends Roo.bootstrap.Component
3577 * Bootstrap Menu class - container for MenuItems
3578 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3579 * @cfg {bool} hidden if the menu should be hidden when rendered.
3580 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3581 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3582 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3583 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3587 * @param {Object} config The config object
3591 Roo.bootstrap.Menu = function(config){
3592 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3593 if (this.registerMenu && this.type != 'treeview') {
3594 Roo.bootstrap.MenuMgr.register(this);
3601 * Fires before this menu is displayed (return false to block)
3602 * @param {Roo.menu.Menu} this
3607 * Fires before this menu is hidden (return false to block)
3608 * @param {Roo.menu.Menu} this
3613 * Fires after this menu is displayed
3614 * @param {Roo.menu.Menu} this
3619 * Fires after this menu is hidden
3620 * @param {Roo.menu.Menu} this
3625 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3626 * @param {Roo.menu.Menu} this
3627 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3628 * @param {Roo.EventObject} e
3633 * Fires when the mouse is hovering over this menu
3634 * @param {Roo.menu.Menu} this
3635 * @param {Roo.EventObject} e
3636 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3641 * Fires when the mouse exits this menu
3642 * @param {Roo.menu.Menu} this
3643 * @param {Roo.EventObject} e
3644 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3649 * Fires when a menu item contained in this menu is clicked
3650 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3651 * @param {Roo.EventObject} e
3655 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3658 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3662 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3665 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3667 registerMenu : true,
3669 menuItems :false, // stores the menu items..
3679 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3681 hideTrigger : false,
3686 getChildContainer : function() {
3690 getAutoCreate : function(){
3692 //if (['right'].indexOf(this.align)!==-1) {
3693 // cfg.cn[1].cls += ' pull-right'
3698 cls : 'dropdown-menu shadow' ,
3699 style : 'z-index:1000'
3703 if (this.type === 'submenu') {
3704 cfg.cls = 'submenu active';
3706 if (this.type === 'treeview') {
3707 cfg.cls = 'treeview-menu';
3712 initEvents : function() {
3714 // Roo.log("ADD event");
3715 // Roo.log(this.triggerEl.dom);
3717 this.triggerEl.on('click', this.onTriggerClick, this);
3719 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3721 if (!this.hideTrigger) {
3722 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3723 // dropdown toggle on the 'a' in BS4?
3724 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3726 this.triggerEl.addClass('dropdown-toggle');
3730 this.el.on('touchstart' , this.onTouch, this);
3732 this.el.on('click' , this.onClick, this);
3734 this.el.on("mouseover", this.onMouseOver, this);
3735 this.el.on("mouseout", this.onMouseOut, this);
3739 findTargetItem : function(e)
3741 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3745 //Roo.log(t); Roo.log(t.id);
3747 //Roo.log(this.menuitems);
3748 return this.menuitems.get(t.id);
3750 //return this.items.get(t.menuItemId);
3756 onTouch : function(e)
3758 Roo.log("menu.onTouch");
3759 //e.stopEvent(); this make the user popdown broken
3763 onClick : function(e)
3765 Roo.log("menu.onClick");
3767 var t = this.findTargetItem(e);
3768 if(!t || t.isContainer){
3773 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3774 if(t == this.activeItem && t.shouldDeactivate(e)){
3775 this.activeItem.deactivate();
3776 delete this.activeItem;
3780 this.setActiveItem(t, true);
3788 Roo.log('pass click event');
3792 this.fireEvent("click", this, t, e);
3796 if(!t.href.length || t.href == '#'){
3797 (function() { _this.hide(); }).defer(100);
3802 onMouseOver : function(e){
3803 var t = this.findTargetItem(e);
3806 // if(t.canActivate && !t.disabled){
3807 // this.setActiveItem(t, true);
3811 this.fireEvent("mouseover", this, e, t);
3813 isVisible : function(){
3814 return !this.hidden;
3816 onMouseOut : function(e){
3817 var t = this.findTargetItem(e);
3820 // if(t == this.activeItem && t.shouldDeactivate(e)){
3821 // this.activeItem.deactivate();
3822 // delete this.activeItem;
3825 this.fireEvent("mouseout", this, e, t);
3830 * Displays this menu relative to another element
3831 * @param {String/HTMLElement/Roo.Element} element The element to align to
3832 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3833 * the element (defaults to this.defaultAlign)
3834 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3836 show : function(el, pos, parentMenu)
3838 if (false === this.fireEvent("beforeshow", this)) {
3839 Roo.log("show canceled");
3842 this.parentMenu = parentMenu;
3846 this.el.addClass('show'); // show otherwise we do not know how big we are..
3848 var xy = this.el.getAlignToXY(el, pos);
3850 // bl-tl << left align below
3851 // tl-bl << left align
3853 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3854 // if it goes to far to the right.. -> align left.
3855 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3858 // was left align - go right?
3859 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3862 // goes down the bottom
3863 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3865 var a = this.align.replace('?', '').split('-');
3866 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3870 this.showAt( xy , parentMenu, false);
3873 * Displays this menu at a specific xy position
3874 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3875 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3877 showAt : function(xy, parentMenu, /* private: */_e){
3878 this.parentMenu = parentMenu;
3883 this.fireEvent("beforeshow", this);
3884 //xy = this.el.adjustForConstraints(xy);
3888 this.hideMenuItems();
3889 this.hidden = false;
3890 this.triggerEl.addClass('open');
3891 this.el.addClass('show');
3895 // reassign x when hitting right
3897 // reassign y when hitting bottom
3899 // but the list may align on trigger left or trigger top... should it be a properity?
3901 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3906 this.fireEvent("show", this);
3912 this.doFocus.defer(50, this);
3916 doFocus : function(){
3918 this.focusEl.focus();
3923 * Hides this menu and optionally all parent menus
3924 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3926 hide : function(deep)
3928 if (false === this.fireEvent("beforehide", this)) {
3929 Roo.log("hide canceled");
3932 this.hideMenuItems();
3933 if(this.el && this.isVisible()){
3935 if(this.activeItem){
3936 this.activeItem.deactivate();
3937 this.activeItem = null;
3939 this.triggerEl.removeClass('open');;
3940 this.el.removeClass('show');
3942 this.fireEvent("hide", this);
3944 if(deep === true && this.parentMenu){
3945 this.parentMenu.hide(true);
3949 onTriggerClick : function(e)
3951 Roo.log('trigger click');
3953 var target = e.getTarget();
3955 Roo.log(target.nodeName.toLowerCase());
3957 if(target.nodeName.toLowerCase() === 'i'){
3963 onTriggerPress : function(e)
3965 Roo.log('trigger press');
3966 //Roo.log(e.getTarget());
3967 // Roo.log(this.triggerEl.dom);
3969 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3970 var pel = Roo.get(e.getTarget());
3971 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3972 Roo.log('is treeview or dropdown?');
3976 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3980 if (this.isVisible()) {
3986 this.show(this.triggerEl, this.align, false);
3989 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3996 hideMenuItems : function()
3998 Roo.log("hide Menu Items");
4003 this.el.select('.open',true).each(function(aa) {
4005 aa.removeClass('open');
4009 addxtypeChild : function (tree, cntr) {
4010 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4012 this.menuitems.add(comp);
4024 this.getEl().dom.innerHTML = '';
4025 this.menuitems.clear();
4039 * @class Roo.bootstrap.MenuItem
4040 * @extends Roo.bootstrap.Component
4041 * Bootstrap MenuItem class
4042 * @cfg {String} html the menu label
4043 * @cfg {String} href the link
4044 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4045 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4046 * @cfg {Boolean} active used on sidebars to highlight active itesm
4047 * @cfg {String} fa favicon to show on left of menu item.
4048 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4052 * Create a new MenuItem
4053 * @param {Object} config The config object
4057 Roo.bootstrap.MenuItem = function(config){
4058 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4063 * The raw click event for the entire grid.
4064 * @param {Roo.bootstrap.MenuItem} this
4065 * @param {Roo.EventObject} e
4071 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4075 preventDefault: false,
4076 isContainer : false,
4080 getAutoCreate : function(){
4082 if(this.isContainer){
4085 cls: 'dropdown-menu-item '
4095 cls : 'dropdown-item',
4100 if (this.fa !== false) {
4103 cls : 'fa fa-' + this.fa
4112 cls: 'dropdown-menu-item',
4115 if (this.parent().type == 'treeview') {
4116 cfg.cls = 'treeview-menu';
4119 cfg.cls += ' active';
4124 anc.href = this.href || cfg.cn[0].href ;
4125 ctag.html = this.html || cfg.cn[0].html ;
4129 initEvents: function()
4131 if (this.parent().type == 'treeview') {
4132 this.el.select('a').on('click', this.onClick, this);
4136 this.menu.parentType = this.xtype;
4137 this.menu.triggerEl = this.el;
4138 this.menu = this.addxtype(Roo.apply({}, this.menu));
4142 onClick : function(e)
4144 Roo.log('item on click ');
4146 if(this.preventDefault){
4149 //this.parent().hideMenuItems();
4151 this.fireEvent('click', this, e);
4170 * @class Roo.bootstrap.MenuSeparator
4171 * @extends Roo.bootstrap.Component
4172 * Bootstrap MenuSeparator class
4175 * Create a new MenuItem
4176 * @param {Object} config The config object
4180 Roo.bootstrap.MenuSeparator = function(config){
4181 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4184 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4186 getAutoCreate : function(){
4205 * @class Roo.bootstrap.Modal
4206 * @extends Roo.bootstrap.Component
4207 * Bootstrap Modal class
4208 * @cfg {String} title Title of dialog
4209 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4210 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4211 * @cfg {Boolean} specificTitle default false
4212 * @cfg {Array} buttons Array of buttons or standard button set..
4213 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4214 * @cfg {Boolean} animate default true
4215 * @cfg {Boolean} allow_close default true
4216 * @cfg {Boolean} fitwindow default false
4217 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4218 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4219 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4220 * @cfg {String} size (sm|lg|xl) default empty
4221 * @cfg {Number} max_width set the max width of modal
4222 * @cfg {Boolean} editableTitle can the title be edited
4227 * Create a new Modal Dialog
4228 * @param {Object} config The config object
4231 Roo.bootstrap.Modal = function(config){
4232 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4237 * The raw btnclick event for the button
4238 * @param {Roo.EventObject} e
4243 * Fire when dialog resize
4244 * @param {Roo.bootstrap.Modal} this
4245 * @param {Roo.EventObject} e
4249 * @event titlechanged
4250 * Fire when the editable title has been changed
4251 * @param {Roo.bootstrap.Modal} this
4252 * @param {Roo.EventObject} value
4254 "titlechanged" : true
4257 this.buttons = this.buttons || [];
4260 this.tmpl = Roo.factory(this.tmpl);
4265 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4267 title : 'test dialog',
4277 specificTitle: false,
4279 buttonPosition: 'right',
4301 editableTitle : false,
4303 onRender : function(ct, position)
4305 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4308 var cfg = Roo.apply({}, this.getAutoCreate());
4311 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4313 //if (!cfg.name.length) {
4317 cfg.cls += ' ' + this.cls;
4320 cfg.style = this.style;
4322 this.el = Roo.get(document.body).createChild(cfg, position);
4324 //var type = this.el.dom.type;
4327 if(this.tabIndex !== undefined){
4328 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4331 this.dialogEl = this.el.select('.modal-dialog',true).first();
4332 this.bodyEl = this.el.select('.modal-body',true).first();
4333 this.closeEl = this.el.select('.modal-header .close', true).first();
4334 this.headerEl = this.el.select('.modal-header',true).first();
4335 this.titleEl = this.el.select('.modal-title',true).first();
4336 this.footerEl = this.el.select('.modal-footer',true).first();
4338 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4340 //this.el.addClass("x-dlg-modal");
4342 if (this.buttons.length) {
4343 Roo.each(this.buttons, function(bb) {
4344 var b = Roo.apply({}, bb);
4345 b.xns = b.xns || Roo.bootstrap;
4346 b.xtype = b.xtype || 'Button';
4347 if (typeof(b.listeners) == 'undefined') {
4348 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4351 var btn = Roo.factory(b);
4353 btn.render(this.getButtonContainer());
4357 // render the children.
4360 if(typeof(this.items) != 'undefined'){
4361 var items = this.items;
4364 for(var i =0;i < items.length;i++) {
4365 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4369 this.items = nitems;
4371 // where are these used - they used to be body/close/footer
4375 //this.el.addClass([this.fieldClass, this.cls]);
4379 getAutoCreate : function()
4381 // we will default to modal-body-overflow - might need to remove or make optional later.
4383 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4384 html : this.html || ''
4389 cls : 'modal-title',
4393 if(this.specificTitle){ // WTF is this?
4398 if (this.allow_close && Roo.bootstrap.version == 3) {
4408 if (this.editableTitle) {
4410 cls: 'form-control roo-editable-title d-none',
4416 if (this.allow_close && Roo.bootstrap.version == 4) {
4426 if(this.size.length){
4427 size = 'modal-' + this.size;
4430 var footer = Roo.bootstrap.version == 3 ?
4432 cls : 'modal-footer',
4436 cls: 'btn-' + this.buttonPosition
4441 { // BS4 uses mr-auto on left buttons....
4442 cls : 'modal-footer'
4453 cls: "modal-dialog " + size,
4456 cls : "modal-content",
4459 cls : 'modal-header',
4474 modal.cls += ' fade';
4480 getChildContainer : function() {
4485 getButtonContainer : function() {
4487 return Roo.bootstrap.version == 4 ?
4488 this.el.select('.modal-footer',true).first()
4489 : this.el.select('.modal-footer div',true).first();
4492 initEvents : function()
4494 if (this.allow_close) {
4495 this.closeEl.on('click', this.hide, this);
4497 Roo.EventManager.onWindowResize(this.resize, this, true);
4498 if (this.editableTitle) {
4499 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4500 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4501 this.headerEditEl.on('keyup', function(e) {
4502 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4503 this.toggleHeaderInput(false)
4506 this.headerEditEl.on('blur', function(e) {
4507 this.toggleHeaderInput(false)
4516 this.maskEl.setSize(
4517 Roo.lib.Dom.getViewWidth(true),
4518 Roo.lib.Dom.getViewHeight(true)
4521 if (this.fitwindow) {
4523 this.dialogEl.setStyle( { 'max-width' : '100%' });
4525 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4526 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4531 if(this.max_width !== 0) {
4533 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4536 this.setSize(w, this.height);
4540 if(this.max_height) {
4541 this.setSize(w,Math.min(
4543 Roo.lib.Dom.getViewportHeight(true) - 60
4549 if(!this.fit_content) {
4550 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4554 this.setSize(w, Math.min(
4556 this.headerEl.getHeight() +
4557 this.footerEl.getHeight() +
4558 this.getChildHeight(this.bodyEl.dom.childNodes),
4559 Roo.lib.Dom.getViewportHeight(true) - 60)
4565 setSize : function(w,h)
4576 if (!this.rendered) {
4579 this.toggleHeaderInput(false);
4580 //this.el.setStyle('display', 'block');
4581 this.el.removeClass('hideing');
4582 this.el.dom.style.display='block';
4584 Roo.get(document.body).addClass('modal-open');
4586 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4589 this.el.addClass('show');
4590 this.el.addClass('in');
4593 this.el.addClass('show');
4594 this.el.addClass('in');
4597 // not sure how we can show data in here..
4599 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4602 Roo.get(document.body).addClass("x-body-masked");
4604 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4605 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4606 this.maskEl.dom.style.display = 'block';
4607 this.maskEl.addClass('show');
4612 this.fireEvent('show', this);
4614 // set zindex here - otherwise it appears to be ignored...
4615 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4618 this.items.forEach( function(e) {
4619 e.layout ? e.layout() : false;
4627 if(this.fireEvent("beforehide", this) !== false){
4629 this.maskEl.removeClass('show');
4631 this.maskEl.dom.style.display = '';
4632 Roo.get(document.body).removeClass("x-body-masked");
4633 this.el.removeClass('in');
4634 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4636 if(this.animate){ // why
4637 this.el.addClass('hideing');
4638 this.el.removeClass('show');
4640 if (!this.el.hasClass('hideing')) {
4641 return; // it's been shown again...
4644 this.el.dom.style.display='';
4646 Roo.get(document.body).removeClass('modal-open');
4647 this.el.removeClass('hideing');
4651 this.el.removeClass('show');
4652 this.el.dom.style.display='';
4653 Roo.get(document.body).removeClass('modal-open');
4656 this.fireEvent('hide', this);
4659 isVisible : function()
4662 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4666 addButton : function(str, cb)
4670 var b = Roo.apply({}, { html : str } );
4671 b.xns = b.xns || Roo.bootstrap;
4672 b.xtype = b.xtype || 'Button';
4673 if (typeof(b.listeners) == 'undefined') {
4674 b.listeners = { click : cb.createDelegate(this) };
4677 var btn = Roo.factory(b);
4679 btn.render(this.getButtonContainer());
4685 setDefaultButton : function(btn)
4687 //this.el.select('.modal-footer').()
4690 resizeTo: function(w,h)
4692 this.dialogEl.setWidth(w);
4694 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4696 this.bodyEl.setHeight(h - diff);
4698 this.fireEvent('resize', this);
4701 setContentSize : function(w, h)
4705 onButtonClick: function(btn,e)
4708 this.fireEvent('btnclick', btn.name, e);
4711 * Set the title of the Dialog
4712 * @param {String} str new Title
4714 setTitle: function(str) {
4715 this.titleEl.dom.innerHTML = str;
4719 * Set the body of the Dialog
4720 * @param {String} str new Title
4722 setBody: function(str) {
4723 this.bodyEl.dom.innerHTML = str;
4726 * Set the body of the Dialog using the template
4727 * @param {Obj} data - apply this data to the template and replace the body contents.
4729 applyBody: function(obj)
4732 Roo.log("Error - using apply Body without a template");
4735 this.tmpl.overwrite(this.bodyEl, obj);
4738 getChildHeight : function(child_nodes)
4742 child_nodes.length == 0
4747 var child_height = 0;
4749 for(var i = 0; i < child_nodes.length; i++) {
4752 * for modal with tabs...
4753 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4755 var layout_childs = child_nodes[i].childNodes;
4757 for(var j = 0; j < layout_childs.length; j++) {
4759 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4761 var layout_body_childs = layout_childs[j].childNodes;
4763 for(var k = 0; k < layout_body_childs.length; k++) {
4765 if(layout_body_childs[k].classList.contains('navbar')) {
4766 child_height += layout_body_childs[k].offsetHeight;
4770 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4772 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4774 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4776 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4777 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4792 child_height += child_nodes[i].offsetHeight;
4793 // Roo.log(child_nodes[i].offsetHeight);
4796 return child_height;
4798 toggleHeaderInput : function(is_edit)
4800 if (!this.editableTitle) {
4801 return; // not editable.
4803 if (is_edit && this.is_header_editing) {
4804 return; // already editing..
4808 this.headerEditEl.dom.value = this.title;
4809 this.headerEditEl.removeClass('d-none');
4810 this.headerEditEl.dom.focus();
4811 this.titleEl.addClass('d-none');
4813 this.is_header_editing = true;
4816 // flip back to not editing.
4817 this.title = this.headerEditEl.dom.value;
4818 this.headerEditEl.addClass('d-none');
4819 this.titleEl.removeClass('d-none');
4820 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4821 this.is_header_editing = false;
4822 this.fireEvent('titlechanged', this, this.title);
4831 Roo.apply(Roo.bootstrap.Modal, {
4833 * Button config that displays a single OK button
4842 * Button config that displays Yes and No buttons
4858 * Button config that displays OK and Cancel buttons
4873 * Button config that displays Yes, No and Cancel buttons
4898 * messagebox - can be used as a replace
4902 * @class Roo.MessageBox
4903 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4907 Roo.Msg.alert('Status', 'Changes saved successfully.');
4909 // Prompt for user data:
4910 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4912 // process text value...
4916 // Show a dialog using config options:
4918 title:'Save Changes?',
4919 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4920 buttons: Roo.Msg.YESNOCANCEL,
4927 Roo.bootstrap.MessageBox = function(){
4928 var dlg, opt, mask, waitTimer;
4929 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4930 var buttons, activeTextEl, bwidth;
4934 var handleButton = function(button){
4936 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4940 var handleHide = function(){
4942 dlg.el.removeClass(opt.cls);
4945 // Roo.TaskMgr.stop(waitTimer);
4946 // waitTimer = null;
4951 var updateButtons = function(b){
4954 buttons["ok"].hide();
4955 buttons["cancel"].hide();
4956 buttons["yes"].hide();
4957 buttons["no"].hide();
4958 dlg.footerEl.hide();
4962 dlg.footerEl.show();
4963 for(var k in buttons){
4964 if(typeof buttons[k] != "function"){
4967 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4968 width += buttons[k].el.getWidth()+15;
4978 var handleEsc = function(d, k, e){
4979 if(opt && opt.closable !== false){
4989 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4990 * @return {Roo.BasicDialog} The BasicDialog element
4992 getDialog : function(){
4994 dlg = new Roo.bootstrap.Modal( {
4997 //constraintoviewport:false,
4999 //collapsible : false,
5004 //buttonAlign:"center",
5005 closeClick : function(){
5006 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5009 handleButton("cancel");
5014 dlg.on("hide", handleHide);
5016 //dlg.addKeyListener(27, handleEsc);
5018 this.buttons = buttons;
5019 var bt = this.buttonText;
5020 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5021 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5022 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5023 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5025 bodyEl = dlg.bodyEl.createChild({
5027 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5028 '<textarea class="roo-mb-textarea"></textarea>' +
5029 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5031 msgEl = bodyEl.dom.firstChild;
5032 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5033 textboxEl.enableDisplayMode();
5034 textboxEl.addKeyListener([10,13], function(){
5035 if(dlg.isVisible() && opt && opt.buttons){
5038 }else if(opt.buttons.yes){
5039 handleButton("yes");
5043 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5044 textareaEl.enableDisplayMode();
5045 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5046 progressEl.enableDisplayMode();
5048 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5049 var pf = progressEl.dom.firstChild;
5051 pp = Roo.get(pf.firstChild);
5052 pp.setHeight(pf.offsetHeight);
5060 * Updates the message box body text
5061 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5062 * the XHTML-compliant non-breaking space character '&#160;')
5063 * @return {Roo.MessageBox} This message box
5065 updateText : function(text)
5067 if(!dlg.isVisible() && !opt.width){
5068 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5069 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5071 msgEl.innerHTML = text || ' ';
5073 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5074 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5076 Math.min(opt.width || cw , this.maxWidth),
5077 Math.max(opt.minWidth || this.minWidth, bwidth)
5080 activeTextEl.setWidth(w);
5082 if(dlg.isVisible()){
5083 dlg.fixedcenter = false;
5085 // to big, make it scroll. = But as usual stupid IE does not support
5088 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5089 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5090 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5092 bodyEl.dom.style.height = '';
5093 bodyEl.dom.style.overflowY = '';
5096 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5098 bodyEl.dom.style.overflowX = '';
5101 dlg.setContentSize(w, bodyEl.getHeight());
5102 if(dlg.isVisible()){
5103 dlg.fixedcenter = true;
5109 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5110 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5111 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5112 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5113 * @return {Roo.MessageBox} This message box
5115 updateProgress : function(value, text){
5117 this.updateText(text);
5120 if (pp) { // weird bug on my firefox - for some reason this is not defined
5121 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5122 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5128 * Returns true if the message box is currently displayed
5129 * @return {Boolean} True if the message box is visible, else false
5131 isVisible : function(){
5132 return dlg && dlg.isVisible();
5136 * Hides the message box if it is displayed
5139 if(this.isVisible()){
5145 * Displays a new message box, or reinitializes an existing message box, based on the config options
5146 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5147 * The following config object properties are supported:
5149 Property Type Description
5150 ---------- --------------- ------------------------------------------------------------------------------------
5151 animEl String/Element An id or Element from which the message box should animate as it opens and
5152 closes (defaults to undefined)
5153 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5154 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5155 closable Boolean False to hide the top-right close button (defaults to true). Note that
5156 progress and wait dialogs will ignore this property and always hide the
5157 close button as they can only be closed programmatically.
5158 cls String A custom CSS class to apply to the message box element
5159 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5160 displayed (defaults to 75)
5161 fn Function A callback function to execute after closing the dialog. The arguments to the
5162 function will be btn (the name of the button that was clicked, if applicable,
5163 e.g. "ok"), and text (the value of the active text field, if applicable).
5164 Progress and wait dialogs will ignore this option since they do not respond to
5165 user actions and can only be closed programmatically, so any required function
5166 should be called by the same code after it closes the dialog.
5167 icon String A CSS class that provides a background image to be used as an icon for
5168 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5169 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5170 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5171 modal Boolean False to allow user interaction with the page while the message box is
5172 displayed (defaults to true)
5173 msg String A string that will replace the existing message box body text (defaults
5174 to the XHTML-compliant non-breaking space character ' ')
5175 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5176 progress Boolean True to display a progress bar (defaults to false)
5177 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5178 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5179 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5180 title String The title text
5181 value String The string value to set into the active textbox element if displayed
5182 wait Boolean True to display a progress bar (defaults to false)
5183 width Number The width of the dialog in pixels
5190 msg: 'Please enter your address:',
5192 buttons: Roo.MessageBox.OKCANCEL,
5195 animEl: 'addAddressBtn'
5198 * @param {Object} config Configuration options
5199 * @return {Roo.MessageBox} This message box
5201 show : function(options)
5204 // this causes nightmares if you show one dialog after another
5205 // especially on callbacks..
5207 if(this.isVisible()){
5210 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5211 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5212 Roo.log("New Dialog Message:" + options.msg )
5213 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5214 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5217 var d = this.getDialog();
5219 d.setTitle(opt.title || " ");
5220 d.closeEl.setDisplayed(opt.closable !== false);
5221 activeTextEl = textboxEl;
5222 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5227 textareaEl.setHeight(typeof opt.multiline == "number" ?
5228 opt.multiline : this.defaultTextHeight);
5229 activeTextEl = textareaEl;
5238 progressEl.setDisplayed(opt.progress === true);
5240 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5242 this.updateProgress(0);
5243 activeTextEl.dom.value = opt.value || "";
5245 dlg.setDefaultButton(activeTextEl);
5247 var bs = opt.buttons;
5251 }else if(bs && bs.yes){
5252 db = buttons["yes"];
5254 dlg.setDefaultButton(db);
5256 bwidth = updateButtons(opt.buttons);
5257 this.updateText(opt.msg);
5259 d.el.addClass(opt.cls);
5261 d.proxyDrag = opt.proxyDrag === true;
5262 d.modal = opt.modal !== false;
5263 d.mask = opt.modal !== false ? mask : false;
5265 // force it to the end of the z-index stack so it gets a cursor in FF
5266 document.body.appendChild(dlg.el.dom);
5267 d.animateTarget = null;
5268 d.show(options.animEl);
5274 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5275 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5276 * and closing the message box when the process is complete.
5277 * @param {String} title The title bar text
5278 * @param {String} msg The message box body text
5279 * @return {Roo.MessageBox} This message box
5281 progress : function(title, msg){
5288 minWidth: this.minProgressWidth,
5295 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5296 * If a callback function is passed it will be called after the user clicks the button, and the
5297 * id of the button that was clicked will be passed as the only parameter to the callback
5298 * (could also be the top-right close button).
5299 * @param {String} title The title bar text
5300 * @param {String} msg The message box body text
5301 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5302 * @param {Object} scope (optional) The scope of the callback function
5303 * @return {Roo.MessageBox} This message box
5305 alert : function(title, msg, fn, scope)
5320 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5321 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5322 * You are responsible for closing the message box when the process is complete.
5323 * @param {String} msg The message box body text
5324 * @param {String} title (optional) The title bar text
5325 * @return {Roo.MessageBox} This message box
5327 wait : function(msg, title){
5338 waitTimer = Roo.TaskMgr.start({
5340 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5348 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5349 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5350 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5351 * @param {String} title The title bar text
5352 * @param {String} msg The message box body text
5353 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5354 * @param {Object} scope (optional) The scope of the callback function
5355 * @return {Roo.MessageBox} This message box
5357 confirm : function(title, msg, fn, scope){
5361 buttons: this.YESNO,
5370 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5371 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5372 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5373 * (could also be the top-right close button) and the text that was entered will be passed as the two
5374 * parameters to the callback.
5375 * @param {String} title The title bar text
5376 * @param {String} msg The message box body text
5377 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5378 * @param {Object} scope (optional) The scope of the callback function
5379 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5380 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5381 * @return {Roo.MessageBox} This message box
5383 prompt : function(title, msg, fn, scope, multiline){
5387 buttons: this.OKCANCEL,
5392 multiline: multiline,
5399 * Button config that displays a single OK button
5404 * Button config that displays Yes and No buttons
5407 YESNO : {yes:true, no:true},
5409 * Button config that displays OK and Cancel buttons
5412 OKCANCEL : {ok:true, cancel:true},
5414 * Button config that displays Yes, No and Cancel buttons
5417 YESNOCANCEL : {yes:true, no:true, cancel:true},
5420 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5423 defaultTextHeight : 75,
5425 * The maximum width in pixels of the message box (defaults to 600)
5430 * The minimum width in pixels of the message box (defaults to 100)
5435 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5436 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5439 minProgressWidth : 250,
5441 * An object containing the default button text strings that can be overriden for localized language support.
5442 * Supported properties are: ok, cancel, yes and no.
5443 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5456 * Shorthand for {@link Roo.MessageBox}
5458 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5459 Roo.Msg = Roo.Msg || Roo.MessageBox;
5468 * @class Roo.bootstrap.Navbar
5469 * @extends Roo.bootstrap.Component
5470 * Bootstrap Navbar class
5473 * Create a new Navbar
5474 * @param {Object} config The config object
5478 Roo.bootstrap.Navbar = function(config){
5479 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5483 * @event beforetoggle
5484 * Fire before toggle the menu
5485 * @param {Roo.EventObject} e
5487 "beforetoggle" : true
5491 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5500 getAutoCreate : function(){
5503 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5507 initEvents :function ()
5509 //Roo.log(this.el.select('.navbar-toggle',true));
5510 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5517 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5519 var size = this.el.getSize();
5520 this.maskEl.setSize(size.width, size.height);
5521 this.maskEl.enableDisplayMode("block");
5530 getChildContainer : function()
5532 if (this.el && this.el.select('.collapse').getCount()) {
5533 return this.el.select('.collapse',true).first();
5548 onToggle : function()
5551 if(this.fireEvent('beforetoggle', this) === false){
5554 var ce = this.el.select('.navbar-collapse',true).first();
5556 if (!ce.hasClass('show')) {
5566 * Expand the navbar pulldown
5568 expand : function ()
5571 var ce = this.el.select('.navbar-collapse',true).first();
5572 if (ce.hasClass('collapsing')) {
5575 ce.dom.style.height = '';
5577 ce.addClass('in'); // old...
5578 ce.removeClass('collapse');
5579 ce.addClass('show');
5580 var h = ce.getHeight();
5582 ce.removeClass('show');
5583 // at this point we should be able to see it..
5584 ce.addClass('collapsing');
5586 ce.setHeight(0); // resize it ...
5587 ce.on('transitionend', function() {
5588 //Roo.log('done transition');
5589 ce.removeClass('collapsing');
5590 ce.addClass('show');
5591 ce.removeClass('collapse');
5593 ce.dom.style.height = '';
5594 }, this, { single: true} );
5596 ce.dom.scrollTop = 0;
5599 * Collapse the navbar pulldown
5601 collapse : function()
5603 var ce = this.el.select('.navbar-collapse',true).first();
5605 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5606 // it's collapsed or collapsing..
5609 ce.removeClass('in'); // old...
5610 ce.setHeight(ce.getHeight());
5611 ce.removeClass('show');
5612 ce.addClass('collapsing');
5614 ce.on('transitionend', function() {
5615 ce.dom.style.height = '';
5616 ce.removeClass('collapsing');
5617 ce.addClass('collapse');
5618 }, this, { single: true} );
5638 * @class Roo.bootstrap.NavSimplebar
5639 * @extends Roo.bootstrap.Navbar
5640 * Bootstrap Sidebar class
5642 * @cfg {Boolean} inverse is inverted color
5644 * @cfg {String} type (nav | pills | tabs)
5645 * @cfg {Boolean} arrangement stacked | justified
5646 * @cfg {String} align (left | right) alignment
5648 * @cfg {Boolean} main (true|false) main nav bar? default false
5649 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5651 * @cfg {String} tag (header|footer|nav|div) default is nav
5653 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5657 * Create a new Sidebar
5658 * @param {Object} config The config object
5662 Roo.bootstrap.NavSimplebar = function(config){
5663 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5666 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5682 getAutoCreate : function(){
5686 tag : this.tag || 'div',
5687 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5689 if (['light','white'].indexOf(this.weight) > -1) {
5690 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5692 cfg.cls += ' bg-' + this.weight;
5695 cfg.cls += ' navbar-inverse';
5699 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5701 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5710 cls: 'nav nav-' + this.xtype,
5716 this.type = this.type || 'nav';
5717 if (['tabs','pills'].indexOf(this.type) != -1) {
5718 cfg.cn[0].cls += ' nav-' + this.type
5722 if (this.type!=='nav') {
5723 Roo.log('nav type must be nav/tabs/pills')
5725 cfg.cn[0].cls += ' navbar-nav'
5731 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5732 cfg.cn[0].cls += ' nav-' + this.arrangement;
5736 if (this.align === 'right') {
5737 cfg.cn[0].cls += ' navbar-right';
5762 * navbar-expand-md fixed-top
5766 * @class Roo.bootstrap.NavHeaderbar
5767 * @extends Roo.bootstrap.NavSimplebar
5768 * Bootstrap Sidebar class
5770 * @cfg {String} brand what is brand
5771 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5772 * @cfg {String} brand_href href of the brand
5773 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5774 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5775 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5776 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5779 * Create a new Sidebar
5780 * @param {Object} config The config object
5784 Roo.bootstrap.NavHeaderbar = function(config){
5785 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5789 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5796 desktopCenter : false,
5799 getAutoCreate : function(){
5802 tag: this.nav || 'nav',
5803 cls: 'navbar navbar-expand-md',
5809 if (this.desktopCenter) {
5810 cn.push({cls : 'container', cn : []});
5818 cls: 'navbar-toggle navbar-toggler',
5819 'data-toggle': 'collapse',
5824 html: 'Toggle navigation'
5828 cls: 'icon-bar navbar-toggler-icon'
5841 cn.push( Roo.bootstrap.version == 4 ? btn : {
5843 cls: 'navbar-header',
5852 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5856 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5858 if (['light','white'].indexOf(this.weight) > -1) {
5859 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5861 cfg.cls += ' bg-' + this.weight;
5864 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5865 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5867 // tag can override this..
5869 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5872 if (this.brand !== '') {
5873 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5874 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5876 href: this.brand_href ? this.brand_href : '#',
5877 cls: 'navbar-brand',
5885 cfg.cls += ' main-nav';
5893 getHeaderChildContainer : function()
5895 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5896 return this.el.select('.navbar-header',true).first();
5899 return this.getChildContainer();
5902 getChildContainer : function()
5905 return this.el.select('.roo-navbar-collapse',true).first();
5910 initEvents : function()
5912 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5914 if (this.autohide) {
5919 Roo.get(document).on('scroll',function(e) {
5920 var ns = Roo.get(document).getScroll().top;
5921 var os = prevScroll;
5925 ft.removeClass('slideDown');
5926 ft.addClass('slideUp');
5929 ft.removeClass('slideUp');
5930 ft.addClass('slideDown');
5951 * @class Roo.bootstrap.NavSidebar
5952 * @extends Roo.bootstrap.Navbar
5953 * Bootstrap Sidebar class
5956 * Create a new Sidebar
5957 * @param {Object} config The config object
5961 Roo.bootstrap.NavSidebar = function(config){
5962 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5965 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5967 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5969 getAutoCreate : function(){
5974 cls: 'sidebar sidebar-nav'
5996 * @class Roo.bootstrap.NavGroup
5997 * @extends Roo.bootstrap.Component
5998 * Bootstrap NavGroup class
5999 * @cfg {String} align (left|right)
6000 * @cfg {Boolean} inverse
6001 * @cfg {String} type (nav|pills|tab) default nav
6002 * @cfg {String} navId - reference Id for navbar.
6003 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6006 * Create a new nav group
6007 * @param {Object} config The config object
6010 Roo.bootstrap.NavGroup = function(config){
6011 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6014 Roo.bootstrap.NavGroup.register(this);
6018 * Fires when the active item changes
6019 * @param {Roo.bootstrap.NavGroup} this
6020 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6021 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6028 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6040 getAutoCreate : function()
6042 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6048 if (Roo.bootstrap.version == 4) {
6049 if (['tabs','pills'].indexOf(this.type) != -1) {
6050 cfg.cls += ' nav-' + this.type;
6052 // trying to remove so header bar can right align top?
6053 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6054 // do not use on header bar...
6055 cfg.cls += ' navbar-nav';
6060 if (['tabs','pills'].indexOf(this.type) != -1) {
6061 cfg.cls += ' nav-' + this.type
6063 if (this.type !== 'nav') {
6064 Roo.log('nav type must be nav/tabs/pills')
6066 cfg.cls += ' navbar-nav'
6070 if (this.parent() && this.parent().sidebar) {
6073 cls: 'dashboard-menu sidebar-menu'
6079 if (this.form === true) {
6082 cls: 'navbar-form form-inline'
6084 //nav navbar-right ml-md-auto
6085 if (this.align === 'right') {
6086 cfg.cls += ' navbar-right ml-md-auto';
6088 cfg.cls += ' navbar-left';
6092 if (this.align === 'right') {
6093 cfg.cls += ' navbar-right ml-md-auto';
6095 cfg.cls += ' mr-auto';
6099 cfg.cls += ' navbar-inverse';
6107 * sets the active Navigation item
6108 * @param {Roo.bootstrap.NavItem} the new current navitem
6110 setActiveItem : function(item)
6113 Roo.each(this.navItems, function(v){
6118 v.setActive(false, true);
6125 item.setActive(true, true);
6126 this.fireEvent('changed', this, item, prev);
6131 * gets the active Navigation item
6132 * @return {Roo.bootstrap.NavItem} the current navitem
6134 getActive : function()
6138 Roo.each(this.navItems, function(v){
6149 indexOfNav : function()
6153 Roo.each(this.navItems, function(v,i){
6164 * adds a Navigation item
6165 * @param {Roo.bootstrap.NavItem} the navitem to add
6167 addItem : function(cfg)
6169 if (this.form && Roo.bootstrap.version == 4) {
6172 var cn = new Roo.bootstrap.NavItem(cfg);
6174 cn.parentId = this.id;
6175 cn.onRender(this.el, null);
6179 * register a Navigation item
6180 * @param {Roo.bootstrap.NavItem} the navitem to add
6182 register : function(item)
6184 this.navItems.push( item);
6185 item.navId = this.navId;
6190 * clear all the Navigation item
6193 clearAll : function()
6196 this.el.dom.innerHTML = '';
6199 getNavItem: function(tabId)
6202 Roo.each(this.navItems, function(e) {
6203 if (e.tabId == tabId) {
6213 setActiveNext : function()
6215 var i = this.indexOfNav(this.getActive());
6216 if (i > this.navItems.length) {
6219 this.setActiveItem(this.navItems[i+1]);
6221 setActivePrev : function()
6223 var i = this.indexOfNav(this.getActive());
6227 this.setActiveItem(this.navItems[i-1]);
6229 clearWasActive : function(except) {
6230 Roo.each(this.navItems, function(e) {
6231 if (e.tabId != except.tabId && e.was_active) {
6232 e.was_active = false;
6239 getWasActive : function ()
6242 Roo.each(this.navItems, function(e) {
6257 Roo.apply(Roo.bootstrap.NavGroup, {
6261 * register a Navigation Group
6262 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6264 register : function(navgrp)
6266 this.groups[navgrp.navId] = navgrp;
6270 * fetch a Navigation Group based on the navigation ID
6271 * @param {string} the navgroup to add
6272 * @returns {Roo.bootstrap.NavGroup} the navgroup
6274 get: function(navId) {
6275 if (typeof(this.groups[navId]) == 'undefined') {
6277 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6279 return this.groups[navId] ;
6294 * @class Roo.bootstrap.NavItem
6295 * @extends Roo.bootstrap.Component
6296 * Bootstrap Navbar.NavItem class
6297 * @cfg {String} href link to
6298 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6299 * @cfg {Boolean} button_outline show and outlined button
6300 * @cfg {String} html content of button
6301 * @cfg {String} badge text inside badge
6302 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6303 * @cfg {String} glyphicon DEPRICATED - use fa
6304 * @cfg {String} icon DEPRICATED - use fa
6305 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6306 * @cfg {Boolean} active Is item active
6307 * @cfg {Boolean} disabled Is item disabled
6308 * @cfg {String} linkcls Link Class
6309 * @cfg {Boolean} preventDefault (true | false) default false
6310 * @cfg {String} tabId the tab that this item activates.
6311 * @cfg {String} tagtype (a|span) render as a href or span?
6312 * @cfg {Boolean} animateRef (true|false) link to element default false
6315 * Create a new Navbar Item
6316 * @param {Object} config The config object
6318 Roo.bootstrap.NavItem = function(config){
6319 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6324 * The raw click event for the entire grid.
6325 * @param {Roo.EventObject} e
6330 * Fires when the active item active state changes
6331 * @param {Roo.bootstrap.NavItem} this
6332 * @param {boolean} state the new state
6338 * Fires when scroll to element
6339 * @param {Roo.bootstrap.NavItem} this
6340 * @param {Object} options
6341 * @param {Roo.EventObject} e
6349 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6358 preventDefault : false,
6366 button_outline : false,
6370 getAutoCreate : function(){
6377 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6380 cfg.cls += ' active' ;
6382 if (this.disabled) {
6383 cfg.cls += ' disabled';
6387 if (this.button_weight.length) {
6388 cfg.tag = this.href ? 'a' : 'button';
6389 cfg.html = this.html || '';
6390 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6392 cfg.href = this.href;
6395 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6397 cfg.cls += " nav-html";
6400 // menu .. should add dropdown-menu class - so no need for carat..
6402 if (this.badge !== '') {
6404 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6409 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6413 href : this.href || "#",
6414 html: this.html || '',
6418 if (this.tagtype == 'a') {
6419 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6423 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6424 } else if (this.fa) {
6425 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6426 } else if(this.glyphicon) {
6427 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6429 cfg.cn[0].cls += " nav-html";
6433 cfg.cn[0].html += " <span class='caret'></span>";
6437 if (this.badge !== '') {
6438 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6446 onRender : function(ct, position)
6448 // Roo.log("Call onRender: " + this.xtype);
6449 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6453 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6454 this.navLink = this.el.select('.nav-link',true).first();
6455 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6460 initEvents: function()
6462 if (typeof (this.menu) != 'undefined') {
6463 this.menu.parentType = this.xtype;
6464 this.menu.triggerEl = this.el;
6465 this.menu = this.addxtype(Roo.apply({}, this.menu));
6468 this.el.on('click', this.onClick, this);
6470 //if(this.tagtype == 'span'){
6471 // this.el.select('span',true).on('click', this.onClick, this);
6474 // at this point parent should be available..
6475 this.parent().register(this);
6478 onClick : function(e)
6480 if (e.getTarget('.dropdown-menu-item')) {
6481 // did you click on a menu itemm.... - then don't trigger onclick..
6486 this.preventDefault ||
6489 Roo.log("NavItem - prevent Default?");
6493 if (this.disabled) {
6497 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6498 if (tg && tg.transition) {
6499 Roo.log("waiting for the transitionend");
6505 //Roo.log("fire event clicked");
6506 if(this.fireEvent('click', this, e) === false){
6510 if(this.tagtype == 'span'){
6514 //Roo.log(this.href);
6515 var ael = this.el.select('a',true).first();
6518 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6519 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6520 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6521 return; // ignore... - it's a 'hash' to another page.
6523 Roo.log("NavItem - prevent Default?");
6525 this.scrollToElement(e);
6529 var p = this.parent();
6531 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6532 if (typeof(p.setActiveItem) !== 'undefined') {
6533 p.setActiveItem(this);
6537 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6538 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6539 // remove the collapsed menu expand...
6540 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6544 isActive: function () {
6547 setActive : function(state, fire, is_was_active)
6549 if (this.active && !state && this.navId) {
6550 this.was_active = true;
6551 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6553 nv.clearWasActive(this);
6557 this.active = state;
6560 this.el.removeClass('active');
6561 this.navLink ? this.navLink.removeClass('active') : false;
6562 } else if (!this.el.hasClass('active')) {
6564 this.el.addClass('active');
6565 if (Roo.bootstrap.version == 4 && this.navLink ) {
6566 this.navLink.addClass('active');
6571 this.fireEvent('changed', this, state);
6574 // show a panel if it's registered and related..
6576 if (!this.navId || !this.tabId || !state || is_was_active) {
6580 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6584 var pan = tg.getPanelByName(this.tabId);
6588 // if we can not flip to new panel - go back to old nav highlight..
6589 if (false == tg.showPanel(pan)) {
6590 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6592 var onav = nv.getWasActive();
6594 onav.setActive(true, false, true);
6603 // this should not be here...
6604 setDisabled : function(state)
6606 this.disabled = state;
6608 this.el.removeClass('disabled');
6609 } else if (!this.el.hasClass('disabled')) {
6610 this.el.addClass('disabled');
6616 * Fetch the element to display the tooltip on.
6617 * @return {Roo.Element} defaults to this.el
6619 tooltipEl : function()
6621 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6624 scrollToElement : function(e)
6626 var c = document.body;
6629 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6631 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6632 c = document.documentElement;
6635 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6641 var o = target.calcOffsetsTo(c);
6648 this.fireEvent('scrollto', this, options, e);
6650 Roo.get(c).scrollTo('top', options.value, true);
6655 * Set the HTML (text content) of the item
6656 * @param {string} html content for the nav item
6658 setHtml : function(html)
6661 this.htmlEl.dom.innerHTML = html;
6673 * <span> icon </span>
6674 * <span> text </span>
6675 * <span>badge </span>
6679 * @class Roo.bootstrap.NavSidebarItem
6680 * @extends Roo.bootstrap.NavItem
6681 * Bootstrap Navbar.NavSidebarItem class
6682 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6683 * {Boolean} open is the menu open
6684 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6685 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6686 * {String} buttonSize (sm|md|lg)the extra classes for the button
6687 * {Boolean} showArrow show arrow next to the text (default true)
6689 * Create a new Navbar Button
6690 * @param {Object} config The config object
6692 Roo.bootstrap.NavSidebarItem = function(config){
6693 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6698 * The raw click event for the entire grid.
6699 * @param {Roo.EventObject} e
6704 * Fires when the active item active state changes
6705 * @param {Roo.bootstrap.NavSidebarItem} this
6706 * @param {boolean} state the new state
6714 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6716 badgeWeight : 'default',
6722 buttonWeight : 'default',
6728 getAutoCreate : function(){
6733 href : this.href || '#',
6739 if(this.buttonView){
6742 href : this.href || '#',
6743 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6756 cfg.cls += ' active';
6759 if (this.disabled) {
6760 cfg.cls += ' disabled';
6763 cfg.cls += ' open x-open';
6766 if (this.glyphicon || this.icon) {
6767 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6768 a.cn.push({ tag : 'i', cls : c }) ;
6771 if(!this.buttonView){
6774 html : this.html || ''
6781 if (this.badge !== '') {
6782 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6788 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6791 a.cls += ' dropdown-toggle treeview' ;
6797 initEvents : function()
6799 if (typeof (this.menu) != 'undefined') {
6800 this.menu.parentType = this.xtype;
6801 this.menu.triggerEl = this.el;
6802 this.menu = this.addxtype(Roo.apply({}, this.menu));
6805 this.el.on('click', this.onClick, this);
6807 if(this.badge !== ''){
6808 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6813 onClick : function(e)
6820 if(this.preventDefault){
6824 this.fireEvent('click', this, e);
6827 disable : function()
6829 this.setDisabled(true);
6834 this.setDisabled(false);
6837 setDisabled : function(state)
6839 if(this.disabled == state){
6843 this.disabled = state;
6846 this.el.addClass('disabled');
6850 this.el.removeClass('disabled');
6855 setActive : function(state)
6857 if(this.active == state){
6861 this.active = state;
6864 this.el.addClass('active');
6868 this.el.removeClass('active');
6873 isActive: function ()
6878 setBadge : function(str)
6884 this.badgeEl.dom.innerHTML = str;
6899 Roo.namespace('Roo.bootstrap.breadcrumb');
6903 * @class Roo.bootstrap.breadcrumb.Nav
6904 * @extends Roo.bootstrap.Component
6905 * Bootstrap Breadcrumb Nav Class
6907 * @children Roo.bootstrap.breadcrumb.Item
6910 * Create a new breadcrumb.Nav
6911 * @param {Object} config The config object
6915 Roo.bootstrap.breadcrumb.Nav = function(config){
6916 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6921 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6923 getAutoCreate : function()
6940 initEvents: function()
6942 this.olEl = this.el.select('ol',true).first();
6944 getChildContainer : function()
6960 * @class Roo.bootstrap.breadcrumb.Nav
6961 * @extends Roo.bootstrap.Component
6962 * Bootstrap Breadcrumb Nav Class
6964 * @children Roo.bootstrap.breadcrumb.Component
6965 * @cfg {String} html the content of the link.
6966 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6967 * @cfg {Boolean} active is it active
6971 * Create a new breadcrumb.Nav
6972 * @param {Object} config The config object
6975 Roo.bootstrap.breadcrumb.Item = function(config){
6976 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6981 * The img click event for the img.
6982 * @param {Roo.EventObject} e
6989 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6994 getAutoCreate : function()
6999 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7001 if (this.href !== false) {
7008 cfg.html = this.html;
7014 initEvents: function()
7017 this.el.select('a', true).first().on('click',this.onClick, this)
7021 onClick : function(e)
7024 this.fireEvent('click',this, e);
7037 * @class Roo.bootstrap.Row
7038 * @extends Roo.bootstrap.Component
7039 * Bootstrap Row class (contains columns...)
7043 * @param {Object} config The config object
7046 Roo.bootstrap.Row = function(config){
7047 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7050 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7052 getAutoCreate : function(){
7071 * @class Roo.bootstrap.Pagination
7072 * @extends Roo.bootstrap.Component
7073 * Bootstrap Pagination class
7074 * @cfg {String} size xs | sm | md | lg
7075 * @cfg {Boolean} inverse false | true
7078 * Create a new Pagination
7079 * @param {Object} config The config object
7082 Roo.bootstrap.Pagination = function(config){
7083 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7086 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7092 getAutoCreate : function(){
7098 cfg.cls += ' inverse';
7104 cfg.cls += " " + this.cls;
7122 * @class Roo.bootstrap.PaginationItem
7123 * @extends Roo.bootstrap.Component
7124 * Bootstrap PaginationItem class
7125 * @cfg {String} html text
7126 * @cfg {String} href the link
7127 * @cfg {Boolean} preventDefault (true | false) default true
7128 * @cfg {Boolean} active (true | false) default false
7129 * @cfg {Boolean} disabled default false
7133 * Create a new PaginationItem
7134 * @param {Object} config The config object
7138 Roo.bootstrap.PaginationItem = function(config){
7139 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7144 * The raw click event for the entire grid.
7145 * @param {Roo.EventObject} e
7151 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7155 preventDefault: true,
7160 getAutoCreate : function(){
7166 href : this.href ? this.href : '#',
7167 html : this.html ? this.html : ''
7177 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7181 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7187 initEvents: function() {
7189 this.el.on('click', this.onClick, this);
7192 onClick : function(e)
7194 Roo.log('PaginationItem on click ');
7195 if(this.preventDefault){
7203 this.fireEvent('click', this, e);
7219 * @class Roo.bootstrap.Slider
7220 * @extends Roo.bootstrap.Component
7221 * Bootstrap Slider class
7224 * Create a new Slider
7225 * @param {Object} config The config object
7228 Roo.bootstrap.Slider = function(config){
7229 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7232 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7234 getAutoCreate : function(){
7238 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7242 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7254 * Ext JS Library 1.1.1
7255 * Copyright(c) 2006-2007, Ext JS, LLC.
7257 * Originally Released Under LGPL - original licence link has changed is not relivant.
7260 * <script type="text/javascript">
7265 * @class Roo.grid.ColumnModel
7266 * @extends Roo.util.Observable
7267 * This is the default implementation of a ColumnModel used by the Grid. It defines
7268 * the columns in the grid.
7271 var colModel = new Roo.grid.ColumnModel([
7272 {header: "Ticker", width: 60, sortable: true, locked: true},
7273 {header: "Company Name", width: 150, sortable: true},
7274 {header: "Market Cap.", width: 100, sortable: true},
7275 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7276 {header: "Employees", width: 100, sortable: true, resizable: false}
7281 * The config options listed for this class are options which may appear in each
7282 * individual column definition.
7283 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7285 * @param {Object} config An Array of column config objects. See this class's
7286 * config objects for details.
7288 Roo.grid.ColumnModel = function(config){
7290 * The config passed into the constructor
7292 this.config = config;
7295 // if no id, create one
7296 // if the column does not have a dataIndex mapping,
7297 // map it to the order it is in the config
7298 for(var i = 0, len = config.length; i < len; i++){
7300 if(typeof c.dataIndex == "undefined"){
7303 if(typeof c.renderer == "string"){
7304 c.renderer = Roo.util.Format[c.renderer];
7306 if(typeof c.id == "undefined"){
7309 if(c.editor && c.editor.xtype){
7310 c.editor = Roo.factory(c.editor, Roo.grid);
7312 if(c.editor && c.editor.isFormField){
7313 c.editor = new Roo.grid.GridEditor(c.editor);
7315 this.lookup[c.id] = c;
7319 * The width of columns which have no width specified (defaults to 100)
7322 this.defaultWidth = 100;
7325 * Default sortable of columns which have no sortable specified (defaults to false)
7328 this.defaultSortable = false;
7332 * @event widthchange
7333 * Fires when the width of a column changes.
7334 * @param {ColumnModel} this
7335 * @param {Number} columnIndex The column index
7336 * @param {Number} newWidth The new width
7338 "widthchange": true,
7340 * @event headerchange
7341 * Fires when the text of a header changes.
7342 * @param {ColumnModel} this
7343 * @param {Number} columnIndex The column index
7344 * @param {Number} newText The new header text
7346 "headerchange": true,
7348 * @event hiddenchange
7349 * Fires when a column is hidden or "unhidden".
7350 * @param {ColumnModel} this
7351 * @param {Number} columnIndex The column index
7352 * @param {Boolean} hidden true if hidden, false otherwise
7354 "hiddenchange": true,
7356 * @event columnmoved
7357 * Fires when a column is moved.
7358 * @param {ColumnModel} this
7359 * @param {Number} oldIndex
7360 * @param {Number} newIndex
7362 "columnmoved" : true,
7364 * @event columlockchange
7365 * Fires when a column's locked state is changed
7366 * @param {ColumnModel} this
7367 * @param {Number} colIndex
7368 * @param {Boolean} locked true if locked
7370 "columnlockchange" : true
7372 Roo.grid.ColumnModel.superclass.constructor.call(this);
7374 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7376 * @cfg {String} header The header text to display in the Grid view.
7379 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7380 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7381 * specified, the column's index is used as an index into the Record's data Array.
7384 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7385 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7388 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7389 * Defaults to the value of the {@link #defaultSortable} property.
7390 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7393 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7396 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7399 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7402 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7405 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7406 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7407 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7408 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7411 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7414 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7417 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7420 * @cfg {String} cursor (Optional)
7423 * @cfg {String} tooltip (Optional)
7426 * @cfg {Number} xs (Optional)
7429 * @cfg {Number} sm (Optional)
7432 * @cfg {Number} md (Optional)
7435 * @cfg {Number} lg (Optional)
7438 * Returns the id of the column at the specified index.
7439 * @param {Number} index The column index
7440 * @return {String} the id
7442 getColumnId : function(index){
7443 return this.config[index].id;
7447 * Returns the column for a specified id.
7448 * @param {String} id The column id
7449 * @return {Object} the column
7451 getColumnById : function(id){
7452 return this.lookup[id];
7457 * Returns the column for a specified dataIndex.
7458 * @param {String} dataIndex The column dataIndex
7459 * @return {Object|Boolean} the column or false if not found
7461 getColumnByDataIndex: function(dataIndex){
7462 var index = this.findColumnIndex(dataIndex);
7463 return index > -1 ? this.config[index] : false;
7467 * Returns the index for a specified column id.
7468 * @param {String} id The column id
7469 * @return {Number} the index, or -1 if not found
7471 getIndexById : function(id){
7472 for(var i = 0, len = this.config.length; i < len; i++){
7473 if(this.config[i].id == id){
7481 * Returns the index for a specified column dataIndex.
7482 * @param {String} dataIndex The column dataIndex
7483 * @return {Number} the index, or -1 if not found
7486 findColumnIndex : function(dataIndex){
7487 for(var i = 0, len = this.config.length; i < len; i++){
7488 if(this.config[i].dataIndex == dataIndex){
7496 moveColumn : function(oldIndex, newIndex){
7497 var c = this.config[oldIndex];
7498 this.config.splice(oldIndex, 1);
7499 this.config.splice(newIndex, 0, c);
7500 this.dataMap = null;
7501 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7504 isLocked : function(colIndex){
7505 return this.config[colIndex].locked === true;
7508 setLocked : function(colIndex, value, suppressEvent){
7509 if(this.isLocked(colIndex) == value){
7512 this.config[colIndex].locked = value;
7514 this.fireEvent("columnlockchange", this, colIndex, value);
7518 getTotalLockedWidth : function(){
7520 for(var i = 0; i < this.config.length; i++){
7521 if(this.isLocked(i) && !this.isHidden(i)){
7522 this.totalWidth += this.getColumnWidth(i);
7528 getLockedCount : function(){
7529 for(var i = 0, len = this.config.length; i < len; i++){
7530 if(!this.isLocked(i)){
7535 return this.config.length;
7539 * Returns the number of columns.
7542 getColumnCount : function(visibleOnly){
7543 if(visibleOnly === true){
7545 for(var i = 0, len = this.config.length; i < len; i++){
7546 if(!this.isHidden(i)){
7552 return this.config.length;
7556 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7557 * @param {Function} fn
7558 * @param {Object} scope (optional)
7559 * @return {Array} result
7561 getColumnsBy : function(fn, scope){
7563 for(var i = 0, len = this.config.length; i < len; i++){
7564 var c = this.config[i];
7565 if(fn.call(scope||this, c, i) === true){
7573 * Returns true if the specified column is sortable.
7574 * @param {Number} col The column index
7577 isSortable : function(col){
7578 if(typeof this.config[col].sortable == "undefined"){
7579 return this.defaultSortable;
7581 return this.config[col].sortable;
7585 * Returns the rendering (formatting) function defined for the column.
7586 * @param {Number} col The column index.
7587 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7589 getRenderer : function(col){
7590 if(!this.config[col].renderer){
7591 return Roo.grid.ColumnModel.defaultRenderer;
7593 return this.config[col].renderer;
7597 * Sets the rendering (formatting) function for a column.
7598 * @param {Number} col The column index
7599 * @param {Function} fn The function to use to process the cell's raw data
7600 * to return HTML markup for the grid view. The render function is called with
7601 * the following parameters:<ul>
7602 * <li>Data value.</li>
7603 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7604 * <li>css A CSS style string to apply to the table cell.</li>
7605 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7606 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7607 * <li>Row index</li>
7608 * <li>Column index</li>
7609 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7611 setRenderer : function(col, fn){
7612 this.config[col].renderer = fn;
7616 * Returns the width for the specified column.
7617 * @param {Number} col The column index
7620 getColumnWidth : function(col){
7621 return this.config[col].width * 1 || this.defaultWidth;
7625 * Sets the width for a column.
7626 * @param {Number} col The column index
7627 * @param {Number} width The new width
7629 setColumnWidth : function(col, width, suppressEvent){
7630 this.config[col].width = width;
7631 this.totalWidth = null;
7633 this.fireEvent("widthchange", this, col, width);
7638 * Returns the total width of all columns.
7639 * @param {Boolean} includeHidden True to include hidden column widths
7642 getTotalWidth : function(includeHidden){
7643 if(!this.totalWidth){
7644 this.totalWidth = 0;
7645 for(var i = 0, len = this.config.length; i < len; i++){
7646 if(includeHidden || !this.isHidden(i)){
7647 this.totalWidth += this.getColumnWidth(i);
7651 return this.totalWidth;
7655 * Returns the header for the specified column.
7656 * @param {Number} col The column index
7659 getColumnHeader : function(col){
7660 return this.config[col].header;
7664 * Sets the header for a column.
7665 * @param {Number} col The column index
7666 * @param {String} header The new header
7668 setColumnHeader : function(col, header){
7669 this.config[col].header = header;
7670 this.fireEvent("headerchange", this, col, header);
7674 * Returns the tooltip for the specified column.
7675 * @param {Number} col The column index
7678 getColumnTooltip : function(col){
7679 return this.config[col].tooltip;
7682 * Sets the tooltip for a column.
7683 * @param {Number} col The column index
7684 * @param {String} tooltip The new tooltip
7686 setColumnTooltip : function(col, tooltip){
7687 this.config[col].tooltip = tooltip;
7691 * Returns the dataIndex for the specified column.
7692 * @param {Number} col The column index
7695 getDataIndex : function(col){
7696 return this.config[col].dataIndex;
7700 * Sets the dataIndex for a column.
7701 * @param {Number} col The column index
7702 * @param {Number} dataIndex The new dataIndex
7704 setDataIndex : function(col, dataIndex){
7705 this.config[col].dataIndex = dataIndex;
7711 * Returns true if the cell is editable.
7712 * @param {Number} colIndex The column index
7713 * @param {Number} rowIndex The row index - this is nto actually used..?
7716 isCellEditable : function(colIndex, rowIndex){
7717 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7721 * Returns the editor defined for the cell/column.
7722 * return false or null to disable editing.
7723 * @param {Number} colIndex The column index
7724 * @param {Number} rowIndex The row index
7727 getCellEditor : function(colIndex, rowIndex){
7728 return this.config[colIndex].editor;
7732 * Sets if a column is editable.
7733 * @param {Number} col The column index
7734 * @param {Boolean} editable True if the column is editable
7736 setEditable : function(col, editable){
7737 this.config[col].editable = editable;
7742 * Returns true if the column is hidden.
7743 * @param {Number} colIndex The column index
7746 isHidden : function(colIndex){
7747 return this.config[colIndex].hidden;
7752 * Returns true if the column width cannot be changed
7754 isFixed : function(colIndex){
7755 return this.config[colIndex].fixed;
7759 * Returns true if the column can be resized
7762 isResizable : function(colIndex){
7763 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7766 * Sets if a column is hidden.
7767 * @param {Number} colIndex The column index
7768 * @param {Boolean} hidden True if the column is hidden
7770 setHidden : function(colIndex, hidden){
7771 this.config[colIndex].hidden = hidden;
7772 this.totalWidth = null;
7773 this.fireEvent("hiddenchange", this, colIndex, hidden);
7777 * Sets the editor for a column.
7778 * @param {Number} col The column index
7779 * @param {Object} editor The editor object
7781 setEditor : function(col, editor){
7782 this.config[col].editor = editor;
7786 Roo.grid.ColumnModel.defaultRenderer = function(value)
7788 if(typeof value == "object") {
7791 if(typeof value == "string" && value.length < 1){
7795 return String.format("{0}", value);
7798 // Alias for backwards compatibility
7799 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7802 * Ext JS Library 1.1.1
7803 * Copyright(c) 2006-2007, Ext JS, LLC.
7805 * Originally Released Under LGPL - original licence link has changed is not relivant.
7808 * <script type="text/javascript">
7812 * @class Roo.LoadMask
7813 * A simple utility class for generically masking elements while loading data. If the element being masked has
7814 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7815 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7816 * element's UpdateManager load indicator and will be destroyed after the initial load.
7818 * Create a new LoadMask
7819 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7820 * @param {Object} config The config object
7822 Roo.LoadMask = function(el, config){
7823 this.el = Roo.get(el);
7824 Roo.apply(this, config);
7826 this.store.on('beforeload', this.onBeforeLoad, this);
7827 this.store.on('load', this.onLoad, this);
7828 this.store.on('loadexception', this.onLoadException, this);
7829 this.removeMask = false;
7831 var um = this.el.getUpdateManager();
7832 um.showLoadIndicator = false; // disable the default indicator
7833 um.on('beforeupdate', this.onBeforeLoad, this);
7834 um.on('update', this.onLoad, this);
7835 um.on('failure', this.onLoad, this);
7836 this.removeMask = true;
7840 Roo.LoadMask.prototype = {
7842 * @cfg {Boolean} removeMask
7843 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7844 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7848 * The text to display in a centered loading message box (defaults to 'Loading...')
7852 * @cfg {String} msgCls
7853 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7855 msgCls : 'x-mask-loading',
7858 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7864 * Disables the mask to prevent it from being displayed
7866 disable : function(){
7867 this.disabled = true;
7871 * Enables the mask so that it can be displayed
7873 enable : function(){
7874 this.disabled = false;
7877 onLoadException : function()
7881 if (typeof(arguments[3]) != 'undefined') {
7882 Roo.MessageBox.alert("Error loading",arguments[3]);
7886 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7887 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7894 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7899 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7903 onBeforeLoad : function(){
7905 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7910 destroy : function(){
7912 this.store.un('beforeload', this.onBeforeLoad, this);
7913 this.store.un('load', this.onLoad, this);
7914 this.store.un('loadexception', this.onLoadException, this);
7916 var um = this.el.getUpdateManager();
7917 um.un('beforeupdate', this.onBeforeLoad, this);
7918 um.un('update', this.onLoad, this);
7919 um.un('failure', this.onLoad, this);
7930 * @class Roo.bootstrap.Table
7931 * @extends Roo.bootstrap.Component
7932 * Bootstrap Table class
7933 * @cfg {String} cls table class
7934 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7935 * @cfg {String} bgcolor Specifies the background color for a table
7936 * @cfg {Number} border Specifies whether the table cells should have borders or not
7937 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7938 * @cfg {Number} cellspacing Specifies the space between cells
7939 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7940 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7941 * @cfg {String} sortable Specifies that the table should be sortable
7942 * @cfg {String} summary Specifies a summary of the content of a table
7943 * @cfg {Number} width Specifies the width of a table
7944 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7946 * @cfg {boolean} striped Should the rows be alternative striped
7947 * @cfg {boolean} bordered Add borders to the table
7948 * @cfg {boolean} hover Add hover highlighting
7949 * @cfg {boolean} condensed Format condensed
7950 * @cfg {boolean} responsive Format condensed
7951 * @cfg {Boolean} loadMask (true|false) default false
7952 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7953 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7954 * @cfg {Boolean} rowSelection (true|false) default false
7955 * @cfg {Boolean} cellSelection (true|false) default false
7956 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7957 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7958 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7959 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7963 * Create a new Table
7964 * @param {Object} config The config object
7967 Roo.bootstrap.Table = function(config){
7968 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7973 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7974 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7975 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7976 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7978 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7980 this.sm.grid = this;
7981 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7982 this.sm = this.selModel;
7983 this.sm.xmodule = this.xmodule || false;
7986 if (this.cm && typeof(this.cm.config) == 'undefined') {
7987 this.colModel = new Roo.grid.ColumnModel(this.cm);
7988 this.cm = this.colModel;
7989 this.cm.xmodule = this.xmodule || false;
7992 this.store= Roo.factory(this.store, Roo.data);
7993 this.ds = this.store;
7994 this.ds.xmodule = this.xmodule || false;
7997 if (this.footer && this.store) {
7998 this.footer.dataSource = this.ds;
7999 this.footer = Roo.factory(this.footer);
8006 * Fires when a cell is clicked
8007 * @param {Roo.bootstrap.Table} this
8008 * @param {Roo.Element} el
8009 * @param {Number} rowIndex
8010 * @param {Number} columnIndex
8011 * @param {Roo.EventObject} e
8015 * @event celldblclick
8016 * Fires when a cell is double clicked
8017 * @param {Roo.bootstrap.Table} this
8018 * @param {Roo.Element} el
8019 * @param {Number} rowIndex
8020 * @param {Number} columnIndex
8021 * @param {Roo.EventObject} e
8023 "celldblclick" : true,
8026 * Fires when a row is clicked
8027 * @param {Roo.bootstrap.Table} this
8028 * @param {Roo.Element} el
8029 * @param {Number} rowIndex
8030 * @param {Roo.EventObject} e
8034 * @event rowdblclick
8035 * Fires when a row is double clicked
8036 * @param {Roo.bootstrap.Table} this
8037 * @param {Roo.Element} el
8038 * @param {Number} rowIndex
8039 * @param {Roo.EventObject} e
8041 "rowdblclick" : true,
8044 * Fires when a mouseover occur
8045 * @param {Roo.bootstrap.Table} this
8046 * @param {Roo.Element} el
8047 * @param {Number} rowIndex
8048 * @param {Number} columnIndex
8049 * @param {Roo.EventObject} e
8054 * Fires when a mouseout occur
8055 * @param {Roo.bootstrap.Table} this
8056 * @param {Roo.Element} el
8057 * @param {Number} rowIndex
8058 * @param {Number} columnIndex
8059 * @param {Roo.EventObject} e
8064 * Fires when a row is rendered, so you can change add a style to it.
8065 * @param {Roo.bootstrap.Table} this
8066 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8070 * @event rowsrendered
8071 * Fires when all the rows have been rendered
8072 * @param {Roo.bootstrap.Table} this
8074 'rowsrendered' : true,
8076 * @event contextmenu
8077 * The raw contextmenu event for the entire grid.
8078 * @param {Roo.EventObject} e
8080 "contextmenu" : true,
8082 * @event rowcontextmenu
8083 * Fires when a row is right clicked
8084 * @param {Roo.bootstrap.Table} this
8085 * @param {Number} rowIndex
8086 * @param {Roo.EventObject} e
8088 "rowcontextmenu" : true,
8090 * @event cellcontextmenu
8091 * Fires when a cell is right clicked
8092 * @param {Roo.bootstrap.Table} this
8093 * @param {Number} rowIndex
8094 * @param {Number} cellIndex
8095 * @param {Roo.EventObject} e
8097 "cellcontextmenu" : true,
8099 * @event headercontextmenu
8100 * Fires when a header is right clicked
8101 * @param {Roo.bootstrap.Table} this
8102 * @param {Number} columnIndex
8103 * @param {Roo.EventObject} e
8105 "headercontextmenu" : true
8109 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8135 rowSelection : false,
8136 cellSelection : false,
8139 // Roo.Element - the tbody
8141 // Roo.Element - thead element
8144 container: false, // used by gridpanel...
8150 auto_hide_footer : false,
8152 getAutoCreate : function()
8154 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8161 if (this.scrollBody) {
8162 cfg.cls += ' table-body-fixed';
8165 cfg.cls += ' table-striped';
8169 cfg.cls += ' table-hover';
8171 if (this.bordered) {
8172 cfg.cls += ' table-bordered';
8174 if (this.condensed) {
8175 cfg.cls += ' table-condensed';
8177 if (this.responsive) {
8178 cfg.cls += ' table-responsive';
8182 cfg.cls+= ' ' +this.cls;
8185 // this lot should be simplifed...
8198 ].forEach(function(k) {
8206 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8209 if(this.store || this.cm){
8210 if(this.headerShow){
8211 cfg.cn.push(this.renderHeader());
8214 cfg.cn.push(this.renderBody());
8216 if(this.footerShow){
8217 cfg.cn.push(this.renderFooter());
8219 // where does this come from?
8220 //cfg.cls+= ' TableGrid';
8223 return { cn : [ cfg ] };
8226 initEvents : function()
8228 if(!this.store || !this.cm){
8231 if (this.selModel) {
8232 this.selModel.initEvents();
8236 //Roo.log('initEvents with ds!!!!');
8238 this.mainBody = this.el.select('tbody', true).first();
8239 this.mainHead = this.el.select('thead', true).first();
8240 this.mainFoot = this.el.select('tfoot', true).first();
8246 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8247 e.on('click', _this.sort, _this);
8250 this.mainBody.on("click", this.onClick, this);
8251 this.mainBody.on("dblclick", this.onDblClick, this);
8253 // why is this done????? = it breaks dialogs??
8254 //this.parent().el.setStyle('position', 'relative');
8258 this.footer.parentId = this.id;
8259 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8262 this.el.select('tfoot tr td').first().addClass('hide');
8267 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8270 this.store.on('load', this.onLoad, this);
8271 this.store.on('beforeload', this.onBeforeLoad, this);
8272 this.store.on('update', this.onUpdate, this);
8273 this.store.on('add', this.onAdd, this);
8274 this.store.on("clear", this.clear, this);
8276 this.el.on("contextmenu", this.onContextMenu, this);
8278 this.mainBody.on('scroll', this.onBodyScroll, this);
8280 this.cm.on("headerchange", this.onHeaderChange, this);
8282 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8286 onContextMenu : function(e, t)
8288 this.processEvent("contextmenu", e);
8291 processEvent : function(name, e)
8293 if (name != 'touchstart' ) {
8294 this.fireEvent(name, e);
8297 var t = e.getTarget();
8299 var cell = Roo.get(t);
8305 if(cell.findParent('tfoot', false, true)){
8309 if(cell.findParent('thead', false, true)){
8311 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8312 cell = Roo.get(t).findParent('th', false, true);
8314 Roo.log("failed to find th in thead?");
8315 Roo.log(e.getTarget());
8320 var cellIndex = cell.dom.cellIndex;
8322 var ename = name == 'touchstart' ? 'click' : name;
8323 this.fireEvent("header" + ename, this, cellIndex, e);
8328 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8329 cell = Roo.get(t).findParent('td', false, true);
8331 Roo.log("failed to find th in tbody?");
8332 Roo.log(e.getTarget());
8337 var row = cell.findParent('tr', false, true);
8338 var cellIndex = cell.dom.cellIndex;
8339 var rowIndex = row.dom.rowIndex - 1;
8343 this.fireEvent("row" + name, this, rowIndex, e);
8347 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8353 onMouseover : function(e, el)
8355 var cell = Roo.get(el);
8361 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8362 cell = cell.findParent('td', false, true);
8365 var row = cell.findParent('tr', false, true);
8366 var cellIndex = cell.dom.cellIndex;
8367 var rowIndex = row.dom.rowIndex - 1; // start from 0
8369 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8373 onMouseout : function(e, el)
8375 var cell = Roo.get(el);
8381 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8382 cell = cell.findParent('td', false, true);
8385 var row = cell.findParent('tr', false, true);
8386 var cellIndex = cell.dom.cellIndex;
8387 var rowIndex = row.dom.rowIndex - 1; // start from 0
8389 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8393 onClick : function(e, el)
8395 var cell = Roo.get(el);
8397 if(!cell || (!this.cellSelection && !this.rowSelection)){
8401 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8402 cell = cell.findParent('td', false, true);
8405 if(!cell || typeof(cell) == 'undefined'){
8409 var row = cell.findParent('tr', false, true);
8411 if(!row || typeof(row) == 'undefined'){
8415 var cellIndex = cell.dom.cellIndex;
8416 var rowIndex = this.getRowIndex(row);
8418 // why??? - should these not be based on SelectionModel?
8419 if(this.cellSelection){
8420 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8423 if(this.rowSelection){
8424 this.fireEvent('rowclick', this, row, rowIndex, e);
8430 onDblClick : function(e,el)
8432 var cell = Roo.get(el);
8434 if(!cell || (!this.cellSelection && !this.rowSelection)){
8438 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8439 cell = cell.findParent('td', false, true);
8442 if(!cell || typeof(cell) == 'undefined'){
8446 var row = cell.findParent('tr', false, true);
8448 if(!row || typeof(row) == 'undefined'){
8452 var cellIndex = cell.dom.cellIndex;
8453 var rowIndex = this.getRowIndex(row);
8455 if(this.cellSelection){
8456 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8459 if(this.rowSelection){
8460 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8464 sort : function(e,el)
8466 var col = Roo.get(el);
8468 if(!col.hasClass('sortable')){
8472 var sort = col.attr('sort');
8475 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8479 this.store.sortInfo = {field : sort, direction : dir};
8482 Roo.log("calling footer first");
8483 this.footer.onClick('first');
8486 this.store.load({ params : { start : 0 } });
8490 renderHeader : function()
8498 this.totalWidth = 0;
8500 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8502 var config = cm.config[i];
8506 cls : 'x-hcol-' + i,
8508 html: cm.getColumnHeader(i)
8513 if(typeof(config.sortable) != 'undefined' && config.sortable){
8515 c.html = '<i class="glyphicon"></i>' + c.html;
8518 // could use BS4 hidden-..-down
8520 if(typeof(config.lgHeader) != 'undefined'){
8521 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8524 if(typeof(config.mdHeader) != 'undefined'){
8525 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8528 if(typeof(config.smHeader) != 'undefined'){
8529 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8532 if(typeof(config.xsHeader) != 'undefined'){
8533 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8540 if(typeof(config.tooltip) != 'undefined'){
8541 c.tooltip = config.tooltip;
8544 if(typeof(config.colspan) != 'undefined'){
8545 c.colspan = config.colspan;
8548 if(typeof(config.hidden) != 'undefined' && config.hidden){
8549 c.style += ' display:none;';
8552 if(typeof(config.dataIndex) != 'undefined'){
8553 c.sort = config.dataIndex;
8558 if(typeof(config.align) != 'undefined' && config.align.length){
8559 c.style += ' text-align:' + config.align + ';';
8562 if(typeof(config.width) != 'undefined'){
8563 c.style += ' width:' + config.width + 'px;';
8564 this.totalWidth += config.width;
8566 this.totalWidth += 100; // assume minimum of 100 per column?
8569 if(typeof(config.cls) != 'undefined'){
8570 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8573 ['xs','sm','md','lg'].map(function(size){
8575 if(typeof(config[size]) == 'undefined'){
8579 if (!config[size]) { // 0 = hidden
8580 // BS 4 '0' is treated as hide that column and below.
8581 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8585 c.cls += ' col-' + size + '-' + config[size] + (
8586 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8598 renderBody : function()
8608 colspan : this.cm.getColumnCount()
8618 renderFooter : function()
8628 colspan : this.cm.getColumnCount()
8642 // Roo.log('ds onload');
8647 var ds = this.store;
8649 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8650 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8651 if (_this.store.sortInfo) {
8653 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8654 e.select('i', true).addClass(['glyphicon-arrow-up']);
8657 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8658 e.select('i', true).addClass(['glyphicon-arrow-down']);
8663 var tbody = this.mainBody;
8665 if(ds.getCount() > 0){
8666 ds.data.each(function(d,rowIndex){
8667 var row = this.renderRow(cm, ds, rowIndex);
8669 tbody.createChild(row);
8673 if(row.cellObjects.length){
8674 Roo.each(row.cellObjects, function(r){
8675 _this.renderCellObject(r);
8682 var tfoot = this.el.select('tfoot', true).first();
8684 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8686 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8688 var total = this.ds.getTotalCount();
8690 if(this.footer.pageSize < total){
8691 this.mainFoot.show();
8695 Roo.each(this.el.select('tbody td', true).elements, function(e){
8696 e.on('mouseover', _this.onMouseover, _this);
8699 Roo.each(this.el.select('tbody td', true).elements, function(e){
8700 e.on('mouseout', _this.onMouseout, _this);
8702 this.fireEvent('rowsrendered', this);
8708 onUpdate : function(ds,record)
8710 this.refreshRow(record);
8714 onRemove : function(ds, record, index, isUpdate){
8715 if(isUpdate !== true){
8716 this.fireEvent("beforerowremoved", this, index, record);
8718 var bt = this.mainBody.dom;
8720 var rows = this.el.select('tbody > tr', true).elements;
8722 if(typeof(rows[index]) != 'undefined'){
8723 bt.removeChild(rows[index].dom);
8726 // if(bt.rows[index]){
8727 // bt.removeChild(bt.rows[index]);
8730 if(isUpdate !== true){
8731 //this.stripeRows(index);
8732 //this.syncRowHeights(index, index);
8734 this.fireEvent("rowremoved", this, index, record);
8738 onAdd : function(ds, records, rowIndex)
8740 //Roo.log('on Add called');
8741 // - note this does not handle multiple adding very well..
8742 var bt = this.mainBody.dom;
8743 for (var i =0 ; i < records.length;i++) {
8744 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8745 //Roo.log(records[i]);
8746 //Roo.log(this.store.getAt(rowIndex+i));
8747 this.insertRow(this.store, rowIndex + i, false);
8754 refreshRow : function(record){
8755 var ds = this.store, index;
8756 if(typeof record == 'number'){
8758 record = ds.getAt(index);
8760 index = ds.indexOf(record);
8762 return; // should not happen - but seems to
8765 this.insertRow(ds, index, true);
8767 this.onRemove(ds, record, index+1, true);
8769 //this.syncRowHeights(index, index);
8771 this.fireEvent("rowupdated", this, index, record);
8774 insertRow : function(dm, rowIndex, isUpdate){
8777 this.fireEvent("beforerowsinserted", this, rowIndex);
8779 //var s = this.getScrollState();
8780 var row = this.renderRow(this.cm, this.store, rowIndex);
8781 // insert before rowIndex..
8782 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8786 if(row.cellObjects.length){
8787 Roo.each(row.cellObjects, function(r){
8788 _this.renderCellObject(r);
8793 this.fireEvent("rowsinserted", this, rowIndex);
8794 //this.syncRowHeights(firstRow, lastRow);
8795 //this.stripeRows(firstRow);
8802 getRowDom : function(rowIndex)
8804 var rows = this.el.select('tbody > tr', true).elements;
8806 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8809 // returns the object tree for a tr..
8812 renderRow : function(cm, ds, rowIndex)
8814 var d = ds.getAt(rowIndex);
8818 cls : 'x-row-' + rowIndex,
8822 var cellObjects = [];
8824 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8825 var config = cm.config[i];
8827 var renderer = cm.getRenderer(i);
8831 if(typeof(renderer) !== 'undefined'){
8832 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8834 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8835 // and are rendered into the cells after the row is rendered - using the id for the element.
8837 if(typeof(value) === 'object'){
8847 rowIndex : rowIndex,
8852 this.fireEvent('rowclass', this, rowcfg);
8856 cls : rowcfg.rowClass + ' x-col-' + i,
8858 html: (typeof(value) === 'object') ? '' : value
8865 if(typeof(config.colspan) != 'undefined'){
8866 td.colspan = config.colspan;
8869 if(typeof(config.hidden) != 'undefined' && config.hidden){
8870 td.style += ' display:none;';
8873 if(typeof(config.align) != 'undefined' && config.align.length){
8874 td.style += ' text-align:' + config.align + ';';
8876 if(typeof(config.valign) != 'undefined' && config.valign.length){
8877 td.style += ' vertical-align:' + config.valign + ';';
8880 if(typeof(config.width) != 'undefined'){
8881 td.style += ' width:' + config.width + 'px;';
8884 if(typeof(config.cursor) != 'undefined'){
8885 td.style += ' cursor:' + config.cursor + ';';
8888 if(typeof(config.cls) != 'undefined'){
8889 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8892 ['xs','sm','md','lg'].map(function(size){
8894 if(typeof(config[size]) == 'undefined'){
8900 if (!config[size]) { // 0 = hidden
8901 // BS 4 '0' is treated as hide that column and below.
8902 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8906 td.cls += ' col-' + size + '-' + config[size] + (
8907 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8917 row.cellObjects = cellObjects;
8925 onBeforeLoad : function()
8934 this.el.select('tbody', true).first().dom.innerHTML = '';
8937 * Show or hide a row.
8938 * @param {Number} rowIndex to show or hide
8939 * @param {Boolean} state hide
8941 setRowVisibility : function(rowIndex, state)
8943 var bt = this.mainBody.dom;
8945 var rows = this.el.select('tbody > tr', true).elements;
8947 if(typeof(rows[rowIndex]) == 'undefined'){
8950 rows[rowIndex].dom.style.display = state ? '' : 'none';
8954 getSelectionModel : function(){
8956 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8958 return this.selModel;
8961 * Render the Roo.bootstrap object from renderder
8963 renderCellObject : function(r)
8967 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8969 var t = r.cfg.render(r.container);
8972 Roo.each(r.cfg.cn, function(c){
8974 container: t.getChildContainer(),
8977 _this.renderCellObject(child);
8982 getRowIndex : function(row)
8986 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8997 * Returns the grid's underlying element = used by panel.Grid
8998 * @return {Element} The element
9000 getGridEl : function(){
9004 * Forces a resize - used by panel.Grid
9005 * @return {Element} The element
9007 autoSize : function()
9009 //var ctr = Roo.get(this.container.dom.parentElement);
9010 var ctr = Roo.get(this.el.dom);
9012 var thd = this.getGridEl().select('thead',true).first();
9013 var tbd = this.getGridEl().select('tbody', true).first();
9014 var tfd = this.getGridEl().select('tfoot', true).first();
9016 var cw = ctr.getWidth();
9017 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9021 tbd.setWidth(ctr.getWidth());
9022 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9023 // this needs fixing for various usage - currently only hydra job advers I think..
9025 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9027 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9030 cw = Math.max(cw, this.totalWidth);
9031 this.getGridEl().select('tbody tr',true).setWidth(cw);
9033 // resize 'expandable coloumn?
9035 return; // we doe not have a view in this design..
9038 onBodyScroll: function()
9040 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9042 this.mainHead.setStyle({
9043 'position' : 'relative',
9044 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9050 var scrollHeight = this.mainBody.dom.scrollHeight;
9052 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9054 var height = this.mainBody.getHeight();
9056 if(scrollHeight - height == scrollTop) {
9058 var total = this.ds.getTotalCount();
9060 if(this.footer.cursor + this.footer.pageSize < total){
9062 this.footer.ds.load({
9064 start : this.footer.cursor + this.footer.pageSize,
9065 limit : this.footer.pageSize
9075 onHeaderChange : function()
9077 var header = this.renderHeader();
9078 var table = this.el.select('table', true).first();
9080 this.mainHead.remove();
9081 this.mainHead = table.createChild(header, this.mainBody, false);
9084 onHiddenChange : function(colModel, colIndex, hidden)
9086 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9087 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9089 this.CSS.updateRule(thSelector, "display", "");
9090 this.CSS.updateRule(tdSelector, "display", "");
9093 this.CSS.updateRule(thSelector, "display", "none");
9094 this.CSS.updateRule(tdSelector, "display", "none");
9097 this.onHeaderChange();
9101 setColumnWidth: function(col_index, width)
9103 // width = "md-2 xs-2..."
9104 if(!this.colModel.config[col_index]) {
9108 var w = width.split(" ");
9110 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9112 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9115 for(var j = 0; j < w.length; j++) {
9121 var size_cls = w[j].split("-");
9123 if(!Number.isInteger(size_cls[1] * 1)) {
9127 if(!this.colModel.config[col_index][size_cls[0]]) {
9131 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9135 h_row[0].classList.replace(
9136 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9137 "col-"+size_cls[0]+"-"+size_cls[1]
9140 for(var i = 0; i < rows.length; i++) {
9142 var size_cls = w[j].split("-");
9144 if(!Number.isInteger(size_cls[1] * 1)) {
9148 if(!this.colModel.config[col_index][size_cls[0]]) {
9152 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9156 rows[i].classList.replace(
9157 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9158 "col-"+size_cls[0]+"-"+size_cls[1]
9162 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9177 * @class Roo.bootstrap.TableCell
9178 * @extends Roo.bootstrap.Component
9179 * Bootstrap TableCell class
9180 * @cfg {String} html cell contain text
9181 * @cfg {String} cls cell class
9182 * @cfg {String} tag cell tag (td|th) default td
9183 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9184 * @cfg {String} align Aligns the content in a cell
9185 * @cfg {String} axis Categorizes cells
9186 * @cfg {String} bgcolor Specifies the background color of a cell
9187 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9188 * @cfg {Number} colspan Specifies the number of columns a cell should span
9189 * @cfg {String} headers Specifies one or more header cells a cell is related to
9190 * @cfg {Number} height Sets the height of a cell
9191 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9192 * @cfg {Number} rowspan Sets the number of rows a cell should span
9193 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9194 * @cfg {String} valign Vertical aligns the content in a cell
9195 * @cfg {Number} width Specifies the width of a cell
9198 * Create a new TableCell
9199 * @param {Object} config The config object
9202 Roo.bootstrap.TableCell = function(config){
9203 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9206 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9226 getAutoCreate : function(){
9227 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9247 cfg.align=this.align
9253 cfg.bgcolor=this.bgcolor
9256 cfg.charoff=this.charoff
9259 cfg.colspan=this.colspan
9262 cfg.headers=this.headers
9265 cfg.height=this.height
9268 cfg.nowrap=this.nowrap
9271 cfg.rowspan=this.rowspan
9274 cfg.scope=this.scope
9277 cfg.valign=this.valign
9280 cfg.width=this.width
9299 * @class Roo.bootstrap.TableRow
9300 * @extends Roo.bootstrap.Component
9301 * Bootstrap TableRow class
9302 * @cfg {String} cls row class
9303 * @cfg {String} align Aligns the content in a table row
9304 * @cfg {String} bgcolor Specifies a background color for a table row
9305 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9306 * @cfg {String} valign Vertical aligns the content in a table row
9309 * Create a new TableRow
9310 * @param {Object} config The config object
9313 Roo.bootstrap.TableRow = function(config){
9314 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9317 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9325 getAutoCreate : function(){
9326 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9336 cfg.align = this.align;
9339 cfg.bgcolor = this.bgcolor;
9342 cfg.charoff = this.charoff;
9345 cfg.valign = this.valign;
9363 * @class Roo.bootstrap.TableBody
9364 * @extends Roo.bootstrap.Component
9365 * Bootstrap TableBody class
9366 * @cfg {String} cls element class
9367 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9368 * @cfg {String} align Aligns the content inside the element
9369 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9370 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9373 * Create a new TableBody
9374 * @param {Object} config The config object
9377 Roo.bootstrap.TableBody = function(config){
9378 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9381 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9389 getAutoCreate : function(){
9390 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9404 cfg.align = this.align;
9407 cfg.charoff = this.charoff;
9410 cfg.valign = this.valign;
9417 // initEvents : function()
9424 // this.store = Roo.factory(this.store, Roo.data);
9425 // this.store.on('load', this.onLoad, this);
9427 // this.store.load();
9431 // onLoad: function ()
9433 // this.fireEvent('load', this);
9443 * Ext JS Library 1.1.1
9444 * Copyright(c) 2006-2007, Ext JS, LLC.
9446 * Originally Released Under LGPL - original licence link has changed is not relivant.
9449 * <script type="text/javascript">
9452 // as we use this in bootstrap.
9453 Roo.namespace('Roo.form');
9455 * @class Roo.form.Action
9456 * Internal Class used to handle form actions
9458 * @param {Roo.form.BasicForm} el The form element or its id
9459 * @param {Object} config Configuration options
9464 // define the action interface
9465 Roo.form.Action = function(form, options){
9467 this.options = options || {};
9470 * Client Validation Failed
9473 Roo.form.Action.CLIENT_INVALID = 'client';
9475 * Server Validation Failed
9478 Roo.form.Action.SERVER_INVALID = 'server';
9480 * Connect to Server Failed
9483 Roo.form.Action.CONNECT_FAILURE = 'connect';
9485 * Reading Data from Server Failed
9488 Roo.form.Action.LOAD_FAILURE = 'load';
9490 Roo.form.Action.prototype = {
9492 failureType : undefined,
9493 response : undefined,
9497 run : function(options){
9502 success : function(response){
9507 handleResponse : function(response){
9511 // default connection failure
9512 failure : function(response){
9514 this.response = response;
9515 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9516 this.form.afterAction(this, false);
9519 processResponse : function(response){
9520 this.response = response;
9521 if(!response.responseText){
9524 this.result = this.handleResponse(response);
9528 // utility functions used internally
9529 getUrl : function(appendParams){
9530 var url = this.options.url || this.form.url || this.form.el.dom.action;
9532 var p = this.getParams();
9534 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9540 getMethod : function(){
9541 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9544 getParams : function(){
9545 var bp = this.form.baseParams;
9546 var p = this.options.params;
9548 if(typeof p == "object"){
9549 p = Roo.urlEncode(Roo.applyIf(p, bp));
9550 }else if(typeof p == 'string' && bp){
9551 p += '&' + Roo.urlEncode(bp);
9554 p = Roo.urlEncode(bp);
9559 createCallback : function(){
9561 success: this.success,
9562 failure: this.failure,
9564 timeout: (this.form.timeout*1000),
9565 upload: this.form.fileUpload ? this.success : undefined
9570 Roo.form.Action.Submit = function(form, options){
9571 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9574 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9577 haveProgress : false,
9578 uploadComplete : false,
9580 // uploadProgress indicator.
9581 uploadProgress : function()
9583 if (!this.form.progressUrl) {
9587 if (!this.haveProgress) {
9588 Roo.MessageBox.progress("Uploading", "Uploading");
9590 if (this.uploadComplete) {
9591 Roo.MessageBox.hide();
9595 this.haveProgress = true;
9597 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9599 var c = new Roo.data.Connection();
9601 url : this.form.progressUrl,
9606 success : function(req){
9607 //console.log(data);
9611 rdata = Roo.decode(req.responseText)
9613 Roo.log("Invalid data from server..");
9617 if (!rdata || !rdata.success) {
9619 Roo.MessageBox.alert(Roo.encode(rdata));
9622 var data = rdata.data;
9624 if (this.uploadComplete) {
9625 Roo.MessageBox.hide();
9630 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9631 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9634 this.uploadProgress.defer(2000,this);
9637 failure: function(data) {
9638 Roo.log('progress url failed ');
9649 // run get Values on the form, so it syncs any secondary forms.
9650 this.form.getValues();
9652 var o = this.options;
9653 var method = this.getMethod();
9654 var isPost = method == 'POST';
9655 if(o.clientValidation === false || this.form.isValid()){
9657 if (this.form.progressUrl) {
9658 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9659 (new Date() * 1) + '' + Math.random());
9664 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9665 form:this.form.el.dom,
9666 url:this.getUrl(!isPost),
9668 params:isPost ? this.getParams() : null,
9669 isUpload: this.form.fileUpload,
9670 formData : this.form.formData
9673 this.uploadProgress();
9675 }else if (o.clientValidation !== false){ // client validation failed
9676 this.failureType = Roo.form.Action.CLIENT_INVALID;
9677 this.form.afterAction(this, false);
9681 success : function(response)
9683 this.uploadComplete= true;
9684 if (this.haveProgress) {
9685 Roo.MessageBox.hide();
9689 var result = this.processResponse(response);
9690 if(result === true || result.success){
9691 this.form.afterAction(this, true);
9695 this.form.markInvalid(result.errors);
9696 this.failureType = Roo.form.Action.SERVER_INVALID;
9698 this.form.afterAction(this, false);
9700 failure : function(response)
9702 this.uploadComplete= true;
9703 if (this.haveProgress) {
9704 Roo.MessageBox.hide();
9707 this.response = response;
9708 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9709 this.form.afterAction(this, false);
9712 handleResponse : function(response){
9713 if(this.form.errorReader){
9714 var rs = this.form.errorReader.read(response);
9717 for(var i = 0, len = rs.records.length; i < len; i++) {
9718 var r = rs.records[i];
9722 if(errors.length < 1){
9726 success : rs.success,
9732 ret = Roo.decode(response.responseText);
9736 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9746 Roo.form.Action.Load = function(form, options){
9747 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9748 this.reader = this.form.reader;
9751 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9756 Roo.Ajax.request(Roo.apply(
9757 this.createCallback(), {
9758 method:this.getMethod(),
9759 url:this.getUrl(false),
9760 params:this.getParams()
9764 success : function(response){
9766 var result = this.processResponse(response);
9767 if(result === true || !result.success || !result.data){
9768 this.failureType = Roo.form.Action.LOAD_FAILURE;
9769 this.form.afterAction(this, false);
9772 this.form.clearInvalid();
9773 this.form.setValues(result.data);
9774 this.form.afterAction(this, true);
9777 handleResponse : function(response){
9778 if(this.form.reader){
9779 var rs = this.form.reader.read(response);
9780 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9782 success : rs.success,
9786 return Roo.decode(response.responseText);
9790 Roo.form.Action.ACTION_TYPES = {
9791 'load' : Roo.form.Action.Load,
9792 'submit' : Roo.form.Action.Submit
9801 * @class Roo.bootstrap.Form
9802 * @extends Roo.bootstrap.Component
9803 * Bootstrap Form class
9804 * @cfg {String} method GET | POST (default POST)
9805 * @cfg {String} labelAlign top | left (default top)
9806 * @cfg {String} align left | right - for navbars
9807 * @cfg {Boolean} loadMask load mask when submit (default true)
9812 * @param {Object} config The config object
9816 Roo.bootstrap.Form = function(config){
9818 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9820 Roo.bootstrap.Form.popover.apply();
9824 * @event clientvalidation
9825 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9826 * @param {Form} this
9827 * @param {Boolean} valid true if the form has passed client-side validation
9829 clientvalidation: true,
9831 * @event beforeaction
9832 * Fires before any action is performed. Return false to cancel the action.
9833 * @param {Form} this
9834 * @param {Action} action The action to be performed
9838 * @event actionfailed
9839 * Fires when an action fails.
9840 * @param {Form} this
9841 * @param {Action} action The action that failed
9843 actionfailed : true,
9845 * @event actioncomplete
9846 * Fires when an action is completed.
9847 * @param {Form} this
9848 * @param {Action} action The action that completed
9850 actioncomplete : true
9854 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9857 * @cfg {String} method
9858 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9863 * The URL to use for form actions if one isn't supplied in the action options.
9866 * @cfg {Boolean} fileUpload
9867 * Set to true if this form is a file upload.
9871 * @cfg {Object} baseParams
9872 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9876 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9880 * @cfg {Sting} align (left|right) for navbar forms
9885 activeAction : null,
9888 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9889 * element by passing it or its id or mask the form itself by passing in true.
9892 waitMsgTarget : false,
9897 * @cfg {Boolean} errorMask (true|false) default false
9902 * @cfg {Number} maskOffset Default 100
9907 * @cfg {Boolean} maskBody
9911 getAutoCreate : function(){
9915 method : this.method || 'POST',
9916 id : this.id || Roo.id(),
9919 if (this.parent().xtype.match(/^Nav/)) {
9920 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9924 if (this.labelAlign == 'left' ) {
9925 cfg.cls += ' form-horizontal';
9931 initEvents : function()
9933 this.el.on('submit', this.onSubmit, this);
9934 // this was added as random key presses on the form where triggering form submit.
9935 this.el.on('keypress', function(e) {
9936 if (e.getCharCode() != 13) {
9939 // we might need to allow it for textareas.. and some other items.
9940 // check e.getTarget().
9942 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9946 Roo.log("keypress blocked");
9954 onSubmit : function(e){
9959 * Returns true if client-side validation on the form is successful.
9962 isValid : function(){
9963 var items = this.getItems();
9967 items.each(function(f){
9973 Roo.log('invalid field: ' + f.name);
9977 if(!target && f.el.isVisible(true)){
9983 if(this.errorMask && !valid){
9984 Roo.bootstrap.Form.popover.mask(this, target);
9991 * Returns true if any fields in this form have changed since their original load.
9994 isDirty : function(){
9996 var items = this.getItems();
9997 items.each(function(f){
10007 * Performs a predefined action (submit or load) or custom actions you define on this form.
10008 * @param {String} actionName The name of the action type
10009 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10010 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10011 * accept other config options):
10013 Property Type Description
10014 ---------------- --------------- ----------------------------------------------------------------------------------
10015 url String The url for the action (defaults to the form's url)
10016 method String The form method to use (defaults to the form's method, or POST if not defined)
10017 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10018 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10019 validate the form on the client (defaults to false)
10021 * @return {BasicForm} this
10023 doAction : function(action, options){
10024 if(typeof action == 'string'){
10025 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10027 if(this.fireEvent('beforeaction', this, action) !== false){
10028 this.beforeAction(action);
10029 action.run.defer(100, action);
10035 beforeAction : function(action){
10036 var o = action.options;
10041 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10043 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10046 // not really supported yet.. ??
10048 //if(this.waitMsgTarget === true){
10049 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10050 //}else if(this.waitMsgTarget){
10051 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10052 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10054 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10060 afterAction : function(action, success){
10061 this.activeAction = null;
10062 var o = action.options;
10067 Roo.get(document.body).unmask();
10073 //if(this.waitMsgTarget === true){
10074 // this.el.unmask();
10075 //}else if(this.waitMsgTarget){
10076 // this.waitMsgTarget.unmask();
10078 // Roo.MessageBox.updateProgress(1);
10079 // Roo.MessageBox.hide();
10086 Roo.callback(o.success, o.scope, [this, action]);
10087 this.fireEvent('actioncomplete', this, action);
10091 // failure condition..
10092 // we have a scenario where updates need confirming.
10093 // eg. if a locking scenario exists..
10094 // we look for { errors : { needs_confirm : true }} in the response.
10096 (typeof(action.result) != 'undefined') &&
10097 (typeof(action.result.errors) != 'undefined') &&
10098 (typeof(action.result.errors.needs_confirm) != 'undefined')
10101 Roo.log("not supported yet");
10104 Roo.MessageBox.confirm(
10105 "Change requires confirmation",
10106 action.result.errorMsg,
10111 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10121 Roo.callback(o.failure, o.scope, [this, action]);
10122 // show an error message if no failed handler is set..
10123 if (!this.hasListener('actionfailed')) {
10124 Roo.log("need to add dialog support");
10126 Roo.MessageBox.alert("Error",
10127 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10128 action.result.errorMsg :
10129 "Saving Failed, please check your entries or try again"
10134 this.fireEvent('actionfailed', this, action);
10139 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10140 * @param {String} id The value to search for
10143 findField : function(id){
10144 var items = this.getItems();
10145 var field = items.get(id);
10147 items.each(function(f){
10148 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10155 return field || null;
10158 * Mark fields in this form invalid in bulk.
10159 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10160 * @return {BasicForm} this
10162 markInvalid : function(errors){
10163 if(errors instanceof Array){
10164 for(var i = 0, len = errors.length; i < len; i++){
10165 var fieldError = errors[i];
10166 var f = this.findField(fieldError.id);
10168 f.markInvalid(fieldError.msg);
10174 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10175 field.markInvalid(errors[id]);
10179 //Roo.each(this.childForms || [], function (f) {
10180 // f.markInvalid(errors);
10187 * Set values for fields in this form in bulk.
10188 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10189 * @return {BasicForm} this
10191 setValues : function(values){
10192 if(values instanceof Array){ // array of objects
10193 for(var i = 0, len = values.length; i < len; i++){
10195 var f = this.findField(v.id);
10197 f.setValue(v.value);
10198 if(this.trackResetOnLoad){
10199 f.originalValue = f.getValue();
10203 }else{ // object hash
10206 if(typeof values[id] != 'function' && (field = this.findField(id))){
10208 if (field.setFromData &&
10209 field.valueField &&
10210 field.displayField &&
10211 // combos' with local stores can
10212 // be queried via setValue()
10213 // to set their value..
10214 (field.store && !field.store.isLocal)
10218 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10219 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10220 field.setFromData(sd);
10222 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10224 field.setFromData(values);
10227 field.setValue(values[id]);
10231 if(this.trackResetOnLoad){
10232 field.originalValue = field.getValue();
10238 //Roo.each(this.childForms || [], function (f) {
10239 // f.setValues(values);
10246 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10247 * they are returned as an array.
10248 * @param {Boolean} asString
10251 getValues : function(asString){
10252 //if (this.childForms) {
10253 // copy values from the child forms
10254 // Roo.each(this.childForms, function (f) {
10255 // this.setValues(f.getValues());
10261 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10262 if(asString === true){
10265 return Roo.urlDecode(fs);
10269 * Returns the fields in this form as an object with key/value pairs.
10270 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10273 getFieldValues : function(with_hidden)
10275 var items = this.getItems();
10277 items.each(function(f){
10279 if (!f.getName()) {
10283 var v = f.getValue();
10285 if (f.inputType =='radio') {
10286 if (typeof(ret[f.getName()]) == 'undefined') {
10287 ret[f.getName()] = ''; // empty..
10290 if (!f.el.dom.checked) {
10294 v = f.el.dom.value;
10298 if(f.xtype == 'MoneyField'){
10299 ret[f.currencyName] = f.getCurrency();
10302 // not sure if this supported any more..
10303 if ((typeof(v) == 'object') && f.getRawValue) {
10304 v = f.getRawValue() ; // dates..
10306 // combo boxes where name != hiddenName...
10307 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10308 ret[f.name] = f.getRawValue();
10310 ret[f.getName()] = v;
10317 * Clears all invalid messages in this form.
10318 * @return {BasicForm} this
10320 clearInvalid : function(){
10321 var items = this.getItems();
10323 items.each(function(f){
10331 * Resets this form.
10332 * @return {BasicForm} this
10334 reset : function(){
10335 var items = this.getItems();
10336 items.each(function(f){
10340 Roo.each(this.childForms || [], function (f) {
10348 getItems : function()
10350 var r=new Roo.util.MixedCollection(false, function(o){
10351 return o.id || (o.id = Roo.id());
10353 var iter = function(el) {
10360 Roo.each(el.items,function(e) {
10369 hideFields : function(items)
10371 Roo.each(items, function(i){
10373 var f = this.findField(i);
10384 showFields : function(items)
10386 Roo.each(items, function(i){
10388 var f = this.findField(i);
10401 Roo.apply(Roo.bootstrap.Form, {
10417 intervalID : false,
10423 if(this.isApplied){
10428 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10429 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10430 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10431 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10434 this.maskEl.top.enableDisplayMode("block");
10435 this.maskEl.left.enableDisplayMode("block");
10436 this.maskEl.bottom.enableDisplayMode("block");
10437 this.maskEl.right.enableDisplayMode("block");
10439 this.toolTip = new Roo.bootstrap.Tooltip({
10440 cls : 'roo-form-error-popover',
10442 'left' : ['r-l', [-2,0], 'right'],
10443 'right' : ['l-r', [2,0], 'left'],
10444 'bottom' : ['tl-bl', [0,2], 'top'],
10445 'top' : [ 'bl-tl', [0,-2], 'bottom']
10449 this.toolTip.render(Roo.get(document.body));
10451 this.toolTip.el.enableDisplayMode("block");
10453 Roo.get(document.body).on('click', function(){
10457 Roo.get(document.body).on('touchstart', function(){
10461 this.isApplied = true
10464 mask : function(form, target)
10468 this.target = target;
10470 if(!this.form.errorMask || !target.el){
10474 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10476 Roo.log(scrollable);
10478 var ot = this.target.el.calcOffsetsTo(scrollable);
10480 var scrollTo = ot[1] - this.form.maskOffset;
10482 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10484 scrollable.scrollTo('top', scrollTo);
10486 var box = this.target.el.getBox();
10488 var zIndex = Roo.bootstrap.Modal.zIndex++;
10491 this.maskEl.top.setStyle('position', 'absolute');
10492 this.maskEl.top.setStyle('z-index', zIndex);
10493 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10494 this.maskEl.top.setLeft(0);
10495 this.maskEl.top.setTop(0);
10496 this.maskEl.top.show();
10498 this.maskEl.left.setStyle('position', 'absolute');
10499 this.maskEl.left.setStyle('z-index', zIndex);
10500 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10501 this.maskEl.left.setLeft(0);
10502 this.maskEl.left.setTop(box.y - this.padding);
10503 this.maskEl.left.show();
10505 this.maskEl.bottom.setStyle('position', 'absolute');
10506 this.maskEl.bottom.setStyle('z-index', zIndex);
10507 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10508 this.maskEl.bottom.setLeft(0);
10509 this.maskEl.bottom.setTop(box.bottom + this.padding);
10510 this.maskEl.bottom.show();
10512 this.maskEl.right.setStyle('position', 'absolute');
10513 this.maskEl.right.setStyle('z-index', zIndex);
10514 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10515 this.maskEl.right.setLeft(box.right + this.padding);
10516 this.maskEl.right.setTop(box.y - this.padding);
10517 this.maskEl.right.show();
10519 this.toolTip.bindEl = this.target.el;
10521 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10523 var tip = this.target.blankText;
10525 if(this.target.getValue() !== '' ) {
10527 if (this.target.invalidText.length) {
10528 tip = this.target.invalidText;
10529 } else if (this.target.regexText.length){
10530 tip = this.target.regexText;
10534 this.toolTip.show(tip);
10536 this.intervalID = window.setInterval(function() {
10537 Roo.bootstrap.Form.popover.unmask();
10540 window.onwheel = function(){ return false;};
10542 (function(){ this.isMasked = true; }).defer(500, this);
10546 unmask : function()
10548 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10552 this.maskEl.top.setStyle('position', 'absolute');
10553 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10554 this.maskEl.top.hide();
10556 this.maskEl.left.setStyle('position', 'absolute');
10557 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10558 this.maskEl.left.hide();
10560 this.maskEl.bottom.setStyle('position', 'absolute');
10561 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10562 this.maskEl.bottom.hide();
10564 this.maskEl.right.setStyle('position', 'absolute');
10565 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10566 this.maskEl.right.hide();
10568 this.toolTip.hide();
10570 this.toolTip.el.hide();
10572 window.onwheel = function(){ return true;};
10574 if(this.intervalID){
10575 window.clearInterval(this.intervalID);
10576 this.intervalID = false;
10579 this.isMasked = false;
10589 * Ext JS Library 1.1.1
10590 * Copyright(c) 2006-2007, Ext JS, LLC.
10592 * Originally Released Under LGPL - original licence link has changed is not relivant.
10595 * <script type="text/javascript">
10598 * @class Roo.form.VTypes
10599 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10602 Roo.form.VTypes = function(){
10603 // closure these in so they are only created once.
10604 var alpha = /^[a-zA-Z_]+$/;
10605 var alphanum = /^[a-zA-Z0-9_]+$/;
10606 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10607 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10609 // All these messages and functions are configurable
10612 * The function used to validate email addresses
10613 * @param {String} value The email address
10615 'email' : function(v){
10616 return email.test(v);
10619 * The error text to display when the email validation function returns false
10622 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10624 * The keystroke filter mask to be applied on email input
10627 'emailMask' : /[a-z0-9_\.\-@]/i,
10630 * The function used to validate URLs
10631 * @param {String} value The URL
10633 'url' : function(v){
10634 return url.test(v);
10637 * The error text to display when the url validation function returns false
10640 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10643 * The function used to validate alpha values
10644 * @param {String} value The value
10646 'alpha' : function(v){
10647 return alpha.test(v);
10650 * The error text to display when the alpha validation function returns false
10653 'alphaText' : 'This field should only contain letters and _',
10655 * The keystroke filter mask to be applied on alpha input
10658 'alphaMask' : /[a-z_]/i,
10661 * The function used to validate alphanumeric values
10662 * @param {String} value The value
10664 'alphanum' : function(v){
10665 return alphanum.test(v);
10668 * The error text to display when the alphanumeric validation function returns false
10671 'alphanumText' : 'This field should only contain letters, numbers and _',
10673 * The keystroke filter mask to be applied on alphanumeric input
10676 'alphanumMask' : /[a-z0-9_]/i
10686 * @class Roo.bootstrap.Input
10687 * @extends Roo.bootstrap.Component
10688 * Bootstrap Input class
10689 * @cfg {Boolean} disabled is it disabled
10690 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10691 * @cfg {String} name name of the input
10692 * @cfg {string} fieldLabel - the label associated
10693 * @cfg {string} placeholder - placeholder to put in text.
10694 * @cfg {string} before - input group add on before
10695 * @cfg {string} after - input group add on after
10696 * @cfg {string} size - (lg|sm) or leave empty..
10697 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10698 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10699 * @cfg {Number} md colspan out of 12 for computer-sized screens
10700 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10701 * @cfg {string} value default value of the input
10702 * @cfg {Number} labelWidth set the width of label
10703 * @cfg {Number} labellg set the width of label (1-12)
10704 * @cfg {Number} labelmd set the width of label (1-12)
10705 * @cfg {Number} labelsm set the width of label (1-12)
10706 * @cfg {Number} labelxs set the width of label (1-12)
10707 * @cfg {String} labelAlign (top|left)
10708 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10709 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10710 * @cfg {String} indicatorpos (left|right) default left
10711 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10712 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10713 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10715 * @cfg {String} align (left|center|right) Default left
10716 * @cfg {Boolean} forceFeedback (true|false) Default false
10719 * Create a new Input
10720 * @param {Object} config The config object
10723 Roo.bootstrap.Input = function(config){
10725 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10730 * Fires when this field receives input focus.
10731 * @param {Roo.form.Field} this
10736 * Fires when this field loses input focus.
10737 * @param {Roo.form.Field} this
10741 * @event specialkey
10742 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10743 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10744 * @param {Roo.form.Field} this
10745 * @param {Roo.EventObject} e The event object
10750 * Fires just before the field blurs if the field value has changed.
10751 * @param {Roo.form.Field} this
10752 * @param {Mixed} newValue The new value
10753 * @param {Mixed} oldValue The original value
10758 * Fires after the field has been marked as invalid.
10759 * @param {Roo.form.Field} this
10760 * @param {String} msg The validation message
10765 * Fires after the field has been validated with no errors.
10766 * @param {Roo.form.Field} this
10771 * Fires after the key up
10772 * @param {Roo.form.Field} this
10773 * @param {Roo.EventObject} e The event Object
10778 * Fires after the user pastes into input
10779 * @param {Roo.form.Field} this
10780 * @param {Roo.EventObject} e The event Object
10786 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10788 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10789 automatic validation (defaults to "keyup").
10791 validationEvent : "keyup",
10793 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10795 validateOnBlur : true,
10797 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10799 validationDelay : 250,
10801 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10803 focusClass : "x-form-focus", // not needed???
10807 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10809 invalidClass : "has-warning",
10812 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10814 validClass : "has-success",
10817 * @cfg {Boolean} hasFeedback (true|false) default true
10819 hasFeedback : true,
10822 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10824 invalidFeedbackClass : "glyphicon-warning-sign",
10827 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10829 validFeedbackClass : "glyphicon-ok",
10832 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10834 selectOnFocus : false,
10837 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10841 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10846 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10848 disableKeyFilter : false,
10851 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10855 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10859 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10861 blankText : "Please complete this mandatory field",
10864 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10868 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10870 maxLength : Number.MAX_VALUE,
10872 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10874 minLengthText : "The minimum length for this field is {0}",
10876 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10878 maxLengthText : "The maximum length for this field is {0}",
10882 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10883 * If available, this function will be called only after the basic validators all return true, and will be passed the
10884 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10888 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10889 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10890 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10894 * @cfg {String} regexText -- Depricated - use Invalid Text
10899 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10905 autocomplete: false,
10909 inputType : 'text',
10912 placeholder: false,
10917 preventMark: false,
10918 isFormField : true,
10921 labelAlign : false,
10924 formatedValue : false,
10925 forceFeedback : false,
10927 indicatorpos : 'left',
10937 parentLabelAlign : function()
10940 while (parent.parent()) {
10941 parent = parent.parent();
10942 if (typeof(parent.labelAlign) !='undefined') {
10943 return parent.labelAlign;
10950 getAutoCreate : function()
10952 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10958 if(this.inputType != 'hidden'){
10959 cfg.cls = 'form-group' //input-group
10965 type : this.inputType,
10966 value : this.value,
10967 cls : 'form-control',
10968 placeholder : this.placeholder || '',
10969 autocomplete : this.autocomplete || 'new-password'
10971 if (this.inputType == 'file') {
10972 input.style = 'overflow:hidden'; // why not in CSS?
10975 if(this.capture.length){
10976 input.capture = this.capture;
10979 if(this.accept.length){
10980 input.accept = this.accept + "/*";
10984 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10987 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10988 input.maxLength = this.maxLength;
10991 if (this.disabled) {
10992 input.disabled=true;
10995 if (this.readOnly) {
10996 input.readonly=true;
11000 input.name = this.name;
11004 input.cls += ' input-' + this.size;
11008 ['xs','sm','md','lg'].map(function(size){
11009 if (settings[size]) {
11010 cfg.cls += ' col-' + size + '-' + settings[size];
11014 var inputblock = input;
11018 cls: 'glyphicon form-control-feedback'
11021 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11024 cls : 'has-feedback',
11032 if (this.before || this.after) {
11035 cls : 'input-group',
11039 if (this.before && typeof(this.before) == 'string') {
11041 inputblock.cn.push({
11043 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11047 if (this.before && typeof(this.before) == 'object') {
11048 this.before = Roo.factory(this.before);
11050 inputblock.cn.push({
11052 cls : 'roo-input-before input-group-prepend input-group-' +
11053 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11057 inputblock.cn.push(input);
11059 if (this.after && typeof(this.after) == 'string') {
11060 inputblock.cn.push({
11062 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11066 if (this.after && typeof(this.after) == 'object') {
11067 this.after = Roo.factory(this.after);
11069 inputblock.cn.push({
11071 cls : 'roo-input-after input-group-append input-group-' +
11072 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11076 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11077 inputblock.cls += ' has-feedback';
11078 inputblock.cn.push(feedback);
11083 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11084 tooltip : 'This field is required'
11086 if (this.allowBlank ) {
11087 indicator.style = this.allowBlank ? ' display:none' : '';
11089 if (align ==='left' && this.fieldLabel.length) {
11091 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11098 cls : 'control-label col-form-label',
11099 html : this.fieldLabel
11110 var labelCfg = cfg.cn[1];
11111 var contentCfg = cfg.cn[2];
11113 if(this.indicatorpos == 'right'){
11118 cls : 'control-label col-form-label',
11122 html : this.fieldLabel
11136 labelCfg = cfg.cn[0];
11137 contentCfg = cfg.cn[1];
11141 if(this.labelWidth > 12){
11142 labelCfg.style = "width: " + this.labelWidth + 'px';
11145 if(this.labelWidth < 13 && this.labelmd == 0){
11146 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11149 if(this.labellg > 0){
11150 labelCfg.cls += ' col-lg-' + this.labellg;
11151 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11154 if(this.labelmd > 0){
11155 labelCfg.cls += ' col-md-' + this.labelmd;
11156 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11159 if(this.labelsm > 0){
11160 labelCfg.cls += ' col-sm-' + this.labelsm;
11161 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11164 if(this.labelxs > 0){
11165 labelCfg.cls += ' col-xs-' + this.labelxs;
11166 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11170 } else if ( this.fieldLabel.length) {
11177 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11178 tooltip : 'This field is required',
11179 style : this.allowBlank ? ' display:none' : ''
11183 //cls : 'input-group-addon',
11184 html : this.fieldLabel
11192 if(this.indicatorpos == 'right'){
11197 //cls : 'input-group-addon',
11198 html : this.fieldLabel
11203 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11204 tooltip : 'This field is required',
11205 style : this.allowBlank ? ' display:none' : ''
11225 if (this.parentType === 'Navbar' && this.parent().bar) {
11226 cfg.cls += ' navbar-form';
11229 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11230 // on BS4 we do this only if not form
11231 cfg.cls += ' navbar-form';
11239 * return the real input element.
11241 inputEl: function ()
11243 return this.el.select('input.form-control',true).first();
11246 tooltipEl : function()
11248 return this.inputEl();
11251 indicatorEl : function()
11253 if (Roo.bootstrap.version == 4) {
11254 return false; // not enabled in v4 yet.
11257 var indicator = this.el.select('i.roo-required-indicator',true).first();
11267 setDisabled : function(v)
11269 var i = this.inputEl().dom;
11271 i.removeAttribute('disabled');
11275 i.setAttribute('disabled','true');
11277 initEvents : function()
11280 this.inputEl().on("keydown" , this.fireKey, this);
11281 this.inputEl().on("focus", this.onFocus, this);
11282 this.inputEl().on("blur", this.onBlur, this);
11284 this.inputEl().relayEvent('keyup', this);
11285 this.inputEl().relayEvent('paste', this);
11287 this.indicator = this.indicatorEl();
11289 if(this.indicator){
11290 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11293 // reference to original value for reset
11294 this.originalValue = this.getValue();
11295 //Roo.form.TextField.superclass.initEvents.call(this);
11296 if(this.validationEvent == 'keyup'){
11297 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11298 this.inputEl().on('keyup', this.filterValidation, this);
11300 else if(this.validationEvent !== false){
11301 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11304 if(this.selectOnFocus){
11305 this.on("focus", this.preFocus, this);
11308 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11309 this.inputEl().on("keypress", this.filterKeys, this);
11311 this.inputEl().relayEvent('keypress', this);
11314 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11315 this.el.on("click", this.autoSize, this);
11318 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11319 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11322 if (typeof(this.before) == 'object') {
11323 this.before.render(this.el.select('.roo-input-before',true).first());
11325 if (typeof(this.after) == 'object') {
11326 this.after.render(this.el.select('.roo-input-after',true).first());
11329 this.inputEl().on('change', this.onChange, this);
11332 filterValidation : function(e){
11333 if(!e.isNavKeyPress()){
11334 this.validationTask.delay(this.validationDelay);
11338 * Validates the field value
11339 * @return {Boolean} True if the value is valid, else false
11341 validate : function(){
11342 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11343 if(this.disabled || this.validateValue(this.getRawValue())){
11348 this.markInvalid();
11354 * Validates a value according to the field's validation rules and marks the field as invalid
11355 * if the validation fails
11356 * @param {Mixed} value The value to validate
11357 * @return {Boolean} True if the value is valid, else false
11359 validateValue : function(value)
11361 if(this.getVisibilityEl().hasClass('hidden')){
11365 if(value.length < 1) { // if it's blank
11366 if(this.allowBlank){
11372 if(value.length < this.minLength){
11375 if(value.length > this.maxLength){
11379 var vt = Roo.form.VTypes;
11380 if(!vt[this.vtype](value, this)){
11384 if(typeof this.validator == "function"){
11385 var msg = this.validator(value);
11389 if (typeof(msg) == 'string') {
11390 this.invalidText = msg;
11394 if(this.regex && !this.regex.test(value)){
11402 fireKey : function(e){
11403 //Roo.log('field ' + e.getKey());
11404 if(e.isNavKeyPress()){
11405 this.fireEvent("specialkey", this, e);
11408 focus : function (selectText){
11410 this.inputEl().focus();
11411 if(selectText === true){
11412 this.inputEl().dom.select();
11418 onFocus : function(){
11419 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11420 // this.el.addClass(this.focusClass);
11422 if(!this.hasFocus){
11423 this.hasFocus = true;
11424 this.startValue = this.getValue();
11425 this.fireEvent("focus", this);
11429 beforeBlur : Roo.emptyFn,
11433 onBlur : function(){
11435 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11436 //this.el.removeClass(this.focusClass);
11438 this.hasFocus = false;
11439 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11442 var v = this.getValue();
11443 if(String(v) !== String(this.startValue)){
11444 this.fireEvent('change', this, v, this.startValue);
11446 this.fireEvent("blur", this);
11449 onChange : function(e)
11451 var v = this.getValue();
11452 if(String(v) !== String(this.startValue)){
11453 this.fireEvent('change', this, v, this.startValue);
11459 * Resets the current field value to the originally loaded value and clears any validation messages
11461 reset : function(){
11462 this.setValue(this.originalValue);
11466 * Returns the name of the field
11467 * @return {Mixed} name The name field
11469 getName: function(){
11473 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11474 * @return {Mixed} value The field value
11476 getValue : function(){
11478 var v = this.inputEl().getValue();
11483 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11484 * @return {Mixed} value The field value
11486 getRawValue : function(){
11487 var v = this.inputEl().getValue();
11493 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11494 * @param {Mixed} value The value to set
11496 setRawValue : function(v){
11497 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11500 selectText : function(start, end){
11501 var v = this.getRawValue();
11503 start = start === undefined ? 0 : start;
11504 end = end === undefined ? v.length : end;
11505 var d = this.inputEl().dom;
11506 if(d.setSelectionRange){
11507 d.setSelectionRange(start, end);
11508 }else if(d.createTextRange){
11509 var range = d.createTextRange();
11510 range.moveStart("character", start);
11511 range.moveEnd("character", v.length-end);
11518 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11519 * @param {Mixed} value The value to set
11521 setValue : function(v){
11524 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11530 processValue : function(value){
11531 if(this.stripCharsRe){
11532 var newValue = value.replace(this.stripCharsRe, '');
11533 if(newValue !== value){
11534 this.setRawValue(newValue);
11541 preFocus : function(){
11543 if(this.selectOnFocus){
11544 this.inputEl().dom.select();
11547 filterKeys : function(e){
11548 var k = e.getKey();
11549 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11552 var c = e.getCharCode(), cc = String.fromCharCode(c);
11553 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11556 if(!this.maskRe.test(cc)){
11561 * Clear any invalid styles/messages for this field
11563 clearInvalid : function(){
11565 if(!this.el || this.preventMark){ // not rendered
11570 this.el.removeClass([this.invalidClass, 'is-invalid']);
11572 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11574 var feedback = this.el.select('.form-control-feedback', true).first();
11577 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11582 if(this.indicator){
11583 this.indicator.removeClass('visible');
11584 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11587 this.fireEvent('valid', this);
11591 * Mark this field as valid
11593 markValid : function()
11595 if(!this.el || this.preventMark){ // not rendered...
11599 this.el.removeClass([this.invalidClass, this.validClass]);
11600 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11602 var feedback = this.el.select('.form-control-feedback', true).first();
11605 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11608 if(this.indicator){
11609 this.indicator.removeClass('visible');
11610 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11618 if(this.allowBlank && !this.getRawValue().length){
11621 if (Roo.bootstrap.version == 3) {
11622 this.el.addClass(this.validClass);
11624 this.inputEl().addClass('is-valid');
11627 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11629 var feedback = this.el.select('.form-control-feedback', true).first();
11632 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11633 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11638 this.fireEvent('valid', this);
11642 * Mark this field as invalid
11643 * @param {String} msg The validation message
11645 markInvalid : function(msg)
11647 if(!this.el || this.preventMark){ // not rendered
11651 this.el.removeClass([this.invalidClass, this.validClass]);
11652 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11654 var feedback = this.el.select('.form-control-feedback', true).first();
11657 this.el.select('.form-control-feedback', true).first().removeClass(
11658 [this.invalidFeedbackClass, this.validFeedbackClass]);
11665 if(this.allowBlank && !this.getRawValue().length){
11669 if(this.indicator){
11670 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11671 this.indicator.addClass('visible');
11673 if (Roo.bootstrap.version == 3) {
11674 this.el.addClass(this.invalidClass);
11676 this.inputEl().addClass('is-invalid');
11681 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11683 var feedback = this.el.select('.form-control-feedback', true).first();
11686 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11688 if(this.getValue().length || this.forceFeedback){
11689 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11696 this.fireEvent('invalid', this, msg);
11699 SafariOnKeyDown : function(event)
11701 // this is a workaround for a password hang bug on chrome/ webkit.
11702 if (this.inputEl().dom.type != 'password') {
11706 var isSelectAll = false;
11708 if(this.inputEl().dom.selectionEnd > 0){
11709 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11711 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11712 event.preventDefault();
11717 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11719 event.preventDefault();
11720 // this is very hacky as keydown always get's upper case.
11722 var cc = String.fromCharCode(event.getCharCode());
11723 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11727 adjustWidth : function(tag, w){
11728 tag = tag.toLowerCase();
11729 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11730 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11731 if(tag == 'input'){
11734 if(tag == 'textarea'){
11737 }else if(Roo.isOpera){
11738 if(tag == 'input'){
11741 if(tag == 'textarea'){
11749 setFieldLabel : function(v)
11751 if(!this.rendered){
11755 if(this.indicatorEl()){
11756 var ar = this.el.select('label > span',true);
11758 if (ar.elements.length) {
11759 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11760 this.fieldLabel = v;
11764 var br = this.el.select('label',true);
11766 if(br.elements.length) {
11767 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11768 this.fieldLabel = v;
11772 Roo.log('Cannot Found any of label > span || label in input');
11776 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11777 this.fieldLabel = v;
11792 * @class Roo.bootstrap.TextArea
11793 * @extends Roo.bootstrap.Input
11794 * Bootstrap TextArea class
11795 * @cfg {Number} cols Specifies the visible width of a text area
11796 * @cfg {Number} rows Specifies the visible number of lines in a text area
11797 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11798 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11799 * @cfg {string} html text
11802 * Create a new TextArea
11803 * @param {Object} config The config object
11806 Roo.bootstrap.TextArea = function(config){
11807 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11811 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11821 getAutoCreate : function(){
11823 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11829 if(this.inputType != 'hidden'){
11830 cfg.cls = 'form-group' //input-group
11838 value : this.value || '',
11839 html: this.html || '',
11840 cls : 'form-control',
11841 placeholder : this.placeholder || ''
11845 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11846 input.maxLength = this.maxLength;
11850 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11854 input.cols = this.cols;
11857 if (this.readOnly) {
11858 input.readonly = true;
11862 input.name = this.name;
11866 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11870 ['xs','sm','md','lg'].map(function(size){
11871 if (settings[size]) {
11872 cfg.cls += ' col-' + size + '-' + settings[size];
11876 var inputblock = input;
11878 if(this.hasFeedback && !this.allowBlank){
11882 cls: 'glyphicon form-control-feedback'
11886 cls : 'has-feedback',
11895 if (this.before || this.after) {
11898 cls : 'input-group',
11902 inputblock.cn.push({
11904 cls : 'input-group-addon',
11909 inputblock.cn.push(input);
11911 if(this.hasFeedback && !this.allowBlank){
11912 inputblock.cls += ' has-feedback';
11913 inputblock.cn.push(feedback);
11917 inputblock.cn.push({
11919 cls : 'input-group-addon',
11926 if (align ==='left' && this.fieldLabel.length) {
11931 cls : 'control-label',
11932 html : this.fieldLabel
11943 if(this.labelWidth > 12){
11944 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11947 if(this.labelWidth < 13 && this.labelmd == 0){
11948 this.labelmd = this.labelWidth;
11951 if(this.labellg > 0){
11952 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11953 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11956 if(this.labelmd > 0){
11957 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11958 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11961 if(this.labelsm > 0){
11962 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11963 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11966 if(this.labelxs > 0){
11967 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11968 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11971 } else if ( this.fieldLabel.length) {
11976 //cls : 'input-group-addon',
11977 html : this.fieldLabel
11995 if (this.disabled) {
11996 input.disabled=true;
12003 * return the real textarea element.
12005 inputEl: function ()
12007 return this.el.select('textarea.form-control',true).first();
12011 * Clear any invalid styles/messages for this field
12013 clearInvalid : function()
12016 if(!this.el || this.preventMark){ // not rendered
12020 var label = this.el.select('label', true).first();
12021 var icon = this.el.select('i.fa-star', true).first();
12026 this.el.removeClass( this.validClass);
12027 this.inputEl().removeClass('is-invalid');
12029 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12031 var feedback = this.el.select('.form-control-feedback', true).first();
12034 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12039 this.fireEvent('valid', this);
12043 * Mark this field as valid
12045 markValid : function()
12047 if(!this.el || this.preventMark){ // not rendered
12051 this.el.removeClass([this.invalidClass, this.validClass]);
12052 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12054 var feedback = this.el.select('.form-control-feedback', true).first();
12057 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12060 if(this.disabled || this.allowBlank){
12064 var label = this.el.select('label', true).first();
12065 var icon = this.el.select('i.fa-star', true).first();
12070 if (Roo.bootstrap.version == 3) {
12071 this.el.addClass(this.validClass);
12073 this.inputEl().addClass('is-valid');
12077 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12079 var feedback = this.el.select('.form-control-feedback', true).first();
12082 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12083 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12088 this.fireEvent('valid', this);
12092 * Mark this field as invalid
12093 * @param {String} msg The validation message
12095 markInvalid : function(msg)
12097 if(!this.el || this.preventMark){ // not rendered
12101 this.el.removeClass([this.invalidClass, this.validClass]);
12102 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12104 var feedback = this.el.select('.form-control-feedback', true).first();
12107 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12110 if(this.disabled || this.allowBlank){
12114 var label = this.el.select('label', true).first();
12115 var icon = this.el.select('i.fa-star', true).first();
12117 if(!this.getValue().length && label && !icon){
12118 this.el.createChild({
12120 cls : 'text-danger fa fa-lg fa-star',
12121 tooltip : 'This field is required',
12122 style : 'margin-right:5px;'
12126 if (Roo.bootstrap.version == 3) {
12127 this.el.addClass(this.invalidClass);
12129 this.inputEl().addClass('is-invalid');
12132 // fixme ... this may be depricated need to test..
12133 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12135 var feedback = this.el.select('.form-control-feedback', true).first();
12138 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12140 if(this.getValue().length || this.forceFeedback){
12141 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12148 this.fireEvent('invalid', this, msg);
12156 * trigger field - base class for combo..
12161 * @class Roo.bootstrap.TriggerField
12162 * @extends Roo.bootstrap.Input
12163 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12164 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12165 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12166 * for which you can provide a custom implementation. For example:
12168 var trigger = new Roo.bootstrap.TriggerField();
12169 trigger.onTriggerClick = myTriggerFn;
12170 trigger.applyTo('my-field');
12173 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12174 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12175 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12176 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12177 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12180 * Create a new TriggerField.
12181 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12182 * to the base TextField)
12184 Roo.bootstrap.TriggerField = function(config){
12185 this.mimicing = false;
12186 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12189 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12191 * @cfg {String} triggerClass A CSS class to apply to the trigger
12194 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12199 * @cfg {Boolean} removable (true|false) special filter default false
12203 /** @cfg {Boolean} grow @hide */
12204 /** @cfg {Number} growMin @hide */
12205 /** @cfg {Number} growMax @hide */
12211 autoSize: Roo.emptyFn,
12215 deferHeight : true,
12218 actionMode : 'wrap',
12223 getAutoCreate : function(){
12225 var align = this.labelAlign || this.parentLabelAlign();
12230 cls: 'form-group' //input-group
12237 type : this.inputType,
12238 cls : 'form-control',
12239 autocomplete: 'new-password',
12240 placeholder : this.placeholder || ''
12244 input.name = this.name;
12247 input.cls += ' input-' + this.size;
12250 if (this.disabled) {
12251 input.disabled=true;
12254 var inputblock = input;
12256 if(this.hasFeedback && !this.allowBlank){
12260 cls: 'glyphicon form-control-feedback'
12263 if(this.removable && !this.editable ){
12265 cls : 'has-feedback',
12271 cls : 'roo-combo-removable-btn close'
12278 cls : 'has-feedback',
12287 if(this.removable && !this.editable ){
12289 cls : 'roo-removable',
12295 cls : 'roo-combo-removable-btn close'
12302 if (this.before || this.after) {
12305 cls : 'input-group',
12309 inputblock.cn.push({
12311 cls : 'input-group-addon input-group-prepend input-group-text',
12316 inputblock.cn.push(input);
12318 if(this.hasFeedback && !this.allowBlank){
12319 inputblock.cls += ' has-feedback';
12320 inputblock.cn.push(feedback);
12324 inputblock.cn.push({
12326 cls : 'input-group-addon input-group-append input-group-text',
12335 var ibwrap = inputblock;
12340 cls: 'roo-select2-choices',
12344 cls: 'roo-select2-search-field',
12356 cls: 'roo-select2-container input-group',
12361 cls: 'form-hidden-field'
12367 if(!this.multiple && this.showToggleBtn){
12373 if (this.caret != false) {
12376 cls: 'fa fa-' + this.caret
12383 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12385 Roo.bootstrap.version == 3 ? caret : '',
12388 cls: 'combobox-clear',
12402 combobox.cls += ' roo-select2-container-multi';
12406 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12407 tooltip : 'This field is required'
12409 if (Roo.bootstrap.version == 4) {
12412 style : 'display:none'
12417 if (align ==='left' && this.fieldLabel.length) {
12419 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12426 cls : 'control-label',
12427 html : this.fieldLabel
12439 var labelCfg = cfg.cn[1];
12440 var contentCfg = cfg.cn[2];
12442 if(this.indicatorpos == 'right'){
12447 cls : 'control-label',
12451 html : this.fieldLabel
12465 labelCfg = cfg.cn[0];
12466 contentCfg = cfg.cn[1];
12469 if(this.labelWidth > 12){
12470 labelCfg.style = "width: " + this.labelWidth + 'px';
12473 if(this.labelWidth < 13 && this.labelmd == 0){
12474 this.labelmd = this.labelWidth;
12477 if(this.labellg > 0){
12478 labelCfg.cls += ' col-lg-' + this.labellg;
12479 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12482 if(this.labelmd > 0){
12483 labelCfg.cls += ' col-md-' + this.labelmd;
12484 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12487 if(this.labelsm > 0){
12488 labelCfg.cls += ' col-sm-' + this.labelsm;
12489 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12492 if(this.labelxs > 0){
12493 labelCfg.cls += ' col-xs-' + this.labelxs;
12494 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12497 } else if ( this.fieldLabel.length) {
12498 // Roo.log(" label");
12503 //cls : 'input-group-addon',
12504 html : this.fieldLabel
12512 if(this.indicatorpos == 'right'){
12520 html : this.fieldLabel
12534 // Roo.log(" no label && no align");
12541 ['xs','sm','md','lg'].map(function(size){
12542 if (settings[size]) {
12543 cfg.cls += ' col-' + size + '-' + settings[size];
12554 onResize : function(w, h){
12555 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12556 // if(typeof w == 'number'){
12557 // var x = w - this.trigger.getWidth();
12558 // this.inputEl().setWidth(this.adjustWidth('input', x));
12559 // this.trigger.setStyle('left', x+'px');
12564 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12567 getResizeEl : function(){
12568 return this.inputEl();
12572 getPositionEl : function(){
12573 return this.inputEl();
12577 alignErrorIcon : function(){
12578 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12582 initEvents : function(){
12586 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12587 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12588 if(!this.multiple && this.showToggleBtn){
12589 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12590 if(this.hideTrigger){
12591 this.trigger.setDisplayed(false);
12593 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12597 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12600 if(this.removable && !this.editable && !this.tickable){
12601 var close = this.closeTriggerEl();
12604 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12605 close.on('click', this.removeBtnClick, this, close);
12609 //this.trigger.addClassOnOver('x-form-trigger-over');
12610 //this.trigger.addClassOnClick('x-form-trigger-click');
12613 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12617 closeTriggerEl : function()
12619 var close = this.el.select('.roo-combo-removable-btn', true).first();
12620 return close ? close : false;
12623 removeBtnClick : function(e, h, el)
12625 e.preventDefault();
12627 if(this.fireEvent("remove", this) !== false){
12629 this.fireEvent("afterremove", this)
12633 createList : function()
12635 this.list = Roo.get(document.body).createChild({
12636 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12637 cls: 'typeahead typeahead-long dropdown-menu shadow',
12638 style: 'display:none'
12641 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12646 initTrigger : function(){
12651 onDestroy : function(){
12653 this.trigger.removeAllListeners();
12654 // this.trigger.remove();
12657 // this.wrap.remove();
12659 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12663 onFocus : function(){
12664 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12666 if(!this.mimicing){
12667 this.wrap.addClass('x-trigger-wrap-focus');
12668 this.mimicing = true;
12669 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12670 if(this.monitorTab){
12671 this.el.on("keydown", this.checkTab, this);
12678 checkTab : function(e){
12679 if(e.getKey() == e.TAB){
12680 this.triggerBlur();
12685 onBlur : function(){
12690 mimicBlur : function(e, t){
12692 if(!this.wrap.contains(t) && this.validateBlur()){
12693 this.triggerBlur();
12699 triggerBlur : function(){
12700 this.mimicing = false;
12701 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12702 if(this.monitorTab){
12703 this.el.un("keydown", this.checkTab, this);
12705 //this.wrap.removeClass('x-trigger-wrap-focus');
12706 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12710 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12711 validateBlur : function(e, t){
12716 onDisable : function(){
12717 this.inputEl().dom.disabled = true;
12718 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12720 // this.wrap.addClass('x-item-disabled');
12725 onEnable : function(){
12726 this.inputEl().dom.disabled = false;
12727 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12729 // this.el.removeClass('x-item-disabled');
12734 onShow : function(){
12735 var ae = this.getActionEl();
12738 ae.dom.style.display = '';
12739 ae.dom.style.visibility = 'visible';
12745 onHide : function(){
12746 var ae = this.getActionEl();
12747 ae.dom.style.display = 'none';
12751 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12752 * by an implementing function.
12754 * @param {EventObject} e
12756 onTriggerClick : Roo.emptyFn
12764 * @class Roo.bootstrap.CardUploader
12765 * @extends Roo.bootstrap.Button
12766 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12767 * @cfg {Number} errorTimeout default 3000
12768 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12769 * @cfg {Array} html The button text.
12773 * Create a new CardUploader
12774 * @param {Object} config The config object
12777 Roo.bootstrap.CardUploader = function(config){
12781 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12784 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12792 * When a image is clicked on - and needs to display a slideshow or similar..
12793 * @param {Roo.bootstrap.Card} this
12794 * @param {Object} The image information data
12800 * When a the download link is clicked
12801 * @param {Roo.bootstrap.Card} this
12802 * @param {Object} The image information data contains
12809 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12812 errorTimeout : 3000,
12816 fileCollection : false,
12819 getAutoCreate : function()
12823 cls :'form-group' ,
12828 //cls : 'input-group-addon',
12829 html : this.fieldLabel
12837 value : this.value,
12838 cls : 'd-none form-control'
12843 multiple : 'multiple',
12845 cls : 'd-none roo-card-upload-selector'
12849 cls : 'roo-card-uploader-button-container w-100 mb-2'
12852 cls : 'card-columns roo-card-uploader-container'
12862 getChildContainer : function() /// what children are added to.
12864 return this.containerEl;
12867 getButtonContainer : function() /// what children are added to.
12869 return this.el.select(".roo-card-uploader-button-container").first();
12872 initEvents : function()
12875 Roo.bootstrap.Input.prototype.initEvents.call(this);
12879 xns: Roo.bootstrap,
12882 container_method : 'getButtonContainer' ,
12883 html : this.html, // fix changable?
12886 'click' : function(btn, e) {
12895 this.urlAPI = (window.createObjectURL && window) ||
12896 (window.URL && URL.revokeObjectURL && URL) ||
12897 (window.webkitURL && webkitURL);
12902 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12904 this.selectorEl.on('change', this.onFileSelected, this);
12907 this.images.forEach(function(img) {
12910 this.images = false;
12912 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12918 onClick : function(e)
12920 e.preventDefault();
12922 this.selectorEl.dom.click();
12926 onFileSelected : function(e)
12928 e.preventDefault();
12930 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12934 Roo.each(this.selectorEl.dom.files, function(file){
12935 this.addFile(file);
12944 addFile : function(file)
12947 if(typeof(file) === 'string'){
12948 throw "Add file by name?"; // should not happen
12952 if(!file || !this.urlAPI){
12962 var url = _this.urlAPI.createObjectURL( file);
12965 id : Roo.bootstrap.CardUploader.ID--,
12966 is_uploaded : false,
12970 mimetype : file.type,
12978 * addCard - add an Attachment to the uploader
12979 * @param data - the data about the image to upload
12983 title : "Title of file",
12984 is_uploaded : false,
12985 src : "http://.....",
12986 srcfile : { the File upload object },
12987 mimetype : file.type,
12990 .. any other data...
12996 addCard : function (data)
12998 // hidden input element?
12999 // if the file is not an image...
13000 //then we need to use something other that and header_image
13005 xns : Roo.bootstrap,
13006 xtype : 'CardFooter',
13009 xns : Roo.bootstrap,
13015 xns : Roo.bootstrap,
13017 html : String.format("<small>{0}</small>", data.title),
13018 cls : 'col-10 text-left',
13023 click : function() {
13025 t.fireEvent( "download", t, data );
13031 xns : Roo.bootstrap,
13033 style: 'max-height: 28px; ',
13039 click : function() {
13040 t.removeCard(data.id)
13052 var cn = this.addxtype(
13055 xns : Roo.bootstrap,
13058 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13059 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13060 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13065 initEvents : function() {
13066 Roo.bootstrap.Card.prototype.initEvents.call(this);
13068 this.imgEl = this.el.select('.card-img-top').first();
13070 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13071 this.imgEl.set({ 'pointer' : 'cursor' });
13074 this.getCardFooter().addClass('p-1');
13081 // dont' really need ot update items.
13082 // this.items.push(cn);
13083 this.fileCollection.add(cn);
13085 if (!data.srcfile) {
13086 this.updateInput();
13091 var reader = new FileReader();
13092 reader.addEventListener("load", function() {
13093 data.srcdata = reader.result;
13096 reader.readAsDataURL(data.srcfile);
13101 removeCard : function(id)
13104 var card = this.fileCollection.get(id);
13105 card.data.is_deleted = 1;
13106 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13107 //this.fileCollection.remove(card);
13108 //this.items = this.items.filter(function(e) { return e != card });
13109 // dont' really need ot update items.
13110 card.el.dom.parentNode.removeChild(card.el.dom);
13111 this.updateInput();
13117 this.fileCollection.each(function(card) {
13118 if (card.el.dom && card.el.dom.parentNode) {
13119 card.el.dom.parentNode.removeChild(card.el.dom);
13122 this.fileCollection.clear();
13123 this.updateInput();
13126 updateInput : function()
13129 this.fileCollection.each(function(e) {
13133 this.inputEl().dom.value = JSON.stringify(data);
13143 Roo.bootstrap.CardUploader.ID = -1;/*
13145 * Ext JS Library 1.1.1
13146 * Copyright(c) 2006-2007, Ext JS, LLC.
13148 * Originally Released Under LGPL - original licence link has changed is not relivant.
13151 * <script type="text/javascript">
13156 * @class Roo.data.SortTypes
13158 * Defines the default sorting (casting?) comparison functions used when sorting data.
13160 Roo.data.SortTypes = {
13162 * Default sort that does nothing
13163 * @param {Mixed} s The value being converted
13164 * @return {Mixed} The comparison value
13166 none : function(s){
13171 * The regular expression used to strip tags
13175 stripTagsRE : /<\/?[^>]+>/gi,
13178 * Strips all HTML tags to sort on text only
13179 * @param {Mixed} s The value being converted
13180 * @return {String} The comparison value
13182 asText : function(s){
13183 return String(s).replace(this.stripTagsRE, "");
13187 * Strips all HTML tags to sort on text only - Case insensitive
13188 * @param {Mixed} s The value being converted
13189 * @return {String} The comparison value
13191 asUCText : function(s){
13192 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13196 * Case insensitive string
13197 * @param {Mixed} s The value being converted
13198 * @return {String} The comparison value
13200 asUCString : function(s) {
13201 return String(s).toUpperCase();
13206 * @param {Mixed} s The value being converted
13207 * @return {Number} The comparison value
13209 asDate : function(s) {
13213 if(s instanceof Date){
13214 return s.getTime();
13216 return Date.parse(String(s));
13221 * @param {Mixed} s The value being converted
13222 * @return {Float} The comparison value
13224 asFloat : function(s) {
13225 var val = parseFloat(String(s).replace(/,/g, ""));
13234 * @param {Mixed} s The value being converted
13235 * @return {Number} The comparison value
13237 asInt : function(s) {
13238 var val = parseInt(String(s).replace(/,/g, ""));
13246 * Ext JS Library 1.1.1
13247 * Copyright(c) 2006-2007, Ext JS, LLC.
13249 * Originally Released Under LGPL - original licence link has changed is not relivant.
13252 * <script type="text/javascript">
13256 * @class Roo.data.Record
13257 * Instances of this class encapsulate both record <em>definition</em> information, and record
13258 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13259 * to access Records cached in an {@link Roo.data.Store} object.<br>
13261 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13262 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13265 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13267 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13268 * {@link #create}. The parameters are the same.
13269 * @param {Array} data An associative Array of data values keyed by the field name.
13270 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13271 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13272 * not specified an integer id is generated.
13274 Roo.data.Record = function(data, id){
13275 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13280 * Generate a constructor for a specific record layout.
13281 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13282 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13283 * Each field definition object may contain the following properties: <ul>
13284 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
13285 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13286 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13287 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13288 * is being used, then this is a string containing the javascript expression to reference the data relative to
13289 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13290 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13291 * this may be omitted.</p></li>
13292 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13293 * <ul><li>auto (Default, implies no conversion)</li>
13298 * <li>date</li></ul></p></li>
13299 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13300 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13301 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13302 * by the Reader into an object that will be stored in the Record. It is passed the
13303 * following parameters:<ul>
13304 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13306 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13308 * <br>usage:<br><pre><code>
13309 var TopicRecord = Roo.data.Record.create(
13310 {name: 'title', mapping: 'topic_title'},
13311 {name: 'author', mapping: 'username'},
13312 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13313 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13314 {name: 'lastPoster', mapping: 'user2'},
13315 {name: 'excerpt', mapping: 'post_text'}
13318 var myNewRecord = new TopicRecord({
13319 title: 'Do my job please',
13322 lastPost: new Date(),
13323 lastPoster: 'Animal',
13324 excerpt: 'No way dude!'
13326 myStore.add(myNewRecord);
13331 Roo.data.Record.create = function(o){
13332 var f = function(){
13333 f.superclass.constructor.apply(this, arguments);
13335 Roo.extend(f, Roo.data.Record);
13336 var p = f.prototype;
13337 p.fields = new Roo.util.MixedCollection(false, function(field){
13340 for(var i = 0, len = o.length; i < len; i++){
13341 p.fields.add(new Roo.data.Field(o[i]));
13343 f.getField = function(name){
13344 return p.fields.get(name);
13349 Roo.data.Record.AUTO_ID = 1000;
13350 Roo.data.Record.EDIT = 'edit';
13351 Roo.data.Record.REJECT = 'reject';
13352 Roo.data.Record.COMMIT = 'commit';
13354 Roo.data.Record.prototype = {
13356 * Readonly flag - true if this record has been modified.
13365 join : function(store){
13366 this.store = store;
13370 * Set the named field to the specified value.
13371 * @param {String} name The name of the field to set.
13372 * @param {Object} value The value to set the field to.
13374 set : function(name, value){
13375 if(this.data[name] == value){
13379 if(!this.modified){
13380 this.modified = {};
13382 if(typeof this.modified[name] == 'undefined'){
13383 this.modified[name] = this.data[name];
13385 this.data[name] = value;
13386 if(!this.editing && this.store){
13387 this.store.afterEdit(this);
13392 * Get the value of the named field.
13393 * @param {String} name The name of the field to get the value of.
13394 * @return {Object} The value of the field.
13396 get : function(name){
13397 return this.data[name];
13401 beginEdit : function(){
13402 this.editing = true;
13403 this.modified = {};
13407 cancelEdit : function(){
13408 this.editing = false;
13409 delete this.modified;
13413 endEdit : function(){
13414 this.editing = false;
13415 if(this.dirty && this.store){
13416 this.store.afterEdit(this);
13421 * Usually called by the {@link Roo.data.Store} which owns the Record.
13422 * Rejects all changes made to the Record since either creation, or the last commit operation.
13423 * Modified fields are reverted to their original values.
13425 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13426 * of reject operations.
13428 reject : function(){
13429 var m = this.modified;
13431 if(typeof m[n] != "function"){
13432 this.data[n] = m[n];
13435 this.dirty = false;
13436 delete this.modified;
13437 this.editing = false;
13439 this.store.afterReject(this);
13444 * Usually called by the {@link Roo.data.Store} which owns the Record.
13445 * Commits all changes made to the Record since either creation, or the last commit operation.
13447 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13448 * of commit operations.
13450 commit : function(){
13451 this.dirty = false;
13452 delete this.modified;
13453 this.editing = false;
13455 this.store.afterCommit(this);
13460 hasError : function(){
13461 return this.error != null;
13465 clearError : function(){
13470 * Creates a copy of this record.
13471 * @param {String} id (optional) A new record id if you don't want to use this record's id
13474 copy : function(newId) {
13475 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13479 * Ext JS Library 1.1.1
13480 * Copyright(c) 2006-2007, Ext JS, LLC.
13482 * Originally Released Under LGPL - original licence link has changed is not relivant.
13485 * <script type="text/javascript">
13491 * @class Roo.data.Store
13492 * @extends Roo.util.Observable
13493 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13494 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13496 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
13497 * has no knowledge of the format of the data returned by the Proxy.<br>
13499 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13500 * instances from the data object. These records are cached and made available through accessor functions.
13502 * Creates a new Store.
13503 * @param {Object} config A config object containing the objects needed for the Store to access data,
13504 * and read the data into Records.
13506 Roo.data.Store = function(config){
13507 this.data = new Roo.util.MixedCollection(false);
13508 this.data.getKey = function(o){
13511 this.baseParams = {};
13513 this.paramNames = {
13518 "multisort" : "_multisort"
13521 if(config && config.data){
13522 this.inlineData = config.data;
13523 delete config.data;
13526 Roo.apply(this, config);
13528 if(this.reader){ // reader passed
13529 this.reader = Roo.factory(this.reader, Roo.data);
13530 this.reader.xmodule = this.xmodule || false;
13531 if(!this.recordType){
13532 this.recordType = this.reader.recordType;
13534 if(this.reader.onMetaChange){
13535 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13539 if(this.recordType){
13540 this.fields = this.recordType.prototype.fields;
13542 this.modified = [];
13546 * @event datachanged
13547 * Fires when the data cache has changed, and a widget which is using this Store
13548 * as a Record cache should refresh its view.
13549 * @param {Store} this
13551 datachanged : true,
13553 * @event metachange
13554 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13555 * @param {Store} this
13556 * @param {Object} meta The JSON metadata
13561 * Fires when Records have been added to the Store
13562 * @param {Store} this
13563 * @param {Roo.data.Record[]} records The array of Records added
13564 * @param {Number} index The index at which the record(s) were added
13569 * Fires when a Record has been removed from the Store
13570 * @param {Store} this
13571 * @param {Roo.data.Record} record The Record that was removed
13572 * @param {Number} index The index at which the record was removed
13577 * Fires when a Record has been updated
13578 * @param {Store} this
13579 * @param {Roo.data.Record} record The Record that was updated
13580 * @param {String} operation The update operation being performed. Value may be one of:
13582 Roo.data.Record.EDIT
13583 Roo.data.Record.REJECT
13584 Roo.data.Record.COMMIT
13590 * Fires when the data cache has been cleared.
13591 * @param {Store} this
13595 * @event beforeload
13596 * Fires before a request is made for a new data object. If the beforeload handler returns false
13597 * the load action will be canceled.
13598 * @param {Store} this
13599 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13603 * @event beforeloadadd
13604 * Fires after a new set of Records has been loaded.
13605 * @param {Store} this
13606 * @param {Roo.data.Record[]} records The Records that were loaded
13607 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13609 beforeloadadd : true,
13612 * Fires after a new set of Records has been loaded, before they are added to the store.
13613 * @param {Store} this
13614 * @param {Roo.data.Record[]} records The Records that were loaded
13615 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13616 * @params {Object} return from reader
13620 * @event loadexception
13621 * Fires if an exception occurs in the Proxy during loading.
13622 * Called with the signature of the Proxy's "loadexception" event.
13623 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13626 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13627 * @param {Object} load options
13628 * @param {Object} jsonData from your request (normally this contains the Exception)
13630 loadexception : true
13634 this.proxy = Roo.factory(this.proxy, Roo.data);
13635 this.proxy.xmodule = this.xmodule || false;
13636 this.relayEvents(this.proxy, ["loadexception"]);
13638 this.sortToggle = {};
13639 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13641 Roo.data.Store.superclass.constructor.call(this);
13643 if(this.inlineData){
13644 this.loadData(this.inlineData);
13645 delete this.inlineData;
13649 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13651 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13652 * without a remote query - used by combo/forms at present.
13656 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13659 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13662 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13663 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13666 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13667 * on any HTTP request
13670 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13673 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13677 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13678 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13680 remoteSort : false,
13683 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13684 * loaded or when a record is removed. (defaults to false).
13686 pruneModifiedRecords : false,
13689 lastOptions : null,
13692 * Add Records to the Store and fires the add event.
13693 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13695 add : function(records){
13696 records = [].concat(records);
13697 for(var i = 0, len = records.length; i < len; i++){
13698 records[i].join(this);
13700 var index = this.data.length;
13701 this.data.addAll(records);
13702 this.fireEvent("add", this, records, index);
13706 * Remove a Record from the Store and fires the remove event.
13707 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13709 remove : function(record){
13710 var index = this.data.indexOf(record);
13711 this.data.removeAt(index);
13713 if(this.pruneModifiedRecords){
13714 this.modified.remove(record);
13716 this.fireEvent("remove", this, record, index);
13720 * Remove all Records from the Store and fires the clear event.
13722 removeAll : function(){
13724 if(this.pruneModifiedRecords){
13725 this.modified = [];
13727 this.fireEvent("clear", this);
13731 * Inserts Records to the Store at the given index and fires the add event.
13732 * @param {Number} index The start index at which to insert the passed Records.
13733 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13735 insert : function(index, records){
13736 records = [].concat(records);
13737 for(var i = 0, len = records.length; i < len; i++){
13738 this.data.insert(index, records[i]);
13739 records[i].join(this);
13741 this.fireEvent("add", this, records, index);
13745 * Get the index within the cache of the passed Record.
13746 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13747 * @return {Number} The index of the passed Record. Returns -1 if not found.
13749 indexOf : function(record){
13750 return this.data.indexOf(record);
13754 * Get the index within the cache of the Record with the passed id.
13755 * @param {String} id The id of the Record to find.
13756 * @return {Number} The index of the Record. Returns -1 if not found.
13758 indexOfId : function(id){
13759 return this.data.indexOfKey(id);
13763 * Get the Record with the specified id.
13764 * @param {String} id The id of the Record to find.
13765 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13767 getById : function(id){
13768 return this.data.key(id);
13772 * Get the Record at the specified index.
13773 * @param {Number} index The index of the Record to find.
13774 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13776 getAt : function(index){
13777 return this.data.itemAt(index);
13781 * Returns a range of Records between specified indices.
13782 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13783 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13784 * @return {Roo.data.Record[]} An array of Records
13786 getRange : function(start, end){
13787 return this.data.getRange(start, end);
13791 storeOptions : function(o){
13792 o = Roo.apply({}, o);
13795 this.lastOptions = o;
13799 * Loads the Record cache from the configured Proxy using the configured Reader.
13801 * If using remote paging, then the first load call must specify the <em>start</em>
13802 * and <em>limit</em> properties in the options.params property to establish the initial
13803 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13805 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13806 * and this call will return before the new data has been loaded. Perform any post-processing
13807 * in a callback function, or in a "load" event handler.</strong>
13809 * @param {Object} options An object containing properties which control loading options:<ul>
13810 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13811 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13812 * passed the following arguments:<ul>
13813 * <li>r : Roo.data.Record[]</li>
13814 * <li>options: Options object from the load call</li>
13815 * <li>success: Boolean success indicator</li></ul></li>
13816 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13817 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13820 load : function(options){
13821 options = options || {};
13822 if(this.fireEvent("beforeload", this, options) !== false){
13823 this.storeOptions(options);
13824 var p = Roo.apply(options.params || {}, this.baseParams);
13825 // if meta was not loaded from remote source.. try requesting it.
13826 if (!this.reader.metaFromRemote) {
13827 p._requestMeta = 1;
13829 if(this.sortInfo && this.remoteSort){
13830 var pn = this.paramNames;
13831 p[pn["sort"]] = this.sortInfo.field;
13832 p[pn["dir"]] = this.sortInfo.direction;
13834 if (this.multiSort) {
13835 var pn = this.paramNames;
13836 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13839 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13844 * Reloads the Record cache from the configured Proxy using the configured Reader and
13845 * the options from the last load operation performed.
13846 * @param {Object} options (optional) An object containing properties which may override the options
13847 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13848 * the most recently used options are reused).
13850 reload : function(options){
13851 this.load(Roo.applyIf(options||{}, this.lastOptions));
13855 // Called as a callback by the Reader during a load operation.
13856 loadRecords : function(o, options, success){
13857 if(!o || success === false){
13858 if(success !== false){
13859 this.fireEvent("load", this, [], options, o);
13861 if(options.callback){
13862 options.callback.call(options.scope || this, [], options, false);
13866 // if data returned failure - throw an exception.
13867 if (o.success === false) {
13868 // show a message if no listener is registered.
13869 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13870 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13872 // loadmask wil be hooked into this..
13873 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13876 var r = o.records, t = o.totalRecords || r.length;
13878 this.fireEvent("beforeloadadd", this, r, options, o);
13880 if(!options || options.add !== true){
13881 if(this.pruneModifiedRecords){
13882 this.modified = [];
13884 for(var i = 0, len = r.length; i < len; i++){
13888 this.data = this.snapshot;
13889 delete this.snapshot;
13892 this.data.addAll(r);
13893 this.totalLength = t;
13895 this.fireEvent("datachanged", this);
13897 this.totalLength = Math.max(t, this.data.length+r.length);
13901 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13903 var e = new Roo.data.Record({});
13905 e.set(this.parent.displayField, this.parent.emptyTitle);
13906 e.set(this.parent.valueField, '');
13911 this.fireEvent("load", this, r, options, o);
13912 if(options.callback){
13913 options.callback.call(options.scope || this, r, options, true);
13919 * Loads data from a passed data block. A Reader which understands the format of the data
13920 * must have been configured in the constructor.
13921 * @param {Object} data The data block from which to read the Records. The format of the data expected
13922 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13923 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13925 loadData : function(o, append){
13926 var r = this.reader.readRecords(o);
13927 this.loadRecords(r, {add: append}, true);
13931 * using 'cn' the nested child reader read the child array into it's child stores.
13932 * @param {Object} rec The record with a 'children array
13934 loadDataFromChildren : function(rec)
13936 this.loadData(this.reader.toLoadData(rec));
13941 * Gets the number of cached records.
13943 * <em>If using paging, this may not be the total size of the dataset. If the data object
13944 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13945 * the data set size</em>
13947 getCount : function(){
13948 return this.data.length || 0;
13952 * Gets the total number of records in the dataset as returned by the server.
13954 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13955 * the dataset size</em>
13957 getTotalCount : function(){
13958 return this.totalLength || 0;
13962 * Returns the sort state of the Store as an object with two properties:
13964 field {String} The name of the field by which the Records are sorted
13965 direction {String} The sort order, "ASC" or "DESC"
13968 getSortState : function(){
13969 return this.sortInfo;
13973 applySort : function(){
13974 if(this.sortInfo && !this.remoteSort){
13975 var s = this.sortInfo, f = s.field;
13976 var st = this.fields.get(f).sortType;
13977 var fn = function(r1, r2){
13978 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13979 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13981 this.data.sort(s.direction, fn);
13982 if(this.snapshot && this.snapshot != this.data){
13983 this.snapshot.sort(s.direction, fn);
13989 * Sets the default sort column and order to be used by the next load operation.
13990 * @param {String} fieldName The name of the field to sort by.
13991 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13993 setDefaultSort : function(field, dir){
13994 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13998 * Sort the Records.
13999 * If remote sorting is used, the sort is performed on the server, and the cache is
14000 * reloaded. If local sorting is used, the cache is sorted internally.
14001 * @param {String} fieldName The name of the field to sort by.
14002 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14004 sort : function(fieldName, dir){
14005 var f = this.fields.get(fieldName);
14007 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14009 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14010 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14015 this.sortToggle[f.name] = dir;
14016 this.sortInfo = {field: f.name, direction: dir};
14017 if(!this.remoteSort){
14019 this.fireEvent("datachanged", this);
14021 this.load(this.lastOptions);
14026 * Calls the specified function for each of the Records in the cache.
14027 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14028 * Returning <em>false</em> aborts and exits the iteration.
14029 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14031 each : function(fn, scope){
14032 this.data.each(fn, scope);
14036 * Gets all records modified since the last commit. Modified records are persisted across load operations
14037 * (e.g., during paging).
14038 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14040 getModifiedRecords : function(){
14041 return this.modified;
14045 createFilterFn : function(property, value, anyMatch){
14046 if(!value.exec){ // not a regex
14047 value = String(value);
14048 if(value.length == 0){
14051 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14053 return function(r){
14054 return value.test(r.data[property]);
14059 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14060 * @param {String} property A field on your records
14061 * @param {Number} start The record index to start at (defaults to 0)
14062 * @param {Number} end The last record index to include (defaults to length - 1)
14063 * @return {Number} The sum
14065 sum : function(property, start, end){
14066 var rs = this.data.items, v = 0;
14067 start = start || 0;
14068 end = (end || end === 0) ? end : rs.length-1;
14070 for(var i = start; i <= end; i++){
14071 v += (rs[i].data[property] || 0);
14077 * Filter the records by a specified property.
14078 * @param {String} field A field on your records
14079 * @param {String/RegExp} value Either a string that the field
14080 * should start with or a RegExp to test against the field
14081 * @param {Boolean} anyMatch True to match any part not just the beginning
14083 filter : function(property, value, anyMatch){
14084 var fn = this.createFilterFn(property, value, anyMatch);
14085 return fn ? this.filterBy(fn) : this.clearFilter();
14089 * Filter by a function. The specified function will be called with each
14090 * record in this data source. If the function returns true the record is included,
14091 * otherwise it is filtered.
14092 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14093 * @param {Object} scope (optional) The scope of the function (defaults to this)
14095 filterBy : function(fn, scope){
14096 this.snapshot = this.snapshot || this.data;
14097 this.data = this.queryBy(fn, scope||this);
14098 this.fireEvent("datachanged", this);
14102 * Query the records by a specified property.
14103 * @param {String} field A field on your records
14104 * @param {String/RegExp} value Either a string that the field
14105 * should start with or a RegExp to test against the field
14106 * @param {Boolean} anyMatch True to match any part not just the beginning
14107 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14109 query : function(property, value, anyMatch){
14110 var fn = this.createFilterFn(property, value, anyMatch);
14111 return fn ? this.queryBy(fn) : this.data.clone();
14115 * Query by a function. The specified function will be called with each
14116 * record in this data source. If the function returns true the record is included
14118 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14119 * @param {Object} scope (optional) The scope of the function (defaults to this)
14120 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14122 queryBy : function(fn, scope){
14123 var data = this.snapshot || this.data;
14124 return data.filterBy(fn, scope||this);
14128 * Collects unique values for a particular dataIndex from this store.
14129 * @param {String} dataIndex The property to collect
14130 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14131 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14132 * @return {Array} An array of the unique values
14134 collect : function(dataIndex, allowNull, bypassFilter){
14135 var d = (bypassFilter === true && this.snapshot) ?
14136 this.snapshot.items : this.data.items;
14137 var v, sv, r = [], l = {};
14138 for(var i = 0, len = d.length; i < len; i++){
14139 v = d[i].data[dataIndex];
14141 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14150 * Revert to a view of the Record cache with no filtering applied.
14151 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14153 clearFilter : function(suppressEvent){
14154 if(this.snapshot && this.snapshot != this.data){
14155 this.data = this.snapshot;
14156 delete this.snapshot;
14157 if(suppressEvent !== true){
14158 this.fireEvent("datachanged", this);
14164 afterEdit : function(record){
14165 if(this.modified.indexOf(record) == -1){
14166 this.modified.push(record);
14168 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14172 afterReject : function(record){
14173 this.modified.remove(record);
14174 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14178 afterCommit : function(record){
14179 this.modified.remove(record);
14180 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14184 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14185 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14187 commitChanges : function(){
14188 var m = this.modified.slice(0);
14189 this.modified = [];
14190 for(var i = 0, len = m.length; i < len; i++){
14196 * Cancel outstanding changes on all changed records.
14198 rejectChanges : function(){
14199 var m = this.modified.slice(0);
14200 this.modified = [];
14201 for(var i = 0, len = m.length; i < len; i++){
14206 onMetaChange : function(meta, rtype, o){
14207 this.recordType = rtype;
14208 this.fields = rtype.prototype.fields;
14209 delete this.snapshot;
14210 this.sortInfo = meta.sortInfo || this.sortInfo;
14211 this.modified = [];
14212 this.fireEvent('metachange', this, this.reader.meta);
14215 moveIndex : function(data, type)
14217 var index = this.indexOf(data);
14219 var newIndex = index + type;
14223 this.insert(newIndex, data);
14228 * Ext JS Library 1.1.1
14229 * Copyright(c) 2006-2007, Ext JS, LLC.
14231 * Originally Released Under LGPL - original licence link has changed is not relivant.
14234 * <script type="text/javascript">
14238 * @class Roo.data.SimpleStore
14239 * @extends Roo.data.Store
14240 * Small helper class to make creating Stores from Array data easier.
14241 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14242 * @cfg {Array} fields An array of field definition objects, or field name strings.
14243 * @cfg {Object} an existing reader (eg. copied from another store)
14244 * @cfg {Array} data The multi-dimensional array of data
14246 * @param {Object} config
14248 Roo.data.SimpleStore = function(config)
14250 Roo.data.SimpleStore.superclass.constructor.call(this, {
14252 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14255 Roo.data.Record.create(config.fields)
14257 proxy : new Roo.data.MemoryProxy(config.data)
14261 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14263 * Ext JS Library 1.1.1
14264 * Copyright(c) 2006-2007, Ext JS, LLC.
14266 * Originally Released Under LGPL - original licence link has changed is not relivant.
14269 * <script type="text/javascript">
14274 * @extends Roo.data.Store
14275 * @class Roo.data.JsonStore
14276 * Small helper class to make creating Stores for JSON data easier. <br/>
14278 var store = new Roo.data.JsonStore({
14279 url: 'get-images.php',
14281 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14284 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14285 * JsonReader and HttpProxy (unless inline data is provided).</b>
14286 * @cfg {Array} fields An array of field definition objects, or field name strings.
14288 * @param {Object} config
14290 Roo.data.JsonStore = function(c){
14291 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14292 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14293 reader: new Roo.data.JsonReader(c, c.fields)
14296 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14298 * Ext JS Library 1.1.1
14299 * Copyright(c) 2006-2007, Ext JS, LLC.
14301 * Originally Released Under LGPL - original licence link has changed is not relivant.
14304 * <script type="text/javascript">
14308 Roo.data.Field = function(config){
14309 if(typeof config == "string"){
14310 config = {name: config};
14312 Roo.apply(this, config);
14315 this.type = "auto";
14318 var st = Roo.data.SortTypes;
14319 // named sortTypes are supported, here we look them up
14320 if(typeof this.sortType == "string"){
14321 this.sortType = st[this.sortType];
14324 // set default sortType for strings and dates
14325 if(!this.sortType){
14328 this.sortType = st.asUCString;
14331 this.sortType = st.asDate;
14334 this.sortType = st.none;
14339 var stripRe = /[\$,%]/g;
14341 // prebuilt conversion function for this field, instead of
14342 // switching every time we're reading a value
14344 var cv, dateFormat = this.dateFormat;
14349 cv = function(v){ return v; };
14352 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14356 return v !== undefined && v !== null && v !== '' ?
14357 parseInt(String(v).replace(stripRe, ""), 10) : '';
14362 return v !== undefined && v !== null && v !== '' ?
14363 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14368 cv = function(v){ return v === true || v === "true" || v == 1; };
14375 if(v instanceof Date){
14379 if(dateFormat == "timestamp"){
14380 return new Date(v*1000);
14382 return Date.parseDate(v, dateFormat);
14384 var parsed = Date.parse(v);
14385 return parsed ? new Date(parsed) : null;
14394 Roo.data.Field.prototype = {
14402 * Ext JS Library 1.1.1
14403 * Copyright(c) 2006-2007, Ext JS, LLC.
14405 * Originally Released Under LGPL - original licence link has changed is not relivant.
14408 * <script type="text/javascript">
14411 // Base class for reading structured data from a data source. This class is intended to be
14412 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14415 * @class Roo.data.DataReader
14416 * Base class for reading structured data from a data source. This class is intended to be
14417 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14420 Roo.data.DataReader = function(meta, recordType){
14424 this.recordType = recordType instanceof Array ?
14425 Roo.data.Record.create(recordType) : recordType;
14428 Roo.data.DataReader.prototype = {
14431 readerType : 'Data',
14433 * Create an empty record
14434 * @param {Object} data (optional) - overlay some values
14435 * @return {Roo.data.Record} record created.
14437 newRow : function(d) {
14439 this.recordType.prototype.fields.each(function(c) {
14441 case 'int' : da[c.name] = 0; break;
14442 case 'date' : da[c.name] = new Date(); break;
14443 case 'float' : da[c.name] = 0.0; break;
14444 case 'boolean' : da[c.name] = false; break;
14445 default : da[c.name] = ""; break;
14449 return new this.recordType(Roo.apply(da, d));
14455 * Ext JS Library 1.1.1
14456 * Copyright(c) 2006-2007, Ext JS, LLC.
14458 * Originally Released Under LGPL - original licence link has changed is not relivant.
14461 * <script type="text/javascript">
14465 * @class Roo.data.DataProxy
14466 * @extends Roo.data.Observable
14467 * This class is an abstract base class for implementations which provide retrieval of
14468 * unformatted data objects.<br>
14470 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14471 * (of the appropriate type which knows how to parse the data object) to provide a block of
14472 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14474 * Custom implementations must implement the load method as described in
14475 * {@link Roo.data.HttpProxy#load}.
14477 Roo.data.DataProxy = function(){
14480 * @event beforeload
14481 * Fires before a network request is made to retrieve a data object.
14482 * @param {Object} This DataProxy object.
14483 * @param {Object} params The params parameter to the load function.
14488 * Fires before the load method's callback is called.
14489 * @param {Object} This DataProxy object.
14490 * @param {Object} o The data object.
14491 * @param {Object} arg The callback argument object passed to the load function.
14495 * @event loadexception
14496 * Fires if an Exception occurs during data retrieval.
14497 * @param {Object} This DataProxy object.
14498 * @param {Object} o The data object.
14499 * @param {Object} arg The callback argument object passed to the load function.
14500 * @param {Object} e The Exception.
14502 loadexception : true
14504 Roo.data.DataProxy.superclass.constructor.call(this);
14507 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14510 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14514 * Ext JS Library 1.1.1
14515 * Copyright(c) 2006-2007, Ext JS, LLC.
14517 * Originally Released Under LGPL - original licence link has changed is not relivant.
14520 * <script type="text/javascript">
14523 * @class Roo.data.MemoryProxy
14524 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14525 * to the Reader when its load method is called.
14527 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14529 Roo.data.MemoryProxy = function(data){
14533 Roo.data.MemoryProxy.superclass.constructor.call(this);
14537 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14540 * Load data from the requested source (in this case an in-memory
14541 * data object passed to the constructor), read the data object into
14542 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14543 * process that block using the passed callback.
14544 * @param {Object} params This parameter is not used by the MemoryProxy class.
14545 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14546 * object into a block of Roo.data.Records.
14547 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14548 * The function must be passed <ul>
14549 * <li>The Record block object</li>
14550 * <li>The "arg" argument from the load function</li>
14551 * <li>A boolean success indicator</li>
14553 * @param {Object} scope The scope in which to call the callback
14554 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14556 load : function(params, reader, callback, scope, arg){
14557 params = params || {};
14560 result = reader.readRecords(params.data ? params.data :this.data);
14562 this.fireEvent("loadexception", this, arg, null, e);
14563 callback.call(scope, null, arg, false);
14566 callback.call(scope, result, arg, true);
14570 update : function(params, records){
14575 * Ext JS Library 1.1.1
14576 * Copyright(c) 2006-2007, Ext JS, LLC.
14578 * Originally Released Under LGPL - original licence link has changed is not relivant.
14581 * <script type="text/javascript">
14584 * @class Roo.data.HttpProxy
14585 * @extends Roo.data.DataProxy
14586 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14587 * configured to reference a certain URL.<br><br>
14589 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14590 * from which the running page was served.<br><br>
14592 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14594 * Be aware that to enable the browser to parse an XML document, the server must set
14595 * the Content-Type header in the HTTP response to "text/xml".
14597 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14598 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14599 * will be used to make the request.
14601 Roo.data.HttpProxy = function(conn){
14602 Roo.data.HttpProxy.superclass.constructor.call(this);
14603 // is conn a conn config or a real conn?
14605 this.useAjax = !conn || !conn.events;
14609 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14610 // thse are take from connection...
14613 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14616 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14617 * extra parameters to each request made by this object. (defaults to undefined)
14620 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14621 * to each request made by this object. (defaults to undefined)
14624 * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
14627 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14630 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14636 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14640 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14641 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14642 * a finer-grained basis than the DataProxy events.
14644 getConnection : function(){
14645 return this.useAjax ? Roo.Ajax : this.conn;
14649 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14650 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14651 * process that block using the passed callback.
14652 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14653 * for the request to the remote server.
14654 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14655 * object into a block of Roo.data.Records.
14656 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14657 * The function must be passed <ul>
14658 * <li>The Record block object</li>
14659 * <li>The "arg" argument from the load function</li>
14660 * <li>A boolean success indicator</li>
14662 * @param {Object} scope The scope in which to call the callback
14663 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14665 load : function(params, reader, callback, scope, arg){
14666 if(this.fireEvent("beforeload", this, params) !== false){
14668 params : params || {},
14670 callback : callback,
14675 callback : this.loadResponse,
14679 Roo.applyIf(o, this.conn);
14680 if(this.activeRequest){
14681 Roo.Ajax.abort(this.activeRequest);
14683 this.activeRequest = Roo.Ajax.request(o);
14685 this.conn.request(o);
14688 callback.call(scope||this, null, arg, false);
14693 loadResponse : function(o, success, response){
14694 delete this.activeRequest;
14696 this.fireEvent("loadexception", this, o, response);
14697 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14702 result = o.reader.read(response);
14704 this.fireEvent("loadexception", this, o, response, e);
14705 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14709 this.fireEvent("load", this, o, o.request.arg);
14710 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14714 update : function(dataSet){
14719 updateResponse : function(dataSet){
14724 * Ext JS Library 1.1.1
14725 * Copyright(c) 2006-2007, Ext JS, LLC.
14727 * Originally Released Under LGPL - original licence link has changed is not relivant.
14730 * <script type="text/javascript">
14734 * @class Roo.data.ScriptTagProxy
14735 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14736 * other than the originating domain of the running page.<br><br>
14738 * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
14739 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14741 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14742 * source code that is used as the source inside a <script> tag.<br><br>
14744 * In order for the browser to process the returned data, the server must wrap the data object
14745 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14746 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14747 * depending on whether the callback name was passed:
14750 boolean scriptTag = false;
14751 String cb = request.getParameter("callback");
14754 response.setContentType("text/javascript");
14756 response.setContentType("application/x-json");
14758 Writer out = response.getWriter();
14760 out.write(cb + "(");
14762 out.print(dataBlock.toJsonString());
14769 * @param {Object} config A configuration object.
14771 Roo.data.ScriptTagProxy = function(config){
14772 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14773 Roo.apply(this, config);
14774 this.head = document.getElementsByTagName("head")[0];
14777 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14779 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14781 * @cfg {String} url The URL from which to request the data object.
14784 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14788 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14789 * the server the name of the callback function set up by the load call to process the returned data object.
14790 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14791 * javascript output which calls this named function passing the data object as its only parameter.
14793 callbackParam : "callback",
14795 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14796 * name to the request.
14801 * Load data from the configured URL, read the data object into
14802 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14803 * process that block using the passed callback.
14804 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14805 * for the request to the remote server.
14806 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14807 * object into a block of Roo.data.Records.
14808 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14809 * The function must be passed <ul>
14810 * <li>The Record block object</li>
14811 * <li>The "arg" argument from the load function</li>
14812 * <li>A boolean success indicator</li>
14814 * @param {Object} scope The scope in which to call the callback
14815 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14817 load : function(params, reader, callback, scope, arg){
14818 if(this.fireEvent("beforeload", this, params) !== false){
14820 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14822 var url = this.url;
14823 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14825 url += "&_dc=" + (new Date().getTime());
14827 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14830 cb : "stcCallback"+transId,
14831 scriptId : "stcScript"+transId,
14835 callback : callback,
14841 window[trans.cb] = function(o){
14842 conn.handleResponse(o, trans);
14845 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14847 if(this.autoAbort !== false){
14851 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14853 var script = document.createElement("script");
14854 script.setAttribute("src", url);
14855 script.setAttribute("type", "text/javascript");
14856 script.setAttribute("id", trans.scriptId);
14857 this.head.appendChild(script);
14859 this.trans = trans;
14861 callback.call(scope||this, null, arg, false);
14866 isLoading : function(){
14867 return this.trans ? true : false;
14871 * Abort the current server request.
14873 abort : function(){
14874 if(this.isLoading()){
14875 this.destroyTrans(this.trans);
14880 destroyTrans : function(trans, isLoaded){
14881 this.head.removeChild(document.getElementById(trans.scriptId));
14882 clearTimeout(trans.timeoutId);
14884 window[trans.cb] = undefined;
14886 delete window[trans.cb];
14889 // if hasn't been loaded, wait for load to remove it to prevent script error
14890 window[trans.cb] = function(){
14891 window[trans.cb] = undefined;
14893 delete window[trans.cb];
14900 handleResponse : function(o, trans){
14901 this.trans = false;
14902 this.destroyTrans(trans, true);
14905 result = trans.reader.readRecords(o);
14907 this.fireEvent("loadexception", this, o, trans.arg, e);
14908 trans.callback.call(trans.scope||window, null, trans.arg, false);
14911 this.fireEvent("load", this, o, trans.arg);
14912 trans.callback.call(trans.scope||window, result, trans.arg, true);
14916 handleFailure : function(trans){
14917 this.trans = false;
14918 this.destroyTrans(trans, false);
14919 this.fireEvent("loadexception", this, null, trans.arg);
14920 trans.callback.call(trans.scope||window, null, trans.arg, false);
14924 * Ext JS Library 1.1.1
14925 * Copyright(c) 2006-2007, Ext JS, LLC.
14927 * Originally Released Under LGPL - original licence link has changed is not relivant.
14930 * <script type="text/javascript">
14934 * @class Roo.data.JsonReader
14935 * @extends Roo.data.DataReader
14936 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14937 * based on mappings in a provided Roo.data.Record constructor.
14939 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14940 * in the reply previously.
14945 var RecordDef = Roo.data.Record.create([
14946 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14947 {name: 'occupation'} // This field will use "occupation" as the mapping.
14949 var myReader = new Roo.data.JsonReader({
14950 totalProperty: "results", // The property which contains the total dataset size (optional)
14951 root: "rows", // The property which contains an Array of row objects
14952 id: "id" // The property within each row object that provides an ID for the record (optional)
14956 * This would consume a JSON file like this:
14958 { 'results': 2, 'rows': [
14959 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14960 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14963 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14964 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14965 * paged from the remote server.
14966 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14967 * @cfg {String} root name of the property which contains the Array of row objects.
14968 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14969 * @cfg {Array} fields Array of field definition objects
14971 * Create a new JsonReader
14972 * @param {Object} meta Metadata configuration options
14973 * @param {Object} recordType Either an Array of field definition objects,
14974 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14976 Roo.data.JsonReader = function(meta, recordType){
14979 // set some defaults:
14980 Roo.applyIf(meta, {
14981 totalProperty: 'total',
14982 successProperty : 'success',
14987 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14989 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14991 readerType : 'Json',
14994 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14995 * Used by Store query builder to append _requestMeta to params.
14998 metaFromRemote : false,
15000 * This method is only used by a DataProxy which has retrieved data from a remote server.
15001 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15002 * @return {Object} data A data block which is used by an Roo.data.Store object as
15003 * a cache of Roo.data.Records.
15005 read : function(response){
15006 var json = response.responseText;
15008 var o = /* eval:var:o */ eval("("+json+")");
15010 throw {message: "JsonReader.read: Json object not found"};
15016 this.metaFromRemote = true;
15017 this.meta = o.metaData;
15018 this.recordType = Roo.data.Record.create(o.metaData.fields);
15019 this.onMetaChange(this.meta, this.recordType, o);
15021 return this.readRecords(o);
15024 // private function a store will implement
15025 onMetaChange : function(meta, recordType, o){
15032 simpleAccess: function(obj, subsc) {
15039 getJsonAccessor: function(){
15041 return function(expr) {
15043 return(re.test(expr))
15044 ? new Function("obj", "return obj." + expr)
15049 return Roo.emptyFn;
15054 * Create a data block containing Roo.data.Records from an XML document.
15055 * @param {Object} o An object which contains an Array of row objects in the property specified
15056 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15057 * which contains the total size of the dataset.
15058 * @return {Object} data A data block which is used by an Roo.data.Store object as
15059 * a cache of Roo.data.Records.
15061 readRecords : function(o){
15063 * After any data loads, the raw JSON data is available for further custom processing.
15067 var s = this.meta, Record = this.recordType,
15068 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15070 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15072 if(s.totalProperty) {
15073 this.getTotal = this.getJsonAccessor(s.totalProperty);
15075 if(s.successProperty) {
15076 this.getSuccess = this.getJsonAccessor(s.successProperty);
15078 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15080 var g = this.getJsonAccessor(s.id);
15081 this.getId = function(rec) {
15083 return (r === undefined || r === "") ? null : r;
15086 this.getId = function(){return null;};
15089 for(var jj = 0; jj < fl; jj++){
15091 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15092 this.ef[jj] = this.getJsonAccessor(map);
15096 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15097 if(s.totalProperty){
15098 var vt = parseInt(this.getTotal(o), 10);
15103 if(s.successProperty){
15104 var vs = this.getSuccess(o);
15105 if(vs === false || vs === 'false'){
15110 for(var i = 0; i < c; i++){
15113 var id = this.getId(n);
15114 for(var j = 0; j < fl; j++){
15116 var v = this.ef[j](n);
15118 Roo.log('missing convert for ' + f.name);
15122 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15124 var record = new Record(values, id);
15126 records[i] = record;
15132 totalRecords : totalRecords
15135 // used when loading children.. @see loadDataFromChildren
15136 toLoadData: function(rec)
15138 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15139 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15140 return { data : data, total : data.length };
15145 * Ext JS Library 1.1.1
15146 * Copyright(c) 2006-2007, Ext JS, LLC.
15148 * Originally Released Under LGPL - original licence link has changed is not relivant.
15151 * <script type="text/javascript">
15155 * @class Roo.data.ArrayReader
15156 * @extends Roo.data.DataReader
15157 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15158 * Each element of that Array represents a row of data fields. The
15159 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15160 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15164 var RecordDef = Roo.data.Record.create([
15165 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15166 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15168 var myReader = new Roo.data.ArrayReader({
15169 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15173 * This would consume an Array like this:
15175 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15179 * Create a new JsonReader
15180 * @param {Object} meta Metadata configuration options.
15181 * @param {Object|Array} recordType Either an Array of field definition objects
15183 * @cfg {Array} fields Array of field definition objects
15184 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15185 * as specified to {@link Roo.data.Record#create},
15186 * or an {@link Roo.data.Record} object
15189 * created using {@link Roo.data.Record#create}.
15191 Roo.data.ArrayReader = function(meta, recordType)
15193 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15196 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15199 * Create a data block containing Roo.data.Records from an XML document.
15200 * @param {Object} o An Array of row objects which represents the dataset.
15201 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15202 * a cache of Roo.data.Records.
15204 readRecords : function(o)
15206 var sid = this.meta ? this.meta.id : null;
15207 var recordType = this.recordType, fields = recordType.prototype.fields;
15210 for(var i = 0; i < root.length; i++){
15213 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15214 for(var j = 0, jlen = fields.length; j < jlen; j++){
15215 var f = fields.items[j];
15216 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15217 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15219 values[f.name] = v;
15221 var record = new recordType(values, id);
15223 records[records.length] = record;
15227 totalRecords : records.length
15230 // used when loading children.. @see loadDataFromChildren
15231 toLoadData: function(rec)
15233 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15234 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15245 * @class Roo.bootstrap.ComboBox
15246 * @extends Roo.bootstrap.TriggerField
15247 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15248 * @cfg {Boolean} append (true|false) default false
15249 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15250 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15251 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15252 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15253 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15254 * @cfg {Boolean} animate default true
15255 * @cfg {Boolean} emptyResultText only for touch device
15256 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15257 * @cfg {String} emptyTitle default ''
15258 * @cfg {Number} width fixed with? experimental
15260 * Create a new ComboBox.
15261 * @param {Object} config Configuration options
15263 Roo.bootstrap.ComboBox = function(config){
15264 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15268 * Fires when the dropdown list is expanded
15269 * @param {Roo.bootstrap.ComboBox} combo This combo box
15274 * Fires when the dropdown list is collapsed
15275 * @param {Roo.bootstrap.ComboBox} combo This combo box
15279 * @event beforeselect
15280 * Fires before a list item is selected. Return false to cancel the selection.
15281 * @param {Roo.bootstrap.ComboBox} combo This combo box
15282 * @param {Roo.data.Record} record The data record returned from the underlying store
15283 * @param {Number} index The index of the selected item in the dropdown list
15285 'beforeselect' : true,
15288 * Fires when a list item is selected
15289 * @param {Roo.bootstrap.ComboBox} combo This combo box
15290 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15291 * @param {Number} index The index of the selected item in the dropdown list
15295 * @event beforequery
15296 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15297 * The event object passed has these properties:
15298 * @param {Roo.bootstrap.ComboBox} combo This combo box
15299 * @param {String} query The query
15300 * @param {Boolean} forceAll true to force "all" query
15301 * @param {Boolean} cancel true to cancel the query
15302 * @param {Object} e The query event object
15304 'beforequery': true,
15307 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15308 * @param {Roo.bootstrap.ComboBox} combo This combo box
15313 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15314 * @param {Roo.bootstrap.ComboBox} combo This combo box
15315 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15320 * Fires when the remove value from the combobox array
15321 * @param {Roo.bootstrap.ComboBox} combo This combo box
15325 * @event afterremove
15326 * Fires when the remove value from the combobox array
15327 * @param {Roo.bootstrap.ComboBox} combo This combo box
15329 'afterremove' : true,
15331 * @event specialfilter
15332 * Fires when specialfilter
15333 * @param {Roo.bootstrap.ComboBox} combo This combo box
15335 'specialfilter' : true,
15338 * Fires when tick the element
15339 * @param {Roo.bootstrap.ComboBox} combo This combo box
15343 * @event touchviewdisplay
15344 * Fires when touch view require special display (default is using displayField)
15345 * @param {Roo.bootstrap.ComboBox} combo This combo box
15346 * @param {Object} cfg set html .
15348 'touchviewdisplay' : true
15353 this.tickItems = [];
15355 this.selectedIndex = -1;
15356 if(this.mode == 'local'){
15357 if(config.queryDelay === undefined){
15358 this.queryDelay = 10;
15360 if(config.minChars === undefined){
15366 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15369 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15370 * rendering into an Roo.Editor, defaults to false)
15373 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15374 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15377 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15380 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15381 * the dropdown list (defaults to undefined, with no header element)
15385 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15389 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15391 listWidth: undefined,
15393 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15394 * mode = 'remote' or 'text' if mode = 'local')
15396 displayField: undefined,
15399 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15400 * mode = 'remote' or 'value' if mode = 'local').
15401 * Note: use of a valueField requires the user make a selection
15402 * in order for a value to be mapped.
15404 valueField: undefined,
15406 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15411 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15412 * field's data value (defaults to the underlying DOM element's name)
15414 hiddenName: undefined,
15416 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15420 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15422 selectedClass: 'active',
15425 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15429 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15430 * anchor positions (defaults to 'tl-bl')
15432 listAlign: 'tl-bl?',
15434 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15438 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15439 * query specified by the allQuery config option (defaults to 'query')
15441 triggerAction: 'query',
15443 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15444 * (defaults to 4, does not apply if editable = false)
15448 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15449 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15453 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15454 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15458 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15459 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15463 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15464 * when editable = true (defaults to false)
15466 selectOnFocus:false,
15468 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15470 queryParam: 'query',
15472 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15473 * when mode = 'remote' (defaults to 'Loading...')
15475 loadingText: 'Loading...',
15477 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15481 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15485 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15486 * traditional select (defaults to true)
15490 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15494 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15498 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15499 * listWidth has a higher value)
15503 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15504 * allow the user to set arbitrary text into the field (defaults to false)
15506 forceSelection:false,
15508 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15509 * if typeAhead = true (defaults to 250)
15511 typeAheadDelay : 250,
15513 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15514 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15516 valueNotFoundText : undefined,
15518 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15520 blockFocus : false,
15523 * @cfg {Boolean} disableClear Disable showing of clear button.
15525 disableClear : false,
15527 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15529 alwaysQuery : false,
15532 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15537 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15539 invalidClass : "has-warning",
15542 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15544 validClass : "has-success",
15547 * @cfg {Boolean} specialFilter (true|false) special filter default false
15549 specialFilter : false,
15552 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15554 mobileTouchView : true,
15557 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15559 useNativeIOS : false,
15562 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15564 mobile_restrict_height : false,
15566 ios_options : false,
15578 btnPosition : 'right',
15579 triggerList : true,
15580 showToggleBtn : true,
15582 emptyResultText: 'Empty',
15583 triggerText : 'Select',
15587 // element that contains real text value.. (when hidden is used..)
15589 getAutoCreate : function()
15594 * Render classic select for iso
15597 if(Roo.isIOS && this.useNativeIOS){
15598 cfg = this.getAutoCreateNativeIOS();
15606 if(Roo.isTouch && this.mobileTouchView){
15607 cfg = this.getAutoCreateTouchView();
15614 if(!this.tickable){
15615 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15620 * ComboBox with tickable selections
15623 var align = this.labelAlign || this.parentLabelAlign();
15626 cls : 'form-group roo-combobox-tickable' //input-group
15629 var btn_text_select = '';
15630 var btn_text_done = '';
15631 var btn_text_cancel = '';
15633 if (this.btn_text_show) {
15634 btn_text_select = 'Select';
15635 btn_text_done = 'Done';
15636 btn_text_cancel = 'Cancel';
15641 cls : 'tickable-buttons',
15646 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15647 //html : this.triggerText
15648 html: btn_text_select
15654 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15656 html: btn_text_done
15662 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15664 html: btn_text_cancel
15670 buttons.cn.unshift({
15672 cls: 'roo-select2-search-field-input'
15678 Roo.each(buttons.cn, function(c){
15680 c.cls += ' btn-' + _this.size;
15683 if (_this.disabled) {
15690 style : 'display: contents',
15695 cls: 'form-hidden-field'
15699 cls: 'roo-select2-choices',
15703 cls: 'roo-select2-search-field',
15714 cls: 'roo-select2-container input-group roo-select2-container-multi',
15720 // cls: 'typeahead typeahead-long dropdown-menu',
15721 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15726 if(this.hasFeedback && !this.allowBlank){
15730 cls: 'glyphicon form-control-feedback'
15733 combobox.cn.push(feedback);
15740 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15741 tooltip : 'This field is required'
15743 if (Roo.bootstrap.version == 4) {
15746 style : 'display:none'
15749 if (align ==='left' && this.fieldLabel.length) {
15751 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15758 cls : 'control-label col-form-label',
15759 html : this.fieldLabel
15771 var labelCfg = cfg.cn[1];
15772 var contentCfg = cfg.cn[2];
15775 if(this.indicatorpos == 'right'){
15781 cls : 'control-label col-form-label',
15785 html : this.fieldLabel
15801 labelCfg = cfg.cn[0];
15802 contentCfg = cfg.cn[1];
15806 if(this.labelWidth > 12){
15807 labelCfg.style = "width: " + this.labelWidth + 'px';
15809 if(this.width * 1 > 0){
15810 contentCfg.style = "width: " + this.width + 'px';
15812 if(this.labelWidth < 13 && this.labelmd == 0){
15813 this.labelmd = this.labelWidth;
15816 if(this.labellg > 0){
15817 labelCfg.cls += ' col-lg-' + this.labellg;
15818 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15821 if(this.labelmd > 0){
15822 labelCfg.cls += ' col-md-' + this.labelmd;
15823 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15826 if(this.labelsm > 0){
15827 labelCfg.cls += ' col-sm-' + this.labelsm;
15828 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15831 if(this.labelxs > 0){
15832 labelCfg.cls += ' col-xs-' + this.labelxs;
15833 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15837 } else if ( this.fieldLabel.length) {
15838 // Roo.log(" label");
15843 //cls : 'input-group-addon',
15844 html : this.fieldLabel
15849 if(this.indicatorpos == 'right'){
15853 //cls : 'input-group-addon',
15854 html : this.fieldLabel
15864 // Roo.log(" no label && no align");
15871 ['xs','sm','md','lg'].map(function(size){
15872 if (settings[size]) {
15873 cfg.cls += ' col-' + size + '-' + settings[size];
15881 _initEventsCalled : false,
15884 initEvents: function()
15886 if (this._initEventsCalled) { // as we call render... prevent looping...
15889 this._initEventsCalled = true;
15892 throw "can not find store for combo";
15895 this.indicator = this.indicatorEl();
15897 this.store = Roo.factory(this.store, Roo.data);
15898 this.store.parent = this;
15900 // if we are building from html. then this element is so complex, that we can not really
15901 // use the rendered HTML.
15902 // so we have to trash and replace the previous code.
15903 if (Roo.XComponent.build_from_html) {
15904 // remove this element....
15905 var e = this.el.dom, k=0;
15906 while (e ) { e = e.previousSibling; ++k;}
15911 this.rendered = false;
15913 this.render(this.parent().getChildContainer(true), k);
15916 if(Roo.isIOS && this.useNativeIOS){
15917 this.initIOSView();
15925 if(Roo.isTouch && this.mobileTouchView){
15926 this.initTouchView();
15931 this.initTickableEvents();
15935 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15937 if(this.hiddenName){
15939 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15941 this.hiddenField.dom.value =
15942 this.hiddenValue !== undefined ? this.hiddenValue :
15943 this.value !== undefined ? this.value : '';
15945 // prevent input submission
15946 this.el.dom.removeAttribute('name');
15947 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15952 // this.el.dom.setAttribute('autocomplete', 'off');
15955 var cls = 'x-combo-list';
15957 //this.list = new Roo.Layer({
15958 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15964 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15965 _this.list.setWidth(lw);
15968 this.list.on('mouseover', this.onViewOver, this);
15969 this.list.on('mousemove', this.onViewMove, this);
15970 this.list.on('scroll', this.onViewScroll, this);
15973 this.list.swallowEvent('mousewheel');
15974 this.assetHeight = 0;
15977 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15978 this.assetHeight += this.header.getHeight();
15981 this.innerList = this.list.createChild({cls:cls+'-inner'});
15982 this.innerList.on('mouseover', this.onViewOver, this);
15983 this.innerList.on('mousemove', this.onViewMove, this);
15984 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15986 if(this.allowBlank && !this.pageSize && !this.disableClear){
15987 this.footer = this.list.createChild({cls:cls+'-ft'});
15988 this.pageTb = new Roo.Toolbar(this.footer);
15992 this.footer = this.list.createChild({cls:cls+'-ft'});
15993 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15994 {pageSize: this.pageSize});
15998 if (this.pageTb && this.allowBlank && !this.disableClear) {
16000 this.pageTb.add(new Roo.Toolbar.Fill(), {
16001 cls: 'x-btn-icon x-btn-clear',
16003 handler: function()
16006 _this.clearValue();
16007 _this.onSelect(false, -1);
16012 this.assetHeight += this.footer.getHeight();
16017 this.tpl = Roo.bootstrap.version == 4 ?
16018 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16019 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16022 this.view = new Roo.View(this.list, this.tpl, {
16023 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16025 //this.view.wrapEl.setDisplayed(false);
16026 this.view.on('click', this.onViewClick, this);
16029 this.store.on('beforeload', this.onBeforeLoad, this);
16030 this.store.on('load', this.onLoad, this);
16031 this.store.on('loadexception', this.onLoadException, this);
16033 if(this.resizable){
16034 this.resizer = new Roo.Resizable(this.list, {
16035 pinned:true, handles:'se'
16037 this.resizer.on('resize', function(r, w, h){
16038 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16039 this.listWidth = w;
16040 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16041 this.restrictHeight();
16043 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16046 if(!this.editable){
16047 this.editable = true;
16048 this.setEditable(false);
16053 if (typeof(this.events.add.listeners) != 'undefined') {
16055 this.addicon = this.wrap.createChild(
16056 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16058 this.addicon.on('click', function(e) {
16059 this.fireEvent('add', this);
16062 if (typeof(this.events.edit.listeners) != 'undefined') {
16064 this.editicon = this.wrap.createChild(
16065 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16066 if (this.addicon) {
16067 this.editicon.setStyle('margin-left', '40px');
16069 this.editicon.on('click', function(e) {
16071 // we fire even if inothing is selected..
16072 this.fireEvent('edit', this, this.lastData );
16078 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16079 "up" : function(e){
16080 this.inKeyMode = true;
16084 "down" : function(e){
16085 if(!this.isExpanded()){
16086 this.onTriggerClick();
16088 this.inKeyMode = true;
16093 "enter" : function(e){
16094 // this.onViewClick();
16098 if(this.fireEvent("specialkey", this, e)){
16099 this.onViewClick(false);
16105 "esc" : function(e){
16109 "tab" : function(e){
16112 if(this.fireEvent("specialkey", this, e)){
16113 this.onViewClick(false);
16121 doRelay : function(foo, bar, hname){
16122 if(hname == 'down' || this.scope.isExpanded()){
16123 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16132 this.queryDelay = Math.max(this.queryDelay || 10,
16133 this.mode == 'local' ? 10 : 250);
16136 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16138 if(this.typeAhead){
16139 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16141 if(this.editable !== false){
16142 this.inputEl().on("keyup", this.onKeyUp, this);
16144 if(this.forceSelection){
16145 this.inputEl().on('blur', this.doForce, this);
16149 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16150 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16154 initTickableEvents: function()
16158 if(this.hiddenName){
16160 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16162 this.hiddenField.dom.value =
16163 this.hiddenValue !== undefined ? this.hiddenValue :
16164 this.value !== undefined ? this.value : '';
16166 // prevent input submission
16167 this.el.dom.removeAttribute('name');
16168 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16173 // this.list = this.el.select('ul.dropdown-menu',true).first();
16175 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16176 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16177 if(this.triggerList){
16178 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16181 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16182 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16184 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16185 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16187 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16188 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16190 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16191 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16192 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16195 this.cancelBtn.hide();
16200 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16201 _this.list.setWidth(lw);
16204 this.list.on('mouseover', this.onViewOver, this);
16205 this.list.on('mousemove', this.onViewMove, this);
16207 this.list.on('scroll', this.onViewScroll, this);
16210 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16211 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16214 this.view = new Roo.View(this.list, this.tpl, {
16219 selectedClass: this.selectedClass
16222 //this.view.wrapEl.setDisplayed(false);
16223 this.view.on('click', this.onViewClick, this);
16227 this.store.on('beforeload', this.onBeforeLoad, this);
16228 this.store.on('load', this.onLoad, this);
16229 this.store.on('loadexception', this.onLoadException, this);
16232 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16233 "up" : function(e){
16234 this.inKeyMode = true;
16238 "down" : function(e){
16239 this.inKeyMode = true;
16243 "enter" : function(e){
16244 if(this.fireEvent("specialkey", this, e)){
16245 this.onViewClick(false);
16251 "esc" : function(e){
16252 this.onTickableFooterButtonClick(e, false, false);
16255 "tab" : function(e){
16256 this.fireEvent("specialkey", this, e);
16258 this.onTickableFooterButtonClick(e, false, false);
16265 doRelay : function(e, fn, key){
16266 if(this.scope.isExpanded()){
16267 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16276 this.queryDelay = Math.max(this.queryDelay || 10,
16277 this.mode == 'local' ? 10 : 250);
16280 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16282 if(this.typeAhead){
16283 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16286 if(this.editable !== false){
16287 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16290 this.indicator = this.indicatorEl();
16292 if(this.indicator){
16293 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16294 this.indicator.hide();
16299 onDestroy : function(){
16301 this.view.setStore(null);
16302 this.view.el.removeAllListeners();
16303 this.view.el.remove();
16304 this.view.purgeListeners();
16307 this.list.dom.innerHTML = '';
16311 this.store.un('beforeload', this.onBeforeLoad, this);
16312 this.store.un('load', this.onLoad, this);
16313 this.store.un('loadexception', this.onLoadException, this);
16315 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16319 fireKey : function(e){
16320 if(e.isNavKeyPress() && !this.list.isVisible()){
16321 this.fireEvent("specialkey", this, e);
16326 onResize: function(w, h)
16330 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16332 // if(typeof w != 'number'){
16333 // // we do not handle it!?!?
16336 // var tw = this.trigger.getWidth();
16337 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16338 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16340 // this.inputEl().setWidth( this.adjustWidth('input', x));
16342 // //this.trigger.setStyle('left', x+'px');
16344 // if(this.list && this.listWidth === undefined){
16345 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16346 // this.list.setWidth(lw);
16347 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16355 * Allow or prevent the user from directly editing the field text. If false is passed,
16356 * the user will only be able to select from the items defined in the dropdown list. This method
16357 * is the runtime equivalent of setting the 'editable' config option at config time.
16358 * @param {Boolean} value True to allow the user to directly edit the field text
16360 setEditable : function(value){
16361 if(value == this.editable){
16364 this.editable = value;
16366 this.inputEl().dom.setAttribute('readOnly', true);
16367 this.inputEl().on('mousedown', this.onTriggerClick, this);
16368 this.inputEl().addClass('x-combo-noedit');
16370 this.inputEl().dom.setAttribute('readOnly', false);
16371 this.inputEl().un('mousedown', this.onTriggerClick, this);
16372 this.inputEl().removeClass('x-combo-noedit');
16378 onBeforeLoad : function(combo,opts){
16379 if(!this.hasFocus){
16383 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16385 this.restrictHeight();
16386 this.selectedIndex = -1;
16390 onLoad : function(){
16392 this.hasQuery = false;
16394 if(!this.hasFocus){
16398 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16399 this.loading.hide();
16402 if(this.store.getCount() > 0){
16405 this.restrictHeight();
16406 if(this.lastQuery == this.allQuery){
16407 if(this.editable && !this.tickable){
16408 this.inputEl().dom.select();
16412 !this.selectByValue(this.value, true) &&
16415 !this.store.lastOptions ||
16416 typeof(this.store.lastOptions.add) == 'undefined' ||
16417 this.store.lastOptions.add != true
16420 this.select(0, true);
16423 if(this.autoFocus){
16426 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16427 this.taTask.delay(this.typeAheadDelay);
16431 this.onEmptyResults();
16437 onLoadException : function()
16439 this.hasQuery = false;
16441 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16442 this.loading.hide();
16445 if(this.tickable && this.editable){
16450 // only causes errors at present
16451 //Roo.log(this.store.reader.jsonData);
16452 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16454 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16460 onTypeAhead : function(){
16461 if(this.store.getCount() > 0){
16462 var r = this.store.getAt(0);
16463 var newValue = r.data[this.displayField];
16464 var len = newValue.length;
16465 var selStart = this.getRawValue().length;
16467 if(selStart != len){
16468 this.setRawValue(newValue);
16469 this.selectText(selStart, newValue.length);
16475 onSelect : function(record, index){
16477 if(this.fireEvent('beforeselect', this, record, index) !== false){
16479 this.setFromData(index > -1 ? record.data : false);
16482 this.fireEvent('select', this, record, index);
16487 * Returns the currently selected field value or empty string if no value is set.
16488 * @return {String} value The selected value
16490 getValue : function()
16492 if(Roo.isIOS && this.useNativeIOS){
16493 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16497 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16500 if(this.valueField){
16501 return typeof this.value != 'undefined' ? this.value : '';
16503 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16507 getRawValue : function()
16509 if(Roo.isIOS && this.useNativeIOS){
16510 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16513 var v = this.inputEl().getValue();
16519 * Clears any text/value currently set in the field
16521 clearValue : function(){
16523 if(this.hiddenField){
16524 this.hiddenField.dom.value = '';
16527 this.setRawValue('');
16528 this.lastSelectionText = '';
16529 this.lastData = false;
16531 var close = this.closeTriggerEl();
16542 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16543 * will be displayed in the field. If the value does not match the data value of an existing item,
16544 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16545 * Otherwise the field will be blank (although the value will still be set).
16546 * @param {String} value The value to match
16548 setValue : function(v)
16550 if(Roo.isIOS && this.useNativeIOS){
16551 this.setIOSValue(v);
16561 if(this.valueField){
16562 var r = this.findRecord(this.valueField, v);
16564 text = r.data[this.displayField];
16565 }else if(this.valueNotFoundText !== undefined){
16566 text = this.valueNotFoundText;
16569 this.lastSelectionText = text;
16570 if(this.hiddenField){
16571 this.hiddenField.dom.value = v;
16573 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16576 var close = this.closeTriggerEl();
16579 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16585 * @property {Object} the last set data for the element
16590 * Sets the value of the field based on a object which is related to the record format for the store.
16591 * @param {Object} value the value to set as. or false on reset?
16593 setFromData : function(o){
16600 var dv = ''; // display value
16601 var vv = ''; // value value..
16603 if (this.displayField) {
16604 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16606 // this is an error condition!!!
16607 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16610 if(this.valueField){
16611 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16614 var close = this.closeTriggerEl();
16617 if(dv.length || vv * 1 > 0){
16619 this.blockFocus=true;
16625 if(this.hiddenField){
16626 this.hiddenField.dom.value = vv;
16628 this.lastSelectionText = dv;
16629 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16633 // no hidden field.. - we store the value in 'value', but still display
16634 // display field!!!!
16635 this.lastSelectionText = dv;
16636 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16643 reset : function(){
16644 // overridden so that last data is reset..
16651 this.setValue(this.originalValue);
16652 //this.clearInvalid();
16653 this.lastData = false;
16655 this.view.clearSelections();
16661 findRecord : function(prop, value){
16663 if(this.store.getCount() > 0){
16664 this.store.each(function(r){
16665 if(r.data[prop] == value){
16675 getName: function()
16677 // returns hidden if it's set..
16678 if (!this.rendered) {return ''};
16679 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16683 onViewMove : function(e, t){
16684 this.inKeyMode = false;
16688 onViewOver : function(e, t){
16689 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16692 var item = this.view.findItemFromChild(t);
16695 var index = this.view.indexOf(item);
16696 this.select(index, false);
16701 onViewClick : function(view, doFocus, el, e)
16703 var index = this.view.getSelectedIndexes()[0];
16705 var r = this.store.getAt(index);
16709 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16716 Roo.each(this.tickItems, function(v,k){
16718 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16720 _this.tickItems.splice(k, 1);
16722 if(typeof(e) == 'undefined' && view == false){
16723 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16735 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16736 this.tickItems.push(r.data);
16739 if(typeof(e) == 'undefined' && view == false){
16740 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16747 this.onSelect(r, index);
16749 if(doFocus !== false && !this.blockFocus){
16750 this.inputEl().focus();
16755 restrictHeight : function(){
16756 //this.innerList.dom.style.height = '';
16757 //var inner = this.innerList.dom;
16758 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16759 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16760 //this.list.beginUpdate();
16761 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16762 this.list.alignTo(this.inputEl(), this.listAlign);
16763 this.list.alignTo(this.inputEl(), this.listAlign);
16764 //this.list.endUpdate();
16768 onEmptyResults : function(){
16770 if(this.tickable && this.editable){
16771 this.hasFocus = false;
16772 this.restrictHeight();
16780 * Returns true if the dropdown list is expanded, else false.
16782 isExpanded : function(){
16783 return this.list.isVisible();
16787 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16788 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16789 * @param {String} value The data value of the item to select
16790 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16791 * selected item if it is not currently in view (defaults to true)
16792 * @return {Boolean} True if the value matched an item in the list, else false
16794 selectByValue : function(v, scrollIntoView){
16795 if(v !== undefined && v !== null){
16796 var r = this.findRecord(this.valueField || this.displayField, v);
16798 this.select(this.store.indexOf(r), scrollIntoView);
16806 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16807 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16808 * @param {Number} index The zero-based index of the list item to select
16809 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16810 * selected item if it is not currently in view (defaults to true)
16812 select : function(index, scrollIntoView){
16813 this.selectedIndex = index;
16814 this.view.select(index);
16815 if(scrollIntoView !== false){
16816 var el = this.view.getNode(index);
16818 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16821 this.list.scrollChildIntoView(el, false);
16827 selectNext : function(){
16828 var ct = this.store.getCount();
16830 if(this.selectedIndex == -1){
16832 }else if(this.selectedIndex < ct-1){
16833 this.select(this.selectedIndex+1);
16839 selectPrev : function(){
16840 var ct = this.store.getCount();
16842 if(this.selectedIndex == -1){
16844 }else if(this.selectedIndex != 0){
16845 this.select(this.selectedIndex-1);
16851 onKeyUp : function(e){
16852 if(this.editable !== false && !e.isSpecialKey()){
16853 this.lastKey = e.getKey();
16854 this.dqTask.delay(this.queryDelay);
16859 validateBlur : function(){
16860 return !this.list || !this.list.isVisible();
16864 initQuery : function(){
16866 var v = this.getRawValue();
16868 if(this.tickable && this.editable){
16869 v = this.tickableInputEl().getValue();
16876 doForce : function(){
16877 if(this.inputEl().dom.value.length > 0){
16878 this.inputEl().dom.value =
16879 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16885 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16886 * query allowing the query action to be canceled if needed.
16887 * @param {String} query The SQL query to execute
16888 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16889 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16890 * saved in the current store (defaults to false)
16892 doQuery : function(q, forceAll){
16894 if(q === undefined || q === null){
16899 forceAll: forceAll,
16903 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16908 forceAll = qe.forceAll;
16909 if(forceAll === true || (q.length >= this.minChars)){
16911 this.hasQuery = true;
16913 if(this.lastQuery != q || this.alwaysQuery){
16914 this.lastQuery = q;
16915 if(this.mode == 'local'){
16916 this.selectedIndex = -1;
16918 this.store.clearFilter();
16921 if(this.specialFilter){
16922 this.fireEvent('specialfilter', this);
16927 this.store.filter(this.displayField, q);
16930 this.store.fireEvent("datachanged", this.store);
16937 this.store.baseParams[this.queryParam] = q;
16939 var options = {params : this.getParams(q)};
16942 options.add = true;
16943 options.params.start = this.page * this.pageSize;
16946 this.store.load(options);
16949 * this code will make the page width larger, at the beginning, the list not align correctly,
16950 * we should expand the list on onLoad
16951 * so command out it
16956 this.selectedIndex = -1;
16961 this.loadNext = false;
16965 getParams : function(q){
16967 //p[this.queryParam] = q;
16971 p.limit = this.pageSize;
16977 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16979 collapse : function(){
16980 if(!this.isExpanded()){
16986 this.hasFocus = false;
16990 this.cancelBtn.hide();
16991 this.trigger.show();
16994 this.tickableInputEl().dom.value = '';
16995 this.tickableInputEl().blur();
17000 Roo.get(document).un('mousedown', this.collapseIf, this);
17001 Roo.get(document).un('mousewheel', this.collapseIf, this);
17002 if (!this.editable) {
17003 Roo.get(document).un('keydown', this.listKeyPress, this);
17005 this.fireEvent('collapse', this);
17011 collapseIf : function(e){
17012 var in_combo = e.within(this.el);
17013 var in_list = e.within(this.list);
17014 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17016 if (in_combo || in_list || is_list) {
17017 //e.stopPropagation();
17022 this.onTickableFooterButtonClick(e, false, false);
17030 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17032 expand : function(){
17034 if(this.isExpanded() || !this.hasFocus){
17038 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17039 this.list.setWidth(lw);
17045 this.restrictHeight();
17049 this.tickItems = Roo.apply([], this.item);
17052 this.cancelBtn.show();
17053 this.trigger.hide();
17056 this.tickableInputEl().focus();
17061 Roo.get(document).on('mousedown', this.collapseIf, this);
17062 Roo.get(document).on('mousewheel', this.collapseIf, this);
17063 if (!this.editable) {
17064 Roo.get(document).on('keydown', this.listKeyPress, this);
17067 this.fireEvent('expand', this);
17071 // Implements the default empty TriggerField.onTriggerClick function
17072 onTriggerClick : function(e)
17074 Roo.log('trigger click');
17076 if(this.disabled || !this.triggerList){
17081 this.loadNext = false;
17083 if(this.isExpanded()){
17085 if (!this.blockFocus) {
17086 this.inputEl().focus();
17090 this.hasFocus = true;
17091 if(this.triggerAction == 'all') {
17092 this.doQuery(this.allQuery, true);
17094 this.doQuery(this.getRawValue());
17096 if (!this.blockFocus) {
17097 this.inputEl().focus();
17102 onTickableTriggerClick : function(e)
17109 this.loadNext = false;
17110 this.hasFocus = true;
17112 if(this.triggerAction == 'all') {
17113 this.doQuery(this.allQuery, true);
17115 this.doQuery(this.getRawValue());
17119 onSearchFieldClick : function(e)
17121 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17122 this.onTickableFooterButtonClick(e, false, false);
17126 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17131 this.loadNext = false;
17132 this.hasFocus = true;
17134 if(this.triggerAction == 'all') {
17135 this.doQuery(this.allQuery, true);
17137 this.doQuery(this.getRawValue());
17141 listKeyPress : function(e)
17143 //Roo.log('listkeypress');
17144 // scroll to first matching element based on key pres..
17145 if (e.isSpecialKey()) {
17148 var k = String.fromCharCode(e.getKey()).toUpperCase();
17151 var csel = this.view.getSelectedNodes();
17152 var cselitem = false;
17154 var ix = this.view.indexOf(csel[0]);
17155 cselitem = this.store.getAt(ix);
17156 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17162 this.store.each(function(v) {
17164 // start at existing selection.
17165 if (cselitem.id == v.id) {
17171 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17172 match = this.store.indexOf(v);
17178 if (match === false) {
17179 return true; // no more action?
17182 this.view.select(match);
17183 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17184 sn.scrollIntoView(sn.dom.parentNode, false);
17187 onViewScroll : function(e, t){
17189 if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
17193 this.hasQuery = true;
17195 this.loading = this.list.select('.loading', true).first();
17197 if(this.loading === null){
17198 this.list.createChild({
17200 cls: 'loading roo-select2-more-results roo-select2-active',
17201 html: 'Loading more results...'
17204 this.loading = this.list.select('.loading', true).first();
17206 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17208 this.loading.hide();
17211 this.loading.show();
17216 this.loadNext = true;
17218 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17223 addItem : function(o)
17225 var dv = ''; // display value
17227 if (this.displayField) {
17228 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17230 // this is an error condition!!!
17231 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17238 var choice = this.choices.createChild({
17240 cls: 'roo-select2-search-choice',
17249 cls: 'roo-select2-search-choice-close fa fa-times',
17254 }, this.searchField);
17256 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17258 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17266 this.inputEl().dom.value = '';
17271 onRemoveItem : function(e, _self, o)
17273 e.preventDefault();
17275 this.lastItem = Roo.apply([], this.item);
17277 var index = this.item.indexOf(o.data) * 1;
17280 Roo.log('not this item?!');
17284 this.item.splice(index, 1);
17289 this.fireEvent('remove', this, e);
17295 syncValue : function()
17297 if(!this.item.length){
17304 Roo.each(this.item, function(i){
17305 if(_this.valueField){
17306 value.push(i[_this.valueField]);
17313 this.value = value.join(',');
17315 if(this.hiddenField){
17316 this.hiddenField.dom.value = this.value;
17319 this.store.fireEvent("datachanged", this.store);
17324 clearItem : function()
17326 if(!this.multiple){
17332 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17340 if(this.tickable && !Roo.isTouch){
17341 this.view.refresh();
17345 inputEl: function ()
17347 if(Roo.isIOS && this.useNativeIOS){
17348 return this.el.select('select.roo-ios-select', true).first();
17351 if(Roo.isTouch && this.mobileTouchView){
17352 return this.el.select('input.form-control',true).first();
17356 return this.searchField;
17359 return this.el.select('input.form-control',true).first();
17362 onTickableFooterButtonClick : function(e, btn, el)
17364 e.preventDefault();
17366 this.lastItem = Roo.apply([], this.item);
17368 if(btn && btn.name == 'cancel'){
17369 this.tickItems = Roo.apply([], this.item);
17378 Roo.each(this.tickItems, function(o){
17386 validate : function()
17388 if(this.getVisibilityEl().hasClass('hidden')){
17392 var v = this.getRawValue();
17395 v = this.getValue();
17398 if(this.disabled || this.allowBlank || v.length){
17403 this.markInvalid();
17407 tickableInputEl : function()
17409 if(!this.tickable || !this.editable){
17410 return this.inputEl();
17413 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17417 getAutoCreateTouchView : function()
17422 cls: 'form-group' //input-group
17428 type : this.inputType,
17429 cls : 'form-control x-combo-noedit',
17430 autocomplete: 'new-password',
17431 placeholder : this.placeholder || '',
17436 input.name = this.name;
17440 input.cls += ' input-' + this.size;
17443 if (this.disabled) {
17444 input.disabled = true;
17448 cls : 'roo-combobox-wrap',
17455 inputblock.cls += ' input-group';
17457 inputblock.cn.unshift({
17459 cls : 'input-group-addon input-group-prepend input-group-text',
17464 if(this.removable && !this.multiple){
17465 inputblock.cls += ' roo-removable';
17467 inputblock.cn.push({
17470 cls : 'roo-combo-removable-btn close'
17474 if(this.hasFeedback && !this.allowBlank){
17476 inputblock.cls += ' has-feedback';
17478 inputblock.cn.push({
17480 cls: 'glyphicon form-control-feedback'
17487 inputblock.cls += (this.before) ? '' : ' input-group';
17489 inputblock.cn.push({
17491 cls : 'input-group-addon input-group-append input-group-text',
17497 var ibwrap = inputblock;
17502 cls: 'roo-select2-choices',
17506 cls: 'roo-select2-search-field',
17519 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17524 cls: 'form-hidden-field'
17530 if(!this.multiple && this.showToggleBtn){
17536 if (this.caret != false) {
17539 cls: 'fa fa-' + this.caret
17546 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17548 Roo.bootstrap.version == 3 ? caret : '',
17551 cls: 'combobox-clear',
17565 combobox.cls += ' roo-select2-container-multi';
17568 var align = this.labelAlign || this.parentLabelAlign();
17570 if (align ==='left' && this.fieldLabel.length) {
17575 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17576 tooltip : 'This field is required'
17580 cls : 'control-label col-form-label',
17581 html : this.fieldLabel
17585 cls : 'roo-combobox-wrap ',
17592 var labelCfg = cfg.cn[1];
17593 var contentCfg = cfg.cn[2];
17596 if(this.indicatorpos == 'right'){
17601 cls : 'control-label col-form-label',
17605 html : this.fieldLabel
17609 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17610 tooltip : 'This field is required'
17615 cls : "roo-combobox-wrap ",
17623 labelCfg = cfg.cn[0];
17624 contentCfg = cfg.cn[1];
17629 if(this.labelWidth > 12){
17630 labelCfg.style = "width: " + this.labelWidth + 'px';
17633 if(this.labelWidth < 13 && this.labelmd == 0){
17634 this.labelmd = this.labelWidth;
17637 if(this.labellg > 0){
17638 labelCfg.cls += ' col-lg-' + this.labellg;
17639 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17642 if(this.labelmd > 0){
17643 labelCfg.cls += ' col-md-' + this.labelmd;
17644 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17647 if(this.labelsm > 0){
17648 labelCfg.cls += ' col-sm-' + this.labelsm;
17649 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17652 if(this.labelxs > 0){
17653 labelCfg.cls += ' col-xs-' + this.labelxs;
17654 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17658 } else if ( this.fieldLabel.length) {
17662 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17663 tooltip : 'This field is required'
17667 cls : 'control-label',
17668 html : this.fieldLabel
17679 if(this.indicatorpos == 'right'){
17683 cls : 'control-label',
17684 html : this.fieldLabel,
17688 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17689 tooltip : 'This field is required'
17706 var settings = this;
17708 ['xs','sm','md','lg'].map(function(size){
17709 if (settings[size]) {
17710 cfg.cls += ' col-' + size + '-' + settings[size];
17717 initTouchView : function()
17719 this.renderTouchView();
17721 this.touchViewEl.on('scroll', function(){
17722 this.el.dom.scrollTop = 0;
17725 this.originalValue = this.getValue();
17727 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17729 this.inputEl().on("click", this.showTouchView, this);
17730 if (this.triggerEl) {
17731 this.triggerEl.on("click", this.showTouchView, this);
17735 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17736 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17738 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17740 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17741 this.store.on('load', this.onTouchViewLoad, this);
17742 this.store.on('loadexception', this.onTouchViewLoadException, this);
17744 if(this.hiddenName){
17746 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17748 this.hiddenField.dom.value =
17749 this.hiddenValue !== undefined ? this.hiddenValue :
17750 this.value !== undefined ? this.value : '';
17752 this.el.dom.removeAttribute('name');
17753 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17757 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17758 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17761 if(this.removable && !this.multiple){
17762 var close = this.closeTriggerEl();
17764 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17765 close.on('click', this.removeBtnClick, this, close);
17769 * fix the bug in Safari iOS8
17771 this.inputEl().on("focus", function(e){
17772 document.activeElement.blur();
17775 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17782 renderTouchView : function()
17784 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17785 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17787 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17788 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17790 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17791 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17792 this.touchViewBodyEl.setStyle('overflow', 'auto');
17794 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17795 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17797 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17798 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17802 showTouchView : function()
17808 this.touchViewHeaderEl.hide();
17810 if(this.modalTitle.length){
17811 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17812 this.touchViewHeaderEl.show();
17815 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17816 this.touchViewEl.show();
17818 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17820 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17821 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17823 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17825 if(this.modalTitle.length){
17826 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17829 this.touchViewBodyEl.setHeight(bodyHeight);
17833 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17835 this.touchViewEl.addClass(['in','show']);
17838 if(this._touchViewMask){
17839 Roo.get(document.body).addClass("x-body-masked");
17840 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17841 this._touchViewMask.setStyle('z-index', 10000);
17842 this._touchViewMask.addClass('show');
17845 this.doTouchViewQuery();
17849 hideTouchView : function()
17851 this.touchViewEl.removeClass(['in','show']);
17855 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17857 this.touchViewEl.setStyle('display', 'none');
17860 if(this._touchViewMask){
17861 this._touchViewMask.removeClass('show');
17862 Roo.get(document.body).removeClass("x-body-masked");
17866 setTouchViewValue : function()
17873 Roo.each(this.tickItems, function(o){
17878 this.hideTouchView();
17881 doTouchViewQuery : function()
17890 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17894 if(!this.alwaysQuery || this.mode == 'local'){
17895 this.onTouchViewLoad();
17902 onTouchViewBeforeLoad : function(combo,opts)
17908 onTouchViewLoad : function()
17910 if(this.store.getCount() < 1){
17911 this.onTouchViewEmptyResults();
17915 this.clearTouchView();
17917 var rawValue = this.getRawValue();
17919 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17921 this.tickItems = [];
17923 this.store.data.each(function(d, rowIndex){
17924 var row = this.touchViewListGroup.createChild(template);
17926 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17927 row.addClass(d.data.cls);
17930 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17933 html : d.data[this.displayField]
17936 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17937 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17940 row.removeClass('selected');
17941 if(!this.multiple && this.valueField &&
17942 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17945 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17946 row.addClass('selected');
17949 if(this.multiple && this.valueField &&
17950 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17954 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17955 this.tickItems.push(d.data);
17958 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17962 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17964 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17966 if(this.modalTitle.length){
17967 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17970 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17972 if(this.mobile_restrict_height && listHeight < bodyHeight){
17973 this.touchViewBodyEl.setHeight(listHeight);
17978 if(firstChecked && listHeight > bodyHeight){
17979 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17984 onTouchViewLoadException : function()
17986 this.hideTouchView();
17989 onTouchViewEmptyResults : function()
17991 this.clearTouchView();
17993 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17995 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17999 clearTouchView : function()
18001 this.touchViewListGroup.dom.innerHTML = '';
18004 onTouchViewClick : function(e, el, o)
18006 e.preventDefault();
18009 var rowIndex = o.rowIndex;
18011 var r = this.store.getAt(rowIndex);
18013 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18015 if(!this.multiple){
18016 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18017 c.dom.removeAttribute('checked');
18020 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18022 this.setFromData(r.data);
18024 var close = this.closeTriggerEl();
18030 this.hideTouchView();
18032 this.fireEvent('select', this, r, rowIndex);
18037 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18038 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18039 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18043 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18044 this.addItem(r.data);
18045 this.tickItems.push(r.data);
18049 getAutoCreateNativeIOS : function()
18052 cls: 'form-group' //input-group,
18057 cls : 'roo-ios-select'
18061 combobox.name = this.name;
18064 if (this.disabled) {
18065 combobox.disabled = true;
18068 var settings = this;
18070 ['xs','sm','md','lg'].map(function(size){
18071 if (settings[size]) {
18072 cfg.cls += ' col-' + size + '-' + settings[size];
18082 initIOSView : function()
18084 this.store.on('load', this.onIOSViewLoad, this);
18089 onIOSViewLoad : function()
18091 if(this.store.getCount() < 1){
18095 this.clearIOSView();
18097 if(this.allowBlank) {
18099 var default_text = '-- SELECT --';
18101 if(this.placeholder.length){
18102 default_text = this.placeholder;
18105 if(this.emptyTitle.length){
18106 default_text += ' - ' + this.emptyTitle + ' -';
18109 var opt = this.inputEl().createChild({
18112 html : default_text
18116 o[this.valueField] = 0;
18117 o[this.displayField] = default_text;
18119 this.ios_options.push({
18126 this.store.data.each(function(d, rowIndex){
18130 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18131 html = d.data[this.displayField];
18136 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18137 value = d.data[this.valueField];
18146 if(this.value == d.data[this.valueField]){
18147 option['selected'] = true;
18150 var opt = this.inputEl().createChild(option);
18152 this.ios_options.push({
18159 this.inputEl().on('change', function(){
18160 this.fireEvent('select', this);
18165 clearIOSView: function()
18167 this.inputEl().dom.innerHTML = '';
18169 this.ios_options = [];
18172 setIOSValue: function(v)
18176 if(!this.ios_options){
18180 Roo.each(this.ios_options, function(opts){
18182 opts.el.dom.removeAttribute('selected');
18184 if(opts.data[this.valueField] != v){
18188 opts.el.dom.setAttribute('selected', true);
18194 * @cfg {Boolean} grow
18198 * @cfg {Number} growMin
18202 * @cfg {Number} growMax
18211 Roo.apply(Roo.bootstrap.ComboBox, {
18215 cls: 'modal-header',
18237 cls: 'list-group-item',
18241 cls: 'roo-combobox-list-group-item-value'
18245 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18259 listItemCheckbox : {
18261 cls: 'list-group-item',
18265 cls: 'roo-combobox-list-group-item-value'
18269 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18285 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18290 cls: 'modal-footer',
18298 cls: 'col-xs-6 text-left',
18301 cls: 'btn btn-danger roo-touch-view-cancel',
18307 cls: 'col-xs-6 text-right',
18310 cls: 'btn btn-success roo-touch-view-ok',
18321 Roo.apply(Roo.bootstrap.ComboBox, {
18323 touchViewTemplate : {
18325 cls: 'modal fade roo-combobox-touch-view',
18329 cls: 'modal-dialog',
18330 style : 'position:fixed', // we have to fix position....
18334 cls: 'modal-content',
18336 Roo.bootstrap.ComboBox.header,
18337 Roo.bootstrap.ComboBox.body,
18338 Roo.bootstrap.ComboBox.footer
18347 * Ext JS Library 1.1.1
18348 * Copyright(c) 2006-2007, Ext JS, LLC.
18350 * Originally Released Under LGPL - original licence link has changed is not relivant.
18353 * <script type="text/javascript">
18358 * @extends Roo.util.Observable
18359 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18360 * This class also supports single and multi selection modes. <br>
18361 * Create a data model bound view:
18363 var store = new Roo.data.Store(...);
18365 var view = new Roo.View({
18367 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18369 singleSelect: true,
18370 selectedClass: "ydataview-selected",
18374 // listen for node click?
18375 view.on("click", function(vw, index, node, e){
18376 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18380 dataModel.load("foobar.xml");
18382 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18384 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18385 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18387 * Note: old style constructor is still suported (container, template, config)
18390 * Create a new View
18391 * @param {Object} config The config object
18394 Roo.View = function(config, depreciated_tpl, depreciated_config){
18396 this.parent = false;
18398 if (typeof(depreciated_tpl) == 'undefined') {
18399 // new way.. - universal constructor.
18400 Roo.apply(this, config);
18401 this.el = Roo.get(this.el);
18404 this.el = Roo.get(config);
18405 this.tpl = depreciated_tpl;
18406 Roo.apply(this, depreciated_config);
18408 this.wrapEl = this.el.wrap().wrap();
18409 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18412 if(typeof(this.tpl) == "string"){
18413 this.tpl = new Roo.Template(this.tpl);
18415 // support xtype ctors..
18416 this.tpl = new Roo.factory(this.tpl, Roo);
18420 this.tpl.compile();
18425 * @event beforeclick
18426 * Fires before a click is processed. Returns false to cancel the default action.
18427 * @param {Roo.View} this
18428 * @param {Number} index The index of the target node
18429 * @param {HTMLElement} node The target node
18430 * @param {Roo.EventObject} e The raw event object
18432 "beforeclick" : true,
18435 * Fires when a template node is clicked.
18436 * @param {Roo.View} this
18437 * @param {Number} index The index of the target node
18438 * @param {HTMLElement} node The target node
18439 * @param {Roo.EventObject} e The raw event object
18444 * Fires when a template node is double clicked.
18445 * @param {Roo.View} this
18446 * @param {Number} index The index of the target node
18447 * @param {HTMLElement} node The target node
18448 * @param {Roo.EventObject} e The raw event object
18452 * @event contextmenu
18453 * Fires when a template node is right clicked.
18454 * @param {Roo.View} this
18455 * @param {Number} index The index of the target node
18456 * @param {HTMLElement} node The target node
18457 * @param {Roo.EventObject} e The raw event object
18459 "contextmenu" : true,
18461 * @event selectionchange
18462 * Fires when the selected nodes change.
18463 * @param {Roo.View} this
18464 * @param {Array} selections Array of the selected nodes
18466 "selectionchange" : true,
18469 * @event beforeselect
18470 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18471 * @param {Roo.View} this
18472 * @param {HTMLElement} node The node to be selected
18473 * @param {Array} selections Array of currently selected nodes
18475 "beforeselect" : true,
18477 * @event preparedata
18478 * Fires on every row to render, to allow you to change the data.
18479 * @param {Roo.View} this
18480 * @param {Object} data to be rendered (change this)
18482 "preparedata" : true
18490 "click": this.onClick,
18491 "dblclick": this.onDblClick,
18492 "contextmenu": this.onContextMenu,
18496 this.selections = [];
18498 this.cmp = new Roo.CompositeElementLite([]);
18500 this.store = Roo.factory(this.store, Roo.data);
18501 this.setStore(this.store, true);
18504 if ( this.footer && this.footer.xtype) {
18506 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18508 this.footer.dataSource = this.store;
18509 this.footer.container = fctr;
18510 this.footer = Roo.factory(this.footer, Roo);
18511 fctr.insertFirst(this.el);
18513 // this is a bit insane - as the paging toolbar seems to detach the el..
18514 // dom.parentNode.parentNode.parentNode
18515 // they get detached?
18519 Roo.View.superclass.constructor.call(this);
18524 Roo.extend(Roo.View, Roo.util.Observable, {
18527 * @cfg {Roo.data.Store} store Data store to load data from.
18532 * @cfg {String|Roo.Element} el The container element.
18537 * @cfg {String|Roo.Template} tpl The template used by this View
18541 * @cfg {String} dataName the named area of the template to use as the data area
18542 * Works with domtemplates roo-name="name"
18546 * @cfg {String} selectedClass The css class to add to selected nodes
18548 selectedClass : "x-view-selected",
18550 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18555 * @cfg {String} text to display on mask (default Loading)
18559 * @cfg {Boolean} multiSelect Allow multiple selection
18561 multiSelect : false,
18563 * @cfg {Boolean} singleSelect Allow single selection
18565 singleSelect: false,
18568 * @cfg {Boolean} toggleSelect - selecting
18570 toggleSelect : false,
18573 * @cfg {Boolean} tickable - selecting
18578 * Returns the element this view is bound to.
18579 * @return {Roo.Element}
18581 getEl : function(){
18582 return this.wrapEl;
18588 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18590 refresh : function(){
18591 //Roo.log('refresh');
18594 // if we are using something like 'domtemplate', then
18595 // the what gets used is:
18596 // t.applySubtemplate(NAME, data, wrapping data..)
18597 // the outer template then get' applied with
18598 // the store 'extra data'
18599 // and the body get's added to the
18600 // roo-name="data" node?
18601 // <span class='roo-tpl-{name}'></span> ?????
18605 this.clearSelections();
18606 this.el.update("");
18608 var records = this.store.getRange();
18609 if(records.length < 1) {
18611 // is this valid?? = should it render a template??
18613 this.el.update(this.emptyText);
18617 if (this.dataName) {
18618 this.el.update(t.apply(this.store.meta)); //????
18619 el = this.el.child('.roo-tpl-' + this.dataName);
18622 for(var i = 0, len = records.length; i < len; i++){
18623 var data = this.prepareData(records[i].data, i, records[i]);
18624 this.fireEvent("preparedata", this, data, i, records[i]);
18626 var d = Roo.apply({}, data);
18629 Roo.apply(d, {'roo-id' : Roo.id()});
18633 Roo.each(this.parent.item, function(item){
18634 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18637 Roo.apply(d, {'roo-data-checked' : 'checked'});
18641 html[html.length] = Roo.util.Format.trim(
18643 t.applySubtemplate(this.dataName, d, this.store.meta) :
18650 el.update(html.join(""));
18651 this.nodes = el.dom.childNodes;
18652 this.updateIndexes(0);
18657 * Function to override to reformat the data that is sent to
18658 * the template for each node.
18659 * DEPRICATED - use the preparedata event handler.
18660 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18661 * a JSON object for an UpdateManager bound view).
18663 prepareData : function(data, index, record)
18665 this.fireEvent("preparedata", this, data, index, record);
18669 onUpdate : function(ds, record){
18670 // Roo.log('on update');
18671 this.clearSelections();
18672 var index = this.store.indexOf(record);
18673 var n = this.nodes[index];
18674 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18675 n.parentNode.removeChild(n);
18676 this.updateIndexes(index, index);
18682 onAdd : function(ds, records, index)
18684 //Roo.log(['on Add', ds, records, index] );
18685 this.clearSelections();
18686 if(this.nodes.length == 0){
18690 var n = this.nodes[index];
18691 for(var i = 0, len = records.length; i < len; i++){
18692 var d = this.prepareData(records[i].data, i, records[i]);
18694 this.tpl.insertBefore(n, d);
18697 this.tpl.append(this.el, d);
18700 this.updateIndexes(index);
18703 onRemove : function(ds, record, index){
18704 // Roo.log('onRemove');
18705 this.clearSelections();
18706 var el = this.dataName ?
18707 this.el.child('.roo-tpl-' + this.dataName) :
18710 el.dom.removeChild(this.nodes[index]);
18711 this.updateIndexes(index);
18715 * Refresh an individual node.
18716 * @param {Number} index
18718 refreshNode : function(index){
18719 this.onUpdate(this.store, this.store.getAt(index));
18722 updateIndexes : function(startIndex, endIndex){
18723 var ns = this.nodes;
18724 startIndex = startIndex || 0;
18725 endIndex = endIndex || ns.length - 1;
18726 for(var i = startIndex; i <= endIndex; i++){
18727 ns[i].nodeIndex = i;
18732 * Changes the data store this view uses and refresh the view.
18733 * @param {Store} store
18735 setStore : function(store, initial){
18736 if(!initial && this.store){
18737 this.store.un("datachanged", this.refresh);
18738 this.store.un("add", this.onAdd);
18739 this.store.un("remove", this.onRemove);
18740 this.store.un("update", this.onUpdate);
18741 this.store.un("clear", this.refresh);
18742 this.store.un("beforeload", this.onBeforeLoad);
18743 this.store.un("load", this.onLoad);
18744 this.store.un("loadexception", this.onLoad);
18748 store.on("datachanged", this.refresh, this);
18749 store.on("add", this.onAdd, this);
18750 store.on("remove", this.onRemove, this);
18751 store.on("update", this.onUpdate, this);
18752 store.on("clear", this.refresh, this);
18753 store.on("beforeload", this.onBeforeLoad, this);
18754 store.on("load", this.onLoad, this);
18755 store.on("loadexception", this.onLoad, this);
18763 * onbeforeLoad - masks the loading area.
18766 onBeforeLoad : function(store,opts)
18768 //Roo.log('onBeforeLoad');
18770 this.el.update("");
18772 this.el.mask(this.mask ? this.mask : "Loading" );
18774 onLoad : function ()
18781 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18782 * @param {HTMLElement} node
18783 * @return {HTMLElement} The template node
18785 findItemFromChild : function(node){
18786 var el = this.dataName ?
18787 this.el.child('.roo-tpl-' + this.dataName,true) :
18790 if(!node || node.parentNode == el){
18793 var p = node.parentNode;
18794 while(p && p != el){
18795 if(p.parentNode == el){
18804 onClick : function(e){
18805 var item = this.findItemFromChild(e.getTarget());
18807 var index = this.indexOf(item);
18808 if(this.onItemClick(item, index, e) !== false){
18809 this.fireEvent("click", this, index, item, e);
18812 this.clearSelections();
18817 onContextMenu : function(e){
18818 var item = this.findItemFromChild(e.getTarget());
18820 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18825 onDblClick : function(e){
18826 var item = this.findItemFromChild(e.getTarget());
18828 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18832 onItemClick : function(item, index, e)
18834 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18837 if (this.toggleSelect) {
18838 var m = this.isSelected(item) ? 'unselect' : 'select';
18841 _t[m](item, true, false);
18844 if(this.multiSelect || this.singleSelect){
18845 if(this.multiSelect && e.shiftKey && this.lastSelection){
18846 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18848 this.select(item, this.multiSelect && e.ctrlKey);
18849 this.lastSelection = item;
18852 if(!this.tickable){
18853 e.preventDefault();
18861 * Get the number of selected nodes.
18864 getSelectionCount : function(){
18865 return this.selections.length;
18869 * Get the currently selected nodes.
18870 * @return {Array} An array of HTMLElements
18872 getSelectedNodes : function(){
18873 return this.selections;
18877 * Get the indexes of the selected nodes.
18880 getSelectedIndexes : function(){
18881 var indexes = [], s = this.selections;
18882 for(var i = 0, len = s.length; i < len; i++){
18883 indexes.push(s[i].nodeIndex);
18889 * Clear all selections
18890 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18892 clearSelections : function(suppressEvent){
18893 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18894 this.cmp.elements = this.selections;
18895 this.cmp.removeClass(this.selectedClass);
18896 this.selections = [];
18897 if(!suppressEvent){
18898 this.fireEvent("selectionchange", this, this.selections);
18904 * Returns true if the passed node is selected
18905 * @param {HTMLElement/Number} node The node or node index
18906 * @return {Boolean}
18908 isSelected : function(node){
18909 var s = this.selections;
18913 node = this.getNode(node);
18914 return s.indexOf(node) !== -1;
18919 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
18920 * @param {Boolean} keepExisting (optional) true to keep existing selections
18921 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18923 select : function(nodeInfo, keepExisting, suppressEvent){
18924 if(nodeInfo instanceof Array){
18926 this.clearSelections(true);
18928 for(var i = 0, len = nodeInfo.length; i < len; i++){
18929 this.select(nodeInfo[i], true, true);
18933 var node = this.getNode(nodeInfo);
18934 if(!node || this.isSelected(node)){
18935 return; // already selected.
18938 this.clearSelections(true);
18941 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18942 Roo.fly(node).addClass(this.selectedClass);
18943 this.selections.push(node);
18944 if(!suppressEvent){
18945 this.fireEvent("selectionchange", this, this.selections);
18953 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
18954 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18955 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18957 unselect : function(nodeInfo, keepExisting, suppressEvent)
18959 if(nodeInfo instanceof Array){
18960 Roo.each(this.selections, function(s) {
18961 this.unselect(s, nodeInfo);
18965 var node = this.getNode(nodeInfo);
18966 if(!node || !this.isSelected(node)){
18967 //Roo.log("not selected");
18968 return; // not selected.
18972 Roo.each(this.selections, function(s) {
18974 Roo.fly(node).removeClass(this.selectedClass);
18981 this.selections= ns;
18982 this.fireEvent("selectionchange", this, this.selections);
18986 * Gets a template node.
18987 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18988 * @return {HTMLElement} The node or null if it wasn't found
18990 getNode : function(nodeInfo){
18991 if(typeof nodeInfo == "string"){
18992 return document.getElementById(nodeInfo);
18993 }else if(typeof nodeInfo == "number"){
18994 return this.nodes[nodeInfo];
19000 * Gets a range template nodes.
19001 * @param {Number} startIndex
19002 * @param {Number} endIndex
19003 * @return {Array} An array of nodes
19005 getNodes : function(start, end){
19006 var ns = this.nodes;
19007 start = start || 0;
19008 end = typeof end == "undefined" ? ns.length - 1 : end;
19011 for(var i = start; i <= end; i++){
19015 for(var i = start; i >= end; i--){
19023 * Finds the index of the passed node
19024 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19025 * @return {Number} The index of the node or -1
19027 indexOf : function(node){
19028 node = this.getNode(node);
19029 if(typeof node.nodeIndex == "number"){
19030 return node.nodeIndex;
19032 var ns = this.nodes;
19033 for(var i = 0, len = ns.length; i < len; i++){
19044 * based on jquery fullcalendar
19048 Roo.bootstrap = Roo.bootstrap || {};
19050 * @class Roo.bootstrap.Calendar
19051 * @extends Roo.bootstrap.Component
19052 * Bootstrap Calendar class
19053 * @cfg {Boolean} loadMask (true|false) default false
19054 * @cfg {Object} header generate the user specific header of the calendar, default false
19057 * Create a new Container
19058 * @param {Object} config The config object
19063 Roo.bootstrap.Calendar = function(config){
19064 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19068 * Fires when a date is selected
19069 * @param {DatePicker} this
19070 * @param {Date} date The selected date
19074 * @event monthchange
19075 * Fires when the displayed month changes
19076 * @param {DatePicker} this
19077 * @param {Date} date The selected month
19079 'monthchange': true,
19081 * @event evententer
19082 * Fires when mouse over an event
19083 * @param {Calendar} this
19084 * @param {event} Event
19086 'evententer': true,
19088 * @event eventleave
19089 * Fires when the mouse leaves an
19090 * @param {Calendar} this
19093 'eventleave': true,
19095 * @event eventclick
19096 * Fires when the mouse click an
19097 * @param {Calendar} this
19106 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19109 * @cfg {Number} startDay
19110 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19118 getAutoCreate : function(){
19121 var fc_button = function(name, corner, style, content ) {
19122 return Roo.apply({},{
19124 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19126 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19129 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19140 style : 'width:100%',
19147 cls : 'fc-header-left',
19149 fc_button('prev', 'left', 'arrow', '‹' ),
19150 fc_button('next', 'right', 'arrow', '›' ),
19151 { tag: 'span', cls: 'fc-header-space' },
19152 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19160 cls : 'fc-header-center',
19164 cls: 'fc-header-title',
19167 html : 'month / year'
19175 cls : 'fc-header-right',
19177 /* fc_button('month', 'left', '', 'month' ),
19178 fc_button('week', '', '', 'week' ),
19179 fc_button('day', 'right', '', 'day' )
19191 header = this.header;
19194 var cal_heads = function() {
19196 // fixme - handle this.
19198 for (var i =0; i < Date.dayNames.length; i++) {
19199 var d = Date.dayNames[i];
19202 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19203 html : d.substring(0,3)
19207 ret[0].cls += ' fc-first';
19208 ret[6].cls += ' fc-last';
19211 var cal_cell = function(n) {
19214 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19219 cls: 'fc-day-number',
19223 cls: 'fc-day-content',
19227 style: 'position: relative;' // height: 17px;
19239 var cal_rows = function() {
19242 for (var r = 0; r < 6; r++) {
19249 for (var i =0; i < Date.dayNames.length; i++) {
19250 var d = Date.dayNames[i];
19251 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19254 row.cn[0].cls+=' fc-first';
19255 row.cn[0].cn[0].style = 'min-height:90px';
19256 row.cn[6].cls+=' fc-last';
19260 ret[0].cls += ' fc-first';
19261 ret[4].cls += ' fc-prev-last';
19262 ret[5].cls += ' fc-last';
19269 cls: 'fc-border-separate',
19270 style : 'width:100%',
19278 cls : 'fc-first fc-last',
19296 cls : 'fc-content',
19297 style : "position: relative;",
19300 cls : 'fc-view fc-view-month fc-grid',
19301 style : 'position: relative',
19302 unselectable : 'on',
19305 cls : 'fc-event-container',
19306 style : 'position:absolute;z-index:8;top:0;left:0;'
19324 initEvents : function()
19327 throw "can not find store for calendar";
19333 style: "text-align:center",
19337 style: "background-color:white;width:50%;margin:250 auto",
19341 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19352 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19354 var size = this.el.select('.fc-content', true).first().getSize();
19355 this.maskEl.setSize(size.width, size.height);
19356 this.maskEl.enableDisplayMode("block");
19357 if(!this.loadMask){
19358 this.maskEl.hide();
19361 this.store = Roo.factory(this.store, Roo.data);
19362 this.store.on('load', this.onLoad, this);
19363 this.store.on('beforeload', this.onBeforeLoad, this);
19367 this.cells = this.el.select('.fc-day',true);
19368 //Roo.log(this.cells);
19369 this.textNodes = this.el.query('.fc-day-number');
19370 this.cells.addClassOnOver('fc-state-hover');
19372 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19373 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19374 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19375 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19377 this.on('monthchange', this.onMonthChange, this);
19379 this.update(new Date().clearTime());
19382 resize : function() {
19383 var sz = this.el.getSize();
19385 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19386 this.el.select('.fc-day-content div',true).setHeight(34);
19391 showPrevMonth : function(e){
19392 this.update(this.activeDate.add("mo", -1));
19394 showToday : function(e){
19395 this.update(new Date().clearTime());
19398 showNextMonth : function(e){
19399 this.update(this.activeDate.add("mo", 1));
19403 showPrevYear : function(){
19404 this.update(this.activeDate.add("y", -1));
19408 showNextYear : function(){
19409 this.update(this.activeDate.add("y", 1));
19414 update : function(date)
19416 var vd = this.activeDate;
19417 this.activeDate = date;
19418 // if(vd && this.el){
19419 // var t = date.getTime();
19420 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19421 // Roo.log('using add remove');
19423 // this.fireEvent('monthchange', this, date);
19425 // this.cells.removeClass("fc-state-highlight");
19426 // this.cells.each(function(c){
19427 // if(c.dateValue == t){
19428 // c.addClass("fc-state-highlight");
19429 // setTimeout(function(){
19430 // try{c.dom.firstChild.focus();}catch(e){}
19440 var days = date.getDaysInMonth();
19442 var firstOfMonth = date.getFirstDateOfMonth();
19443 var startingPos = firstOfMonth.getDay()-this.startDay;
19445 if(startingPos < this.startDay){
19449 var pm = date.add(Date.MONTH, -1);
19450 var prevStart = pm.getDaysInMonth()-startingPos;
19452 this.cells = this.el.select('.fc-day',true);
19453 this.textNodes = this.el.query('.fc-day-number');
19454 this.cells.addClassOnOver('fc-state-hover');
19456 var cells = this.cells.elements;
19457 var textEls = this.textNodes;
19459 Roo.each(cells, function(cell){
19460 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19463 days += startingPos;
19465 // convert everything to numbers so it's fast
19466 var day = 86400000;
19467 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19470 //Roo.log(prevStart);
19472 var today = new Date().clearTime().getTime();
19473 var sel = date.clearTime().getTime();
19474 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19475 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19476 var ddMatch = this.disabledDatesRE;
19477 var ddText = this.disabledDatesText;
19478 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19479 var ddaysText = this.disabledDaysText;
19480 var format = this.format;
19482 var setCellClass = function(cal, cell){
19486 //Roo.log('set Cell Class');
19488 var t = d.getTime();
19492 cell.dateValue = t;
19494 cell.className += " fc-today";
19495 cell.className += " fc-state-highlight";
19496 cell.title = cal.todayText;
19499 // disable highlight in other month..
19500 //cell.className += " fc-state-highlight";
19505 cell.className = " fc-state-disabled";
19506 cell.title = cal.minText;
19510 cell.className = " fc-state-disabled";
19511 cell.title = cal.maxText;
19515 if(ddays.indexOf(d.getDay()) != -1){
19516 cell.title = ddaysText;
19517 cell.className = " fc-state-disabled";
19520 if(ddMatch && format){
19521 var fvalue = d.dateFormat(format);
19522 if(ddMatch.test(fvalue)){
19523 cell.title = ddText.replace("%0", fvalue);
19524 cell.className = " fc-state-disabled";
19528 if (!cell.initialClassName) {
19529 cell.initialClassName = cell.dom.className;
19532 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19537 for(; i < startingPos; i++) {
19538 textEls[i].innerHTML = (++prevStart);
19539 d.setDate(d.getDate()+1);
19541 cells[i].className = "fc-past fc-other-month";
19542 setCellClass(this, cells[i]);
19547 for(; i < days; i++){
19548 intDay = i - startingPos + 1;
19549 textEls[i].innerHTML = (intDay);
19550 d.setDate(d.getDate()+1);
19552 cells[i].className = ''; // "x-date-active";
19553 setCellClass(this, cells[i]);
19557 for(; i < 42; i++) {
19558 textEls[i].innerHTML = (++extraDays);
19559 d.setDate(d.getDate()+1);
19561 cells[i].className = "fc-future fc-other-month";
19562 setCellClass(this, cells[i]);
19565 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19567 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19569 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19570 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19572 if(totalRows != 6){
19573 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19574 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19577 this.fireEvent('monthchange', this, date);
19581 if(!this.internalRender){
19582 var main = this.el.dom.firstChild;
19583 var w = main.offsetWidth;
19584 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19585 Roo.fly(main).setWidth(w);
19586 this.internalRender = true;
19587 // opera does not respect the auto grow header center column
19588 // then, after it gets a width opera refuses to recalculate
19589 // without a second pass
19590 if(Roo.isOpera && !this.secondPass){
19591 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19592 this.secondPass = true;
19593 this.update.defer(10, this, [date]);
19600 findCell : function(dt) {
19601 dt = dt.clearTime().getTime();
19603 this.cells.each(function(c){
19604 //Roo.log("check " +c.dateValue + '?=' + dt);
19605 if(c.dateValue == dt){
19615 findCells : function(ev) {
19616 var s = ev.start.clone().clearTime().getTime();
19618 var e= ev.end.clone().clearTime().getTime();
19621 this.cells.each(function(c){
19622 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19624 if(c.dateValue > e){
19627 if(c.dateValue < s){
19636 // findBestRow: function(cells)
19640 // for (var i =0 ; i < cells.length;i++) {
19641 // ret = Math.max(cells[i].rows || 0,ret);
19648 addItem : function(ev)
19650 // look for vertical location slot in
19651 var cells = this.findCells(ev);
19653 // ev.row = this.findBestRow(cells);
19655 // work out the location.
19659 for(var i =0; i < cells.length; i++) {
19661 cells[i].row = cells[0].row;
19664 cells[i].row = cells[i].row + 1;
19674 if (crow.start.getY() == cells[i].getY()) {
19676 crow.end = cells[i];
19693 cells[0].events.push(ev);
19695 this.calevents.push(ev);
19698 clearEvents: function() {
19700 if(!this.calevents){
19704 Roo.each(this.cells.elements, function(c){
19710 Roo.each(this.calevents, function(e) {
19711 Roo.each(e.els, function(el) {
19712 el.un('mouseenter' ,this.onEventEnter, this);
19713 el.un('mouseleave' ,this.onEventLeave, this);
19718 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19724 renderEvents: function()
19728 this.cells.each(function(c) {
19737 if(c.row != c.events.length){
19738 r = 4 - (4 - (c.row - c.events.length));
19741 c.events = ev.slice(0, r);
19742 c.more = ev.slice(r);
19744 if(c.more.length && c.more.length == 1){
19745 c.events.push(c.more.pop());
19748 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19752 this.cells.each(function(c) {
19754 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19757 for (var e = 0; e < c.events.length; e++){
19758 var ev = c.events[e];
19759 var rows = ev.rows;
19761 for(var i = 0; i < rows.length; i++) {
19763 // how many rows should it span..
19766 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19767 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19769 unselectable : "on",
19772 cls: 'fc-event-inner',
19776 // cls: 'fc-event-time',
19777 // html : cells.length > 1 ? '' : ev.time
19781 cls: 'fc-event-title',
19782 html : String.format('{0}', ev.title)
19789 cls: 'ui-resizable-handle ui-resizable-e',
19790 html : '  '
19797 cfg.cls += ' fc-event-start';
19799 if ((i+1) == rows.length) {
19800 cfg.cls += ' fc-event-end';
19803 var ctr = _this.el.select('.fc-event-container',true).first();
19804 var cg = ctr.createChild(cfg);
19806 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19807 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19809 var r = (c.more.length) ? 1 : 0;
19810 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19811 cg.setWidth(ebox.right - sbox.x -2);
19813 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19814 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19815 cg.on('click', _this.onEventClick, _this, ev);
19826 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19827 style : 'position: absolute',
19828 unselectable : "on",
19831 cls: 'fc-event-inner',
19835 cls: 'fc-event-title',
19843 cls: 'ui-resizable-handle ui-resizable-e',
19844 html : '  '
19850 var ctr = _this.el.select('.fc-event-container',true).first();
19851 var cg = ctr.createChild(cfg);
19853 var sbox = c.select('.fc-day-content',true).first().getBox();
19854 var ebox = c.select('.fc-day-content',true).first().getBox();
19856 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19857 cg.setWidth(ebox.right - sbox.x -2);
19859 cg.on('click', _this.onMoreEventClick, _this, c.more);
19869 onEventEnter: function (e, el,event,d) {
19870 this.fireEvent('evententer', this, el, event);
19873 onEventLeave: function (e, el,event,d) {
19874 this.fireEvent('eventleave', this, el, event);
19877 onEventClick: function (e, el,event,d) {
19878 this.fireEvent('eventclick', this, el, event);
19881 onMonthChange: function () {
19885 onMoreEventClick: function(e, el, more)
19889 this.calpopover.placement = 'right';
19890 this.calpopover.setTitle('More');
19892 this.calpopover.setContent('');
19894 var ctr = this.calpopover.el.select('.popover-content', true).first();
19896 Roo.each(more, function(m){
19898 cls : 'fc-event-hori fc-event-draggable',
19901 var cg = ctr.createChild(cfg);
19903 cg.on('click', _this.onEventClick, _this, m);
19906 this.calpopover.show(el);
19911 onLoad: function ()
19913 this.calevents = [];
19916 if(this.store.getCount() > 0){
19917 this.store.data.each(function(d){
19920 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19921 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19922 time : d.data.start_time,
19923 title : d.data.title,
19924 description : d.data.description,
19925 venue : d.data.venue
19930 this.renderEvents();
19932 if(this.calevents.length && this.loadMask){
19933 this.maskEl.hide();
19937 onBeforeLoad: function()
19939 this.clearEvents();
19941 this.maskEl.show();
19955 * @class Roo.bootstrap.Popover
19956 * @extends Roo.bootstrap.Component
19957 * Bootstrap Popover class
19958 * @cfg {String} html contents of the popover (or false to use children..)
19959 * @cfg {String} title of popover (or false to hide)
19960 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19961 * @cfg {String} trigger click || hover (or false to trigger manually)
19962 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19963 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19964 * - if false and it has a 'parent' then it will be automatically added to that element
19965 * - if string - Roo.get will be called
19966 * @cfg {Number} delay - delay before showing
19969 * Create a new Popover
19970 * @param {Object} config The config object
19973 Roo.bootstrap.Popover = function(config){
19974 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19980 * After the popover show
19982 * @param {Roo.bootstrap.Popover} this
19987 * After the popover hide
19989 * @param {Roo.bootstrap.Popover} this
19995 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20000 placement : 'right',
20001 trigger : 'hover', // hover
20007 can_build_overlaid : false,
20009 maskEl : false, // the mask element
20012 alignEl : false, // when show is called with an element - this get's stored.
20014 getChildContainer : function()
20016 return this.contentEl;
20019 getPopoverHeader : function()
20021 this.title = true; // flag not to hide it..
20022 this.headerEl.addClass('p-0');
20023 return this.headerEl
20027 getAutoCreate : function(){
20030 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20031 style: 'display:block',
20037 cls : 'popover-inner ',
20041 cls: 'popover-title popover-header',
20042 html : this.title === false ? '' : this.title
20045 cls : 'popover-content popover-body ' + (this.cls || ''),
20046 html : this.html || ''
20057 * @param {string} the title
20059 setTitle: function(str)
20063 this.headerEl.dom.innerHTML = str;
20068 * @param {string} the body content
20070 setContent: function(str)
20073 if (this.contentEl) {
20074 this.contentEl.dom.innerHTML = str;
20078 // as it get's added to the bottom of the page.
20079 onRender : function(ct, position)
20081 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20086 var cfg = Roo.apply({}, this.getAutoCreate());
20090 cfg.cls += ' ' + this.cls;
20093 cfg.style = this.style;
20095 //Roo.log("adding to ");
20096 this.el = Roo.get(document.body).createChild(cfg, position);
20097 // Roo.log(this.el);
20100 this.contentEl = this.el.select('.popover-content',true).first();
20101 this.headerEl = this.el.select('.popover-title',true).first();
20104 if(typeof(this.items) != 'undefined'){
20105 var items = this.items;
20108 for(var i =0;i < items.length;i++) {
20109 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20113 this.items = nitems;
20115 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20116 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20123 resizeMask : function()
20125 this.maskEl.setSize(
20126 Roo.lib.Dom.getViewWidth(true),
20127 Roo.lib.Dom.getViewHeight(true)
20131 initEvents : function()
20135 Roo.bootstrap.Popover.register(this);
20138 this.arrowEl = this.el.select('.arrow',true).first();
20139 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20140 this.el.enableDisplayMode('block');
20144 if (this.over === false && !this.parent()) {
20147 if (this.triggers === false) {
20152 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20153 var triggers = this.trigger ? this.trigger.split(' ') : [];
20154 Roo.each(triggers, function(trigger) {
20156 if (trigger == 'click') {
20157 on_el.on('click', this.toggle, this);
20158 } else if (trigger != 'manual') {
20159 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20160 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20162 on_el.on(eventIn ,this.enter, this);
20163 on_el.on(eventOut, this.leave, this);
20173 toggle : function () {
20174 this.hoverState == 'in' ? this.leave() : this.enter();
20177 enter : function () {
20179 clearTimeout(this.timeout);
20181 this.hoverState = 'in';
20183 if (!this.delay || !this.delay.show) {
20188 this.timeout = setTimeout(function () {
20189 if (_t.hoverState == 'in') {
20192 }, this.delay.show)
20195 leave : function() {
20196 clearTimeout(this.timeout);
20198 this.hoverState = 'out';
20200 if (!this.delay || !this.delay.hide) {
20205 this.timeout = setTimeout(function () {
20206 if (_t.hoverState == 'out') {
20209 }, this.delay.hide)
20213 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20214 * @param {string} (left|right|top|bottom) position
20216 show : function (on_el, placement)
20218 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20219 on_el = on_el || false; // default to false
20222 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20223 on_el = this.parent().el;
20224 } else if (this.over) {
20225 Roo.get(this.over);
20230 this.alignEl = Roo.get( on_el );
20233 this.render(document.body);
20239 if (this.title === false) {
20240 this.headerEl.hide();
20245 this.el.dom.style.display = 'block';
20248 if (this.alignEl) {
20249 this.updatePosition(this.placement, true);
20252 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20253 var es = this.el.getSize();
20254 var x = Roo.lib.Dom.getViewWidth()/2;
20255 var y = Roo.lib.Dom.getViewHeight()/2;
20256 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20261 //var arrow = this.el.select('.arrow',true).first();
20262 //arrow.set(align[2],
20264 this.el.addClass('in');
20268 this.hoverState = 'in';
20271 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20272 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20273 this.maskEl.dom.style.display = 'block';
20274 this.maskEl.addClass('show');
20276 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20278 this.fireEvent('show', this);
20282 * fire this manually after loading a grid in the table for example
20283 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20284 * @param {Boolean} try and move it if we cant get right position.
20286 updatePosition : function(placement, try_move)
20288 // allow for calling with no parameters
20289 placement = placement ? placement : this.placement;
20290 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20292 this.el.removeClass([
20293 'fade','top','bottom', 'left', 'right','in',
20294 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20296 this.el.addClass(placement + ' bs-popover-' + placement);
20298 if (!this.alignEl ) {
20302 switch (placement) {
20304 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20305 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20306 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20307 //normal display... or moved up/down.
20308 this.el.setXY(offset);
20309 var xy = this.alignEl.getAnchorXY('tr', false);
20311 this.arrowEl.setXY(xy);
20314 // continue through...
20315 return this.updatePosition('left', false);
20319 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20320 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20321 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20322 //normal display... or moved up/down.
20323 this.el.setXY(offset);
20324 var xy = this.alignEl.getAnchorXY('tl', false);
20325 xy[0]-=10;xy[1]+=5; // << fix me
20326 this.arrowEl.setXY(xy);
20330 return this.updatePosition('right', false);
20333 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20334 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20335 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20336 //normal display... or moved up/down.
20337 this.el.setXY(offset);
20338 var xy = this.alignEl.getAnchorXY('t', false);
20339 xy[1]-=10; // << fix me
20340 this.arrowEl.setXY(xy);
20344 return this.updatePosition('bottom', false);
20347 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20348 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20349 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20350 //normal display... or moved up/down.
20351 this.el.setXY(offset);
20352 var xy = this.alignEl.getAnchorXY('b', false);
20353 xy[1]+=2; // << fix me
20354 this.arrowEl.setXY(xy);
20358 return this.updatePosition('top', false);
20369 this.el.setXY([0,0]);
20370 this.el.removeClass('in');
20372 this.hoverState = null;
20373 this.maskEl.hide(); // always..
20374 this.fireEvent('hide', this);
20380 Roo.apply(Roo.bootstrap.Popover, {
20383 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20384 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20385 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20386 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20391 clickHander : false,
20394 onMouseDown : function(e)
20396 if (!e.getTarget(".roo-popover")) {
20404 register : function(popup)
20406 if (!Roo.bootstrap.Popover.clickHandler) {
20407 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20409 // hide other popups.
20411 this.popups.push(popup);
20413 hideAll : function()
20415 this.popups.forEach(function(p) {
20423 * Card header - holder for the card header elements.
20428 * @class Roo.bootstrap.PopoverNav
20429 * @extends Roo.bootstrap.NavGroup
20430 * Bootstrap Popover header navigation class
20432 * Create a new Popover Header Navigation
20433 * @param {Object} config The config object
20436 Roo.bootstrap.PopoverNav = function(config){
20437 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20440 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20443 container_method : 'getPopoverHeader'
20461 * @class Roo.bootstrap.Progress
20462 * @extends Roo.bootstrap.Component
20463 * Bootstrap Progress class
20464 * @cfg {Boolean} striped striped of the progress bar
20465 * @cfg {Boolean} active animated of the progress bar
20469 * Create a new Progress
20470 * @param {Object} config The config object
20473 Roo.bootstrap.Progress = function(config){
20474 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20477 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20482 getAutoCreate : function(){
20490 cfg.cls += ' progress-striped';
20494 cfg.cls += ' active';
20513 * @class Roo.bootstrap.ProgressBar
20514 * @extends Roo.bootstrap.Component
20515 * Bootstrap ProgressBar class
20516 * @cfg {Number} aria_valuenow aria-value now
20517 * @cfg {Number} aria_valuemin aria-value min
20518 * @cfg {Number} aria_valuemax aria-value max
20519 * @cfg {String} label label for the progress bar
20520 * @cfg {String} panel (success | info | warning | danger )
20521 * @cfg {String} role role of the progress bar
20522 * @cfg {String} sr_only text
20526 * Create a new ProgressBar
20527 * @param {Object} config The config object
20530 Roo.bootstrap.ProgressBar = function(config){
20531 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20534 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20538 aria_valuemax : 100,
20544 getAutoCreate : function()
20549 cls: 'progress-bar',
20550 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20562 cfg.role = this.role;
20565 if(this.aria_valuenow){
20566 cfg['aria-valuenow'] = this.aria_valuenow;
20569 if(this.aria_valuemin){
20570 cfg['aria-valuemin'] = this.aria_valuemin;
20573 if(this.aria_valuemax){
20574 cfg['aria-valuemax'] = this.aria_valuemax;
20577 if(this.label && !this.sr_only){
20578 cfg.html = this.label;
20582 cfg.cls += ' progress-bar-' + this.panel;
20588 update : function(aria_valuenow)
20590 this.aria_valuenow = aria_valuenow;
20592 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20607 * @class Roo.bootstrap.TabGroup
20608 * @extends Roo.bootstrap.Column
20609 * Bootstrap Column class
20610 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20611 * @cfg {Boolean} carousel true to make the group behave like a carousel
20612 * @cfg {Boolean} bullets show bullets for the panels
20613 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20614 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20615 * @cfg {Boolean} showarrow (true|false) show arrow default true
20618 * Create a new TabGroup
20619 * @param {Object} config The config object
20622 Roo.bootstrap.TabGroup = function(config){
20623 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20625 this.navId = Roo.id();
20628 Roo.bootstrap.TabGroup.register(this);
20632 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20635 transition : false,
20640 slideOnTouch : false,
20643 getAutoCreate : function()
20645 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20647 cfg.cls += ' tab-content';
20649 if (this.carousel) {
20650 cfg.cls += ' carousel slide';
20653 cls : 'carousel-inner',
20657 if(this.bullets && !Roo.isTouch){
20660 cls : 'carousel-bullets',
20664 if(this.bullets_cls){
20665 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20672 cfg.cn[0].cn.push(bullets);
20675 if(this.showarrow){
20676 cfg.cn[0].cn.push({
20678 class : 'carousel-arrow',
20682 class : 'carousel-prev',
20686 class : 'fa fa-chevron-left'
20692 class : 'carousel-next',
20696 class : 'fa fa-chevron-right'
20709 initEvents: function()
20711 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20712 // this.el.on("touchstart", this.onTouchStart, this);
20715 if(this.autoslide){
20718 this.slideFn = window.setInterval(function() {
20719 _this.showPanelNext();
20723 if(this.showarrow){
20724 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20725 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20731 // onTouchStart : function(e, el, o)
20733 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20737 // this.showPanelNext();
20741 getChildContainer : function()
20743 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20747 * register a Navigation item
20748 * @param {Roo.bootstrap.NavItem} the navitem to add
20750 register : function(item)
20752 this.tabs.push( item);
20753 item.navId = this.navId; // not really needed..
20758 getActivePanel : function()
20761 Roo.each(this.tabs, function(t) {
20771 getPanelByName : function(n)
20774 Roo.each(this.tabs, function(t) {
20775 if (t.tabId == n) {
20783 indexOfPanel : function(p)
20786 Roo.each(this.tabs, function(t,i) {
20787 if (t.tabId == p.tabId) {
20796 * show a specific panel
20797 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20798 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20800 showPanel : function (pan)
20802 if(this.transition || typeof(pan) == 'undefined'){
20803 Roo.log("waiting for the transitionend");
20807 if (typeof(pan) == 'number') {
20808 pan = this.tabs[pan];
20811 if (typeof(pan) == 'string') {
20812 pan = this.getPanelByName(pan);
20815 var cur = this.getActivePanel();
20818 Roo.log('pan or acitve pan is undefined');
20822 if (pan.tabId == this.getActivePanel().tabId) {
20826 if (false === cur.fireEvent('beforedeactivate')) {
20830 if(this.bullets > 0 && !Roo.isTouch){
20831 this.setActiveBullet(this.indexOfPanel(pan));
20834 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20836 //class="carousel-item carousel-item-next carousel-item-left"
20838 this.transition = true;
20839 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20840 var lr = dir == 'next' ? 'left' : 'right';
20841 pan.el.addClass(dir); // or prev
20842 pan.el.addClass('carousel-item-' + dir); // or prev
20843 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20844 cur.el.addClass(lr); // or right
20845 pan.el.addClass(lr);
20846 cur.el.addClass('carousel-item-' +lr); // or right
20847 pan.el.addClass('carousel-item-' +lr);
20851 cur.el.on('transitionend', function() {
20852 Roo.log("trans end?");
20854 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20855 pan.setActive(true);
20857 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20858 cur.setActive(false);
20860 _this.transition = false;
20862 }, this, { single: true } );
20867 cur.setActive(false);
20868 pan.setActive(true);
20873 showPanelNext : function()
20875 var i = this.indexOfPanel(this.getActivePanel());
20877 if (i >= this.tabs.length - 1 && !this.autoslide) {
20881 if (i >= this.tabs.length - 1 && this.autoslide) {
20885 this.showPanel(this.tabs[i+1]);
20888 showPanelPrev : function()
20890 var i = this.indexOfPanel(this.getActivePanel());
20892 if (i < 1 && !this.autoslide) {
20896 if (i < 1 && this.autoslide) {
20897 i = this.tabs.length;
20900 this.showPanel(this.tabs[i-1]);
20904 addBullet: function()
20906 if(!this.bullets || Roo.isTouch){
20909 var ctr = this.el.select('.carousel-bullets',true).first();
20910 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20911 var bullet = ctr.createChild({
20912 cls : 'bullet bullet-' + i
20913 },ctr.dom.lastChild);
20918 bullet.on('click', (function(e, el, o, ii, t){
20920 e.preventDefault();
20922 this.showPanel(ii);
20924 if(this.autoslide && this.slideFn){
20925 clearInterval(this.slideFn);
20926 this.slideFn = window.setInterval(function() {
20927 _this.showPanelNext();
20931 }).createDelegate(this, [i, bullet], true));
20936 setActiveBullet : function(i)
20942 Roo.each(this.el.select('.bullet', true).elements, function(el){
20943 el.removeClass('selected');
20946 var bullet = this.el.select('.bullet-' + i, true).first();
20952 bullet.addClass('selected');
20963 Roo.apply(Roo.bootstrap.TabGroup, {
20967 * register a Navigation Group
20968 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20970 register : function(navgrp)
20972 this.groups[navgrp.navId] = navgrp;
20976 * fetch a Navigation Group based on the navigation ID
20977 * if one does not exist , it will get created.
20978 * @param {string} the navgroup to add
20979 * @returns {Roo.bootstrap.NavGroup} the navgroup
20981 get: function(navId) {
20982 if (typeof(this.groups[navId]) == 'undefined') {
20983 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20985 return this.groups[navId] ;
21000 * @class Roo.bootstrap.TabPanel
21001 * @extends Roo.bootstrap.Component
21002 * Bootstrap TabPanel class
21003 * @cfg {Boolean} active panel active
21004 * @cfg {String} html panel content
21005 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21006 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21007 * @cfg {String} href click to link..
21008 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21012 * Create a new TabPanel
21013 * @param {Object} config The config object
21016 Roo.bootstrap.TabPanel = function(config){
21017 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21021 * Fires when the active status changes
21022 * @param {Roo.bootstrap.TabPanel} this
21023 * @param {Boolean} state the new state
21028 * @event beforedeactivate
21029 * Fires before a tab is de-activated - can be used to do validation on a form.
21030 * @param {Roo.bootstrap.TabPanel} this
21031 * @return {Boolean} false if there is an error
21034 'beforedeactivate': true
21037 this.tabId = this.tabId || Roo.id();
21041 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21048 touchSlide : false,
21049 getAutoCreate : function(){
21054 // item is needed for carousel - not sure if it has any effect otherwise
21055 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21056 html: this.html || ''
21060 cfg.cls += ' active';
21064 cfg.tabId = this.tabId;
21072 initEvents: function()
21074 var p = this.parent();
21076 this.navId = this.navId || p.navId;
21078 if (typeof(this.navId) != 'undefined') {
21079 // not really needed.. but just in case.. parent should be a NavGroup.
21080 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21084 var i = tg.tabs.length - 1;
21086 if(this.active && tg.bullets > 0 && i < tg.bullets){
21087 tg.setActiveBullet(i);
21091 this.el.on('click', this.onClick, this);
21093 if(Roo.isTouch && this.touchSlide){
21094 this.el.on("touchstart", this.onTouchStart, this);
21095 this.el.on("touchmove", this.onTouchMove, this);
21096 this.el.on("touchend", this.onTouchEnd, this);
21101 onRender : function(ct, position)
21103 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21106 setActive : function(state)
21108 Roo.log("panel - set active " + this.tabId + "=" + state);
21110 this.active = state;
21112 this.el.removeClass('active');
21114 } else if (!this.el.hasClass('active')) {
21115 this.el.addClass('active');
21118 this.fireEvent('changed', this, state);
21121 onClick : function(e)
21123 e.preventDefault();
21125 if(!this.href.length){
21129 window.location.href = this.href;
21138 onTouchStart : function(e)
21140 this.swiping = false;
21142 this.startX = e.browserEvent.touches[0].clientX;
21143 this.startY = e.browserEvent.touches[0].clientY;
21146 onTouchMove : function(e)
21148 this.swiping = true;
21150 this.endX = e.browserEvent.touches[0].clientX;
21151 this.endY = e.browserEvent.touches[0].clientY;
21154 onTouchEnd : function(e)
21161 var tabGroup = this.parent();
21163 if(this.endX > this.startX){ // swiping right
21164 tabGroup.showPanelPrev();
21168 if(this.startX > this.endX){ // swiping left
21169 tabGroup.showPanelNext();
21188 * @class Roo.bootstrap.DateField
21189 * @extends Roo.bootstrap.Input
21190 * Bootstrap DateField class
21191 * @cfg {Number} weekStart default 0
21192 * @cfg {String} viewMode default empty, (months|years)
21193 * @cfg {String} minViewMode default empty, (months|years)
21194 * @cfg {Number} startDate default -Infinity
21195 * @cfg {Number} endDate default Infinity
21196 * @cfg {Boolean} todayHighlight default false
21197 * @cfg {Boolean} todayBtn default false
21198 * @cfg {Boolean} calendarWeeks default false
21199 * @cfg {Object} daysOfWeekDisabled default empty
21200 * @cfg {Boolean} singleMode default false (true | false)
21202 * @cfg {Boolean} keyboardNavigation default true
21203 * @cfg {String} language default en
21206 * Create a new DateField
21207 * @param {Object} config The config object
21210 Roo.bootstrap.DateField = function(config){
21211 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21215 * Fires when this field show.
21216 * @param {Roo.bootstrap.DateField} this
21217 * @param {Mixed} date The date value
21222 * Fires when this field hide.
21223 * @param {Roo.bootstrap.DateField} this
21224 * @param {Mixed} date The date value
21229 * Fires when select a date.
21230 * @param {Roo.bootstrap.DateField} this
21231 * @param {Mixed} date The date value
21235 * @event beforeselect
21236 * Fires when before select a date.
21237 * @param {Roo.bootstrap.DateField} this
21238 * @param {Mixed} date The date value
21240 beforeselect : true
21244 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21247 * @cfg {String} format
21248 * The default date format string which can be overriden for localization support. The format must be
21249 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21253 * @cfg {String} altFormats
21254 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21255 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21257 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21265 todayHighlight : false,
21271 keyboardNavigation: true,
21273 calendarWeeks: false,
21275 startDate: -Infinity,
21279 daysOfWeekDisabled: [],
21283 singleMode : false,
21285 UTCDate: function()
21287 return new Date(Date.UTC.apply(Date, arguments));
21290 UTCToday: function()
21292 var today = new Date();
21293 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21296 getDate: function() {
21297 var d = this.getUTCDate();
21298 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21301 getUTCDate: function() {
21305 setDate: function(d) {
21306 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21309 setUTCDate: function(d) {
21311 this.setValue(this.formatDate(this.date));
21314 onRender: function(ct, position)
21317 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21319 this.language = this.language || 'en';
21320 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21321 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21323 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21324 this.format = this.format || 'm/d/y';
21325 this.isInline = false;
21326 this.isInput = true;
21327 this.component = this.el.select('.add-on', true).first() || false;
21328 this.component = (this.component && this.component.length === 0) ? false : this.component;
21329 this.hasInput = this.component && this.inputEl().length;
21331 if (typeof(this.minViewMode === 'string')) {
21332 switch (this.minViewMode) {
21334 this.minViewMode = 1;
21337 this.minViewMode = 2;
21340 this.minViewMode = 0;
21345 if (typeof(this.viewMode === 'string')) {
21346 switch (this.viewMode) {
21359 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21361 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21363 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21365 this.picker().on('mousedown', this.onMousedown, this);
21366 this.picker().on('click', this.onClick, this);
21368 this.picker().addClass('datepicker-dropdown');
21370 this.startViewMode = this.viewMode;
21372 if(this.singleMode){
21373 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21374 v.setVisibilityMode(Roo.Element.DISPLAY);
21378 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21379 v.setStyle('width', '189px');
21383 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21384 if(!this.calendarWeeks){
21389 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21390 v.attr('colspan', function(i, val){
21391 return parseInt(val) + 1;
21396 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21398 this.setStartDate(this.startDate);
21399 this.setEndDate(this.endDate);
21401 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21408 if(this.isInline) {
21413 picker : function()
21415 return this.pickerEl;
21416 // return this.el.select('.datepicker', true).first();
21419 fillDow: function()
21421 var dowCnt = this.weekStart;
21430 if(this.calendarWeeks){
21438 while (dowCnt < this.weekStart + 7) {
21442 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21446 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21449 fillMonths: function()
21452 var months = this.picker().select('>.datepicker-months td', true).first();
21454 months.dom.innerHTML = '';
21460 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21463 months.createChild(month);
21470 this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
21472 if (this.date < this.startDate) {
21473 this.viewDate = new Date(this.startDate);
21474 } else if (this.date > this.endDate) {
21475 this.viewDate = new Date(this.endDate);
21477 this.viewDate = new Date(this.date);
21485 var d = new Date(this.viewDate),
21486 year = d.getUTCFullYear(),
21487 month = d.getUTCMonth(),
21488 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21489 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21490 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21491 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21492 currentDate = this.date && this.date.valueOf(),
21493 today = this.UTCToday();
21495 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21497 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21499 // this.picker.select('>tfoot th.today').
21500 // .text(dates[this.language].today)
21501 // .toggle(this.todayBtn !== false);
21503 this.updateNavArrows();
21506 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21508 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21510 prevMonth.setUTCDate(day);
21512 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21514 var nextMonth = new Date(prevMonth);
21516 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21518 nextMonth = nextMonth.valueOf();
21520 var fillMonths = false;
21522 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21524 while(prevMonth.valueOf() <= nextMonth) {
21527 if (prevMonth.getUTCDay() === this.weekStart) {
21529 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21537 if(this.calendarWeeks){
21538 // ISO 8601: First week contains first thursday.
21539 // ISO also states week starts on Monday, but we can be more abstract here.
21541 // Start of current week: based on weekstart/current date
21542 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21543 // Thursday of this week
21544 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21545 // First Thursday of year, year from thursday
21546 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21547 // Calendar week: ms between thursdays, div ms per day, div 7 days
21548 calWeek = (th - yth) / 864e5 / 7 + 1;
21550 fillMonths.cn.push({
21558 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21560 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21563 if (this.todayHighlight &&
21564 prevMonth.getUTCFullYear() == today.getFullYear() &&
21565 prevMonth.getUTCMonth() == today.getMonth() &&
21566 prevMonth.getUTCDate() == today.getDate()) {
21567 clsName += ' today';
21570 if (currentDate && prevMonth.valueOf() === currentDate) {
21571 clsName += ' active';
21574 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21575 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21576 clsName += ' disabled';
21579 fillMonths.cn.push({
21581 cls: 'day ' + clsName,
21582 html: prevMonth.getDate()
21585 prevMonth.setDate(prevMonth.getDate()+1);
21588 var currentYear = this.date && this.date.getUTCFullYear();
21589 var currentMonth = this.date && this.date.getUTCMonth();
21591 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21593 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21594 v.removeClass('active');
21596 if(currentYear === year && k === currentMonth){
21597 v.addClass('active');
21600 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21601 v.addClass('disabled');
21607 year = parseInt(year/10, 10) * 10;
21609 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21611 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21614 for (var i = -1; i < 11; i++) {
21615 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21617 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21625 showMode: function(dir)
21628 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21631 Roo.each(this.picker().select('>div',true).elements, function(v){
21632 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21635 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21640 if(this.isInline) {
21644 this.picker().removeClass(['bottom', 'top']);
21646 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21648 * place to the top of element!
21652 this.picker().addClass('top');
21653 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21658 this.picker().addClass('bottom');
21660 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21663 parseDate : function(value)
21665 if(!value || value instanceof Date){
21668 var v = Date.parseDate(value, this.format);
21669 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21670 v = Date.parseDate(value, 'Y-m-d');
21672 if(!v && this.altFormats){
21673 if(!this.altFormatsArray){
21674 this.altFormatsArray = this.altFormats.split("|");
21676 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21677 v = Date.parseDate(value, this.altFormatsArray[i]);
21683 formatDate : function(date, fmt)
21685 return (!date || !(date instanceof Date)) ?
21686 date : date.dateFormat(fmt || this.format);
21689 onFocus : function()
21691 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21695 onBlur : function()
21697 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21699 var d = this.inputEl().getValue();
21706 showPopup : function()
21708 this.picker().show();
21712 this.fireEvent('showpopup', this, this.date);
21715 hidePopup : function()
21717 if(this.isInline) {
21720 this.picker().hide();
21721 this.viewMode = this.startViewMode;
21724 this.fireEvent('hidepopup', this, this.date);
21728 onMousedown: function(e)
21730 e.stopPropagation();
21731 e.preventDefault();
21736 Roo.bootstrap.DateField.superclass.keyup.call(this);
21740 setValue: function(v)
21742 if(this.fireEvent('beforeselect', this, v) !== false){
21743 var d = new Date(this.parseDate(v) ).clearTime();
21745 if(isNaN(d.getTime())){
21746 this.date = this.viewDate = '';
21747 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21751 v = this.formatDate(d);
21753 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21755 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21759 this.fireEvent('select', this, this.date);
21763 getValue: function()
21765 return this.formatDate(this.date);
21768 fireKey: function(e)
21770 if (!this.picker().isVisible()){
21771 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21777 var dateChanged = false,
21779 newDate, newViewDate;
21784 e.preventDefault();
21788 if (!this.keyboardNavigation) {
21791 dir = e.keyCode == 37 ? -1 : 1;
21794 newDate = this.moveYear(this.date, dir);
21795 newViewDate = this.moveYear(this.viewDate, dir);
21796 } else if (e.shiftKey){
21797 newDate = this.moveMonth(this.date, dir);
21798 newViewDate = this.moveMonth(this.viewDate, dir);
21800 newDate = new Date(this.date);
21801 newDate.setUTCDate(this.date.getUTCDate() + dir);
21802 newViewDate = new Date(this.viewDate);
21803 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21805 if (this.dateWithinRange(newDate)){
21806 this.date = newDate;
21807 this.viewDate = newViewDate;
21808 this.setValue(this.formatDate(this.date));
21810 e.preventDefault();
21811 dateChanged = true;
21816 if (!this.keyboardNavigation) {
21819 dir = e.keyCode == 38 ? -1 : 1;
21821 newDate = this.moveYear(this.date, dir);
21822 newViewDate = this.moveYear(this.viewDate, dir);
21823 } else if (e.shiftKey){
21824 newDate = this.moveMonth(this.date, dir);
21825 newViewDate = this.moveMonth(this.viewDate, dir);
21827 newDate = new Date(this.date);
21828 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21829 newViewDate = new Date(this.viewDate);
21830 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21832 if (this.dateWithinRange(newDate)){
21833 this.date = newDate;
21834 this.viewDate = newViewDate;
21835 this.setValue(this.formatDate(this.date));
21837 e.preventDefault();
21838 dateChanged = true;
21842 this.setValue(this.formatDate(this.date));
21844 e.preventDefault();
21847 this.setValue(this.formatDate(this.date));
21861 onClick: function(e)
21863 e.stopPropagation();
21864 e.preventDefault();
21866 var target = e.getTarget();
21868 if(target.nodeName.toLowerCase() === 'i'){
21869 target = Roo.get(target).dom.parentNode;
21872 var nodeName = target.nodeName;
21873 var className = target.className;
21874 var html = target.innerHTML;
21875 //Roo.log(nodeName);
21877 switch(nodeName.toLowerCase()) {
21879 switch(className) {
21885 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21886 switch(this.viewMode){
21888 this.viewDate = this.moveMonth(this.viewDate, dir);
21892 this.viewDate = this.moveYear(this.viewDate, dir);
21898 var date = new Date();
21899 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21901 this.setValue(this.formatDate(this.date));
21908 if (className.indexOf('disabled') < 0) {
21909 this.viewDate.setUTCDate(1);
21910 if (className.indexOf('month') > -1) {
21911 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21913 var year = parseInt(html, 10) || 0;
21914 this.viewDate.setUTCFullYear(year);
21918 if(this.singleMode){
21919 this.setValue(this.formatDate(this.viewDate));
21930 //Roo.log(className);
21931 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21932 var day = parseInt(html, 10) || 1;
21933 var year = (this.viewDate || new Date()).getUTCFullYear(),
21934 month = (this.viewDate || new Date()).getUTCMonth();
21936 if (className.indexOf('old') > -1) {
21943 } else if (className.indexOf('new') > -1) {
21951 //Roo.log([year,month,day]);
21952 this.date = this.UTCDate(year, month, day,0,0,0,0);
21953 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21955 //Roo.log(this.formatDate(this.date));
21956 this.setValue(this.formatDate(this.date));
21963 setStartDate: function(startDate)
21965 this.startDate = startDate || -Infinity;
21966 if (this.startDate !== -Infinity) {
21967 this.startDate = this.parseDate(this.startDate);
21970 this.updateNavArrows();
21973 setEndDate: function(endDate)
21975 this.endDate = endDate || Infinity;
21976 if (this.endDate !== Infinity) {
21977 this.endDate = this.parseDate(this.endDate);
21980 this.updateNavArrows();
21983 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21985 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21986 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21987 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21989 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21990 return parseInt(d, 10);
21993 this.updateNavArrows();
21996 updateNavArrows: function()
21998 if(this.singleMode){
22002 var d = new Date(this.viewDate),
22003 year = d.getUTCFullYear(),
22004 month = d.getUTCMonth();
22006 Roo.each(this.picker().select('.prev', true).elements, function(v){
22008 switch (this.viewMode) {
22011 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22017 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22024 Roo.each(this.picker().select('.next', true).elements, function(v){
22026 switch (this.viewMode) {
22029 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22035 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22043 moveMonth: function(date, dir)
22048 var new_date = new Date(date.valueOf()),
22049 day = new_date.getUTCDate(),
22050 month = new_date.getUTCMonth(),
22051 mag = Math.abs(dir),
22053 dir = dir > 0 ? 1 : -1;
22056 // If going back one month, make sure month is not current month
22057 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22059 return new_date.getUTCMonth() == month;
22061 // If going forward one month, make sure month is as expected
22062 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22064 return new_date.getUTCMonth() != new_month;
22066 new_month = month + dir;
22067 new_date.setUTCMonth(new_month);
22068 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22069 if (new_month < 0 || new_month > 11) {
22070 new_month = (new_month + 12) % 12;
22073 // For magnitudes >1, move one month at a time...
22074 for (var i=0; i<mag; i++) {
22075 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22076 new_date = this.moveMonth(new_date, dir);
22078 // ...then reset the day, keeping it in the new month
22079 new_month = new_date.getUTCMonth();
22080 new_date.setUTCDate(day);
22082 return new_month != new_date.getUTCMonth();
22085 // Common date-resetting loop -- if date is beyond end of month, make it
22088 new_date.setUTCDate(--day);
22089 new_date.setUTCMonth(new_month);
22094 moveYear: function(date, dir)
22096 return this.moveMonth(date, dir*12);
22099 dateWithinRange: function(date)
22101 return date >= this.startDate && date <= this.endDate;
22107 this.picker().remove();
22110 validateValue : function(value)
22112 if(this.getVisibilityEl().hasClass('hidden')){
22116 if(value.length < 1) {
22117 if(this.allowBlank){
22123 if(value.length < this.minLength){
22126 if(value.length > this.maxLength){
22130 var vt = Roo.form.VTypes;
22131 if(!vt[this.vtype](value, this)){
22135 if(typeof this.validator == "function"){
22136 var msg = this.validator(value);
22142 if(this.regex && !this.regex.test(value)){
22146 if(typeof(this.parseDate(value)) == 'undefined'){
22150 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22154 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22164 this.date = this.viewDate = '';
22166 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22171 Roo.apply(Roo.bootstrap.DateField, {
22182 html: '<i class="fa fa-arrow-left"/>'
22192 html: '<i class="fa fa-arrow-right"/>'
22234 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22235 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22236 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22237 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22238 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22251 navFnc: 'FullYear',
22256 navFnc: 'FullYear',
22261 Roo.apply(Roo.bootstrap.DateField, {
22265 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22269 cls: 'datepicker-days',
22273 cls: 'table-condensed',
22275 Roo.bootstrap.DateField.head,
22279 Roo.bootstrap.DateField.footer
22286 cls: 'datepicker-months',
22290 cls: 'table-condensed',
22292 Roo.bootstrap.DateField.head,
22293 Roo.bootstrap.DateField.content,
22294 Roo.bootstrap.DateField.footer
22301 cls: 'datepicker-years',
22305 cls: 'table-condensed',
22307 Roo.bootstrap.DateField.head,
22308 Roo.bootstrap.DateField.content,
22309 Roo.bootstrap.DateField.footer
22328 * @class Roo.bootstrap.TimeField
22329 * @extends Roo.bootstrap.Input
22330 * Bootstrap DateField class
22334 * Create a new TimeField
22335 * @param {Object} config The config object
22338 Roo.bootstrap.TimeField = function(config){
22339 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22343 * Fires when this field show.
22344 * @param {Roo.bootstrap.DateField} thisthis
22345 * @param {Mixed} date The date value
22350 * Fires when this field hide.
22351 * @param {Roo.bootstrap.DateField} this
22352 * @param {Mixed} date The date value
22357 * Fires when select a date.
22358 * @param {Roo.bootstrap.DateField} this
22359 * @param {Mixed} date The date value
22365 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22368 * @cfg {String} format
22369 * The default time format string which can be overriden for localization support. The format must be
22370 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22374 getAutoCreate : function()
22376 this.after = '<i class="fa far fa-clock"></i>';
22377 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22381 onRender: function(ct, position)
22384 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22386 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22388 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22390 this.pop = this.picker().select('>.datepicker-time',true).first();
22391 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22393 this.picker().on('mousedown', this.onMousedown, this);
22394 this.picker().on('click', this.onClick, this);
22396 this.picker().addClass('datepicker-dropdown');
22401 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22402 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22403 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22404 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22405 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22406 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22410 fireKey: function(e){
22411 if (!this.picker().isVisible()){
22412 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22418 e.preventDefault();
22426 this.onTogglePeriod();
22429 this.onIncrementMinutes();
22432 this.onDecrementMinutes();
22441 onClick: function(e) {
22442 e.stopPropagation();
22443 e.preventDefault();
22446 picker : function()
22448 return this.pickerEl;
22451 fillTime: function()
22453 var time = this.pop.select('tbody', true).first();
22455 time.dom.innerHTML = '';
22470 cls: 'hours-up fa fas fa-chevron-up'
22490 cls: 'minutes-up fa fas fa-chevron-up'
22511 cls: 'timepicker-hour',
22526 cls: 'timepicker-minute',
22541 cls: 'btn btn-primary period',
22563 cls: 'hours-down fa fas fa-chevron-down'
22583 cls: 'minutes-down fa fas fa-chevron-down'
22601 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22608 var hours = this.time.getHours();
22609 var minutes = this.time.getMinutes();
22622 hours = hours - 12;
22626 hours = '0' + hours;
22630 minutes = '0' + minutes;
22633 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22634 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22635 this.pop.select('button', true).first().dom.innerHTML = period;
22641 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22643 var cls = ['bottom'];
22645 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22652 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22656 //this.picker().setXY(20000,20000);
22657 this.picker().addClass(cls.join('-'));
22661 Roo.each(cls, function(c){
22666 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22667 //_this.picker().setTop(_this.inputEl().getHeight());
22671 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22673 //_this.picker().setTop(0 - _this.picker().getHeight());
22678 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22682 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22690 onFocus : function()
22692 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22696 onBlur : function()
22698 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22704 this.picker().show();
22709 this.fireEvent('show', this, this.date);
22714 this.picker().hide();
22717 this.fireEvent('hide', this, this.date);
22720 setTime : function()
22723 this.setValue(this.time.format(this.format));
22725 this.fireEvent('select', this, this.date);
22730 onMousedown: function(e){
22731 e.stopPropagation();
22732 e.preventDefault();
22735 onIncrementHours: function()
22737 Roo.log('onIncrementHours');
22738 this.time = this.time.add(Date.HOUR, 1);
22743 onDecrementHours: function()
22745 Roo.log('onDecrementHours');
22746 this.time = this.time.add(Date.HOUR, -1);
22750 onIncrementMinutes: function()
22752 Roo.log('onIncrementMinutes');
22753 this.time = this.time.add(Date.MINUTE, 1);
22757 onDecrementMinutes: function()
22759 Roo.log('onDecrementMinutes');
22760 this.time = this.time.add(Date.MINUTE, -1);
22764 onTogglePeriod: function()
22766 Roo.log('onTogglePeriod');
22767 this.time = this.time.add(Date.HOUR, 12);
22775 Roo.apply(Roo.bootstrap.TimeField, {
22779 cls: 'datepicker dropdown-menu',
22783 cls: 'datepicker-time',
22787 cls: 'table-condensed',
22816 cls: 'btn btn-info ok',
22844 * @class Roo.bootstrap.MonthField
22845 * @extends Roo.bootstrap.Input
22846 * Bootstrap MonthField class
22848 * @cfg {String} language default en
22851 * Create a new MonthField
22852 * @param {Object} config The config object
22855 Roo.bootstrap.MonthField = function(config){
22856 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22861 * Fires when this field show.
22862 * @param {Roo.bootstrap.MonthField} this
22863 * @param {Mixed} date The date value
22868 * Fires when this field hide.
22869 * @param {Roo.bootstrap.MonthField} this
22870 * @param {Mixed} date The date value
22875 * Fires when select a date.
22876 * @param {Roo.bootstrap.MonthField} this
22877 * @param {String} oldvalue The old value
22878 * @param {String} newvalue The new value
22884 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22886 onRender: function(ct, position)
22889 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22891 this.language = this.language || 'en';
22892 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22893 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22895 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22896 this.isInline = false;
22897 this.isInput = true;
22898 this.component = this.el.select('.add-on', true).first() || false;
22899 this.component = (this.component && this.component.length === 0) ? false : this.component;
22900 this.hasInput = this.component && this.inputEL().length;
22902 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22904 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22906 this.picker().on('mousedown', this.onMousedown, this);
22907 this.picker().on('click', this.onClick, this);
22909 this.picker().addClass('datepicker-dropdown');
22911 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22912 v.setStyle('width', '189px');
22919 if(this.isInline) {
22925 setValue: function(v, suppressEvent)
22927 var o = this.getValue();
22929 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22933 if(suppressEvent !== true){
22934 this.fireEvent('select', this, o, v);
22939 getValue: function()
22944 onClick: function(e)
22946 e.stopPropagation();
22947 e.preventDefault();
22949 var target = e.getTarget();
22951 if(target.nodeName.toLowerCase() === 'i'){
22952 target = Roo.get(target).dom.parentNode;
22955 var nodeName = target.nodeName;
22956 var className = target.className;
22957 var html = target.innerHTML;
22959 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22963 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22965 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22971 picker : function()
22973 return this.pickerEl;
22976 fillMonths: function()
22979 var months = this.picker().select('>.datepicker-months td', true).first();
22981 months.dom.innerHTML = '';
22987 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22990 months.createChild(month);
22999 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23000 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23003 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23004 e.removeClass('active');
23006 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23007 e.addClass('active');
23014 if(this.isInline) {
23018 this.picker().removeClass(['bottom', 'top']);
23020 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23022 * place to the top of element!
23026 this.picker().addClass('top');
23027 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23032 this.picker().addClass('bottom');
23034 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23037 onFocus : function()
23039 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23043 onBlur : function()
23045 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23047 var d = this.inputEl().getValue();
23056 this.picker().show();
23057 this.picker().select('>.datepicker-months', true).first().show();
23061 this.fireEvent('show', this, this.date);
23066 if(this.isInline) {
23069 this.picker().hide();
23070 this.fireEvent('hide', this, this.date);
23074 onMousedown: function(e)
23076 e.stopPropagation();
23077 e.preventDefault();
23082 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23086 fireKey: function(e)
23088 if (!this.picker().isVisible()){
23089 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23100 e.preventDefault();
23104 dir = e.keyCode == 37 ? -1 : 1;
23106 this.vIndex = this.vIndex + dir;
23108 if(this.vIndex < 0){
23112 if(this.vIndex > 11){
23116 if(isNaN(this.vIndex)){
23120 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23126 dir = e.keyCode == 38 ? -1 : 1;
23128 this.vIndex = this.vIndex + dir * 4;
23130 if(this.vIndex < 0){
23134 if(this.vIndex > 11){
23138 if(isNaN(this.vIndex)){
23142 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23147 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23148 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23152 e.preventDefault();
23155 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23156 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23172 this.picker().remove();
23177 Roo.apply(Roo.bootstrap.MonthField, {
23196 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23197 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23202 Roo.apply(Roo.bootstrap.MonthField, {
23206 cls: 'datepicker dropdown-menu roo-dynamic',
23210 cls: 'datepicker-months',
23214 cls: 'table-condensed',
23216 Roo.bootstrap.DateField.content
23236 * @class Roo.bootstrap.CheckBox
23237 * @extends Roo.bootstrap.Input
23238 * Bootstrap CheckBox class
23240 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23241 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23242 * @cfg {String} boxLabel The text that appears beside the checkbox
23243 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23244 * @cfg {Boolean} checked initnal the element
23245 * @cfg {Boolean} inline inline the element (default false)
23246 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23247 * @cfg {String} tooltip label tooltip
23250 * Create a new CheckBox
23251 * @param {Object} config The config object
23254 Roo.bootstrap.CheckBox = function(config){
23255 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23260 * Fires when the element is checked or unchecked.
23261 * @param {Roo.bootstrap.CheckBox} this This input
23262 * @param {Boolean} checked The new checked value
23267 * Fires when the element is click.
23268 * @param {Roo.bootstrap.CheckBox} this This input
23275 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23277 inputType: 'checkbox',
23286 // checkbox success does not make any sense really..
23291 getAutoCreate : function()
23293 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23299 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23302 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23308 type : this.inputType,
23309 value : this.inputValue,
23310 cls : 'roo-' + this.inputType, //'form-box',
23311 placeholder : this.placeholder || ''
23315 if(this.inputType != 'radio'){
23319 cls : 'roo-hidden-value',
23320 value : this.checked ? this.inputValue : this.valueOff
23325 if (this.weight) { // Validity check?
23326 cfg.cls += " " + this.inputType + "-" + this.weight;
23329 if (this.disabled) {
23330 input.disabled=true;
23334 input.checked = this.checked;
23339 input.name = this.name;
23341 if(this.inputType != 'radio'){
23342 hidden.name = this.name;
23343 input.name = '_hidden_' + this.name;
23348 input.cls += ' input-' + this.size;
23353 ['xs','sm','md','lg'].map(function(size){
23354 if (settings[size]) {
23355 cfg.cls += ' col-' + size + '-' + settings[size];
23359 var inputblock = input;
23361 if (this.before || this.after) {
23364 cls : 'input-group',
23369 inputblock.cn.push({
23371 cls : 'input-group-addon',
23376 inputblock.cn.push(input);
23378 if(this.inputType != 'radio'){
23379 inputblock.cn.push(hidden);
23383 inputblock.cn.push({
23385 cls : 'input-group-addon',
23391 var boxLabelCfg = false;
23397 //'for': id, // box label is handled by onclick - so no for...
23399 html: this.boxLabel
23402 boxLabelCfg.tooltip = this.tooltip;
23408 if (align ==='left' && this.fieldLabel.length) {
23409 // Roo.log("left and has label");
23414 cls : 'control-label',
23415 html : this.fieldLabel
23426 cfg.cn[1].cn.push(boxLabelCfg);
23429 if(this.labelWidth > 12){
23430 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23433 if(this.labelWidth < 13 && this.labelmd == 0){
23434 this.labelmd = this.labelWidth;
23437 if(this.labellg > 0){
23438 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23439 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23442 if(this.labelmd > 0){
23443 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23444 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23447 if(this.labelsm > 0){
23448 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23449 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23452 if(this.labelxs > 0){
23453 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23454 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23457 } else if ( this.fieldLabel.length) {
23458 // Roo.log(" label");
23462 tag: this.boxLabel ? 'span' : 'label',
23464 cls: 'control-label box-input-label',
23465 //cls : 'input-group-addon',
23466 html : this.fieldLabel
23473 cfg.cn.push(boxLabelCfg);
23478 // Roo.log(" no label && no align");
23479 cfg.cn = [ inputblock ] ;
23481 cfg.cn.push(boxLabelCfg);
23489 if(this.inputType != 'radio'){
23490 cfg.cn.push(hidden);
23498 * return the real input element.
23500 inputEl: function ()
23502 return this.el.select('input.roo-' + this.inputType,true).first();
23504 hiddenEl: function ()
23506 return this.el.select('input.roo-hidden-value',true).first();
23509 labelEl: function()
23511 return this.el.select('label.control-label',true).first();
23513 /* depricated... */
23517 return this.labelEl();
23520 boxLabelEl: function()
23522 return this.el.select('label.box-label',true).first();
23525 initEvents : function()
23527 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23529 this.inputEl().on('click', this.onClick, this);
23531 if (this.boxLabel) {
23532 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23535 this.startValue = this.getValue();
23538 Roo.bootstrap.CheckBox.register(this);
23542 onClick : function(e)
23544 if(this.fireEvent('click', this, e) !== false){
23545 this.setChecked(!this.checked);
23550 setChecked : function(state,suppressEvent)
23552 this.startValue = this.getValue();
23554 if(this.inputType == 'radio'){
23556 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23557 e.dom.checked = false;
23560 this.inputEl().dom.checked = true;
23562 this.inputEl().dom.value = this.inputValue;
23564 if(suppressEvent !== true){
23565 this.fireEvent('check', this, true);
23573 this.checked = state;
23575 this.inputEl().dom.checked = state;
23578 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23580 if(suppressEvent !== true){
23581 this.fireEvent('check', this, state);
23587 getValue : function()
23589 if(this.inputType == 'radio'){
23590 return this.getGroupValue();
23593 return this.hiddenEl().dom.value;
23597 getGroupValue : function()
23599 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23603 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23606 setValue : function(v,suppressEvent)
23608 if(this.inputType == 'radio'){
23609 this.setGroupValue(v, suppressEvent);
23613 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23618 setGroupValue : function(v, suppressEvent)
23620 this.startValue = this.getValue();
23622 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23623 e.dom.checked = false;
23625 if(e.dom.value == v){
23626 e.dom.checked = true;
23630 if(suppressEvent !== true){
23631 this.fireEvent('check', this, true);
23639 validate : function()
23641 if(this.getVisibilityEl().hasClass('hidden')){
23647 (this.inputType == 'radio' && this.validateRadio()) ||
23648 (this.inputType == 'checkbox' && this.validateCheckbox())
23654 this.markInvalid();
23658 validateRadio : function()
23660 if(this.getVisibilityEl().hasClass('hidden')){
23664 if(this.allowBlank){
23670 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23671 if(!e.dom.checked){
23683 validateCheckbox : function()
23686 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23687 //return (this.getValue() == this.inputValue) ? true : false;
23690 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23698 for(var i in group){
23699 if(group[i].el.isVisible(true)){
23707 for(var i in group){
23712 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23719 * Mark this field as valid
23721 markValid : function()
23725 this.fireEvent('valid', this);
23727 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23730 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23737 if(this.inputType == 'radio'){
23738 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23739 var fg = e.findParent('.form-group', false, true);
23740 if (Roo.bootstrap.version == 3) {
23741 fg.removeClass([_this.invalidClass, _this.validClass]);
23742 fg.addClass(_this.validClass);
23744 fg.removeClass(['is-valid', 'is-invalid']);
23745 fg.addClass('is-valid');
23753 var fg = this.el.findParent('.form-group', false, true);
23754 if (Roo.bootstrap.version == 3) {
23755 fg.removeClass([this.invalidClass, this.validClass]);
23756 fg.addClass(this.validClass);
23758 fg.removeClass(['is-valid', 'is-invalid']);
23759 fg.addClass('is-valid');
23764 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23770 for(var i in group){
23771 var fg = group[i].el.findParent('.form-group', false, true);
23772 if (Roo.bootstrap.version == 3) {
23773 fg.removeClass([this.invalidClass, this.validClass]);
23774 fg.addClass(this.validClass);
23776 fg.removeClass(['is-valid', 'is-invalid']);
23777 fg.addClass('is-valid');
23783 * Mark this field as invalid
23784 * @param {String} msg The validation message
23786 markInvalid : function(msg)
23788 if(this.allowBlank){
23794 this.fireEvent('invalid', this, msg);
23796 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23799 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23803 label.markInvalid();
23806 if(this.inputType == 'radio'){
23808 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23809 var fg = e.findParent('.form-group', false, true);
23810 if (Roo.bootstrap.version == 3) {
23811 fg.removeClass([_this.invalidClass, _this.validClass]);
23812 fg.addClass(_this.invalidClass);
23814 fg.removeClass(['is-invalid', 'is-valid']);
23815 fg.addClass('is-invalid');
23823 var fg = this.el.findParent('.form-group', false, true);
23824 if (Roo.bootstrap.version == 3) {
23825 fg.removeClass([_this.invalidClass, _this.validClass]);
23826 fg.addClass(_this.invalidClass);
23828 fg.removeClass(['is-invalid', 'is-valid']);
23829 fg.addClass('is-invalid');
23834 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23840 for(var i in group){
23841 var fg = group[i].el.findParent('.form-group', false, true);
23842 if (Roo.bootstrap.version == 3) {
23843 fg.removeClass([_this.invalidClass, _this.validClass]);
23844 fg.addClass(_this.invalidClass);
23846 fg.removeClass(['is-invalid', 'is-valid']);
23847 fg.addClass('is-invalid');
23853 clearInvalid : function()
23855 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23857 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23859 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23861 if (label && label.iconEl) {
23862 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23863 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23867 disable : function()
23869 if(this.inputType != 'radio'){
23870 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23877 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23878 _this.getActionEl().addClass(this.disabledClass);
23879 e.dom.disabled = true;
23883 this.disabled = true;
23884 this.fireEvent("disable", this);
23888 enable : function()
23890 if(this.inputType != 'radio'){
23891 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23898 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23899 _this.getActionEl().removeClass(this.disabledClass);
23900 e.dom.disabled = false;
23904 this.disabled = false;
23905 this.fireEvent("enable", this);
23909 setBoxLabel : function(v)
23914 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23920 Roo.apply(Roo.bootstrap.CheckBox, {
23925 * register a CheckBox Group
23926 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23928 register : function(checkbox)
23930 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23931 this.groups[checkbox.groupId] = {};
23934 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23938 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23942 * fetch a CheckBox Group based on the group ID
23943 * @param {string} the group ID
23944 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23946 get: function(groupId) {
23947 if (typeof(this.groups[groupId]) == 'undefined') {
23951 return this.groups[groupId] ;
23964 * @class Roo.bootstrap.Radio
23965 * @extends Roo.bootstrap.Component
23966 * Bootstrap Radio class
23967 * @cfg {String} boxLabel - the label associated
23968 * @cfg {String} value - the value of radio
23971 * Create a new Radio
23972 * @param {Object} config The config object
23974 Roo.bootstrap.Radio = function(config){
23975 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23979 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23985 getAutoCreate : function()
23989 cls : 'form-group radio',
23994 html : this.boxLabel
24002 initEvents : function()
24004 this.parent().register(this);
24006 this.el.on('click', this.onClick, this);
24010 onClick : function(e)
24012 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24013 this.setChecked(true);
24017 setChecked : function(state, suppressEvent)
24019 this.parent().setValue(this.value, suppressEvent);
24023 setBoxLabel : function(v)
24028 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24043 * @class Roo.bootstrap.SecurePass
24044 * @extends Roo.bootstrap.Input
24045 * Bootstrap SecurePass class
24049 * Create a new SecurePass
24050 * @param {Object} config The config object
24053 Roo.bootstrap.SecurePass = function (config) {
24054 // these go here, so the translation tool can replace them..
24056 PwdEmpty: "Please type a password, and then retype it to confirm.",
24057 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24058 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24059 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24060 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24061 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24062 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24063 TooWeak: "Your password is Too Weak."
24065 this.meterLabel = "Password strength:";
24066 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24067 this.meterClass = [
24068 "roo-password-meter-tooweak",
24069 "roo-password-meter-weak",
24070 "roo-password-meter-medium",
24071 "roo-password-meter-strong",
24072 "roo-password-meter-grey"
24077 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24080 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24082 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24084 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24085 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24086 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24087 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24088 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24089 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24090 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24100 * @cfg {String/Object} Label for the strength meter (defaults to
24101 * 'Password strength:')
24106 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24107 * ['Weak', 'Medium', 'Strong'])
24110 pwdStrengths: false,
24123 initEvents: function ()
24125 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24127 if (this.el.is('input[type=password]') && Roo.isSafari) {
24128 this.el.on('keydown', this.SafariOnKeyDown, this);
24131 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24134 onRender: function (ct, position)
24136 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24137 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24138 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24140 this.trigger.createChild({
24145 cls: 'roo-password-meter-grey col-xs-12',
24148 //width: this.meterWidth + 'px'
24152 cls: 'roo-password-meter-text'
24158 if (this.hideTrigger) {
24159 this.trigger.setDisplayed(false);
24161 this.setSize(this.width || '', this.height || '');
24164 onDestroy: function ()
24166 if (this.trigger) {
24167 this.trigger.removeAllListeners();
24168 this.trigger.remove();
24171 this.wrap.remove();
24173 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24176 checkStrength: function ()
24178 var pwd = this.inputEl().getValue();
24179 if (pwd == this._lastPwd) {
24184 if (this.ClientSideStrongPassword(pwd)) {
24186 } else if (this.ClientSideMediumPassword(pwd)) {
24188 } else if (this.ClientSideWeakPassword(pwd)) {
24194 Roo.log('strength1: ' + strength);
24196 //var pm = this.trigger.child('div/div/div').dom;
24197 var pm = this.trigger.child('div/div');
24198 pm.removeClass(this.meterClass);
24199 pm.addClass(this.meterClass[strength]);
24202 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24204 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24206 this._lastPwd = pwd;
24210 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24212 this._lastPwd = '';
24214 var pm = this.trigger.child('div/div');
24215 pm.removeClass(this.meterClass);
24216 pm.addClass('roo-password-meter-grey');
24219 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24222 this.inputEl().dom.type='password';
24225 validateValue: function (value)
24227 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24230 if (value.length == 0) {
24231 if (this.allowBlank) {
24232 this.clearInvalid();
24236 this.markInvalid(this.errors.PwdEmpty);
24237 this.errorMsg = this.errors.PwdEmpty;
24245 if (!value.match(/[\x21-\x7e]+/)) {
24246 this.markInvalid(this.errors.PwdBadChar);
24247 this.errorMsg = this.errors.PwdBadChar;
24250 if (value.length < 6) {
24251 this.markInvalid(this.errors.PwdShort);
24252 this.errorMsg = this.errors.PwdShort;
24255 if (value.length > 16) {
24256 this.markInvalid(this.errors.PwdLong);
24257 this.errorMsg = this.errors.PwdLong;
24261 if (this.ClientSideStrongPassword(value)) {
24263 } else if (this.ClientSideMediumPassword(value)) {
24265 } else if (this.ClientSideWeakPassword(value)) {
24272 if (strength < 2) {
24273 //this.markInvalid(this.errors.TooWeak);
24274 this.errorMsg = this.errors.TooWeak;
24279 console.log('strength2: ' + strength);
24281 //var pm = this.trigger.child('div/div/div').dom;
24283 var pm = this.trigger.child('div/div');
24284 pm.removeClass(this.meterClass);
24285 pm.addClass(this.meterClass[strength]);
24287 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24289 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24291 this.errorMsg = '';
24295 CharacterSetChecks: function (type)
24298 this.fResult = false;
24301 isctype: function (character, type)
24304 case this.kCapitalLetter:
24305 if (character >= 'A' && character <= 'Z') {
24310 case this.kSmallLetter:
24311 if (character >= 'a' && character <= 'z') {
24317 if (character >= '0' && character <= '9') {
24322 case this.kPunctuation:
24323 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24334 IsLongEnough: function (pwd, size)
24336 return !(pwd == null || isNaN(size) || pwd.length < size);
24339 SpansEnoughCharacterSets: function (word, nb)
24341 if (!this.IsLongEnough(word, nb))
24346 var characterSetChecks = new Array(
24347 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24348 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24351 for (var index = 0; index < word.length; ++index) {
24352 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24353 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24354 characterSetChecks[nCharSet].fResult = true;
24361 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24362 if (characterSetChecks[nCharSet].fResult) {
24367 if (nCharSets < nb) {
24373 ClientSideStrongPassword: function (pwd)
24375 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24378 ClientSideMediumPassword: function (pwd)
24380 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24383 ClientSideWeakPassword: function (pwd)
24385 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24388 })//<script type="text/javascript">
24391 * Based Ext JS Library 1.1.1
24392 * Copyright(c) 2006-2007, Ext JS, LLC.
24398 * @class Roo.HtmlEditorCore
24399 * @extends Roo.Component
24400 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24402 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24405 Roo.HtmlEditorCore = function(config){
24408 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24413 * @event initialize
24414 * Fires when the editor is fully initialized (including the iframe)
24415 * @param {Roo.HtmlEditorCore} this
24420 * Fires when the editor is first receives the focus. Any insertion must wait
24421 * until after this event.
24422 * @param {Roo.HtmlEditorCore} this
24426 * @event beforesync
24427 * Fires before the textarea is updated with content from the editor iframe. Return false
24428 * to cancel the sync.
24429 * @param {Roo.HtmlEditorCore} this
24430 * @param {String} html
24434 * @event beforepush
24435 * Fires before the iframe editor is updated with content from the textarea. Return false
24436 * to cancel the push.
24437 * @param {Roo.HtmlEditorCore} this
24438 * @param {String} html
24443 * Fires when the textarea is updated with content from the editor iframe.
24444 * @param {Roo.HtmlEditorCore} this
24445 * @param {String} html
24450 * Fires when the iframe editor is updated with content from the textarea.
24451 * @param {Roo.HtmlEditorCore} this
24452 * @param {String} html
24457 * @event editorevent
24458 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24459 * @param {Roo.HtmlEditorCore} this
24465 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24467 // defaults : white / black...
24468 this.applyBlacklists();
24475 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24479 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24485 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24490 * @cfg {Number} height (in pixels)
24494 * @cfg {Number} width (in pixels)
24499 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24502 stylesheets: false,
24507 // private properties
24508 validationEvent : false,
24510 initialized : false,
24512 sourceEditMode : false,
24513 onFocus : Roo.emptyFn,
24515 hideMode:'offsets',
24519 // blacklist + whitelisted elements..
24526 * Protected method that will not generally be called directly. It
24527 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24528 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24530 getDocMarkup : function(){
24534 // inherit styels from page...??
24535 if (this.stylesheets === false) {
24537 Roo.get(document.head).select('style').each(function(node) {
24538 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24541 Roo.get(document.head).select('link').each(function(node) {
24542 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24545 } else if (!this.stylesheets.length) {
24547 st = '<style type="text/css">' +
24548 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24551 for (var i in this.stylesheets) {
24552 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24557 st += '<style type="text/css">' +
24558 'IMG { cursor: pointer } ' +
24561 var cls = 'roo-htmleditor-body';
24563 if(this.bodyCls.length){
24564 cls += ' ' + this.bodyCls;
24567 return '<html><head>' + st +
24568 //<style type="text/css">' +
24569 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24571 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24575 onRender : function(ct, position)
24578 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24579 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24582 this.el.dom.style.border = '0 none';
24583 this.el.dom.setAttribute('tabIndex', -1);
24584 this.el.addClass('x-hidden hide');
24588 if(Roo.isIE){ // fix IE 1px bogus margin
24589 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24593 this.frameId = Roo.id();
24597 var iframe = this.owner.wrap.createChild({
24599 cls: 'form-control', // bootstrap..
24601 name: this.frameId,
24602 frameBorder : 'no',
24603 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24608 this.iframe = iframe.dom;
24610 this.assignDocWin();
24612 this.doc.designMode = 'on';
24615 this.doc.write(this.getDocMarkup());
24619 var task = { // must defer to wait for browser to be ready
24621 //console.log("run task?" + this.doc.readyState);
24622 this.assignDocWin();
24623 if(this.doc.body || this.doc.readyState == 'complete'){
24625 this.doc.designMode="on";
24629 Roo.TaskMgr.stop(task);
24630 this.initEditor.defer(10, this);
24637 Roo.TaskMgr.start(task);
24642 onResize : function(w, h)
24644 Roo.log('resize: ' +w + ',' + h );
24645 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24649 if(typeof w == 'number'){
24651 this.iframe.style.width = w + 'px';
24653 if(typeof h == 'number'){
24655 this.iframe.style.height = h + 'px';
24657 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24664 * Toggles the editor between standard and source edit mode.
24665 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24667 toggleSourceEdit : function(sourceEditMode){
24669 this.sourceEditMode = sourceEditMode === true;
24671 if(this.sourceEditMode){
24673 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24676 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24677 //this.iframe.className = '';
24680 //this.setSize(this.owner.wrap.getSize());
24681 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24688 * Protected method that will not generally be called directly. If you need/want
24689 * custom HTML cleanup, this is the method you should override.
24690 * @param {String} html The HTML to be cleaned
24691 * return {String} The cleaned HTML
24693 cleanHtml : function(html){
24694 html = String(html);
24695 if(html.length > 5){
24696 if(Roo.isSafari){ // strip safari nonsense
24697 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24700 if(html == ' '){
24707 * HTML Editor -> Textarea
24708 * Protected method that will not generally be called directly. Syncs the contents
24709 * of the editor iframe with the textarea.
24711 syncValue : function(){
24712 if(this.initialized){
24713 var bd = (this.doc.body || this.doc.documentElement);
24714 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24715 var html = bd.innerHTML;
24717 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24718 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24720 html = '<div style="'+m[0]+'">' + html + '</div>';
24723 html = this.cleanHtml(html);
24724 // fix up the special chars.. normaly like back quotes in word...
24725 // however we do not want to do this with chinese..
24726 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24728 var cc = match.charCodeAt();
24730 // Get the character value, handling surrogate pairs
24731 if (match.length == 2) {
24732 // It's a surrogate pair, calculate the Unicode code point
24733 var high = match.charCodeAt(0) - 0xD800;
24734 var low = match.charCodeAt(1) - 0xDC00;
24735 cc = (high * 0x400) + low + 0x10000;
24737 (cc >= 0x4E00 && cc < 0xA000 ) ||
24738 (cc >= 0x3400 && cc < 0x4E00 ) ||
24739 (cc >= 0xf900 && cc < 0xfb00 )
24744 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24745 return "&#" + cc + ";";
24752 if(this.owner.fireEvent('beforesync', this, html) !== false){
24753 this.el.dom.value = html;
24754 this.owner.fireEvent('sync', this, html);
24760 * Protected method that will not generally be called directly. Pushes the value of the textarea
24761 * into the iframe editor.
24763 pushValue : function(){
24764 if(this.initialized){
24765 var v = this.el.dom.value.trim();
24767 // if(v.length < 1){
24771 if(this.owner.fireEvent('beforepush', this, v) !== false){
24772 var d = (this.doc.body || this.doc.documentElement);
24774 this.cleanUpPaste();
24775 this.el.dom.value = d.innerHTML;
24776 this.owner.fireEvent('push', this, v);
24782 deferFocus : function(){
24783 this.focus.defer(10, this);
24787 focus : function(){
24788 if(this.win && !this.sourceEditMode){
24795 assignDocWin: function()
24797 var iframe = this.iframe;
24800 this.doc = iframe.contentWindow.document;
24801 this.win = iframe.contentWindow;
24803 // if (!Roo.get(this.frameId)) {
24806 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24807 // this.win = Roo.get(this.frameId).dom.contentWindow;
24809 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24813 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24814 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24819 initEditor : function(){
24820 //console.log("INIT EDITOR");
24821 this.assignDocWin();
24825 this.doc.designMode="on";
24827 this.doc.write(this.getDocMarkup());
24830 var dbody = (this.doc.body || this.doc.documentElement);
24831 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24832 // this copies styles from the containing element into thsi one..
24833 // not sure why we need all of this..
24834 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24836 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24837 //ss['background-attachment'] = 'fixed'; // w3c
24838 dbody.bgProperties = 'fixed'; // ie
24839 //Roo.DomHelper.applyStyles(dbody, ss);
24840 Roo.EventManager.on(this.doc, {
24841 //'mousedown': this.onEditorEvent,
24842 'mouseup': this.onEditorEvent,
24843 'dblclick': this.onEditorEvent,
24844 'click': this.onEditorEvent,
24845 'keyup': this.onEditorEvent,
24850 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24852 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24853 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24855 this.initialized = true;
24857 this.owner.fireEvent('initialize', this);
24862 onDestroy : function(){
24868 //for (var i =0; i < this.toolbars.length;i++) {
24869 // // fixme - ask toolbars for heights?
24870 // this.toolbars[i].onDestroy();
24873 //this.wrap.dom.innerHTML = '';
24874 //this.wrap.remove();
24879 onFirstFocus : function(){
24881 this.assignDocWin();
24884 this.activated = true;
24887 if(Roo.isGecko){ // prevent silly gecko errors
24889 var s = this.win.getSelection();
24890 if(!s.focusNode || s.focusNode.nodeType != 3){
24891 var r = s.getRangeAt(0);
24892 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24897 this.execCmd('useCSS', true);
24898 this.execCmd('styleWithCSS', false);
24901 this.owner.fireEvent('activate', this);
24905 adjustFont: function(btn){
24906 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24907 //if(Roo.isSafari){ // safari
24910 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24911 if(Roo.isSafari){ // safari
24912 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24913 v = (v < 10) ? 10 : v;
24914 v = (v > 48) ? 48 : v;
24915 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24920 v = Math.max(1, v+adjust);
24922 this.execCmd('FontSize', v );
24925 onEditorEvent : function(e)
24927 this.owner.fireEvent('editorevent', this, e);
24928 // this.updateToolbar();
24929 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24932 insertTag : function(tg)
24934 // could be a bit smarter... -> wrap the current selected tRoo..
24935 if (tg.toLowerCase() == 'span' ||
24936 tg.toLowerCase() == 'code' ||
24937 tg.toLowerCase() == 'sup' ||
24938 tg.toLowerCase() == 'sub'
24941 range = this.createRange(this.getSelection());
24942 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24943 wrappingNode.appendChild(range.extractContents());
24944 range.insertNode(wrappingNode);
24951 this.execCmd("formatblock", tg);
24955 insertText : function(txt)
24959 var range = this.createRange();
24960 range.deleteContents();
24961 //alert(Sender.getAttribute('label'));
24963 range.insertNode(this.doc.createTextNode(txt));
24969 * Executes a Midas editor command on the editor document and performs necessary focus and
24970 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24971 * @param {String} cmd The Midas command
24972 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24974 relayCmd : function(cmd, value){
24976 this.execCmd(cmd, value);
24977 this.owner.fireEvent('editorevent', this);
24978 //this.updateToolbar();
24979 this.owner.deferFocus();
24983 * Executes a Midas editor command directly on the editor document.
24984 * For visual commands, you should use {@link #relayCmd} instead.
24985 * <b>This should only be called after the editor is initialized.</b>
24986 * @param {String} cmd The Midas command
24987 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24989 execCmd : function(cmd, value){
24990 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24997 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24999 * @param {String} text | dom node..
25001 insertAtCursor : function(text)
25004 if(!this.activated){
25010 var r = this.doc.selection.createRange();
25021 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25025 // from jquery ui (MIT licenced)
25027 var win = this.win;
25029 if (win.getSelection && win.getSelection().getRangeAt) {
25030 range = win.getSelection().getRangeAt(0);
25031 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25032 range.insertNode(node);
25033 } else if (win.document.selection && win.document.selection.createRange) {
25034 // no firefox support
25035 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25036 win.document.selection.createRange().pasteHTML(txt);
25038 // no firefox support
25039 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25040 this.execCmd('InsertHTML', txt);
25049 mozKeyPress : function(e){
25051 var c = e.getCharCode(), cmd;
25054 c = String.fromCharCode(c).toLowerCase();
25068 this.cleanUpPaste.defer(100, this);
25076 e.preventDefault();
25084 fixKeys : function(){ // load time branching for fastest keydown performance
25086 return function(e){
25087 var k = e.getKey(), r;
25090 r = this.doc.selection.createRange();
25093 r.pasteHTML('    ');
25100 r = this.doc.selection.createRange();
25102 var target = r.parentElement();
25103 if(!target || target.tagName.toLowerCase() != 'li'){
25105 r.pasteHTML('<br />');
25111 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25112 this.cleanUpPaste.defer(100, this);
25118 }else if(Roo.isOpera){
25119 return function(e){
25120 var k = e.getKey();
25124 this.execCmd('InsertHTML','    ');
25127 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25128 this.cleanUpPaste.defer(100, this);
25133 }else if(Roo.isSafari){
25134 return function(e){
25135 var k = e.getKey();
25139 this.execCmd('InsertText','\t');
25143 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25144 this.cleanUpPaste.defer(100, this);
25152 getAllAncestors: function()
25154 var p = this.getSelectedNode();
25157 a.push(p); // push blank onto stack..
25158 p = this.getParentElement();
25162 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25166 a.push(this.doc.body);
25170 lastSelNode : false,
25173 getSelection : function()
25175 this.assignDocWin();
25176 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25179 getSelectedNode: function()
25181 // this may only work on Gecko!!!
25183 // should we cache this!!!!
25188 var range = this.createRange(this.getSelection()).cloneRange();
25191 var parent = range.parentElement();
25193 var testRange = range.duplicate();
25194 testRange.moveToElementText(parent);
25195 if (testRange.inRange(range)) {
25198 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25201 parent = parent.parentElement;
25206 // is ancestor a text element.
25207 var ac = range.commonAncestorContainer;
25208 if (ac.nodeType == 3) {
25209 ac = ac.parentNode;
25212 var ar = ac.childNodes;
25215 var other_nodes = [];
25216 var has_other_nodes = false;
25217 for (var i=0;i<ar.length;i++) {
25218 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25221 // fullly contained node.
25223 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25228 // probably selected..
25229 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25230 other_nodes.push(ar[i]);
25234 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25239 has_other_nodes = true;
25241 if (!nodes.length && other_nodes.length) {
25242 nodes= other_nodes;
25244 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25250 createRange: function(sel)
25252 // this has strange effects when using with
25253 // top toolbar - not sure if it's a great idea.
25254 //this.editor.contentWindow.focus();
25255 if (typeof sel != "undefined") {
25257 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25259 return this.doc.createRange();
25262 return this.doc.createRange();
25265 getParentElement: function()
25268 this.assignDocWin();
25269 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25271 var range = this.createRange(sel);
25274 var p = range.commonAncestorContainer;
25275 while (p.nodeType == 3) { // text node
25286 * Range intersection.. the hard stuff...
25290 * [ -- selected range --- ]
25294 * if end is before start or hits it. fail.
25295 * if start is after end or hits it fail.
25297 * if either hits (but other is outside. - then it's not
25303 // @see http://www.thismuchiknow.co.uk/?p=64.
25304 rangeIntersectsNode : function(range, node)
25306 var nodeRange = node.ownerDocument.createRange();
25308 nodeRange.selectNode(node);
25310 nodeRange.selectNodeContents(node);
25313 var rangeStartRange = range.cloneRange();
25314 rangeStartRange.collapse(true);
25316 var rangeEndRange = range.cloneRange();
25317 rangeEndRange.collapse(false);
25319 var nodeStartRange = nodeRange.cloneRange();
25320 nodeStartRange.collapse(true);
25322 var nodeEndRange = nodeRange.cloneRange();
25323 nodeEndRange.collapse(false);
25325 return rangeStartRange.compareBoundaryPoints(
25326 Range.START_TO_START, nodeEndRange) == -1 &&
25327 rangeEndRange.compareBoundaryPoints(
25328 Range.START_TO_START, nodeStartRange) == 1;
25332 rangeCompareNode : function(range, node)
25334 var nodeRange = node.ownerDocument.createRange();
25336 nodeRange.selectNode(node);
25338 nodeRange.selectNodeContents(node);
25342 range.collapse(true);
25344 nodeRange.collapse(true);
25346 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25347 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25349 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25351 var nodeIsBefore = ss == 1;
25352 var nodeIsAfter = ee == -1;
25354 if (nodeIsBefore && nodeIsAfter) {
25357 if (!nodeIsBefore && nodeIsAfter) {
25358 return 1; //right trailed.
25361 if (nodeIsBefore && !nodeIsAfter) {
25362 return 2; // left trailed.
25368 // private? - in a new class?
25369 cleanUpPaste : function()
25371 // cleans up the whole document..
25372 Roo.log('cleanuppaste');
25374 this.cleanUpChildren(this.doc.body);
25375 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25376 if (clean != this.doc.body.innerHTML) {
25377 this.doc.body.innerHTML = clean;
25382 cleanWordChars : function(input) {// change the chars to hex code
25383 var he = Roo.HtmlEditorCore;
25385 var output = input;
25386 Roo.each(he.swapCodes, function(sw) {
25387 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25389 output = output.replace(swapper, sw[1]);
25396 cleanUpChildren : function (n)
25398 if (!n.childNodes.length) {
25401 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25402 this.cleanUpChild(n.childNodes[i]);
25409 cleanUpChild : function (node)
25412 //console.log(node);
25413 if (node.nodeName == "#text") {
25414 // clean up silly Windows -- stuff?
25417 if (node.nodeName == "#comment") {
25418 node.parentNode.removeChild(node);
25419 // clean up silly Windows -- stuff?
25422 var lcname = node.tagName.toLowerCase();
25423 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25424 // whitelist of tags..
25426 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25428 node.parentNode.removeChild(node);
25433 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25435 // spans with no attributes - just remove them..
25436 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25437 remove_keep_children = true;
25440 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25441 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25443 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25444 // remove_keep_children = true;
25447 if (remove_keep_children) {
25448 this.cleanUpChildren(node);
25449 // inserts everything just before this node...
25450 while (node.childNodes.length) {
25451 var cn = node.childNodes[0];
25452 node.removeChild(cn);
25453 node.parentNode.insertBefore(cn, node);
25455 node.parentNode.removeChild(node);
25459 if (!node.attributes || !node.attributes.length) {
25464 this.cleanUpChildren(node);
25468 function cleanAttr(n,v)
25471 if (v.match(/^\./) || v.match(/^\//)) {
25474 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25477 if (v.match(/^#/)) {
25480 if (v.match(/^\{/)) { // allow template editing.
25483 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25484 node.removeAttribute(n);
25488 var cwhite = this.cwhite;
25489 var cblack = this.cblack;
25491 function cleanStyle(n,v)
25493 if (v.match(/expression/)) { //XSS?? should we even bother..
25494 node.removeAttribute(n);
25498 var parts = v.split(/;/);
25501 Roo.each(parts, function(p) {
25502 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25506 var l = p.split(':').shift().replace(/\s+/g,'');
25507 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25509 if ( cwhite.length && cblack.indexOf(l) > -1) {
25510 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25511 //node.removeAttribute(n);
25515 // only allow 'c whitelisted system attributes'
25516 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25517 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25518 //node.removeAttribute(n);
25528 if (clean.length) {
25529 node.setAttribute(n, clean.join(';'));
25531 node.removeAttribute(n);
25537 for (var i = node.attributes.length-1; i > -1 ; i--) {
25538 var a = node.attributes[i];
25541 if (a.name.toLowerCase().substr(0,2)=='on') {
25542 node.removeAttribute(a.name);
25545 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25546 node.removeAttribute(a.name);
25549 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25550 cleanAttr(a.name,a.value); // fixme..
25553 if (a.name == 'style') {
25554 cleanStyle(a.name,a.value);
25557 /// clean up MS crap..
25558 // tecnically this should be a list of valid class'es..
25561 if (a.name == 'class') {
25562 if (a.value.match(/^Mso/)) {
25563 node.removeAttribute('class');
25566 if (a.value.match(/^body$/)) {
25567 node.removeAttribute('class');
25578 this.cleanUpChildren(node);
25584 * Clean up MS wordisms...
25586 cleanWord : function(node)
25589 this.cleanWord(this.doc.body);
25594 node.nodeName == 'SPAN' &&
25595 !node.hasAttributes() &&
25596 node.childNodes.length == 1 &&
25597 node.firstChild.nodeName == "#text"
25599 var textNode = node.firstChild;
25600 node.removeChild(textNode);
25601 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25602 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25604 node.parentNode.insertBefore(textNode, node);
25605 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25606 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25608 node.parentNode.removeChild(node);
25611 if (node.nodeName == "#text") {
25612 // clean up silly Windows -- stuff?
25615 if (node.nodeName == "#comment") {
25616 node.parentNode.removeChild(node);
25617 // clean up silly Windows -- stuff?
25621 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25622 node.parentNode.removeChild(node);
25625 //Roo.log(node.tagName);
25626 // remove - but keep children..
25627 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25628 //Roo.log('-- removed');
25629 while (node.childNodes.length) {
25630 var cn = node.childNodes[0];
25631 node.removeChild(cn);
25632 node.parentNode.insertBefore(cn, node);
25633 // move node to parent - and clean it..
25634 this.cleanWord(cn);
25636 node.parentNode.removeChild(node);
25637 /// no need to iterate chidlren = it's got none..
25638 //this.iterateChildren(node, this.cleanWord);
25642 if (node.className.length) {
25644 var cn = node.className.split(/\W+/);
25646 Roo.each(cn, function(cls) {
25647 if (cls.match(/Mso[a-zA-Z]+/)) {
25652 node.className = cna.length ? cna.join(' ') : '';
25654 node.removeAttribute("class");
25658 if (node.hasAttribute("lang")) {
25659 node.removeAttribute("lang");
25662 if (node.hasAttribute("style")) {
25664 var styles = node.getAttribute("style").split(";");
25666 Roo.each(styles, function(s) {
25667 if (!s.match(/:/)) {
25670 var kv = s.split(":");
25671 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25674 // what ever is left... we allow.
25677 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25678 if (!nstyle.length) {
25679 node.removeAttribute('style');
25682 this.iterateChildren(node, this.cleanWord);
25688 * iterateChildren of a Node, calling fn each time, using this as the scole..
25689 * @param {DomNode} node node to iterate children of.
25690 * @param {Function} fn method of this class to call on each item.
25692 iterateChildren : function(node, fn)
25694 if (!node.childNodes.length) {
25697 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25698 fn.call(this, node.childNodes[i])
25704 * cleanTableWidths.
25706 * Quite often pasting from word etc.. results in tables with column and widths.
25707 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25710 cleanTableWidths : function(node)
25715 this.cleanTableWidths(this.doc.body);
25720 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25723 Roo.log(node.tagName);
25724 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25725 this.iterateChildren(node, this.cleanTableWidths);
25728 if (node.hasAttribute('width')) {
25729 node.removeAttribute('width');
25733 if (node.hasAttribute("style")) {
25736 var styles = node.getAttribute("style").split(";");
25738 Roo.each(styles, function(s) {
25739 if (!s.match(/:/)) {
25742 var kv = s.split(":");
25743 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25746 // what ever is left... we allow.
25749 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25750 if (!nstyle.length) {
25751 node.removeAttribute('style');
25755 this.iterateChildren(node, this.cleanTableWidths);
25763 domToHTML : function(currentElement, depth, nopadtext) {
25765 depth = depth || 0;
25766 nopadtext = nopadtext || false;
25768 if (!currentElement) {
25769 return this.domToHTML(this.doc.body);
25772 //Roo.log(currentElement);
25774 var allText = false;
25775 var nodeName = currentElement.nodeName;
25776 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25778 if (nodeName == '#text') {
25780 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25785 if (nodeName != 'BODY') {
25788 // Prints the node tagName, such as <A>, <IMG>, etc
25791 for(i = 0; i < currentElement.attributes.length;i++) {
25793 var aname = currentElement.attributes.item(i).name;
25794 if (!currentElement.attributes.item(i).value.length) {
25797 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25800 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25809 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25812 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25817 // Traverse the tree
25819 var currentElementChild = currentElement.childNodes.item(i);
25820 var allText = true;
25821 var innerHTML = '';
25823 while (currentElementChild) {
25824 // Formatting code (indent the tree so it looks nice on the screen)
25825 var nopad = nopadtext;
25826 if (lastnode == 'SPAN') {
25830 if (currentElementChild.nodeName == '#text') {
25831 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25832 toadd = nopadtext ? toadd : toadd.trim();
25833 if (!nopad && toadd.length > 80) {
25834 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25836 innerHTML += toadd;
25839 currentElementChild = currentElement.childNodes.item(i);
25845 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25847 // Recursively traverse the tree structure of the child node
25848 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25849 lastnode = currentElementChild.nodeName;
25851 currentElementChild=currentElement.childNodes.item(i);
25857 // The remaining code is mostly for formatting the tree
25858 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25863 ret+= "</"+tagName+">";
25869 applyBlacklists : function()
25871 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25872 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25876 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25877 if (b.indexOf(tag) > -1) {
25880 this.white.push(tag);
25884 Roo.each(w, function(tag) {
25885 if (b.indexOf(tag) > -1) {
25888 if (this.white.indexOf(tag) > -1) {
25891 this.white.push(tag);
25896 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25897 if (w.indexOf(tag) > -1) {
25900 this.black.push(tag);
25904 Roo.each(b, function(tag) {
25905 if (w.indexOf(tag) > -1) {
25908 if (this.black.indexOf(tag) > -1) {
25911 this.black.push(tag);
25916 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25917 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25921 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25922 if (b.indexOf(tag) > -1) {
25925 this.cwhite.push(tag);
25929 Roo.each(w, function(tag) {
25930 if (b.indexOf(tag) > -1) {
25933 if (this.cwhite.indexOf(tag) > -1) {
25936 this.cwhite.push(tag);
25941 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25942 if (w.indexOf(tag) > -1) {
25945 this.cblack.push(tag);
25949 Roo.each(b, function(tag) {
25950 if (w.indexOf(tag) > -1) {
25953 if (this.cblack.indexOf(tag) > -1) {
25956 this.cblack.push(tag);
25961 setStylesheets : function(stylesheets)
25963 if(typeof(stylesheets) == 'string'){
25964 Roo.get(this.iframe.contentDocument.head).createChild({
25966 rel : 'stylesheet',
25975 Roo.each(stylesheets, function(s) {
25980 Roo.get(_this.iframe.contentDocument.head).createChild({
25982 rel : 'stylesheet',
25991 removeStylesheets : function()
25995 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26000 setStyle : function(style)
26002 Roo.get(this.iframe.contentDocument.head).createChild({
26011 // hide stuff that is not compatible
26025 * @event specialkey
26029 * @cfg {String} fieldClass @hide
26032 * @cfg {String} focusClass @hide
26035 * @cfg {String} autoCreate @hide
26038 * @cfg {String} inputType @hide
26041 * @cfg {String} invalidClass @hide
26044 * @cfg {String} invalidText @hide
26047 * @cfg {String} msgFx @hide
26050 * @cfg {String} validateOnBlur @hide
26054 Roo.HtmlEditorCore.white = [
26055 'area', 'br', 'img', 'input', 'hr', 'wbr',
26057 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26058 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26059 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26060 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26061 'table', 'ul', 'xmp',
26063 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26066 'dir', 'menu', 'ol', 'ul', 'dl',
26072 Roo.HtmlEditorCore.black = [
26073 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26075 'base', 'basefont', 'bgsound', 'blink', 'body',
26076 'frame', 'frameset', 'head', 'html', 'ilayer',
26077 'iframe', 'layer', 'link', 'meta', 'object',
26078 'script', 'style' ,'title', 'xml' // clean later..
26080 Roo.HtmlEditorCore.clean = [
26081 'script', 'style', 'title', 'xml'
26083 Roo.HtmlEditorCore.remove = [
26088 Roo.HtmlEditorCore.ablack = [
26092 Roo.HtmlEditorCore.aclean = [
26093 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26097 Roo.HtmlEditorCore.pwhite= [
26098 'http', 'https', 'mailto'
26101 // white listed style attributes.
26102 Roo.HtmlEditorCore.cwhite= [
26103 // 'text-align', /// default is to allow most things..
26109 // black listed style attributes.
26110 Roo.HtmlEditorCore.cblack= [
26111 // 'font-size' -- this can be set by the project
26115 Roo.HtmlEditorCore.swapCodes =[
26116 [ 8211, "–" ],
26117 [ 8212, "—" ],
26134 * @class Roo.bootstrap.HtmlEditor
26135 * @extends Roo.bootstrap.TextArea
26136 * Bootstrap HtmlEditor class
26139 * Create a new HtmlEditor
26140 * @param {Object} config The config object
26143 Roo.bootstrap.HtmlEditor = function(config){
26144 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26145 if (!this.toolbars) {
26146 this.toolbars = [];
26149 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26152 * @event initialize
26153 * Fires when the editor is fully initialized (including the iframe)
26154 * @param {HtmlEditor} this
26159 * Fires when the editor is first receives the focus. Any insertion must wait
26160 * until after this event.
26161 * @param {HtmlEditor} this
26165 * @event beforesync
26166 * Fires before the textarea is updated with content from the editor iframe. Return false
26167 * to cancel the sync.
26168 * @param {HtmlEditor} this
26169 * @param {String} html
26173 * @event beforepush
26174 * Fires before the iframe editor is updated with content from the textarea. Return false
26175 * to cancel the push.
26176 * @param {HtmlEditor} this
26177 * @param {String} html
26182 * Fires when the textarea is updated with content from the editor iframe.
26183 * @param {HtmlEditor} this
26184 * @param {String} html
26189 * Fires when the iframe editor is updated with content from the textarea.
26190 * @param {HtmlEditor} this
26191 * @param {String} html
26195 * @event editmodechange
26196 * Fires when the editor switches edit modes
26197 * @param {HtmlEditor} this
26198 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26200 editmodechange: true,
26202 * @event editorevent
26203 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26204 * @param {HtmlEditor} this
26208 * @event firstfocus
26209 * Fires when on first focus - needed by toolbars..
26210 * @param {HtmlEditor} this
26215 * Auto save the htmlEditor value as a file into Events
26216 * @param {HtmlEditor} this
26220 * @event savedpreview
26221 * preview the saved version of htmlEditor
26222 * @param {HtmlEditor} this
26229 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26233 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26238 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26243 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26248 * @cfg {Number} height (in pixels)
26252 * @cfg {Number} width (in pixels)
26257 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26260 stylesheets: false,
26265 // private properties
26266 validationEvent : false,
26268 initialized : false,
26271 onFocus : Roo.emptyFn,
26273 hideMode:'offsets',
26275 tbContainer : false,
26279 toolbarContainer :function() {
26280 return this.wrap.select('.x-html-editor-tb',true).first();
26284 * Protected method that will not generally be called directly. It
26285 * is called when the editor creates its toolbar. Override this method if you need to
26286 * add custom toolbar buttons.
26287 * @param {HtmlEditor} editor
26289 createToolbar : function(){
26290 Roo.log('renewing');
26291 Roo.log("create toolbars");
26293 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26294 this.toolbars[0].render(this.toolbarContainer());
26298 // if (!editor.toolbars || !editor.toolbars.length) {
26299 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26302 // for (var i =0 ; i < editor.toolbars.length;i++) {
26303 // editor.toolbars[i] = Roo.factory(
26304 // typeof(editor.toolbars[i]) == 'string' ?
26305 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26306 // Roo.bootstrap.HtmlEditor);
26307 // editor.toolbars[i].init(editor);
26313 onRender : function(ct, position)
26315 // Roo.log("Call onRender: " + this.xtype);
26317 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26319 this.wrap = this.inputEl().wrap({
26320 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26323 this.editorcore.onRender(ct, position);
26325 if (this.resizable) {
26326 this.resizeEl = new Roo.Resizable(this.wrap, {
26330 minHeight : this.height,
26331 height: this.height,
26332 handles : this.resizable,
26335 resize : function(r, w, h) {
26336 _t.onResize(w,h); // -something
26342 this.createToolbar(this);
26345 if(!this.width && this.resizable){
26346 this.setSize(this.wrap.getSize());
26348 if (this.resizeEl) {
26349 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26350 // should trigger onReize..
26356 onResize : function(w, h)
26358 Roo.log('resize: ' +w + ',' + h );
26359 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26363 if(this.inputEl() ){
26364 if(typeof w == 'number'){
26365 var aw = w - this.wrap.getFrameWidth('lr');
26366 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26369 if(typeof h == 'number'){
26370 var tbh = -11; // fixme it needs to tool bar size!
26371 for (var i =0; i < this.toolbars.length;i++) {
26372 // fixme - ask toolbars for heights?
26373 tbh += this.toolbars[i].el.getHeight();
26374 //if (this.toolbars[i].footer) {
26375 // tbh += this.toolbars[i].footer.el.getHeight();
26383 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26384 ah -= 5; // knock a few pixes off for look..
26385 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26389 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26390 this.editorcore.onResize(ew,eh);
26395 * Toggles the editor between standard and source edit mode.
26396 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26398 toggleSourceEdit : function(sourceEditMode)
26400 this.editorcore.toggleSourceEdit(sourceEditMode);
26402 if(this.editorcore.sourceEditMode){
26403 Roo.log('editor - showing textarea');
26406 // Roo.log(this.syncValue());
26408 this.inputEl().removeClass(['hide', 'x-hidden']);
26409 this.inputEl().dom.removeAttribute('tabIndex');
26410 this.inputEl().focus();
26412 Roo.log('editor - hiding textarea');
26414 // Roo.log(this.pushValue());
26417 this.inputEl().addClass(['hide', 'x-hidden']);
26418 this.inputEl().dom.setAttribute('tabIndex', -1);
26419 //this.deferFocus();
26422 if(this.resizable){
26423 this.setSize(this.wrap.getSize());
26426 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26429 // private (for BoxComponent)
26430 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26432 // private (for BoxComponent)
26433 getResizeEl : function(){
26437 // private (for BoxComponent)
26438 getPositionEl : function(){
26443 initEvents : function(){
26444 this.originalValue = this.getValue();
26448 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26451 // markInvalid : Roo.emptyFn,
26453 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26456 // clearInvalid : Roo.emptyFn,
26458 setValue : function(v){
26459 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26460 this.editorcore.pushValue();
26465 deferFocus : function(){
26466 this.focus.defer(10, this);
26470 focus : function(){
26471 this.editorcore.focus();
26477 onDestroy : function(){
26483 for (var i =0; i < this.toolbars.length;i++) {
26484 // fixme - ask toolbars for heights?
26485 this.toolbars[i].onDestroy();
26488 this.wrap.dom.innerHTML = '';
26489 this.wrap.remove();
26494 onFirstFocus : function(){
26495 //Roo.log("onFirstFocus");
26496 this.editorcore.onFirstFocus();
26497 for (var i =0; i < this.toolbars.length;i++) {
26498 this.toolbars[i].onFirstFocus();
26504 syncValue : function()
26506 this.editorcore.syncValue();
26509 pushValue : function()
26511 this.editorcore.pushValue();
26515 // hide stuff that is not compatible
26529 * @event specialkey
26533 * @cfg {String} fieldClass @hide
26536 * @cfg {String} focusClass @hide
26539 * @cfg {String} autoCreate @hide
26542 * @cfg {String} inputType @hide
26546 * @cfg {String} invalidText @hide
26549 * @cfg {String} msgFx @hide
26552 * @cfg {String} validateOnBlur @hide
26561 Roo.namespace('Roo.bootstrap.htmleditor');
26563 * @class Roo.bootstrap.HtmlEditorToolbar1
26569 new Roo.bootstrap.HtmlEditor({
26572 new Roo.bootstrap.HtmlEditorToolbar1({
26573 disable : { fonts: 1 , format: 1, ..., ... , ...],
26579 * @cfg {Object} disable List of elements to disable..
26580 * @cfg {Array} btns List of additional buttons.
26584 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26587 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26590 Roo.apply(this, config);
26592 // default disabled, based on 'good practice'..
26593 this.disable = this.disable || {};
26594 Roo.applyIf(this.disable, {
26597 specialElements : true
26599 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26601 this.editor = config.editor;
26602 this.editorcore = config.editor.editorcore;
26604 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26606 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26607 // dont call parent... till later.
26609 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26614 editorcore : false,
26619 "h1","h2","h3","h4","h5","h6",
26621 "abbr", "acronym", "address", "cite", "samp", "var",
26625 onRender : function(ct, position)
26627 // Roo.log("Call onRender: " + this.xtype);
26629 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26631 this.el.dom.style.marginBottom = '0';
26633 var editorcore = this.editorcore;
26634 var editor= this.editor;
26637 var btn = function(id,cmd , toggle, handler, html){
26639 var event = toggle ? 'toggle' : 'click';
26644 xns: Roo.bootstrap,
26648 enableToggle:toggle !== false,
26650 pressed : toggle ? false : null,
26653 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26654 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26660 // var cb_box = function...
26665 xns: Roo.bootstrap,
26670 xns: Roo.bootstrap,
26674 Roo.each(this.formats, function(f) {
26675 style.menu.items.push({
26677 xns: Roo.bootstrap,
26678 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26683 editorcore.insertTag(this.tagname);
26690 children.push(style);
26692 btn('bold',false,true);
26693 btn('italic',false,true);
26694 btn('align-left', 'justifyleft',true);
26695 btn('align-center', 'justifycenter',true);
26696 btn('align-right' , 'justifyright',true);
26697 btn('link', false, false, function(btn) {
26698 //Roo.log("create link?");
26699 var url = prompt(this.createLinkText, this.defaultLinkValue);
26700 if(url && url != 'http:/'+'/'){
26701 this.editorcore.relayCmd('createlink', url);
26704 btn('list','insertunorderedlist',true);
26705 btn('pencil', false,true, function(btn){
26707 this.toggleSourceEdit(btn.pressed);
26710 if (this.editor.btns.length > 0) {
26711 for (var i = 0; i<this.editor.btns.length; i++) {
26712 children.push(this.editor.btns[i]);
26720 xns: Roo.bootstrap,
26725 xns: Roo.bootstrap,
26730 cog.menu.items.push({
26732 xns: Roo.bootstrap,
26733 html : Clean styles,
26738 editorcore.insertTag(this.tagname);
26747 this.xtype = 'NavSimplebar';
26749 for(var i=0;i< children.length;i++) {
26751 this.buttons.add(this.addxtypeChild(children[i]));
26755 editor.on('editorevent', this.updateToolbar, this);
26757 onBtnClick : function(id)
26759 this.editorcore.relayCmd(id);
26760 this.editorcore.focus();
26764 * Protected method that will not generally be called directly. It triggers
26765 * a toolbar update by reading the markup state of the current selection in the editor.
26767 updateToolbar: function(){
26769 if(!this.editorcore.activated){
26770 this.editor.onFirstFocus(); // is this neeed?
26774 var btns = this.buttons;
26775 var doc = this.editorcore.doc;
26776 btns.get('bold').setActive(doc.queryCommandState('bold'));
26777 btns.get('italic').setActive(doc.queryCommandState('italic'));
26778 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26780 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26781 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26782 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26784 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26785 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26788 var ans = this.editorcore.getAllAncestors();
26789 if (this.formatCombo) {
26792 var store = this.formatCombo.store;
26793 this.formatCombo.setValue("");
26794 for (var i =0; i < ans.length;i++) {
26795 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26797 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26805 // hides menus... - so this cant be on a menu...
26806 Roo.bootstrap.MenuMgr.hideAll();
26808 Roo.bootstrap.MenuMgr.hideAll();
26809 //this.editorsyncValue();
26811 onFirstFocus: function() {
26812 this.buttons.each(function(item){
26816 toggleSourceEdit : function(sourceEditMode){
26819 if(sourceEditMode){
26820 Roo.log("disabling buttons");
26821 this.buttons.each( function(item){
26822 if(item.cmd != 'pencil'){
26828 Roo.log("enabling buttons");
26829 if(this.editorcore.initialized){
26830 this.buttons.each( function(item){
26836 Roo.log("calling toggole on editor");
26837 // tell the editor that it's been pressed..
26838 this.editor.toggleSourceEdit(sourceEditMode);
26852 * @class Roo.bootstrap.Markdown
26853 * @extends Roo.bootstrap.TextArea
26854 * Bootstrap Showdown editable area
26855 * @cfg {string} content
26858 * Create a new Showdown
26861 Roo.bootstrap.Markdown = function(config){
26862 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26866 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26870 initEvents : function()
26873 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26874 this.markdownEl = this.el.createChild({
26875 cls : 'roo-markdown-area'
26877 this.inputEl().addClass('d-none');
26878 if (this.getValue() == '') {
26879 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26882 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26884 this.markdownEl.on('click', this.toggleTextEdit, this);
26885 this.on('blur', this.toggleTextEdit, this);
26886 this.on('specialkey', this.resizeTextArea, this);
26889 toggleTextEdit : function()
26891 var sh = this.markdownEl.getHeight();
26892 this.inputEl().addClass('d-none');
26893 this.markdownEl.addClass('d-none');
26894 if (!this.editing) {
26896 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26897 this.inputEl().removeClass('d-none');
26898 this.inputEl().focus();
26899 this.editing = true;
26902 // show showdown...
26903 this.updateMarkdown();
26904 this.markdownEl.removeClass('d-none');
26905 this.editing = false;
26908 updateMarkdown : function()
26910 if (this.getValue() == '') {
26911 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26915 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26918 resizeTextArea: function () {
26921 Roo.log([sh, this.getValue().split("\n").length * 30]);
26922 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26924 setValue : function(val)
26926 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26927 if (!this.editing) {
26928 this.updateMarkdown();
26934 if (!this.editing) {
26935 this.toggleTextEdit();
26943 * @class Roo.bootstrap.Table.AbstractSelectionModel
26944 * @extends Roo.util.Observable
26945 * Abstract base class for grid SelectionModels. It provides the interface that should be
26946 * implemented by descendant classes. This class should not be directly instantiated.
26949 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26950 this.locked = false;
26951 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26955 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26956 /** @ignore Called by the grid automatically. Do not call directly. */
26957 init : function(grid){
26963 * Locks the selections.
26966 this.locked = true;
26970 * Unlocks the selections.
26972 unlock : function(){
26973 this.locked = false;
26977 * Returns true if the selections are locked.
26978 * @return {Boolean}
26980 isLocked : function(){
26981 return this.locked;
26985 initEvents : function ()
26991 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26992 * @class Roo.bootstrap.Table.RowSelectionModel
26993 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26994 * It supports multiple selections and keyboard selection/navigation.
26996 * @param {Object} config
26999 Roo.bootstrap.Table.RowSelectionModel = function(config){
27000 Roo.apply(this, config);
27001 this.selections = new Roo.util.MixedCollection(false, function(o){
27006 this.lastActive = false;
27010 * @event selectionchange
27011 * Fires when the selection changes
27012 * @param {SelectionModel} this
27014 "selectionchange" : true,
27016 * @event afterselectionchange
27017 * Fires after the selection changes (eg. by key press or clicking)
27018 * @param {SelectionModel} this
27020 "afterselectionchange" : true,
27022 * @event beforerowselect
27023 * Fires when a row is selected being selected, return false to cancel.
27024 * @param {SelectionModel} this
27025 * @param {Number} rowIndex The selected index
27026 * @param {Boolean} keepExisting False if other selections will be cleared
27028 "beforerowselect" : true,
27031 * Fires when a row is selected.
27032 * @param {SelectionModel} this
27033 * @param {Number} rowIndex The selected index
27034 * @param {Roo.data.Record} r The record
27036 "rowselect" : true,
27038 * @event rowdeselect
27039 * Fires when a row is deselected.
27040 * @param {SelectionModel} this
27041 * @param {Number} rowIndex The selected index
27043 "rowdeselect" : true
27045 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27046 this.locked = false;
27049 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27051 * @cfg {Boolean} singleSelect
27052 * True to allow selection of only one row at a time (defaults to false)
27054 singleSelect : false,
27057 initEvents : function()
27060 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27061 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27062 //}else{ // allow click to work like normal
27063 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27065 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27066 this.grid.on("rowclick", this.handleMouseDown, this);
27068 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27069 "up" : function(e){
27071 this.selectPrevious(e.shiftKey);
27072 }else if(this.last !== false && this.lastActive !== false){
27073 var last = this.last;
27074 this.selectRange(this.last, this.lastActive-1);
27075 this.grid.getView().focusRow(this.lastActive);
27076 if(last !== false){
27080 this.selectFirstRow();
27082 this.fireEvent("afterselectionchange", this);
27084 "down" : function(e){
27086 this.selectNext(e.shiftKey);
27087 }else if(this.last !== false && this.lastActive !== false){
27088 var last = this.last;
27089 this.selectRange(this.last, this.lastActive+1);
27090 this.grid.getView().focusRow(this.lastActive);
27091 if(last !== false){
27095 this.selectFirstRow();
27097 this.fireEvent("afterselectionchange", this);
27101 this.grid.store.on('load', function(){
27102 this.selections.clear();
27105 var view = this.grid.view;
27106 view.on("refresh", this.onRefresh, this);
27107 view.on("rowupdated", this.onRowUpdated, this);
27108 view.on("rowremoved", this.onRemove, this);
27113 onRefresh : function()
27115 var ds = this.grid.store, i, v = this.grid.view;
27116 var s = this.selections;
27117 s.each(function(r){
27118 if((i = ds.indexOfId(r.id)) != -1){
27127 onRemove : function(v, index, r){
27128 this.selections.remove(r);
27132 onRowUpdated : function(v, index, r){
27133 if(this.isSelected(r)){
27134 v.onRowSelect(index);
27140 * @param {Array} records The records to select
27141 * @param {Boolean} keepExisting (optional) True to keep existing selections
27143 selectRecords : function(records, keepExisting)
27146 this.clearSelections();
27148 var ds = this.grid.store;
27149 for(var i = 0, len = records.length; i < len; i++){
27150 this.selectRow(ds.indexOf(records[i]), true);
27155 * Gets the number of selected rows.
27158 getCount : function(){
27159 return this.selections.length;
27163 * Selects the first row in the grid.
27165 selectFirstRow : function(){
27170 * Select the last row.
27171 * @param {Boolean} keepExisting (optional) True to keep existing selections
27173 selectLastRow : function(keepExisting){
27174 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27175 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27179 * Selects the row immediately following the last selected row.
27180 * @param {Boolean} keepExisting (optional) True to keep existing selections
27182 selectNext : function(keepExisting)
27184 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27185 this.selectRow(this.last+1, keepExisting);
27186 this.grid.getView().focusRow(this.last);
27191 * Selects the row that precedes the last selected row.
27192 * @param {Boolean} keepExisting (optional) True to keep existing selections
27194 selectPrevious : function(keepExisting){
27196 this.selectRow(this.last-1, keepExisting);
27197 this.grid.getView().focusRow(this.last);
27202 * Returns the selected records
27203 * @return {Array} Array of selected records
27205 getSelections : function(){
27206 return [].concat(this.selections.items);
27210 * Returns the first selected record.
27213 getSelected : function(){
27214 return this.selections.itemAt(0);
27219 * Clears all selections.
27221 clearSelections : function(fast)
27227 var ds = this.grid.store;
27228 var s = this.selections;
27229 s.each(function(r){
27230 this.deselectRow(ds.indexOfId(r.id));
27234 this.selections.clear();
27241 * Selects all rows.
27243 selectAll : function(){
27247 this.selections.clear();
27248 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27249 this.selectRow(i, true);
27254 * Returns True if there is a selection.
27255 * @return {Boolean}
27257 hasSelection : function(){
27258 return this.selections.length > 0;
27262 * Returns True if the specified row is selected.
27263 * @param {Number/Record} record The record or index of the record to check
27264 * @return {Boolean}
27266 isSelected : function(index){
27267 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27268 return (r && this.selections.key(r.id) ? true : false);
27272 * Returns True if the specified record id is selected.
27273 * @param {String} id The id of record to check
27274 * @return {Boolean}
27276 isIdSelected : function(id){
27277 return (this.selections.key(id) ? true : false);
27282 handleMouseDBClick : function(e, t){
27286 handleMouseDown : function(e, t)
27288 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27289 if(this.isLocked() || rowIndex < 0 ){
27292 if(e.shiftKey && this.last !== false){
27293 var last = this.last;
27294 this.selectRange(last, rowIndex, e.ctrlKey);
27295 this.last = last; // reset the last
27299 var isSelected = this.isSelected(rowIndex);
27300 //Roo.log("select row:" + rowIndex);
27302 this.deselectRow(rowIndex);
27304 this.selectRow(rowIndex, true);
27308 if(e.button !== 0 && isSelected){
27309 alert('rowIndex 2: ' + rowIndex);
27310 view.focusRow(rowIndex);
27311 }else if(e.ctrlKey && isSelected){
27312 this.deselectRow(rowIndex);
27313 }else if(!isSelected){
27314 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27315 view.focusRow(rowIndex);
27319 this.fireEvent("afterselectionchange", this);
27322 handleDragableRowClick : function(grid, rowIndex, e)
27324 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27325 this.selectRow(rowIndex, false);
27326 grid.view.focusRow(rowIndex);
27327 this.fireEvent("afterselectionchange", this);
27332 * Selects multiple rows.
27333 * @param {Array} rows Array of the indexes of the row to select
27334 * @param {Boolean} keepExisting (optional) True to keep existing selections
27336 selectRows : function(rows, keepExisting){
27338 this.clearSelections();
27340 for(var i = 0, len = rows.length; i < len; i++){
27341 this.selectRow(rows[i], true);
27346 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27347 * @param {Number} startRow The index of the first row in the range
27348 * @param {Number} endRow The index of the last row in the range
27349 * @param {Boolean} keepExisting (optional) True to retain existing selections
27351 selectRange : function(startRow, endRow, keepExisting){
27356 this.clearSelections();
27358 if(startRow <= endRow){
27359 for(var i = startRow; i <= endRow; i++){
27360 this.selectRow(i, true);
27363 for(var i = startRow; i >= endRow; i--){
27364 this.selectRow(i, true);
27370 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27371 * @param {Number} startRow The index of the first row in the range
27372 * @param {Number} endRow The index of the last row in the range
27374 deselectRange : function(startRow, endRow, preventViewNotify){
27378 for(var i = startRow; i <= endRow; i++){
27379 this.deselectRow(i, preventViewNotify);
27385 * @param {Number} row The index of the row to select
27386 * @param {Boolean} keepExisting (optional) True to keep existing selections
27388 selectRow : function(index, keepExisting, preventViewNotify)
27390 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27393 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27394 if(!keepExisting || this.singleSelect){
27395 this.clearSelections();
27398 var r = this.grid.store.getAt(index);
27399 //console.log('selectRow - record id :' + r.id);
27401 this.selections.add(r);
27402 this.last = this.lastActive = index;
27403 if(!preventViewNotify){
27404 var proxy = new Roo.Element(
27405 this.grid.getRowDom(index)
27407 proxy.addClass('bg-info info');
27409 this.fireEvent("rowselect", this, index, r);
27410 this.fireEvent("selectionchange", this);
27416 * @param {Number} row The index of the row to deselect
27418 deselectRow : function(index, preventViewNotify)
27423 if(this.last == index){
27426 if(this.lastActive == index){
27427 this.lastActive = false;
27430 var r = this.grid.store.getAt(index);
27435 this.selections.remove(r);
27436 //.console.log('deselectRow - record id :' + r.id);
27437 if(!preventViewNotify){
27439 var proxy = new Roo.Element(
27440 this.grid.getRowDom(index)
27442 proxy.removeClass('bg-info info');
27444 this.fireEvent("rowdeselect", this, index);
27445 this.fireEvent("selectionchange", this);
27449 restoreLast : function(){
27451 this.last = this._last;
27456 acceptsNav : function(row, col, cm){
27457 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27461 onEditorKey : function(field, e){
27462 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27467 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27469 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27471 }else if(k == e.ENTER && !e.ctrlKey){
27475 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27477 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27479 }else if(k == e.ESC){
27483 g.startEditing(newCell[0], newCell[1]);
27489 * Ext JS Library 1.1.1
27490 * Copyright(c) 2006-2007, Ext JS, LLC.
27492 * Originally Released Under LGPL - original licence link has changed is not relivant.
27495 * <script type="text/javascript">
27499 * @class Roo.bootstrap.PagingToolbar
27500 * @extends Roo.bootstrap.NavSimplebar
27501 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27503 * Create a new PagingToolbar
27504 * @param {Object} config The config object
27505 * @param {Roo.data.Store} store
27507 Roo.bootstrap.PagingToolbar = function(config)
27509 // old args format still supported... - xtype is prefered..
27510 // created from xtype...
27512 this.ds = config.dataSource;
27514 if (config.store && !this.ds) {
27515 this.store= Roo.factory(config.store, Roo.data);
27516 this.ds = this.store;
27517 this.ds.xmodule = this.xmodule || false;
27520 this.toolbarItems = [];
27521 if (config.items) {
27522 this.toolbarItems = config.items;
27525 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27530 this.bind(this.ds);
27533 if (Roo.bootstrap.version == 4) {
27534 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27536 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27541 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27543 * @cfg {Roo.data.Store} dataSource
27544 * The underlying data store providing the paged data
27547 * @cfg {String/HTMLElement/Element} container
27548 * container The id or element that will contain the toolbar
27551 * @cfg {Boolean} displayInfo
27552 * True to display the displayMsg (defaults to false)
27555 * @cfg {Number} pageSize
27556 * The number of records to display per page (defaults to 20)
27560 * @cfg {String} displayMsg
27561 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27563 displayMsg : 'Displaying {0} - {1} of {2}',
27565 * @cfg {String} emptyMsg
27566 * The message to display when no records are found (defaults to "No data to display")
27568 emptyMsg : 'No data to display',
27570 * Customizable piece of the default paging text (defaults to "Page")
27573 beforePageText : "Page",
27575 * Customizable piece of the default paging text (defaults to "of %0")
27578 afterPageText : "of {0}",
27580 * Customizable piece of the default paging text (defaults to "First Page")
27583 firstText : "First Page",
27585 * Customizable piece of the default paging text (defaults to "Previous Page")
27588 prevText : "Previous Page",
27590 * Customizable piece of the default paging text (defaults to "Next Page")
27593 nextText : "Next Page",
27595 * Customizable piece of the default paging text (defaults to "Last Page")
27598 lastText : "Last Page",
27600 * Customizable piece of the default paging text (defaults to "Refresh")
27603 refreshText : "Refresh",
27607 onRender : function(ct, position)
27609 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27610 this.navgroup.parentId = this.id;
27611 this.navgroup.onRender(this.el, null);
27612 // add the buttons to the navgroup
27614 if(this.displayInfo){
27615 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27616 this.displayEl = this.el.select('.x-paging-info', true).first();
27617 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27618 // this.displayEl = navel.el.select('span',true).first();
27624 Roo.each(_this.buttons, function(e){ // this might need to use render????
27625 Roo.factory(e).render(_this.el);
27629 Roo.each(_this.toolbarItems, function(e) {
27630 _this.navgroup.addItem(e);
27634 this.first = this.navgroup.addItem({
27635 tooltip: this.firstText,
27636 cls: "prev btn-outline-secondary",
27637 html : ' <i class="fa fa-step-backward"></i>',
27639 preventDefault: true,
27640 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27643 this.prev = this.navgroup.addItem({
27644 tooltip: this.prevText,
27645 cls: "prev btn-outline-secondary",
27646 html : ' <i class="fa fa-backward"></i>',
27648 preventDefault: true,
27649 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27651 //this.addSeparator();
27654 var field = this.navgroup.addItem( {
27656 cls : 'x-paging-position btn-outline-secondary',
27658 html : this.beforePageText +
27659 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27660 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27663 this.field = field.el.select('input', true).first();
27664 this.field.on("keydown", this.onPagingKeydown, this);
27665 this.field.on("focus", function(){this.dom.select();});
27668 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27669 //this.field.setHeight(18);
27670 //this.addSeparator();
27671 this.next = this.navgroup.addItem({
27672 tooltip: this.nextText,
27673 cls: "next btn-outline-secondary",
27674 html : ' <i class="fa fa-forward"></i>',
27676 preventDefault: true,
27677 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27679 this.last = this.navgroup.addItem({
27680 tooltip: this.lastText,
27681 html : ' <i class="fa fa-step-forward"></i>',
27682 cls: "next btn-outline-secondary",
27684 preventDefault: true,
27685 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27687 //this.addSeparator();
27688 this.loading = this.navgroup.addItem({
27689 tooltip: this.refreshText,
27690 cls: "btn-outline-secondary",
27691 html : ' <i class="fa fa-refresh"></i>',
27692 preventDefault: true,
27693 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27699 updateInfo : function(){
27700 if(this.displayEl){
27701 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27702 var msg = count == 0 ?
27706 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27708 this.displayEl.update(msg);
27713 onLoad : function(ds, r, o)
27715 this.cursor = o.params && o.params.start ? o.params.start : 0;
27717 var d = this.getPageData(),
27722 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27723 this.field.dom.value = ap;
27724 this.first.setDisabled(ap == 1);
27725 this.prev.setDisabled(ap == 1);
27726 this.next.setDisabled(ap == ps);
27727 this.last.setDisabled(ap == ps);
27728 this.loading.enable();
27733 getPageData : function(){
27734 var total = this.ds.getTotalCount();
27737 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27738 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27743 onLoadError : function(){
27744 this.loading.enable();
27748 onPagingKeydown : function(e){
27749 var k = e.getKey();
27750 var d = this.getPageData();
27752 var v = this.field.dom.value, pageNum;
27753 if(!v || isNaN(pageNum = parseInt(v, 10))){
27754 this.field.dom.value = d.activePage;
27757 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27758 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27761 else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
27763 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27764 this.field.dom.value = pageNum;
27765 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27768 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27770 var v = this.field.dom.value, pageNum;
27771 var increment = (e.shiftKey) ? 10 : 1;
27772 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27775 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27776 this.field.dom.value = d.activePage;
27779 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27781 this.field.dom.value = parseInt(v, 10) + increment;
27782 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27783 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27790 beforeLoad : function(){
27792 this.loading.disable();
27797 onClick : function(which){
27806 ds.load({params:{start: 0, limit: this.pageSize}});
27809 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27812 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27815 var total = ds.getTotalCount();
27816 var extra = total % this.pageSize;
27817 var lastStart = extra ? (total - extra) : total-this.pageSize;
27818 ds.load({params:{start: lastStart, limit: this.pageSize}});
27821 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27827 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27828 * @param {Roo.data.Store} store The data store to unbind
27830 unbind : function(ds){
27831 ds.un("beforeload", this.beforeLoad, this);
27832 ds.un("load", this.onLoad, this);
27833 ds.un("loadexception", this.onLoadError, this);
27834 ds.un("remove", this.updateInfo, this);
27835 ds.un("add", this.updateInfo, this);
27836 this.ds = undefined;
27840 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27841 * @param {Roo.data.Store} store The data store to bind
27843 bind : function(ds){
27844 ds.on("beforeload", this.beforeLoad, this);
27845 ds.on("load", this.onLoad, this);
27846 ds.on("loadexception", this.onLoadError, this);
27847 ds.on("remove", this.updateInfo, this);
27848 ds.on("add", this.updateInfo, this);
27859 * @class Roo.bootstrap.MessageBar
27860 * @extends Roo.bootstrap.Component
27861 * Bootstrap MessageBar class
27862 * @cfg {String} html contents of the MessageBar
27863 * @cfg {String} weight (info | success | warning | danger) default info
27864 * @cfg {String} beforeClass insert the bar before the given class
27865 * @cfg {Boolean} closable (true | false) default false
27866 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27869 * Create a new Element
27870 * @param {Object} config The config object
27873 Roo.bootstrap.MessageBar = function(config){
27874 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27877 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27883 beforeClass: 'bootstrap-sticky-wrap',
27885 getAutoCreate : function(){
27889 cls: 'alert alert-dismissable alert-' + this.weight,
27894 html: this.html || ''
27900 cfg.cls += ' alert-messages-fixed';
27914 onRender : function(ct, position)
27916 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27919 var cfg = Roo.apply({}, this.getAutoCreate());
27923 cfg.cls += ' ' + this.cls;
27926 cfg.style = this.style;
27928 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27930 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27933 this.el.select('>button.close').on('click', this.hide, this);
27939 if (!this.rendered) {
27945 this.fireEvent('show', this);
27951 if (!this.rendered) {
27957 this.fireEvent('hide', this);
27960 update : function()
27962 // var e = this.el.dom.firstChild;
27964 // if(this.closable){
27965 // e = e.nextSibling;
27968 // e.data = this.html || '';
27970 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27986 * @class Roo.bootstrap.Graph
27987 * @extends Roo.bootstrap.Component
27988 * Bootstrap Graph class
27992 @cfg {String} graphtype bar | vbar | pie
27993 @cfg {number} g_x coodinator | centre x (pie)
27994 @cfg {number} g_y coodinator | centre y (pie)
27995 @cfg {number} g_r radius (pie)
27996 @cfg {number} g_height height of the chart (respected by all elements in the set)
27997 @cfg {number} g_width width of the chart (respected by all elements in the set)
27998 @cfg {Object} title The title of the chart
28001 -opts (object) options for the chart
28003 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28004 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28006 o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
28007 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28009 o stretch (boolean)
28011 -opts (object) options for the pie
28014 o startAngle (number)
28015 o endAngle (number)
28019 * Create a new Input
28020 * @param {Object} config The config object
28023 Roo.bootstrap.Graph = function(config){
28024 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28030 * The img click event for the img.
28031 * @param {Roo.EventObject} e
28037 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28048 //g_colors: this.colors,
28055 getAutoCreate : function(){
28066 onRender : function(ct,position){
28069 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28071 if (typeof(Raphael) == 'undefined') {
28072 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28076 this.raphael = Raphael(this.el.dom);
28078 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28079 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28080 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28081 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28083 r.text(160, 10, "Single Series Chart").attr(txtattr);
28084 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28085 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28086 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28088 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28089 r.barchart(330, 10, 300, 220, data1);
28090 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28091 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28094 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28095 // r.barchart(30, 30, 560, 250, xdata, {
28096 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28097 // axis : "0 0 1 1",
28098 // axisxlabels : xdata
28099 // //yvalues : cols,
28102 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28104 // this.load(null,xdata,{
28105 // axis : "0 0 1 1",
28106 // axisxlabels : xdata
28111 load : function(graphtype,xdata,opts)
28113 this.raphael.clear();
28115 graphtype = this.graphtype;
28120 var r = this.raphael,
28121 fin = function () {
28122 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28124 fout = function () {
28125 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28127 pfin = function() {
28128 this.sector.stop();
28129 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28132 this.label[0].stop();
28133 this.label[0].attr({ r: 7.5 });
28134 this.label[1].attr({ "font-weight": 800 });
28137 pfout = function() {
28138 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28141 this.label[0].animate({ r: 5 }, 500, "bounce");
28142 this.label[1].attr({ "font-weight": 400 });
28148 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28151 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28154 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28155 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28157 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28164 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28169 setTitle: function(o)
28174 initEvents: function() {
28177 this.el.on('click', this.onClick, this);
28181 onClick : function(e)
28183 Roo.log('img onclick');
28184 this.fireEvent('click', this, e);
28196 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28199 * @class Roo.bootstrap.dash.NumberBox
28200 * @extends Roo.bootstrap.Component
28201 * Bootstrap NumberBox class
28202 * @cfg {String} headline Box headline
28203 * @cfg {String} content Box content
28204 * @cfg {String} icon Box icon
28205 * @cfg {String} footer Footer text
28206 * @cfg {String} fhref Footer href
28209 * Create a new NumberBox
28210 * @param {Object} config The config object
28214 Roo.bootstrap.dash.NumberBox = function(config){
28215 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28219 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28228 getAutoCreate : function(){
28232 cls : 'small-box ',
28240 cls : 'roo-headline',
28241 html : this.headline
28245 cls : 'roo-content',
28246 html : this.content
28260 cls : 'ion ' + this.icon
28269 cls : 'small-box-footer',
28270 href : this.fhref || '#',
28274 cfg.cn.push(footer);
28281 onRender : function(ct,position){
28282 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28289 setHeadline: function (value)
28291 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28294 setFooter: function (value, href)
28296 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28299 this.el.select('a.small-box-footer',true).first().attr('href', href);
28304 setContent: function (value)
28306 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28309 initEvents: function()
28323 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28326 * @class Roo.bootstrap.dash.TabBox
28327 * @extends Roo.bootstrap.Component
28328 * Bootstrap TabBox class
28329 * @cfg {String} title Title of the TabBox
28330 * @cfg {String} icon Icon of the TabBox
28331 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28332 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28335 * Create a new TabBox
28336 * @param {Object} config The config object
28340 Roo.bootstrap.dash.TabBox = function(config){
28341 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28346 * When a pane is added
28347 * @param {Roo.bootstrap.dash.TabPane} pane
28351 * @event activatepane
28352 * When a pane is activated
28353 * @param {Roo.bootstrap.dash.TabPane} pane
28355 "activatepane" : true
28363 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28368 tabScrollable : false,
28370 getChildContainer : function()
28372 return this.el.select('.tab-content', true).first();
28375 getAutoCreate : function(){
28379 cls: 'pull-left header',
28387 cls: 'fa ' + this.icon
28393 cls: 'nav nav-tabs pull-right',
28399 if(this.tabScrollable){
28406 cls: 'nav nav-tabs pull-right',
28417 cls: 'nav-tabs-custom',
28422 cls: 'tab-content no-padding',
28430 initEvents : function()
28432 //Roo.log('add add pane handler');
28433 this.on('addpane', this.onAddPane, this);
28436 * Updates the box title
28437 * @param {String} html to set the title to.
28439 setTitle : function(value)
28441 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28443 onAddPane : function(pane)
28445 this.panes.push(pane);
28446 //Roo.log('addpane');
28448 // tabs are rendere left to right..
28449 if(!this.showtabs){
28453 var ctr = this.el.select('.nav-tabs', true).first();
28456 var existing = ctr.select('.nav-tab',true);
28457 var qty = existing.getCount();;
28460 var tab = ctr.createChild({
28462 cls : 'nav-tab' + (qty ? '' : ' active'),
28470 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28473 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28475 pane.el.addClass('active');
28480 onTabClick : function(ev,un,ob,pane)
28482 //Roo.log('tab - prev default');
28483 ev.preventDefault();
28486 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28487 pane.tab.addClass('active');
28488 //Roo.log(pane.title);
28489 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28490 // technically we should have a deactivate event.. but maybe add later.
28491 // and it should not de-activate the selected tab...
28492 this.fireEvent('activatepane', pane);
28493 pane.el.addClass('active');
28494 pane.fireEvent('activate');
28499 getActivePane : function()
28502 Roo.each(this.panes, function(p) {
28503 if(p.el.hasClass('active')){
28524 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28526 * @class Roo.bootstrap.TabPane
28527 * @extends Roo.bootstrap.Component
28528 * Bootstrap TabPane class
28529 * @cfg {Boolean} active (false | true) Default false
28530 * @cfg {String} title title of panel
28534 * Create a new TabPane
28535 * @param {Object} config The config object
28538 Roo.bootstrap.dash.TabPane = function(config){
28539 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28545 * When a pane is activated
28546 * @param {Roo.bootstrap.dash.TabPane} pane
28553 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28558 // the tabBox that this is attached to.
28561 getAutoCreate : function()
28569 cfg.cls += ' active';
28574 initEvents : function()
28576 //Roo.log('trigger add pane handler');
28577 this.parent().fireEvent('addpane', this)
28581 * Updates the tab title
28582 * @param {String} html to set the title to.
28584 setTitle: function(str)
28590 this.tab.select('a', true).first().dom.innerHTML = str;
28607 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28610 * @class Roo.bootstrap.menu.Menu
28611 * @extends Roo.bootstrap.Component
28612 * Bootstrap Menu class - container for Menu
28613 * @cfg {String} html Text of the menu
28614 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28615 * @cfg {String} icon Font awesome icon
28616 * @cfg {String} pos Menu align to (top | bottom) default bottom
28620 * Create a new Menu
28621 * @param {Object} config The config object
28625 Roo.bootstrap.menu.Menu = function(config){
28626 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28630 * @event beforeshow
28631 * Fires before this menu is displayed
28632 * @param {Roo.bootstrap.menu.Menu} this
28636 * @event beforehide
28637 * Fires before this menu is hidden
28638 * @param {Roo.bootstrap.menu.Menu} this
28643 * Fires after this menu is displayed
28644 * @param {Roo.bootstrap.menu.Menu} this
28649 * Fires after this menu is hidden
28650 * @param {Roo.bootstrap.menu.Menu} this
28655 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28656 * @param {Roo.bootstrap.menu.Menu} this
28657 * @param {Roo.EventObject} e
28664 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28668 weight : 'default',
28673 getChildContainer : function() {
28674 if(this.isSubMenu){
28678 return this.el.select('ul.dropdown-menu', true).first();
28681 getAutoCreate : function()
28686 cls : 'roo-menu-text',
28694 cls : 'fa ' + this.icon
28705 cls : 'dropdown-button btn btn-' + this.weight,
28710 cls : 'dropdown-toggle btn btn-' + this.weight,
28720 cls : 'dropdown-menu'
28726 if(this.pos == 'top'){
28727 cfg.cls += ' dropup';
28730 if(this.isSubMenu){
28733 cls : 'dropdown-menu'
28740 onRender : function(ct, position)
28742 this.isSubMenu = ct.hasClass('dropdown-submenu');
28744 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28747 initEvents : function()
28749 if(this.isSubMenu){
28753 this.hidden = true;
28755 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28756 this.triggerEl.on('click', this.onTriggerPress, this);
28758 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28759 this.buttonEl.on('click', this.onClick, this);
28765 if(this.isSubMenu){
28769 return this.el.select('ul.dropdown-menu', true).first();
28772 onClick : function(e)
28774 this.fireEvent("click", this, e);
28777 onTriggerPress : function(e)
28779 if (this.isVisible()) {
28786 isVisible : function(){
28787 return !this.hidden;
28792 this.fireEvent("beforeshow", this);
28794 this.hidden = false;
28795 this.el.addClass('open');
28797 Roo.get(document).on("mouseup", this.onMouseUp, this);
28799 this.fireEvent("show", this);
28806 this.fireEvent("beforehide", this);
28808 this.hidden = true;
28809 this.el.removeClass('open');
28811 Roo.get(document).un("mouseup", this.onMouseUp);
28813 this.fireEvent("hide", this);
28816 onMouseUp : function()
28830 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28833 * @class Roo.bootstrap.menu.Item
28834 * @extends Roo.bootstrap.Component
28835 * Bootstrap MenuItem class
28836 * @cfg {Boolean} submenu (true | false) default false
28837 * @cfg {String} html text of the item
28838 * @cfg {String} href the link
28839 * @cfg {Boolean} disable (true | false) default false
28840 * @cfg {Boolean} preventDefault (true | false) default true
28841 * @cfg {String} icon Font awesome icon
28842 * @cfg {String} pos Submenu align to (left | right) default right
28846 * Create a new Item
28847 * @param {Object} config The config object
28851 Roo.bootstrap.menu.Item = function(config){
28852 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28856 * Fires when the mouse is hovering over this menu
28857 * @param {Roo.bootstrap.menu.Item} this
28858 * @param {Roo.EventObject} e
28863 * Fires when the mouse exits this menu
28864 * @param {Roo.bootstrap.menu.Item} this
28865 * @param {Roo.EventObject} e
28871 * The raw click event for the entire grid.
28872 * @param {Roo.EventObject} e
28878 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28883 preventDefault: true,
28888 getAutoCreate : function()
28893 cls : 'roo-menu-item-text',
28901 cls : 'fa ' + this.icon
28910 href : this.href || '#',
28917 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28921 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28923 if(this.pos == 'left'){
28924 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28931 initEvents : function()
28933 this.el.on('mouseover', this.onMouseOver, this);
28934 this.el.on('mouseout', this.onMouseOut, this);
28936 this.el.select('a', true).first().on('click', this.onClick, this);
28940 onClick : function(e)
28942 if(this.preventDefault){
28943 e.preventDefault();
28946 this.fireEvent("click", this, e);
28949 onMouseOver : function(e)
28951 if(this.submenu && this.pos == 'left'){
28952 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28955 this.fireEvent("mouseover", this, e);
28958 onMouseOut : function(e)
28960 this.fireEvent("mouseout", this, e);
28972 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28975 * @class Roo.bootstrap.menu.Separator
28976 * @extends Roo.bootstrap.Component
28977 * Bootstrap Separator class
28980 * Create a new Separator
28981 * @param {Object} config The config object
28985 Roo.bootstrap.menu.Separator = function(config){
28986 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28989 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28991 getAutoCreate : function(){
28994 cls: 'dropdown-divider divider'
29012 * @class Roo.bootstrap.Tooltip
29013 * Bootstrap Tooltip class
29014 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29015 * to determine which dom element triggers the tooltip.
29017 * It needs to add support for additional attributes like tooltip-position
29020 * Create a new Toolti
29021 * @param {Object} config The config object
29024 Roo.bootstrap.Tooltip = function(config){
29025 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29027 this.alignment = Roo.bootstrap.Tooltip.alignment;
29029 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29030 this.alignment = config.alignment;
29035 Roo.apply(Roo.bootstrap.Tooltip, {
29037 * @function init initialize tooltip monitoring.
29041 currentTip : false,
29042 currentRegion : false,
29048 Roo.get(document).on('mouseover', this.enter ,this);
29049 Roo.get(document).on('mouseout', this.leave, this);
29052 this.currentTip = new Roo.bootstrap.Tooltip();
29055 enter : function(ev)
29057 var dom = ev.getTarget();
29059 //Roo.log(['enter',dom]);
29060 var el = Roo.fly(dom);
29061 if (this.currentEl) {
29063 //Roo.log(this.currentEl);
29064 //Roo.log(this.currentEl.contains(dom));
29065 if (this.currentEl == el) {
29068 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29074 if (this.currentTip.el) {
29075 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29079 if(!el || el.dom == document){
29085 // you can not look for children, as if el is the body.. then everythign is the child..
29086 if (!el.attr('tooltip')) { //
29087 if (!el.select("[tooltip]").elements.length) {
29090 // is the mouse over this child...?
29091 bindEl = el.select("[tooltip]").first();
29092 var xy = ev.getXY();
29093 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29094 //Roo.log("not in region.");
29097 //Roo.log("child element over..");
29100 this.currentEl = bindEl;
29101 this.currentTip.bind(bindEl);
29102 this.currentRegion = Roo.lib.Region.getRegion(dom);
29103 this.currentTip.enter();
29106 leave : function(ev)
29108 var dom = ev.getTarget();
29109 //Roo.log(['leave',dom]);
29110 if (!this.currentEl) {
29115 if (dom != this.currentEl.dom) {
29118 var xy = ev.getXY();
29119 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29122 // only activate leave if mouse cursor is outside... bounding box..
29127 if (this.currentTip) {
29128 this.currentTip.leave();
29130 //Roo.log('clear currentEl');
29131 this.currentEl = false;
29136 'left' : ['r-l', [-2,0], 'right'],
29137 'right' : ['l-r', [2,0], 'left'],
29138 'bottom' : ['t-b', [0,2], 'top'],
29139 'top' : [ 'b-t', [0,-2], 'bottom']
29145 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29150 delay : null, // can be { show : 300 , hide: 500}
29154 hoverState : null, //???
29156 placement : 'bottom',
29160 getAutoCreate : function(){
29167 cls : 'tooltip-arrow arrow'
29170 cls : 'tooltip-inner'
29177 bind : function(el)
29182 initEvents : function()
29184 this.arrowEl = this.el.select('.arrow', true).first();
29185 this.innerEl = this.el.select('.tooltip-inner', true).first();
29188 enter : function () {
29190 if (this.timeout != null) {
29191 clearTimeout(this.timeout);
29194 this.hoverState = 'in';
29195 //Roo.log("enter - show");
29196 if (!this.delay || !this.delay.show) {
29201 this.timeout = setTimeout(function () {
29202 if (_t.hoverState == 'in') {
29205 }, this.delay.show);
29209 clearTimeout(this.timeout);
29211 this.hoverState = 'out';
29212 if (!this.delay || !this.delay.hide) {
29218 this.timeout = setTimeout(function () {
29219 //Roo.log("leave - timeout");
29221 if (_t.hoverState == 'out') {
29223 Roo.bootstrap.Tooltip.currentEl = false;
29228 show : function (msg)
29231 this.render(document.body);
29234 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29236 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29238 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29240 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29241 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29243 var placement = typeof this.placement == 'function' ?
29244 this.placement.call(this, this.el, on_el) :
29247 var autoToken = /\s?auto?\s?/i;
29248 var autoPlace = autoToken.test(placement);
29250 placement = placement.replace(autoToken, '') || 'top';
29254 //this.el.setXY([0,0]);
29256 //this.el.dom.style.display='block';
29258 //this.el.appendTo(on_el);
29260 var p = this.getPosition();
29261 var box = this.el.getBox();
29267 var align = this.alignment[placement];
29269 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29271 if(placement == 'top' || placement == 'bottom'){
29273 placement = 'right';
29276 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29277 placement = 'left';
29280 var scroll = Roo.select('body', true).first().getScroll();
29282 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29286 align = this.alignment[placement];
29288 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29292 this.el.alignTo(this.bindEl, align[0],align[1]);
29293 //var arrow = this.el.select('.arrow',true).first();
29294 //arrow.set(align[2],
29296 this.el.addClass(placement);
29297 this.el.addClass("bs-tooltip-"+ placement);
29299 this.el.addClass('in fade show');
29301 this.hoverState = null;
29303 if (this.el.hasClass('fade')) {
29318 //this.el.setXY([0,0]);
29319 this.el.removeClass(['show', 'in']);
29335 * @class Roo.bootstrap.LocationPicker
29336 * @extends Roo.bootstrap.Component
29337 * Bootstrap LocationPicker class
29338 * @cfg {Number} latitude Position when init default 0
29339 * @cfg {Number} longitude Position when init default 0
29340 * @cfg {Number} zoom default 15
29341 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29342 * @cfg {Boolean} mapTypeControl default false
29343 * @cfg {Boolean} disableDoubleClickZoom default false
29344 * @cfg {Boolean} scrollwheel default true
29345 * @cfg {Boolean} streetViewControl default false
29346 * @cfg {Number} radius default 0
29347 * @cfg {String} locationName
29348 * @cfg {Boolean} draggable default true
29349 * @cfg {Boolean} enableAutocomplete default false
29350 * @cfg {Boolean} enableReverseGeocode default true
29351 * @cfg {String} markerTitle
29354 * Create a new LocationPicker
29355 * @param {Object} config The config object
29359 Roo.bootstrap.LocationPicker = function(config){
29361 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29366 * Fires when the picker initialized.
29367 * @param {Roo.bootstrap.LocationPicker} this
29368 * @param {Google Location} location
29372 * @event positionchanged
29373 * Fires when the picker position changed.
29374 * @param {Roo.bootstrap.LocationPicker} this
29375 * @param {Google Location} location
29377 positionchanged : true,
29380 * Fires when the map resize.
29381 * @param {Roo.bootstrap.LocationPicker} this
29386 * Fires when the map show.
29387 * @param {Roo.bootstrap.LocationPicker} this
29392 * Fires when the map hide.
29393 * @param {Roo.bootstrap.LocationPicker} this
29398 * Fires when click the map.
29399 * @param {Roo.bootstrap.LocationPicker} this
29400 * @param {Map event} e
29404 * @event mapRightClick
29405 * Fires when right click the map.
29406 * @param {Roo.bootstrap.LocationPicker} this
29407 * @param {Map event} e
29409 mapRightClick : true,
29411 * @event markerClick
29412 * Fires when click the marker.
29413 * @param {Roo.bootstrap.LocationPicker} this
29414 * @param {Map event} e
29416 markerClick : true,
29418 * @event markerRightClick
29419 * Fires when right click the marker.
29420 * @param {Roo.bootstrap.LocationPicker} this
29421 * @param {Map event} e
29423 markerRightClick : true,
29425 * @event OverlayViewDraw
29426 * Fires when OverlayView Draw
29427 * @param {Roo.bootstrap.LocationPicker} this
29429 OverlayViewDraw : true,
29431 * @event OverlayViewOnAdd
29432 * Fires when OverlayView Draw
29433 * @param {Roo.bootstrap.LocationPicker} this
29435 OverlayViewOnAdd : true,
29437 * @event OverlayViewOnRemove
29438 * Fires when OverlayView Draw
29439 * @param {Roo.bootstrap.LocationPicker} this
29441 OverlayViewOnRemove : true,
29443 * @event OverlayViewShow
29444 * Fires when OverlayView Draw
29445 * @param {Roo.bootstrap.LocationPicker} this
29446 * @param {Pixel} cpx
29448 OverlayViewShow : true,
29450 * @event OverlayViewHide
29451 * Fires when OverlayView Draw
29452 * @param {Roo.bootstrap.LocationPicker} this
29454 OverlayViewHide : true,
29456 * @event loadexception
29457 * Fires when load google lib failed.
29458 * @param {Roo.bootstrap.LocationPicker} this
29460 loadexception : true
29465 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29467 gMapContext: false,
29473 mapTypeControl: false,
29474 disableDoubleClickZoom: false,
29476 streetViewControl: false,
29480 enableAutocomplete: false,
29481 enableReverseGeocode: true,
29484 getAutoCreate: function()
29489 cls: 'roo-location-picker'
29495 initEvents: function(ct, position)
29497 if(!this.el.getWidth() || this.isApplied()){
29501 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29506 initial: function()
29508 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29509 this.fireEvent('loadexception', this);
29513 if(!this.mapTypeId){
29514 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29517 this.gMapContext = this.GMapContext();
29519 this.initOverlayView();
29521 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29525 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29526 _this.setPosition(_this.gMapContext.marker.position);
29529 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29530 _this.fireEvent('mapClick', this, event);
29534 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29535 _this.fireEvent('mapRightClick', this, event);
29539 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29540 _this.fireEvent('markerClick', this, event);
29544 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29545 _this.fireEvent('markerRightClick', this, event);
29549 this.setPosition(this.gMapContext.location);
29551 this.fireEvent('initial', this, this.gMapContext.location);
29554 initOverlayView: function()
29558 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29562 _this.fireEvent('OverlayViewDraw', _this);
29567 _this.fireEvent('OverlayViewOnAdd', _this);
29570 onRemove: function()
29572 _this.fireEvent('OverlayViewOnRemove', _this);
29575 show: function(cpx)
29577 _this.fireEvent('OverlayViewShow', _this, cpx);
29582 _this.fireEvent('OverlayViewHide', _this);
29588 fromLatLngToContainerPixel: function(event)
29590 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29593 isApplied: function()
29595 return this.getGmapContext() == false ? false : true;
29598 getGmapContext: function()
29600 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29603 GMapContext: function()
29605 var position = new google.maps.LatLng(this.latitude, this.longitude);
29607 var _map = new google.maps.Map(this.el.dom, {
29610 mapTypeId: this.mapTypeId,
29611 mapTypeControl: this.mapTypeControl,
29612 disableDoubleClickZoom: this.disableDoubleClickZoom,
29613 scrollwheel: this.scrollwheel,
29614 streetViewControl: this.streetViewControl,
29615 locationName: this.locationName,
29616 draggable: this.draggable,
29617 enableAutocomplete: this.enableAutocomplete,
29618 enableReverseGeocode: this.enableReverseGeocode
29621 var _marker = new google.maps.Marker({
29622 position: position,
29624 title: this.markerTitle,
29625 draggable: this.draggable
29632 location: position,
29633 radius: this.radius,
29634 locationName: this.locationName,
29635 addressComponents: {
29636 formatted_address: null,
29637 addressLine1: null,
29638 addressLine2: null,
29640 streetNumber: null,
29644 stateOrProvince: null
29647 domContainer: this.el.dom,
29648 geodecoder: new google.maps.Geocoder()
29652 drawCircle: function(center, radius, options)
29654 if (this.gMapContext.circle != null) {
29655 this.gMapContext.circle.setMap(null);
29659 options = Roo.apply({}, options, {
29660 strokeColor: "#0000FF",
29661 strokeOpacity: .35,
29663 fillColor: "#0000FF",
29667 options.map = this.gMapContext.map;
29668 options.radius = radius;
29669 options.center = center;
29670 this.gMapContext.circle = new google.maps.Circle(options);
29671 return this.gMapContext.circle;
29677 setPosition: function(location)
29679 this.gMapContext.location = location;
29680 this.gMapContext.marker.setPosition(location);
29681 this.gMapContext.map.panTo(location);
29682 this.drawCircle(location, this.gMapContext.radius, {});
29686 if (this.gMapContext.settings.enableReverseGeocode) {
29687 this.gMapContext.geodecoder.geocode({
29688 latLng: this.gMapContext.location
29689 }, function(results, status) {
29691 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29692 _this.gMapContext.locationName = results[0].formatted_address;
29693 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29695 _this.fireEvent('positionchanged', this, location);
29702 this.fireEvent('positionchanged', this, location);
29707 google.maps.event.trigger(this.gMapContext.map, "resize");
29709 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29711 this.fireEvent('resize', this);
29714 setPositionByLatLng: function(latitude, longitude)
29716 this.setPosition(new google.maps.LatLng(latitude, longitude));
29719 getCurrentPosition: function()
29722 latitude: this.gMapContext.location.lat(),
29723 longitude: this.gMapContext.location.lng()
29727 getAddressName: function()
29729 return this.gMapContext.locationName;
29732 getAddressComponents: function()
29734 return this.gMapContext.addressComponents;
29737 address_component_from_google_geocode: function(address_components)
29741 for (var i = 0; i < address_components.length; i++) {
29742 var component = address_components[i];
29743 if (component.types.indexOf("postal_code") >= 0) {
29744 result.postalCode = component.short_name;
29745 } else if (component.types.indexOf("street_number") >= 0) {
29746 result.streetNumber = component.short_name;
29747 } else if (component.types.indexOf("route") >= 0) {
29748 result.streetName = component.short_name;
29749 } else if (component.types.indexOf("neighborhood") >= 0) {
29750 result.city = component.short_name;
29751 } else if (component.types.indexOf("locality") >= 0) {
29752 result.city = component.short_name;
29753 } else if (component.types.indexOf("sublocality") >= 0) {
29754 result.district = component.short_name;
29755 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29756 result.stateOrProvince = component.short_name;
29757 } else if (component.types.indexOf("country") >= 0) {
29758 result.country = component.short_name;
29762 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29763 result.addressLine2 = "";
29767 setZoomLevel: function(zoom)
29769 this.gMapContext.map.setZoom(zoom);
29782 this.fireEvent('show', this);
29793 this.fireEvent('hide', this);
29798 Roo.apply(Roo.bootstrap.LocationPicker, {
29800 OverlayView : function(map, options)
29802 options = options || {};
29809 * @class Roo.bootstrap.Alert
29810 * @extends Roo.bootstrap.Component
29811 * Bootstrap Alert class - shows an alert area box
29813 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29814 Enter a valid email address
29817 * @cfg {String} title The title of alert
29818 * @cfg {String} html The content of alert
29819 * @cfg {String} weight ( success | info | warning | danger )
29820 * @cfg {String} fa font-awesomeicon
29821 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29822 * @cfg {Boolean} close true to show a x closer
29826 * Create a new alert
29827 * @param {Object} config The config object
29831 Roo.bootstrap.Alert = function(config){
29832 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29836 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29842 faicon: false, // BC
29846 getAutoCreate : function()
29858 style : this.close ? '' : 'display:none'
29862 cls : 'roo-alert-icon'
29867 cls : 'roo-alert-title',
29872 cls : 'roo-alert-text',
29879 cfg.cn[0].cls += ' fa ' + this.faicon;
29882 cfg.cn[0].cls += ' fa ' + this.fa;
29886 cfg.cls += ' alert-' + this.weight;
29892 initEvents: function()
29894 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29895 this.titleEl = this.el.select('.roo-alert-title',true).first();
29896 this.iconEl = this.el.select('.roo-alert-icon',true).first();
29897 if (this.seconds > 0) {
29898 this.hide.defer(this.seconds, this);
29902 setTitle : function(str)
29904 this.titleEl.dom.innerHTML = str;
29907 setText : function(str)
29909 this.titleEl.dom.innerHTML = str;
29912 setWeight : function(weight)
29915 this.el.removeClass('alert-' + this.weight);
29918 this.weight = weight;
29920 this.el.addClass('alert-' + this.weight);
29923 setIcon : function(icon)
29926 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29929 this.faicon = icon;
29931 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
29952 * @class Roo.bootstrap.UploadCropbox
29953 * @extends Roo.bootstrap.Component
29954 * Bootstrap UploadCropbox class
29955 * @cfg {String} emptyText show when image has been loaded
29956 * @cfg {String} rotateNotify show when image too small to rotate
29957 * @cfg {Number} errorTimeout default 3000
29958 * @cfg {Number} minWidth default 300
29959 * @cfg {Number} minHeight default 300
29960 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29961 * @cfg {Boolean} isDocument (true|false) default false
29962 * @cfg {String} url action url
29963 * @cfg {String} paramName default 'imageUpload'
29964 * @cfg {String} method default POST
29965 * @cfg {Boolean} loadMask (true|false) default true
29966 * @cfg {Boolean} loadingText default 'Loading...'
29969 * Create a new UploadCropbox
29970 * @param {Object} config The config object
29973 Roo.bootstrap.UploadCropbox = function(config){
29974 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29978 * @event beforeselectfile
29979 * Fire before select file
29980 * @param {Roo.bootstrap.UploadCropbox} this
29982 "beforeselectfile" : true,
29985 * Fire after initEvent
29986 * @param {Roo.bootstrap.UploadCropbox} this
29991 * Fire after initEvent
29992 * @param {Roo.bootstrap.UploadCropbox} this
29993 * @param {String} data
29998 * Fire when preparing the file data
29999 * @param {Roo.bootstrap.UploadCropbox} this
30000 * @param {Object} file
30005 * Fire when get exception
30006 * @param {Roo.bootstrap.UploadCropbox} this
30007 * @param {XMLHttpRequest} xhr
30009 "exception" : true,
30011 * @event beforeloadcanvas
30012 * Fire before load the canvas
30013 * @param {Roo.bootstrap.UploadCropbox} this
30014 * @param {String} src
30016 "beforeloadcanvas" : true,
30019 * Fire when trash image
30020 * @param {Roo.bootstrap.UploadCropbox} this
30025 * Fire when download the image
30026 * @param {Roo.bootstrap.UploadCropbox} this
30030 * @event footerbuttonclick
30031 * Fire when footerbuttonclick
30032 * @param {Roo.bootstrap.UploadCropbox} this
30033 * @param {String} type
30035 "footerbuttonclick" : true,
30039 * @param {Roo.bootstrap.UploadCropbox} this
30044 * Fire when rotate the image
30045 * @param {Roo.bootstrap.UploadCropbox} this
30046 * @param {String} pos
30051 * Fire when inspect the file
30052 * @param {Roo.bootstrap.UploadCropbox} this
30053 * @param {Object} file
30058 * Fire when xhr upload the file
30059 * @param {Roo.bootstrap.UploadCropbox} this
30060 * @param {Object} data
30065 * Fire when arrange the file data
30066 * @param {Roo.bootstrap.UploadCropbox} this
30067 * @param {Object} formData
30072 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30075 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30077 emptyText : 'Click to upload image',
30078 rotateNotify : 'Image is too small to rotate',
30079 errorTimeout : 3000,
30093 cropType : 'image/jpeg',
30095 canvasLoaded : false,
30096 isDocument : false,
30098 paramName : 'imageUpload',
30100 loadingText : 'Loading...',
30103 getAutoCreate : function()
30107 cls : 'roo-upload-cropbox',
30111 cls : 'roo-upload-cropbox-selector',
30116 cls : 'roo-upload-cropbox-body',
30117 style : 'cursor:pointer',
30121 cls : 'roo-upload-cropbox-preview'
30125 cls : 'roo-upload-cropbox-thumb'
30129 cls : 'roo-upload-cropbox-empty-notify',
30130 html : this.emptyText
30134 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30135 html : this.rotateNotify
30141 cls : 'roo-upload-cropbox-footer',
30144 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30154 onRender : function(ct, position)
30156 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30158 if (this.buttons.length) {
30160 Roo.each(this.buttons, function(bb) {
30162 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30164 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30170 this.maskEl = this.el;
30174 initEvents : function()
30176 this.urlAPI = (window.createObjectURL && window) ||
30177 (window.URL && URL.revokeObjectURL && URL) ||
30178 (window.webkitURL && webkitURL);
30180 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30181 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30183 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30184 this.selectorEl.hide();
30186 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30187 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30189 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30190 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30191 this.thumbEl.hide();
30193 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30194 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30196 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30197 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30198 this.errorEl.hide();
30200 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30201 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30202 this.footerEl.hide();
30204 this.setThumbBoxSize();
30210 this.fireEvent('initial', this);
30217 window.addEventListener("resize", function() { _this.resize(); } );
30219 this.bodyEl.on('click', this.beforeSelectFile, this);
30222 this.bodyEl.on('touchstart', this.onTouchStart, this);
30223 this.bodyEl.on('touchmove', this.onTouchMove, this);
30224 this.bodyEl.on('touchend', this.onTouchEnd, this);
30228 this.bodyEl.on('mousedown', this.onMouseDown, this);
30229 this.bodyEl.on('mousemove', this.onMouseMove, this);
30230 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30231 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30232 Roo.get(document).on('mouseup', this.onMouseUp, this);
30235 this.selectorEl.on('change', this.onFileSelected, this);
30241 this.baseScale = 1;
30243 this.baseRotate = 1;
30244 this.dragable = false;
30245 this.pinching = false;
30248 this.cropData = false;
30249 this.notifyEl.dom.innerHTML = this.emptyText;
30251 this.selectorEl.dom.value = '';
30255 resize : function()
30257 if(this.fireEvent('resize', this) != false){
30258 this.setThumbBoxPosition();
30259 this.setCanvasPosition();
30263 onFooterButtonClick : function(e, el, o, type)
30266 case 'rotate-left' :
30267 this.onRotateLeft(e);
30269 case 'rotate-right' :
30270 this.onRotateRight(e);
30273 this.beforeSelectFile(e);
30288 this.fireEvent('footerbuttonclick', this, type);
30291 beforeSelectFile : function(e)
30293 e.preventDefault();
30295 if(this.fireEvent('beforeselectfile', this) != false){
30296 this.selectorEl.dom.click();
30300 onFileSelected : function(e)
30302 e.preventDefault();
30304 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30308 var file = this.selectorEl.dom.files[0];
30310 if(this.fireEvent('inspect', this, file) != false){
30311 this.prepare(file);
30316 trash : function(e)
30318 this.fireEvent('trash', this);
30321 download : function(e)
30323 this.fireEvent('download', this);
30326 loadCanvas : function(src)
30328 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30332 this.imageEl = document.createElement('img');
30336 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30338 this.imageEl.src = src;
30342 onLoadCanvas : function()
30344 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30345 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30347 this.bodyEl.un('click', this.beforeSelectFile, this);
30349 this.notifyEl.hide();
30350 this.thumbEl.show();
30351 this.footerEl.show();
30353 this.baseRotateLevel();
30355 if(this.isDocument){
30356 this.setThumbBoxSize();
30359 this.setThumbBoxPosition();
30361 this.baseScaleLevel();
30367 this.canvasLoaded = true;
30370 this.maskEl.unmask();
30375 setCanvasPosition : function()
30377 if(!this.canvasEl){
30381 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30382 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30384 this.previewEl.setLeft(pw);
30385 this.previewEl.setTop(ph);
30389 onMouseDown : function(e)
30393 this.dragable = true;
30394 this.pinching = false;
30396 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30397 this.dragable = false;
30401 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30402 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30406 onMouseMove : function(e)
30410 if(!this.canvasLoaded){
30414 if (!this.dragable){
30418 var minX = Math.ceil(this.thumbEl.getLeft(true));
30419 var minY = Math.ceil(this.thumbEl.getTop(true));
30421 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30422 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30424 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30425 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30427 x = x - this.mouseX;
30428 y = y - this.mouseY;
30430 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30431 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30433 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30434 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30436 this.previewEl.setLeft(bgX);
30437 this.previewEl.setTop(bgY);
30439 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30440 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30443 onMouseUp : function(e)
30447 this.dragable = false;
30450 onMouseWheel : function(e)
30454 this.startScale = this.scale;
30456 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30458 if(!this.zoomable()){
30459 this.scale = this.startScale;
30468 zoomable : function()
30470 var minScale = this.thumbEl.getWidth() / this.minWidth;
30472 if(this.minWidth < this.minHeight){
30473 minScale = this.thumbEl.getHeight() / this.minHeight;
30476 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30477 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30481 (this.rotate == 0 || this.rotate == 180) &&
30483 width > this.imageEl.OriginWidth ||
30484 height > this.imageEl.OriginHeight ||
30485 (width < this.minWidth && height < this.minHeight)
30493 (this.rotate == 90 || this.rotate == 270) &&
30495 width > this.imageEl.OriginWidth ||
30496 height > this.imageEl.OriginHeight ||
30497 (width < this.minHeight && height < this.minWidth)
30504 !this.isDocument &&
30505 (this.rotate == 0 || this.rotate == 180) &&
30507 width < this.minWidth ||
30508 width > this.imageEl.OriginWidth ||
30509 height < this.minHeight ||
30510 height > this.imageEl.OriginHeight
30517 !this.isDocument &&
30518 (this.rotate == 90 || this.rotate == 270) &&
30520 width < this.minHeight ||
30521 width > this.imageEl.OriginWidth ||
30522 height < this.minWidth ||
30523 height > this.imageEl.OriginHeight
30533 onRotateLeft : function(e)
30535 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30537 var minScale = this.thumbEl.getWidth() / this.minWidth;
30539 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30540 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30542 this.startScale = this.scale;
30544 while (this.getScaleLevel() < minScale){
30546 this.scale = this.scale + 1;
30548 if(!this.zoomable()){
30553 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30554 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30559 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30566 this.scale = this.startScale;
30568 this.onRotateFail();
30573 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30575 if(this.isDocument){
30576 this.setThumbBoxSize();
30577 this.setThumbBoxPosition();
30578 this.setCanvasPosition();
30583 this.fireEvent('rotate', this, 'left');
30587 onRotateRight : function(e)
30589 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30591 var minScale = this.thumbEl.getWidth() / this.minWidth;
30593 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30594 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30596 this.startScale = this.scale;
30598 while (this.getScaleLevel() < minScale){
30600 this.scale = this.scale + 1;
30602 if(!this.zoomable()){
30607 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30608 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30613 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30620 this.scale = this.startScale;
30622 this.onRotateFail();
30627 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30629 if(this.isDocument){
30630 this.setThumbBoxSize();
30631 this.setThumbBoxPosition();
30632 this.setCanvasPosition();
30637 this.fireEvent('rotate', this, 'right');
30640 onRotateFail : function()
30642 this.errorEl.show(true);
30646 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30651 this.previewEl.dom.innerHTML = '';
30653 var canvasEl = document.createElement("canvas");
30655 var contextEl = canvasEl.getContext("2d");
30657 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30658 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30659 var center = this.imageEl.OriginWidth / 2;
30661 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30662 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30663 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30664 center = this.imageEl.OriginHeight / 2;
30667 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30669 contextEl.translate(center, center);
30670 contextEl.rotate(this.rotate * Math.PI / 180);
30672 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30674 this.canvasEl = document.createElement("canvas");
30676 this.contextEl = this.canvasEl.getContext("2d");
30678 switch (this.rotate) {
30681 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30682 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30684 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30689 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30690 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30692 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30693 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);
30697 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30702 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30703 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30705 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30706 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);
30710 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);
30715 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30716 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30718 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30719 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30723 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);
30730 this.previewEl.appendChild(this.canvasEl);
30732 this.setCanvasPosition();
30737 if(!this.canvasLoaded){
30741 var imageCanvas = document.createElement("canvas");
30743 var imageContext = imageCanvas.getContext("2d");
30745 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30746 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30748 var center = imageCanvas.width / 2;
30750 imageContext.translate(center, center);
30752 imageContext.rotate(this.rotate * Math.PI / 180);
30754 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30756 var canvas = document.createElement("canvas");
30758 var context = canvas.getContext("2d");
30760 canvas.width = this.minWidth;
30761 canvas.height = this.minHeight;
30763 switch (this.rotate) {
30766 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30767 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30769 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30770 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30772 var targetWidth = this.minWidth - 2 * x;
30773 var targetHeight = this.minHeight - 2 * y;
30777 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30778 scale = targetWidth / width;
30781 if(x > 0 && y == 0){
30782 scale = targetHeight / height;
30785 if(x > 0 && y > 0){
30786 scale = targetWidth / width;
30788 if(width < height){
30789 scale = targetHeight / height;
30793 context.scale(scale, scale);
30795 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30796 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30798 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30799 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30801 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30806 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30807 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30809 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30810 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30812 var targetWidth = this.minWidth - 2 * x;
30813 var targetHeight = this.minHeight - 2 * y;
30817 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30818 scale = targetWidth / width;
30821 if(x > 0 && y == 0){
30822 scale = targetHeight / height;
30825 if(x > 0 && y > 0){
30826 scale = targetWidth / width;
30828 if(width < height){
30829 scale = targetHeight / height;
30833 context.scale(scale, scale);
30835 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30836 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30838 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30839 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30841 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30843 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30848 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30849 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30851 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30852 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30854 var targetWidth = this.minWidth - 2 * x;
30855 var targetHeight = this.minHeight - 2 * y;
30859 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30860 scale = targetWidth / width;
30863 if(x > 0 && y == 0){
30864 scale = targetHeight / height;
30867 if(x > 0 && y > 0){
30868 scale = targetWidth / width;
30870 if(width < height){
30871 scale = targetHeight / height;
30875 context.scale(scale, scale);
30877 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30878 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30880 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30881 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30883 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30884 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30886 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30891 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30892 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30894 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30895 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30897 var targetWidth = this.minWidth - 2 * x;
30898 var targetHeight = this.minHeight - 2 * y;
30902 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30903 scale = targetWidth / width;
30906 if(x > 0 && y == 0){
30907 scale = targetHeight / height;
30910 if(x > 0 && y > 0){
30911 scale = targetWidth / width;
30913 if(width < height){
30914 scale = targetHeight / height;
30918 context.scale(scale, scale);
30920 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30921 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30923 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30924 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30926 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30928 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30935 this.cropData = canvas.toDataURL(this.cropType);
30937 if(this.fireEvent('crop', this, this.cropData) !== false){
30938 this.process(this.file, this.cropData);
30945 setThumbBoxSize : function()
30949 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30950 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30951 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30953 this.minWidth = width;
30954 this.minHeight = height;
30956 if(this.rotate == 90 || this.rotate == 270){
30957 this.minWidth = height;
30958 this.minHeight = width;
30963 width = Math.ceil(this.minWidth * height / this.minHeight);
30965 if(this.minWidth > this.minHeight){
30967 height = Math.ceil(this.minHeight * width / this.minWidth);
30970 this.thumbEl.setStyle({
30971 width : width + 'px',
30972 height : height + 'px'
30979 setThumbBoxPosition : function()
30981 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30982 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30984 this.thumbEl.setLeft(x);
30985 this.thumbEl.setTop(y);
30989 baseRotateLevel : function()
30991 this.baseRotate = 1;
30994 typeof(this.exif) != 'undefined' &&
30995 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30996 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30998 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31001 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31005 baseScaleLevel : function()
31009 if(this.isDocument){
31011 if(this.baseRotate == 6 || this.baseRotate == 8){
31013 height = this.thumbEl.getHeight();
31014 this.baseScale = height / this.imageEl.OriginWidth;
31016 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31017 width = this.thumbEl.getWidth();
31018 this.baseScale = width / this.imageEl.OriginHeight;
31024 height = this.thumbEl.getHeight();
31025 this.baseScale = height / this.imageEl.OriginHeight;
31027 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31028 width = this.thumbEl.getWidth();
31029 this.baseScale = width / this.imageEl.OriginWidth;
31035 if(this.baseRotate == 6 || this.baseRotate == 8){
31037 width = this.thumbEl.getHeight();
31038 this.baseScale = width / this.imageEl.OriginHeight;
31040 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31041 height = this.thumbEl.getWidth();
31042 this.baseScale = height / this.imageEl.OriginHeight;
31045 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31046 height = this.thumbEl.getWidth();
31047 this.baseScale = height / this.imageEl.OriginHeight;
31049 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31050 width = this.thumbEl.getHeight();
31051 this.baseScale = width / this.imageEl.OriginWidth;
31058 width = this.thumbEl.getWidth();
31059 this.baseScale = width / this.imageEl.OriginWidth;
31061 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31062 height = this.thumbEl.getHeight();
31063 this.baseScale = height / this.imageEl.OriginHeight;
31066 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31068 height = this.thumbEl.getHeight();
31069 this.baseScale = height / this.imageEl.OriginHeight;
31071 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31072 width = this.thumbEl.getWidth();
31073 this.baseScale = width / this.imageEl.OriginWidth;
31081 getScaleLevel : function()
31083 return this.baseScale * Math.pow(1.1, this.scale);
31086 onTouchStart : function(e)
31088 if(!this.canvasLoaded){
31089 this.beforeSelectFile(e);
31093 var touches = e.browserEvent.touches;
31099 if(touches.length == 1){
31100 this.onMouseDown(e);
31104 if(touches.length != 2){
31110 for(var i = 0, finger; finger = touches[i]; i++){
31111 coords.push(finger.pageX, finger.pageY);
31114 var x = Math.pow(coords[0] - coords[2], 2);
31115 var y = Math.pow(coords[1] - coords[3], 2);
31117 this.startDistance = Math.sqrt(x + y);
31119 this.startScale = this.scale;
31121 this.pinching = true;
31122 this.dragable = false;
31126 onTouchMove : function(e)
31128 if(!this.pinching && !this.dragable){
31132 var touches = e.browserEvent.touches;
31139 this.onMouseMove(e);
31145 for(var i = 0, finger; finger = touches[i]; i++){
31146 coords.push(finger.pageX, finger.pageY);
31149 var x = Math.pow(coords[0] - coords[2], 2);
31150 var y = Math.pow(coords[1] - coords[3], 2);
31152 this.endDistance = Math.sqrt(x + y);
31154 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31156 if(!this.zoomable()){
31157 this.scale = this.startScale;
31165 onTouchEnd : function(e)
31167 this.pinching = false;
31168 this.dragable = false;
31172 process : function(file, crop)
31175 this.maskEl.mask(this.loadingText);
31178 this.xhr = new XMLHttpRequest();
31180 file.xhr = this.xhr;
31182 this.xhr.open(this.method, this.url, true);
31185 "Accept": "application/json",
31186 "Cache-Control": "no-cache",
31187 "X-Requested-With": "XMLHttpRequest"
31190 for (var headerName in headers) {
31191 var headerValue = headers[headerName];
31193 this.xhr.setRequestHeader(headerName, headerValue);
31199 this.xhr.onload = function()
31201 _this.xhrOnLoad(_this.xhr);
31204 this.xhr.onerror = function()
31206 _this.xhrOnError(_this.xhr);
31209 var formData = new FormData();
31211 formData.append('returnHTML', 'NO');
31214 formData.append('crop', crop);
31217 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31218 formData.append(this.paramName, file, file.name);
31221 if(typeof(file.filename) != 'undefined'){
31222 formData.append('filename', file.filename);
31225 if(typeof(file.mimetype) != 'undefined'){
31226 formData.append('mimetype', file.mimetype);
31229 if(this.fireEvent('arrange', this, formData) != false){
31230 this.xhr.send(formData);
31234 xhrOnLoad : function(xhr)
31237 this.maskEl.unmask();
31240 if (xhr.readyState !== 4) {
31241 this.fireEvent('exception', this, xhr);
31245 var response = Roo.decode(xhr.responseText);
31247 if(!response.success){
31248 this.fireEvent('exception', this, xhr);
31252 var response = Roo.decode(xhr.responseText);
31254 this.fireEvent('upload', this, response);
31258 xhrOnError : function()
31261 this.maskEl.unmask();
31264 Roo.log('xhr on error');
31266 var response = Roo.decode(xhr.responseText);
31272 prepare : function(file)
31275 this.maskEl.mask(this.loadingText);
31281 if(typeof(file) === 'string'){
31282 this.loadCanvas(file);
31286 if(!file || !this.urlAPI){
31291 this.cropType = file.type;
31295 if(this.fireEvent('prepare', this, this.file) != false){
31297 var reader = new FileReader();
31299 reader.onload = function (e) {
31300 if (e.target.error) {
31301 Roo.log(e.target.error);
31305 var buffer = e.target.result,
31306 dataView = new DataView(buffer),
31308 maxOffset = dataView.byteLength - 4,
31312 if (dataView.getUint16(0) === 0xffd8) {
31313 while (offset < maxOffset) {
31314 markerBytes = dataView.getUint16(offset);
31316 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31317 markerLength = dataView.getUint16(offset + 2) + 2;
31318 if (offset + markerLength > dataView.byteLength) {
31319 Roo.log('Invalid meta data: Invalid segment size.');
31323 if(markerBytes == 0xffe1){
31324 _this.parseExifData(
31331 offset += markerLength;
31341 var url = _this.urlAPI.createObjectURL(_this.file);
31343 _this.loadCanvas(url);
31348 reader.readAsArrayBuffer(this.file);
31354 parseExifData : function(dataView, offset, length)
31356 var tiffOffset = offset + 10,
31360 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31361 // No Exif data, might be XMP data instead
31365 // Check for the ASCII code for "Exif" (0x45786966):
31366 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31367 // No Exif data, might be XMP data instead
31370 if (tiffOffset + 8 > dataView.byteLength) {
31371 Roo.log('Invalid Exif data: Invalid segment size.');
31374 // Check for the two null bytes:
31375 if (dataView.getUint16(offset + 8) !== 0x0000) {
31376 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31379 // Check the byte alignment:
31380 switch (dataView.getUint16(tiffOffset)) {
31382 littleEndian = true;
31385 littleEndian = false;
31388 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31391 // Check for the TIFF tag marker (0x002A):
31392 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31393 Roo.log('Invalid Exif data: Missing TIFF marker.');
31396 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31397 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31399 this.parseExifTags(
31402 tiffOffset + dirOffset,
31407 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31412 if (dirOffset + 6 > dataView.byteLength) {
31413 Roo.log('Invalid Exif data: Invalid directory offset.');
31416 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31417 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31418 if (dirEndOffset + 4 > dataView.byteLength) {
31419 Roo.log('Invalid Exif data: Invalid directory size.');
31422 for (i = 0; i < tagsNumber; i += 1) {
31426 dirOffset + 2 + 12 * i, // tag offset
31430 // Return the offset to the next directory:
31431 return dataView.getUint32(dirEndOffset, littleEndian);
31434 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31436 var tag = dataView.getUint16(offset, littleEndian);
31438 this.exif[tag] = this.getExifValue(
31442 dataView.getUint16(offset + 2, littleEndian), // tag type
31443 dataView.getUint32(offset + 4, littleEndian), // tag length
31448 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31450 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31459 Roo.log('Invalid Exif data: Invalid tag type.');
31463 tagSize = tagType.size * length;
31464 // Determine if the value is contained in the dataOffset bytes,
31465 // or if the value at the dataOffset is a pointer to the actual data:
31466 dataOffset = tagSize > 4 ?
31467 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31468 if (dataOffset + tagSize > dataView.byteLength) {
31469 Roo.log('Invalid Exif data: Invalid data offset.');
31472 if (length === 1) {
31473 return tagType.getValue(dataView, dataOffset, littleEndian);
31476 for (i = 0; i < length; i += 1) {
31477 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31480 if (tagType.ascii) {
31482 // Concatenate the chars:
31483 for (i = 0; i < values.length; i += 1) {
31485 // Ignore the terminating NULL byte(s):
31486 if (c === '\u0000') {
31498 Roo.apply(Roo.bootstrap.UploadCropbox, {
31500 'Orientation': 0x0112
31504 1: 0, //'top-left',
31506 3: 180, //'bottom-right',
31507 // 4: 'bottom-left',
31509 6: 90, //'right-top',
31510 // 7: 'right-bottom',
31511 8: 270 //'left-bottom'
31515 // byte, 8-bit unsigned int:
31517 getValue: function (dataView, dataOffset) {
31518 return dataView.getUint8(dataOffset);
31522 // ascii, 8-bit byte:
31524 getValue: function (dataView, dataOffset) {
31525 return String.fromCharCode(dataView.getUint8(dataOffset));
31530 // short, 16 bit int:
31532 getValue: function (dataView, dataOffset, littleEndian) {
31533 return dataView.getUint16(dataOffset, littleEndian);
31537 // long, 32 bit int:
31539 getValue: function (dataView, dataOffset, littleEndian) {
31540 return dataView.getUint32(dataOffset, littleEndian);
31544 // rational = two long values, first is numerator, second is denominator:
31546 getValue: function (dataView, dataOffset, littleEndian) {
31547 return dataView.getUint32(dataOffset, littleEndian) /
31548 dataView.getUint32(dataOffset + 4, littleEndian);
31552 // slong, 32 bit signed int:
31554 getValue: function (dataView, dataOffset, littleEndian) {
31555 return dataView.getInt32(dataOffset, littleEndian);
31559 // srational, two slongs, first is numerator, second is denominator:
31561 getValue: function (dataView, dataOffset, littleEndian) {
31562 return dataView.getInt32(dataOffset, littleEndian) /
31563 dataView.getInt32(dataOffset + 4, littleEndian);
31573 cls : 'btn-group roo-upload-cropbox-rotate-left',
31574 action : 'rotate-left',
31578 cls : 'btn btn-default',
31579 html : '<i class="fa fa-undo"></i>'
31585 cls : 'btn-group roo-upload-cropbox-picture',
31586 action : 'picture',
31590 cls : 'btn btn-default',
31591 html : '<i class="fa fa-picture-o"></i>'
31597 cls : 'btn-group roo-upload-cropbox-rotate-right',
31598 action : 'rotate-right',
31602 cls : 'btn btn-default',
31603 html : '<i class="fa fa-repeat"></i>'
31611 cls : 'btn-group roo-upload-cropbox-rotate-left',
31612 action : 'rotate-left',
31616 cls : 'btn btn-default',
31617 html : '<i class="fa fa-undo"></i>'
31623 cls : 'btn-group roo-upload-cropbox-download',
31624 action : 'download',
31628 cls : 'btn btn-default',
31629 html : '<i class="fa fa-download"></i>'
31635 cls : 'btn-group roo-upload-cropbox-crop',
31640 cls : 'btn btn-default',
31641 html : '<i class="fa fa-crop"></i>'
31647 cls : 'btn-group roo-upload-cropbox-trash',
31652 cls : 'btn btn-default',
31653 html : '<i class="fa fa-trash"></i>'
31659 cls : 'btn-group roo-upload-cropbox-rotate-right',
31660 action : 'rotate-right',
31664 cls : 'btn btn-default',
31665 html : '<i class="fa fa-repeat"></i>'
31673 cls : 'btn-group roo-upload-cropbox-rotate-left',
31674 action : 'rotate-left',
31678 cls : 'btn btn-default',
31679 html : '<i class="fa fa-undo"></i>'
31685 cls : 'btn-group roo-upload-cropbox-rotate-right',
31686 action : 'rotate-right',
31690 cls : 'btn btn-default',
31691 html : '<i class="fa fa-repeat"></i>'
31704 * @class Roo.bootstrap.DocumentManager
31705 * @extends Roo.bootstrap.Component
31706 * Bootstrap DocumentManager class
31707 * @cfg {String} paramName default 'imageUpload'
31708 * @cfg {String} toolTipName default 'filename'
31709 * @cfg {String} method default POST
31710 * @cfg {String} url action url
31711 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31712 * @cfg {Boolean} multiple multiple upload default true
31713 * @cfg {Number} thumbSize default 300
31714 * @cfg {String} fieldLabel
31715 * @cfg {Number} labelWidth default 4
31716 * @cfg {String} labelAlign (left|top) default left
31717 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31718 * @cfg {Number} labellg set the width of label (1-12)
31719 * @cfg {Number} labelmd set the width of label (1-12)
31720 * @cfg {Number} labelsm set the width of label (1-12)
31721 * @cfg {Number} labelxs set the width of label (1-12)
31724 * Create a new DocumentManager
31725 * @param {Object} config The config object
31728 Roo.bootstrap.DocumentManager = function(config){
31729 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31732 this.delegates = [];
31737 * Fire when initial the DocumentManager
31738 * @param {Roo.bootstrap.DocumentManager} this
31743 * inspect selected file
31744 * @param {Roo.bootstrap.DocumentManager} this
31745 * @param {File} file
31750 * Fire when xhr load exception
31751 * @param {Roo.bootstrap.DocumentManager} this
31752 * @param {XMLHttpRequest} xhr
31754 "exception" : true,
31756 * @event afterupload
31757 * Fire when xhr load exception
31758 * @param {Roo.bootstrap.DocumentManager} this
31759 * @param {XMLHttpRequest} xhr
31761 "afterupload" : true,
31764 * prepare the form data
31765 * @param {Roo.bootstrap.DocumentManager} this
31766 * @param {Object} formData
31771 * Fire when remove the file
31772 * @param {Roo.bootstrap.DocumentManager} this
31773 * @param {Object} file
31778 * Fire after refresh the file
31779 * @param {Roo.bootstrap.DocumentManager} this
31784 * Fire after click the image
31785 * @param {Roo.bootstrap.DocumentManager} this
31786 * @param {Object} file
31791 * Fire when upload a image and editable set to true
31792 * @param {Roo.bootstrap.DocumentManager} this
31793 * @param {Object} file
31797 * @event beforeselectfile
31798 * Fire before select file
31799 * @param {Roo.bootstrap.DocumentManager} this
31801 "beforeselectfile" : true,
31804 * Fire before process file
31805 * @param {Roo.bootstrap.DocumentManager} this
31806 * @param {Object} file
31810 * @event previewrendered
31811 * Fire when preview rendered
31812 * @param {Roo.bootstrap.DocumentManager} this
31813 * @param {Object} file
31815 "previewrendered" : true,
31818 "previewResize" : true
31823 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31832 paramName : 'imageUpload',
31833 toolTipName : 'filename',
31836 labelAlign : 'left',
31846 getAutoCreate : function()
31848 var managerWidget = {
31850 cls : 'roo-document-manager',
31854 cls : 'roo-document-manager-selector',
31859 cls : 'roo-document-manager-uploader',
31863 cls : 'roo-document-manager-upload-btn',
31864 html : '<i class="fa fa-plus"></i>'
31875 cls : 'column col-md-12',
31880 if(this.fieldLabel.length){
31885 cls : 'column col-md-12',
31886 html : this.fieldLabel
31890 cls : 'column col-md-12',
31895 if(this.labelAlign == 'left'){
31900 html : this.fieldLabel
31909 if(this.labelWidth > 12){
31910 content[0].style = "width: " + this.labelWidth + 'px';
31913 if(this.labelWidth < 13 && this.labelmd == 0){
31914 this.labelmd = this.labelWidth;
31917 if(this.labellg > 0){
31918 content[0].cls += ' col-lg-' + this.labellg;
31919 content[1].cls += ' col-lg-' + (12 - this.labellg);
31922 if(this.labelmd > 0){
31923 content[0].cls += ' col-md-' + this.labelmd;
31924 content[1].cls += ' col-md-' + (12 - this.labelmd);
31927 if(this.labelsm > 0){
31928 content[0].cls += ' col-sm-' + this.labelsm;
31929 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31932 if(this.labelxs > 0){
31933 content[0].cls += ' col-xs-' + this.labelxs;
31934 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31942 cls : 'row clearfix',
31950 initEvents : function()
31952 this.managerEl = this.el.select('.roo-document-manager', true).first();
31953 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31955 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31956 this.selectorEl.hide();
31959 this.selectorEl.attr('multiple', 'multiple');
31962 this.selectorEl.on('change', this.onFileSelected, this);
31964 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31965 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31967 this.uploader.on('click', this.onUploaderClick, this);
31969 this.renderProgressDialog();
31973 window.addEventListener("resize", function() { _this.refresh(); } );
31975 this.fireEvent('initial', this);
31978 renderProgressDialog : function()
31982 this.progressDialog = new Roo.bootstrap.Modal({
31983 cls : 'roo-document-manager-progress-dialog',
31984 allow_close : false,
31995 btnclick : function() {
31996 _this.uploadCancel();
32002 this.progressDialog.render(Roo.get(document.body));
32004 this.progress = new Roo.bootstrap.Progress({
32005 cls : 'roo-document-manager-progress',
32010 this.progress.render(this.progressDialog.getChildContainer());
32012 this.progressBar = new Roo.bootstrap.ProgressBar({
32013 cls : 'roo-document-manager-progress-bar',
32016 aria_valuemax : 12,
32020 this.progressBar.render(this.progress.getChildContainer());
32023 onUploaderClick : function(e)
32025 e.preventDefault();
32027 if(this.fireEvent('beforeselectfile', this) != false){
32028 this.selectorEl.dom.click();
32033 onFileSelected : function(e)
32035 e.preventDefault();
32037 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32041 Roo.each(this.selectorEl.dom.files, function(file){
32042 if(this.fireEvent('inspect', this, file) != false){
32043 this.files.push(file);
32053 this.selectorEl.dom.value = '';
32055 if(!this.files || !this.files.length){
32059 if(this.boxes > 0 && this.files.length > this.boxes){
32060 this.files = this.files.slice(0, this.boxes);
32063 this.uploader.show();
32065 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32066 this.uploader.hide();
32075 Roo.each(this.files, function(file){
32077 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32078 var f = this.renderPreview(file);
32083 if(file.type.indexOf('image') != -1){
32084 this.delegates.push(
32086 _this.process(file);
32087 }).createDelegate(this)
32095 _this.process(file);
32096 }).createDelegate(this)
32101 this.files = files;
32103 this.delegates = this.delegates.concat(docs);
32105 if(!this.delegates.length){
32110 this.progressBar.aria_valuemax = this.delegates.length;
32117 arrange : function()
32119 if(!this.delegates.length){
32120 this.progressDialog.hide();
32125 var delegate = this.delegates.shift();
32127 this.progressDialog.show();
32129 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32131 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32136 refresh : function()
32138 this.uploader.show();
32140 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32141 this.uploader.hide();
32144 Roo.isTouch ? this.closable(false) : this.closable(true);
32146 this.fireEvent('refresh', this);
32149 onRemove : function(e, el, o)
32151 e.preventDefault();
32153 this.fireEvent('remove', this, o);
32157 remove : function(o)
32161 Roo.each(this.files, function(file){
32162 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32171 this.files = files;
32178 Roo.each(this.files, function(file){
32183 file.target.remove();
32192 onClick : function(e, el, o)
32194 e.preventDefault();
32196 this.fireEvent('click', this, o);
32200 closable : function(closable)
32202 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32204 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32216 xhrOnLoad : function(xhr)
32218 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32222 if (xhr.readyState !== 4) {
32224 this.fireEvent('exception', this, xhr);
32228 var response = Roo.decode(xhr.responseText);
32230 if(!response.success){
32232 this.fireEvent('exception', this, xhr);
32236 var file = this.renderPreview(response.data);
32238 this.files.push(file);
32242 this.fireEvent('afterupload', this, xhr);
32246 xhrOnError : function(xhr)
32248 Roo.log('xhr on error');
32250 var response = Roo.decode(xhr.responseText);
32257 process : function(file)
32259 if(this.fireEvent('process', this, file) !== false){
32260 if(this.editable && file.type.indexOf('image') != -1){
32261 this.fireEvent('edit', this, file);
32265 this.uploadStart(file, false);
32272 uploadStart : function(file, crop)
32274 this.xhr = new XMLHttpRequest();
32276 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32281 file.xhr = this.xhr;
32283 this.managerEl.createChild({
32285 cls : 'roo-document-manager-loading',
32289 tooltip : file.name,
32290 cls : 'roo-document-manager-thumb',
32291 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32297 this.xhr.open(this.method, this.url, true);
32300 "Accept": "application/json",
32301 "Cache-Control": "no-cache",
32302 "X-Requested-With": "XMLHttpRequest"
32305 for (var headerName in headers) {
32306 var headerValue = headers[headerName];
32308 this.xhr.setRequestHeader(headerName, headerValue);
32314 this.xhr.onload = function()
32316 _this.xhrOnLoad(_this.xhr);
32319 this.xhr.onerror = function()
32321 _this.xhrOnError(_this.xhr);
32324 var formData = new FormData();
32326 formData.append('returnHTML', 'NO');
32329 formData.append('crop', crop);
32332 formData.append(this.paramName, file, file.name);
32339 if(this.fireEvent('prepare', this, formData, options) != false){
32341 if(options.manually){
32345 this.xhr.send(formData);
32349 this.uploadCancel();
32352 uploadCancel : function()
32358 this.delegates = [];
32360 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32367 renderPreview : function(file)
32369 if(typeof(file.target) != 'undefined' && file.target){
32373 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32375 var previewEl = this.managerEl.createChild({
32377 cls : 'roo-document-manager-preview',
32381 tooltip : file[this.toolTipName],
32382 cls : 'roo-document-manager-thumb',
32383 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32388 html : '<i class="fa fa-times-circle"></i>'
32393 var close = previewEl.select('button.close', true).first();
32395 close.on('click', this.onRemove, this, file);
32397 file.target = previewEl;
32399 var image = previewEl.select('img', true).first();
32403 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32405 image.on('click', this.onClick, this, file);
32407 this.fireEvent('previewrendered', this, file);
32413 onPreviewLoad : function(file, image)
32415 if(typeof(file.target) == 'undefined' || !file.target){
32419 var width = image.dom.naturalWidth || image.dom.width;
32420 var height = image.dom.naturalHeight || image.dom.height;
32422 if(!this.previewResize) {
32426 if(width > height){
32427 file.target.addClass('wide');
32431 file.target.addClass('tall');
32436 uploadFromSource : function(file, crop)
32438 this.xhr = new XMLHttpRequest();
32440 this.managerEl.createChild({
32442 cls : 'roo-document-manager-loading',
32446 tooltip : file.name,
32447 cls : 'roo-document-manager-thumb',
32448 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32454 this.xhr.open(this.method, this.url, true);
32457 "Accept": "application/json",
32458 "Cache-Control": "no-cache",
32459 "X-Requested-With": "XMLHttpRequest"
32462 for (var headerName in headers) {
32463 var headerValue = headers[headerName];
32465 this.xhr.setRequestHeader(headerName, headerValue);
32471 this.xhr.onload = function()
32473 _this.xhrOnLoad(_this.xhr);
32476 this.xhr.onerror = function()
32478 _this.xhrOnError(_this.xhr);
32481 var formData = new FormData();
32483 formData.append('returnHTML', 'NO');
32485 formData.append('crop', crop);
32487 if(typeof(file.filename) != 'undefined'){
32488 formData.append('filename', file.filename);
32491 if(typeof(file.mimetype) != 'undefined'){
32492 formData.append('mimetype', file.mimetype);
32497 if(this.fireEvent('prepare', this, formData) != false){
32498 this.xhr.send(formData);
32508 * @class Roo.bootstrap.DocumentViewer
32509 * @extends Roo.bootstrap.Component
32510 * Bootstrap DocumentViewer class
32511 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32512 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32515 * Create a new DocumentViewer
32516 * @param {Object} config The config object
32519 Roo.bootstrap.DocumentViewer = function(config){
32520 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32525 * Fire after initEvent
32526 * @param {Roo.bootstrap.DocumentViewer} this
32532 * @param {Roo.bootstrap.DocumentViewer} this
32537 * Fire after download button
32538 * @param {Roo.bootstrap.DocumentViewer} this
32543 * Fire after trash button
32544 * @param {Roo.bootstrap.DocumentViewer} this
32551 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32553 showDownload : true,
32557 getAutoCreate : function()
32561 cls : 'roo-document-viewer',
32565 cls : 'roo-document-viewer-body',
32569 cls : 'roo-document-viewer-thumb',
32573 cls : 'roo-document-viewer-image'
32581 cls : 'roo-document-viewer-footer',
32584 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32588 cls : 'btn-group roo-document-viewer-download',
32592 cls : 'btn btn-default',
32593 html : '<i class="fa fa-download"></i>'
32599 cls : 'btn-group roo-document-viewer-trash',
32603 cls : 'btn btn-default',
32604 html : '<i class="fa fa-trash"></i>'
32617 initEvents : function()
32619 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32620 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32622 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32623 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32625 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32626 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32628 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32629 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32631 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32632 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32634 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32635 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32637 this.bodyEl.on('click', this.onClick, this);
32638 this.downloadBtn.on('click', this.onDownload, this);
32639 this.trashBtn.on('click', this.onTrash, this);
32641 this.downloadBtn.hide();
32642 this.trashBtn.hide();
32644 if(this.showDownload){
32645 this.downloadBtn.show();
32648 if(this.showTrash){
32649 this.trashBtn.show();
32652 if(!this.showDownload && !this.showTrash) {
32653 this.footerEl.hide();
32658 initial : function()
32660 this.fireEvent('initial', this);
32664 onClick : function(e)
32666 e.preventDefault();
32668 this.fireEvent('click', this);
32671 onDownload : function(e)
32673 e.preventDefault();
32675 this.fireEvent('download', this);
32678 onTrash : function(e)
32680 e.preventDefault();
32682 this.fireEvent('trash', this);
32694 * @class Roo.bootstrap.NavProgressBar
32695 * @extends Roo.bootstrap.Component
32696 * Bootstrap NavProgressBar class
32699 * Create a new nav progress bar
32700 * @param {Object} config The config object
32703 Roo.bootstrap.NavProgressBar = function(config){
32704 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32706 this.bullets = this.bullets || [];
32708 // Roo.bootstrap.NavProgressBar.register(this);
32712 * Fires when the active item changes
32713 * @param {Roo.bootstrap.NavProgressBar} this
32714 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32715 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32722 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32727 getAutoCreate : function()
32729 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32733 cls : 'roo-navigation-bar-group',
32737 cls : 'roo-navigation-top-bar'
32741 cls : 'roo-navigation-bullets-bar',
32745 cls : 'roo-navigation-bar'
32752 cls : 'roo-navigation-bottom-bar'
32762 initEvents: function()
32767 onRender : function(ct, position)
32769 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32771 if(this.bullets.length){
32772 Roo.each(this.bullets, function(b){
32781 addItem : function(cfg)
32783 var item = new Roo.bootstrap.NavProgressItem(cfg);
32785 item.parentId = this.id;
32786 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32789 var top = new Roo.bootstrap.Element({
32791 cls : 'roo-navigation-bar-text'
32794 var bottom = new Roo.bootstrap.Element({
32796 cls : 'roo-navigation-bar-text'
32799 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32800 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32802 var topText = new Roo.bootstrap.Element({
32804 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32807 var bottomText = new Roo.bootstrap.Element({
32809 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32812 topText.onRender(top.el, null);
32813 bottomText.onRender(bottom.el, null);
32816 item.bottomEl = bottom;
32819 this.barItems.push(item);
32824 getActive : function()
32826 var active = false;
32828 Roo.each(this.barItems, function(v){
32830 if (!v.isActive()) {
32842 setActiveItem : function(item)
32846 Roo.each(this.barItems, function(v){
32847 if (v.rid == item.rid) {
32851 if (v.isActive()) {
32852 v.setActive(false);
32857 item.setActive(true);
32859 this.fireEvent('changed', this, item, prev);
32862 getBarItem: function(rid)
32866 Roo.each(this.barItems, function(e) {
32867 if (e.rid != rid) {
32878 indexOfItem : function(item)
32882 Roo.each(this.barItems, function(v, i){
32884 if (v.rid != item.rid) {
32895 setActiveNext : function()
32897 var i = this.indexOfItem(this.getActive());
32899 if (i > this.barItems.length) {
32903 this.setActiveItem(this.barItems[i+1]);
32906 setActivePrev : function()
32908 var i = this.indexOfItem(this.getActive());
32914 this.setActiveItem(this.barItems[i-1]);
32917 format : function()
32919 if(!this.barItems.length){
32923 var width = 100 / this.barItems.length;
32925 Roo.each(this.barItems, function(i){
32926 i.el.setStyle('width', width + '%');
32927 i.topEl.el.setStyle('width', width + '%');
32928 i.bottomEl.el.setStyle('width', width + '%');
32937 * Nav Progress Item
32942 * @class Roo.bootstrap.NavProgressItem
32943 * @extends Roo.bootstrap.Component
32944 * Bootstrap NavProgressItem class
32945 * @cfg {String} rid the reference id
32946 * @cfg {Boolean} active (true|false) Is item active default false
32947 * @cfg {Boolean} disabled (true|false) Is item active default false
32948 * @cfg {String} html
32949 * @cfg {String} position (top|bottom) text position default bottom
32950 * @cfg {String} icon show icon instead of number
32953 * Create a new NavProgressItem
32954 * @param {Object} config The config object
32956 Roo.bootstrap.NavProgressItem = function(config){
32957 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32962 * The raw click event for the entire grid.
32963 * @param {Roo.bootstrap.NavProgressItem} this
32964 * @param {Roo.EventObject} e
32971 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32977 position : 'bottom',
32980 getAutoCreate : function()
32982 var iconCls = 'roo-navigation-bar-item-icon';
32984 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32988 cls: 'roo-navigation-bar-item',
32998 cfg.cls += ' active';
33001 cfg.cls += ' disabled';
33007 disable : function()
33009 this.setDisabled(true);
33012 enable : function()
33014 this.setDisabled(false);
33017 initEvents: function()
33019 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33021 this.iconEl.on('click', this.onClick, this);
33024 onClick : function(e)
33026 e.preventDefault();
33032 if(this.fireEvent('click', this, e) === false){
33036 this.parent().setActiveItem(this);
33039 isActive: function ()
33041 return this.active;
33044 setActive : function(state)
33046 if(this.active == state){
33050 this.active = state;
33053 this.el.addClass('active');
33057 this.el.removeClass('active');
33062 setDisabled : function(state)
33064 if(this.disabled == state){
33068 this.disabled = state;
33071 this.el.addClass('disabled');
33075 this.el.removeClass('disabled');
33078 tooltipEl : function()
33080 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33093 * @class Roo.bootstrap.FieldLabel
33094 * @extends Roo.bootstrap.Component
33095 * Bootstrap FieldLabel class
33096 * @cfg {String} html contents of the element
33097 * @cfg {String} tag tag of the element default label
33098 * @cfg {String} cls class of the element
33099 * @cfg {String} target label target
33100 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33101 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33102 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33103 * @cfg {String} iconTooltip default "This field is required"
33104 * @cfg {String} indicatorpos (left|right) default left
33107 * Create a new FieldLabel
33108 * @param {Object} config The config object
33111 Roo.bootstrap.FieldLabel = function(config){
33112 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33117 * Fires after the field has been marked as invalid.
33118 * @param {Roo.form.FieldLabel} this
33119 * @param {String} msg The validation message
33124 * Fires after the field has been validated with no errors.
33125 * @param {Roo.form.FieldLabel} this
33131 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33138 invalidClass : 'has-warning',
33139 validClass : 'has-success',
33140 iconTooltip : 'This field is required',
33141 indicatorpos : 'left',
33143 getAutoCreate : function(){
33146 if (!this.allowBlank) {
33152 cls : 'roo-bootstrap-field-label ' + this.cls,
33157 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33158 tooltip : this.iconTooltip
33167 if(this.indicatorpos == 'right'){
33170 cls : 'roo-bootstrap-field-label ' + this.cls,
33179 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33180 tooltip : this.iconTooltip
33189 initEvents: function()
33191 Roo.bootstrap.Element.superclass.initEvents.call(this);
33193 this.indicator = this.indicatorEl();
33195 if(this.indicator){
33196 this.indicator.removeClass('visible');
33197 this.indicator.addClass('invisible');
33200 Roo.bootstrap.FieldLabel.register(this);
33203 indicatorEl : function()
33205 var indicator = this.el.select('i.roo-required-indicator',true).first();
33216 * Mark this field as valid
33218 markValid : function()
33220 if(this.indicator){
33221 this.indicator.removeClass('visible');
33222 this.indicator.addClass('invisible');
33224 if (Roo.bootstrap.version == 3) {
33225 this.el.removeClass(this.invalidClass);
33226 this.el.addClass(this.validClass);
33228 this.el.removeClass('is-invalid');
33229 this.el.addClass('is-valid');
33233 this.fireEvent('valid', this);
33237 * Mark this field as invalid
33238 * @param {String} msg The validation message
33240 markInvalid : function(msg)
33242 if(this.indicator){
33243 this.indicator.removeClass('invisible');
33244 this.indicator.addClass('visible');
33246 if (Roo.bootstrap.version == 3) {
33247 this.el.removeClass(this.validClass);
33248 this.el.addClass(this.invalidClass);
33250 this.el.removeClass('is-valid');
33251 this.el.addClass('is-invalid');
33255 this.fireEvent('invalid', this, msg);
33261 Roo.apply(Roo.bootstrap.FieldLabel, {
33266 * register a FieldLabel Group
33267 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33269 register : function(label)
33271 if(this.groups.hasOwnProperty(label.target)){
33275 this.groups[label.target] = label;
33279 * fetch a FieldLabel Group based on the target
33280 * @param {string} target
33281 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33283 get: function(target) {
33284 if (typeof(this.groups[target]) == 'undefined') {
33288 return this.groups[target] ;
33297 * page DateSplitField.
33303 * @class Roo.bootstrap.DateSplitField
33304 * @extends Roo.bootstrap.Component
33305 * Bootstrap DateSplitField class
33306 * @cfg {string} fieldLabel - the label associated
33307 * @cfg {Number} labelWidth set the width of label (0-12)
33308 * @cfg {String} labelAlign (top|left)
33309 * @cfg {Boolean} dayAllowBlank (true|false) default false
33310 * @cfg {Boolean} monthAllowBlank (true|false) default false
33311 * @cfg {Boolean} yearAllowBlank (true|false) default false
33312 * @cfg {string} dayPlaceholder
33313 * @cfg {string} monthPlaceholder
33314 * @cfg {string} yearPlaceholder
33315 * @cfg {string} dayFormat default 'd'
33316 * @cfg {string} monthFormat default 'm'
33317 * @cfg {string} yearFormat default 'Y'
33318 * @cfg {Number} labellg set the width of label (1-12)
33319 * @cfg {Number} labelmd set the width of label (1-12)
33320 * @cfg {Number} labelsm set the width of label (1-12)
33321 * @cfg {Number} labelxs set the width of label (1-12)
33325 * Create a new DateSplitField
33326 * @param {Object} config The config object
33329 Roo.bootstrap.DateSplitField = function(config){
33330 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33336 * getting the data of years
33337 * @param {Roo.bootstrap.DateSplitField} this
33338 * @param {Object} years
33343 * getting the data of days
33344 * @param {Roo.bootstrap.DateSplitField} this
33345 * @param {Object} days
33350 * Fires after the field has been marked as invalid.
33351 * @param {Roo.form.Field} this
33352 * @param {String} msg The validation message
33357 * Fires after the field has been validated with no errors.
33358 * @param {Roo.form.Field} this
33364 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33367 labelAlign : 'top',
33369 dayAllowBlank : false,
33370 monthAllowBlank : false,
33371 yearAllowBlank : false,
33372 dayPlaceholder : '',
33373 monthPlaceholder : '',
33374 yearPlaceholder : '',
33378 isFormField : true,
33384 getAutoCreate : function()
33388 cls : 'row roo-date-split-field-group',
33393 cls : 'form-hidden-field roo-date-split-field-group-value',
33399 var labelCls = 'col-md-12';
33400 var contentCls = 'col-md-4';
33402 if(this.fieldLabel){
33406 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33410 html : this.fieldLabel
33415 if(this.labelAlign == 'left'){
33417 if(this.labelWidth > 12){
33418 label.style = "width: " + this.labelWidth + 'px';
33421 if(this.labelWidth < 13 && this.labelmd == 0){
33422 this.labelmd = this.labelWidth;
33425 if(this.labellg > 0){
33426 labelCls = ' col-lg-' + this.labellg;
33427 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33430 if(this.labelmd > 0){
33431 labelCls = ' col-md-' + this.labelmd;
33432 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33435 if(this.labelsm > 0){
33436 labelCls = ' col-sm-' + this.labelsm;
33437 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33440 if(this.labelxs > 0){
33441 labelCls = ' col-xs-' + this.labelxs;
33442 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33446 label.cls += ' ' + labelCls;
33448 cfg.cn.push(label);
33451 Roo.each(['day', 'month', 'year'], function(t){
33454 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33461 inputEl: function ()
33463 return this.el.select('.roo-date-split-field-group-value', true).first();
33466 onRender : function(ct, position)
33470 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33472 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33474 this.dayField = new Roo.bootstrap.ComboBox({
33475 allowBlank : this.dayAllowBlank,
33476 alwaysQuery : true,
33477 displayField : 'value',
33480 forceSelection : true,
33482 placeholder : this.dayPlaceholder,
33483 selectOnFocus : true,
33484 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33485 triggerAction : 'all',
33487 valueField : 'value',
33488 store : new Roo.data.SimpleStore({
33489 data : (function() {
33491 _this.fireEvent('days', _this, days);
33494 fields : [ 'value' ]
33497 select : function (_self, record, index)
33499 _this.setValue(_this.getValue());
33504 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33506 this.monthField = new Roo.bootstrap.MonthField({
33507 after : '<i class=\"fa fa-calendar\"></i>',
33508 allowBlank : this.monthAllowBlank,
33509 placeholder : this.monthPlaceholder,
33512 render : function (_self)
33514 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33515 e.preventDefault();
33519 select : function (_self, oldvalue, newvalue)
33521 _this.setValue(_this.getValue());
33526 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33528 this.yearField = new Roo.bootstrap.ComboBox({
33529 allowBlank : this.yearAllowBlank,
33530 alwaysQuery : true,
33531 displayField : 'value',
33534 forceSelection : true,
33536 placeholder : this.yearPlaceholder,
33537 selectOnFocus : true,
33538 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33539 triggerAction : 'all',
33541 valueField : 'value',
33542 store : new Roo.data.SimpleStore({
33543 data : (function() {
33545 _this.fireEvent('years', _this, years);
33548 fields : [ 'value' ]
33551 select : function (_self, record, index)
33553 _this.setValue(_this.getValue());
33558 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33561 setValue : function(v, format)
33563 this.inputEl.dom.value = v;
33565 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33567 var d = Date.parseDate(v, f);
33574 this.setDay(d.format(this.dayFormat));
33575 this.setMonth(d.format(this.monthFormat));
33576 this.setYear(d.format(this.yearFormat));
33583 setDay : function(v)
33585 this.dayField.setValue(v);
33586 this.inputEl.dom.value = this.getValue();
33591 setMonth : function(v)
33593 this.monthField.setValue(v, true);
33594 this.inputEl.dom.value = this.getValue();
33599 setYear : function(v)
33601 this.yearField.setValue(v);
33602 this.inputEl.dom.value = this.getValue();
33607 getDay : function()
33609 return this.dayField.getValue();
33612 getMonth : function()
33614 return this.monthField.getValue();
33617 getYear : function()
33619 return this.yearField.getValue();
33622 getValue : function()
33624 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33626 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33636 this.inputEl.dom.value = '';
33641 validate : function()
33643 var d = this.dayField.validate();
33644 var m = this.monthField.validate();
33645 var y = this.yearField.validate();
33650 (!this.dayAllowBlank && !d) ||
33651 (!this.monthAllowBlank && !m) ||
33652 (!this.yearAllowBlank && !y)
33657 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33666 this.markInvalid();
33671 markValid : function()
33674 var label = this.el.select('label', true).first();
33675 var icon = this.el.select('i.fa-star', true).first();
33681 this.fireEvent('valid', this);
33685 * Mark this field as invalid
33686 * @param {String} msg The validation message
33688 markInvalid : function(msg)
33691 var label = this.el.select('label', true).first();
33692 var icon = this.el.select('i.fa-star', true).first();
33694 if(label && !icon){
33695 this.el.select('.roo-date-split-field-label', true).createChild({
33697 cls : 'text-danger fa fa-lg fa-star',
33698 tooltip : 'This field is required',
33699 style : 'margin-right:5px;'
33703 this.fireEvent('invalid', this, msg);
33706 clearInvalid : function()
33708 var label = this.el.select('label', true).first();
33709 var icon = this.el.select('i.fa-star', true).first();
33715 this.fireEvent('valid', this);
33718 getName: function()
33728 * http://masonry.desandro.com
33730 * The idea is to render all the bricks based on vertical width...
33732 * The original code extends 'outlayer' - we might need to use that....
33738 * @class Roo.bootstrap.LayoutMasonry
33739 * @extends Roo.bootstrap.Component
33740 * Bootstrap Layout Masonry class
33743 * Create a new Element
33744 * @param {Object} config The config object
33747 Roo.bootstrap.LayoutMasonry = function(config){
33749 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33753 Roo.bootstrap.LayoutMasonry.register(this);
33759 * Fire after layout the items
33760 * @param {Roo.bootstrap.LayoutMasonry} this
33761 * @param {Roo.EventObject} e
33768 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33771 * @cfg {Boolean} isLayoutInstant = no animation?
33773 isLayoutInstant : false, // needed?
33776 * @cfg {Number} boxWidth width of the columns
33781 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33786 * @cfg {Number} padWidth padding below box..
33791 * @cfg {Number} gutter gutter width..
33796 * @cfg {Number} maxCols maximum number of columns
33802 * @cfg {Boolean} isAutoInitial defalut true
33804 isAutoInitial : true,
33809 * @cfg {Boolean} isHorizontal defalut false
33811 isHorizontal : false,
33813 currentSize : null,
33819 bricks: null, //CompositeElement
33823 _isLayoutInited : false,
33825 // isAlternative : false, // only use for vertical layout...
33828 * @cfg {Number} alternativePadWidth padding below box..
33830 alternativePadWidth : 50,
33832 selectedBrick : [],
33834 getAutoCreate : function(){
33836 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33840 cls: 'blog-masonary-wrapper ' + this.cls,
33842 cls : 'mas-boxes masonary'
33849 getChildContainer: function( )
33851 if (this.boxesEl) {
33852 return this.boxesEl;
33855 this.boxesEl = this.el.select('.mas-boxes').first();
33857 return this.boxesEl;
33861 initEvents : function()
33865 if(this.isAutoInitial){
33866 Roo.log('hook children rendered');
33867 this.on('childrenrendered', function() {
33868 Roo.log('children rendered');
33874 initial : function()
33876 this.selectedBrick = [];
33878 this.currentSize = this.el.getBox(true);
33880 Roo.EventManager.onWindowResize(this.resize, this);
33882 if(!this.isAutoInitial){
33890 //this.layout.defer(500,this);
33894 resize : function()
33896 var cs = this.el.getBox(true);
33899 this.currentSize.width == cs.width &&
33900 this.currentSize.x == cs.x &&
33901 this.currentSize.height == cs.height &&
33902 this.currentSize.y == cs.y
33904 Roo.log("no change in with or X or Y");
33908 this.currentSize = cs;
33914 layout : function()
33916 this._resetLayout();
33918 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33920 this.layoutItems( isInstant );
33922 this._isLayoutInited = true;
33924 this.fireEvent('layout', this);
33928 _resetLayout : function()
33930 if(this.isHorizontal){
33931 this.horizontalMeasureColumns();
33935 this.verticalMeasureColumns();
33939 verticalMeasureColumns : function()
33941 this.getContainerWidth();
33943 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33944 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33948 var boxWidth = this.boxWidth + this.padWidth;
33950 if(this.containerWidth < this.boxWidth){
33951 boxWidth = this.containerWidth
33954 var containerWidth = this.containerWidth;
33956 var cols = Math.floor(containerWidth / boxWidth);
33958 this.cols = Math.max( cols, 1 );
33960 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33962 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33964 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33966 this.colWidth = boxWidth + avail - this.padWidth;
33968 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33969 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33972 horizontalMeasureColumns : function()
33974 this.getContainerWidth();
33976 var boxWidth = this.boxWidth;
33978 if(this.containerWidth < boxWidth){
33979 boxWidth = this.containerWidth;
33982 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33984 this.el.setHeight(boxWidth);
33988 getContainerWidth : function()
33990 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33993 layoutItems : function( isInstant )
33995 Roo.log(this.bricks);
33997 var items = Roo.apply([], this.bricks);
33999 if(this.isHorizontal){
34000 this._horizontalLayoutItems( items , isInstant );
34004 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34005 // this._verticalAlternativeLayoutItems( items , isInstant );
34009 this._verticalLayoutItems( items , isInstant );
34013 _verticalLayoutItems : function ( items , isInstant)
34015 if ( !items || !items.length ) {
34020 ['xs', 'xs', 'xs', 'tall'],
34021 ['xs', 'xs', 'tall'],
34022 ['xs', 'xs', 'sm'],
34023 ['xs', 'xs', 'xs'],
34029 ['sm', 'xs', 'xs'],
34033 ['tall', 'xs', 'xs', 'xs'],
34034 ['tall', 'xs', 'xs'],
34046 Roo.each(items, function(item, k){
34048 switch (item.size) {
34049 // these layouts take up a full box,
34060 boxes.push([item]);
34083 var filterPattern = function(box, length)
34091 var pattern = box.slice(0, length);
34095 Roo.each(pattern, function(i){
34096 format.push(i.size);
34099 Roo.each(standard, function(s){
34101 if(String(s) != String(format)){
34110 if(!match && length == 1){
34115 filterPattern(box, length - 1);
34119 queue.push(pattern);
34121 box = box.slice(length, box.length);
34123 filterPattern(box, 4);
34129 Roo.each(boxes, function(box, k){
34135 if(box.length == 1){
34140 filterPattern(box, 4);
34144 this._processVerticalLayoutQueue( queue, isInstant );
34148 // _verticalAlternativeLayoutItems : function( items , isInstant )
34150 // if ( !items || !items.length ) {
34154 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34158 _horizontalLayoutItems : function ( items , isInstant)
34160 if ( !items || !items.length || items.length < 3) {
34166 var eItems = items.slice(0, 3);
34168 items = items.slice(3, items.length);
34171 ['xs', 'xs', 'xs', 'wide'],
34172 ['xs', 'xs', 'wide'],
34173 ['xs', 'xs', 'sm'],
34174 ['xs', 'xs', 'xs'],
34180 ['sm', 'xs', 'xs'],
34184 ['wide', 'xs', 'xs', 'xs'],
34185 ['wide', 'xs', 'xs'],
34198 Roo.each(items, function(item, k){
34200 switch (item.size) {
34211 boxes.push([item]);
34235 var filterPattern = function(box, length)
34243 var pattern = box.slice(0, length);
34247 Roo.each(pattern, function(i){
34248 format.push(i.size);
34251 Roo.each(standard, function(s){
34253 if(String(s) != String(format)){
34262 if(!match && length == 1){
34267 filterPattern(box, length - 1);
34271 queue.push(pattern);
34273 box = box.slice(length, box.length);
34275 filterPattern(box, 4);
34281 Roo.each(boxes, function(box, k){
34287 if(box.length == 1){
34292 filterPattern(box, 4);
34299 var pos = this.el.getBox(true);
34303 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34305 var hit_end = false;
34307 Roo.each(queue, function(box){
34311 Roo.each(box, function(b){
34313 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34323 Roo.each(box, function(b){
34325 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34328 mx = Math.max(mx, b.x);
34332 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34336 Roo.each(box, function(b){
34338 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34352 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34355 /** Sets position of item in DOM
34356 * @param {Element} item
34357 * @param {Number} x - horizontal position
34358 * @param {Number} y - vertical position
34359 * @param {Boolean} isInstant - disables transitions
34361 _processVerticalLayoutQueue : function( queue, isInstant )
34363 var pos = this.el.getBox(true);
34368 for (var i = 0; i < this.cols; i++){
34372 Roo.each(queue, function(box, k){
34374 var col = k % this.cols;
34376 Roo.each(box, function(b,kk){
34378 b.el.position('absolute');
34380 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34381 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34383 if(b.size == 'md-left' || b.size == 'md-right'){
34384 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34385 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34388 b.el.setWidth(width);
34389 b.el.setHeight(height);
34391 b.el.select('iframe',true).setSize(width,height);
34395 for (var i = 0; i < this.cols; i++){
34397 if(maxY[i] < maxY[col]){
34402 col = Math.min(col, i);
34406 x = pos.x + col * (this.colWidth + this.padWidth);
34410 var positions = [];
34412 switch (box.length){
34414 positions = this.getVerticalOneBoxColPositions(x, y, box);
34417 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34420 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34423 positions = this.getVerticalFourBoxColPositions(x, y, box);
34429 Roo.each(box, function(b,kk){
34431 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34433 var sz = b.el.getSize();
34435 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34443 for (var i = 0; i < this.cols; i++){
34444 mY = Math.max(mY, maxY[i]);
34447 this.el.setHeight(mY - pos.y);
34451 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34453 // var pos = this.el.getBox(true);
34456 // var maxX = pos.right;
34458 // var maxHeight = 0;
34460 // Roo.each(items, function(item, k){
34464 // item.el.position('absolute');
34466 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34468 // item.el.setWidth(width);
34470 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34472 // item.el.setHeight(height);
34475 // item.el.setXY([x, y], isInstant ? false : true);
34477 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34480 // y = y + height + this.alternativePadWidth;
34482 // maxHeight = maxHeight + height + this.alternativePadWidth;
34486 // this.el.setHeight(maxHeight);
34490 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34492 var pos = this.el.getBox(true);
34497 var maxX = pos.right;
34499 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34501 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34503 Roo.each(queue, function(box, k){
34505 Roo.each(box, function(b, kk){
34507 b.el.position('absolute');
34509 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34510 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34512 if(b.size == 'md-left' || b.size == 'md-right'){
34513 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34514 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34517 b.el.setWidth(width);
34518 b.el.setHeight(height);
34526 var positions = [];
34528 switch (box.length){
34530 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34533 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34536 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34539 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34545 Roo.each(box, function(b,kk){
34547 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34549 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34557 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34559 Roo.each(eItems, function(b,k){
34561 b.size = (k == 0) ? 'sm' : 'xs';
34562 b.x = (k == 0) ? 2 : 1;
34563 b.y = (k == 0) ? 2 : 1;
34565 b.el.position('absolute');
34567 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34569 b.el.setWidth(width);
34571 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34573 b.el.setHeight(height);
34577 var positions = [];
34580 x : maxX - this.unitWidth * 2 - this.gutter,
34585 x : maxX - this.unitWidth,
34586 y : minY + (this.unitWidth + this.gutter) * 2
34590 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34594 Roo.each(eItems, function(b,k){
34596 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34602 getVerticalOneBoxColPositions : function(x, y, box)
34606 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34608 if(box[0].size == 'md-left'){
34612 if(box[0].size == 'md-right'){
34617 x : x + (this.unitWidth + this.gutter) * rand,
34624 getVerticalTwoBoxColPositions : function(x, y, box)
34628 if(box[0].size == 'xs'){
34632 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34636 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34650 x : x + (this.unitWidth + this.gutter) * 2,
34651 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34658 getVerticalThreeBoxColPositions : function(x, y, box)
34662 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34670 x : x + (this.unitWidth + this.gutter) * 1,
34675 x : x + (this.unitWidth + this.gutter) * 2,
34683 if(box[0].size == 'xs' && box[1].size == 'xs'){
34692 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34696 x : x + (this.unitWidth + this.gutter) * 1,
34710 x : x + (this.unitWidth + this.gutter) * 2,
34715 x : x + (this.unitWidth + this.gutter) * 2,
34716 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34723 getVerticalFourBoxColPositions : function(x, y, box)
34727 if(box[0].size == 'xs'){
34736 y : y + (this.unitHeight + this.gutter) * 1
34741 y : y + (this.unitHeight + this.gutter) * 2
34745 x : x + (this.unitWidth + this.gutter) * 1,
34759 x : x + (this.unitWidth + this.gutter) * 2,
34764 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34765 y : y + (this.unitHeight + this.gutter) * 1
34769 x : x + (this.unitWidth + this.gutter) * 2,
34770 y : y + (this.unitWidth + this.gutter) * 2
34777 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34781 if(box[0].size == 'md-left'){
34783 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34790 if(box[0].size == 'md-right'){
34792 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34793 y : minY + (this.unitWidth + this.gutter) * 1
34799 var rand = Math.floor(Math.random() * (4 - box[0].y));
34802 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34803 y : minY + (this.unitWidth + this.gutter) * rand
34810 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34814 if(box[0].size == 'xs'){
34817 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34822 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34823 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34831 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34836 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34837 y : minY + (this.unitWidth + this.gutter) * 2
34844 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34848 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34851 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34856 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34857 y : minY + (this.unitWidth + this.gutter) * 1
34861 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34862 y : minY + (this.unitWidth + this.gutter) * 2
34869 if(box[0].size == 'xs' && box[1].size == 'xs'){
34872 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34877 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34882 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34883 y : minY + (this.unitWidth + this.gutter) * 1
34891 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34896 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34897 y : minY + (this.unitWidth + this.gutter) * 2
34901 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34902 y : minY + (this.unitWidth + this.gutter) * 2
34909 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34913 if(box[0].size == 'xs'){
34916 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34921 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34926 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),
34931 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34932 y : minY + (this.unitWidth + this.gutter) * 1
34940 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34945 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34946 y : minY + (this.unitWidth + this.gutter) * 2
34950 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34951 y : minY + (this.unitWidth + this.gutter) * 2
34955 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),
34956 y : minY + (this.unitWidth + this.gutter) * 2
34964 * remove a Masonry Brick
34965 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34967 removeBrick : function(brick_id)
34973 for (var i = 0; i<this.bricks.length; i++) {
34974 if (this.bricks[i].id == brick_id) {
34975 this.bricks.splice(i,1);
34976 this.el.dom.removeChild(Roo.get(brick_id).dom);
34983 * adds a Masonry Brick
34984 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34986 addBrick : function(cfg)
34988 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34989 //this.register(cn);
34990 cn.parentId = this.id;
34991 cn.render(this.el);
34996 * register a Masonry Brick
34997 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35000 register : function(brick)
35002 this.bricks.push(brick);
35003 brick.masonryId = this.id;
35007 * clear all the Masonry Brick
35009 clearAll : function()
35012 //this.getChildContainer().dom.innerHTML = "";
35013 this.el.dom.innerHTML = '';
35016 getSelected : function()
35018 if (!this.selectedBrick) {
35022 return this.selectedBrick;
35026 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35030 * register a Masonry Layout
35031 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35034 register : function(layout)
35036 this.groups[layout.id] = layout;
35039 * fetch a Masonry Layout based on the masonry layout ID
35040 * @param {string} the masonry layout to add
35041 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35044 get: function(layout_id) {
35045 if (typeof(this.groups[layout_id]) == 'undefined') {
35048 return this.groups[layout_id] ;
35060 * http://masonry.desandro.com
35062 * The idea is to render all the bricks based on vertical width...
35064 * The original code extends 'outlayer' - we might need to use that....
35070 * @class Roo.bootstrap.LayoutMasonryAuto
35071 * @extends Roo.bootstrap.Component
35072 * Bootstrap Layout Masonry class
35075 * Create a new Element
35076 * @param {Object} config The config object
35079 Roo.bootstrap.LayoutMasonryAuto = function(config){
35080 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35083 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35086 * @cfg {Boolean} isFitWidth - resize the width..
35088 isFitWidth : false, // options..
35090 * @cfg {Boolean} isOriginLeft = left align?
35092 isOriginLeft : true,
35094 * @cfg {Boolean} isOriginTop = top align?
35096 isOriginTop : false,
35098 * @cfg {Boolean} isLayoutInstant = no animation?
35100 isLayoutInstant : false, // needed?
35102 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35104 isResizingContainer : true,
35106 * @cfg {Number} columnWidth width of the columns
35112 * @cfg {Number} maxCols maximum number of columns
35117 * @cfg {Number} padHeight padding below box..
35123 * @cfg {Boolean} isAutoInitial defalut true
35126 isAutoInitial : true,
35132 initialColumnWidth : 0,
35133 currentSize : null,
35135 colYs : null, // array.
35142 bricks: null, //CompositeElement
35143 cols : 0, // array?
35144 // element : null, // wrapped now this.el
35145 _isLayoutInited : null,
35148 getAutoCreate : function(){
35152 cls: 'blog-masonary-wrapper ' + this.cls,
35154 cls : 'mas-boxes masonary'
35161 getChildContainer: function( )
35163 if (this.boxesEl) {
35164 return this.boxesEl;
35167 this.boxesEl = this.el.select('.mas-boxes').first();
35169 return this.boxesEl;
35173 initEvents : function()
35177 if(this.isAutoInitial){
35178 Roo.log('hook children rendered');
35179 this.on('childrenrendered', function() {
35180 Roo.log('children rendered');
35187 initial : function()
35189 this.reloadItems();
35191 this.currentSize = this.el.getBox(true);
35193 /// was window resize... - let's see if this works..
35194 Roo.EventManager.onWindowResize(this.resize, this);
35196 if(!this.isAutoInitial){
35201 this.layout.defer(500,this);
35204 reloadItems: function()
35206 this.bricks = this.el.select('.masonry-brick', true);
35208 this.bricks.each(function(b) {
35209 //Roo.log(b.getSize());
35210 if (!b.attr('originalwidth')) {
35211 b.attr('originalwidth', b.getSize().width);
35216 Roo.log(this.bricks.elements.length);
35219 resize : function()
35222 var cs = this.el.getBox(true);
35224 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35225 Roo.log("no change in with or X");
35228 this.currentSize = cs;
35232 layout : function()
35235 this._resetLayout();
35236 //this._manageStamps();
35238 // don't animate first layout
35239 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35240 this.layoutItems( isInstant );
35242 // flag for initalized
35243 this._isLayoutInited = true;
35246 layoutItems : function( isInstant )
35248 //var items = this._getItemsForLayout( this.items );
35249 // original code supports filtering layout items.. we just ignore it..
35251 this._layoutItems( this.bricks , isInstant );
35253 this._postLayout();
35255 _layoutItems : function ( items , isInstant)
35257 //this.fireEvent( 'layout', this, items );
35260 if ( !items || !items.elements.length ) {
35261 // no items, emit event with empty array
35266 items.each(function(item) {
35267 Roo.log("layout item");
35269 // get x/y object from method
35270 var position = this._getItemLayoutPosition( item );
35272 position.item = item;
35273 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35274 queue.push( position );
35277 this._processLayoutQueue( queue );
35279 /** Sets position of item in DOM
35280 * @param {Element} item
35281 * @param {Number} x - horizontal position
35282 * @param {Number} y - vertical position
35283 * @param {Boolean} isInstant - disables transitions
35285 _processLayoutQueue : function( queue )
35287 for ( var i=0, len = queue.length; i < len; i++ ) {
35288 var obj = queue[i];
35289 obj.item.position('absolute');
35290 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35296 * Any logic you want to do after each layout,
35297 * i.e. size the container
35299 _postLayout : function()
35301 this.resizeContainer();
35304 resizeContainer : function()
35306 if ( !this.isResizingContainer ) {
35309 var size = this._getContainerSize();
35311 this.el.setSize(size.width,size.height);
35312 this.boxesEl.setSize(size.width,size.height);
35318 _resetLayout : function()
35320 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35321 this.colWidth = this.el.getWidth();
35322 //this.gutter = this.el.getWidth();
35324 this.measureColumns();
35330 this.colYs.push( 0 );
35336 measureColumns : function()
35338 this.getContainerWidth();
35339 // if columnWidth is 0, default to outerWidth of first item
35340 if ( !this.columnWidth ) {
35341 var firstItem = this.bricks.first();
35342 Roo.log(firstItem);
35343 this.columnWidth = this.containerWidth;
35344 if (firstItem && firstItem.attr('originalwidth') ) {
35345 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35347 // columnWidth fall back to item of first element
35348 Roo.log("set column width?");
35349 this.initialColumnWidth = this.columnWidth ;
35351 // if first elem has no width, default to size of container
35356 if (this.initialColumnWidth) {
35357 this.columnWidth = this.initialColumnWidth;
35362 // column width is fixed at the top - however if container width get's smaller we should
35365 // this bit calcs how man columns..
35367 var columnWidth = this.columnWidth += this.gutter;
35369 // calculate columns
35370 var containerWidth = this.containerWidth + this.gutter;
35372 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35373 // fix rounding errors, typically with gutters
35374 var excess = columnWidth - containerWidth % columnWidth;
35377 // if overshoot is less than a pixel, round up, otherwise floor it
35378 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35379 cols = Math[ mathMethod ]( cols );
35380 this.cols = Math.max( cols, 1 );
35381 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35383 // padding positioning..
35384 var totalColWidth = this.cols * this.columnWidth;
35385 var padavail = this.containerWidth - totalColWidth;
35386 // so for 2 columns - we need 3 'pads'
35388 var padNeeded = (1+this.cols) * this.padWidth;
35390 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35392 this.columnWidth += padExtra
35393 //this.padWidth = Math.floor(padavail / ( this.cols));
35395 // adjust colum width so that padding is fixed??
35397 // we have 3 columns ... total = width * 3
35398 // we have X left over... that should be used by
35400 //if (this.expandC) {
35408 getContainerWidth : function()
35410 /* // container is parent if fit width
35411 var container = this.isFitWidth ? this.element.parentNode : this.element;
35412 // check that this.size and size are there
35413 // IE8 triggers resize on body size change, so they might not be
35415 var size = getSize( container ); //FIXME
35416 this.containerWidth = size && size.innerWidth; //FIXME
35419 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35423 _getItemLayoutPosition : function( item ) // what is item?
35425 // we resize the item to our columnWidth..
35427 item.setWidth(this.columnWidth);
35428 item.autoBoxAdjust = false;
35430 var sz = item.getSize();
35432 // how many columns does this brick span
35433 var remainder = this.containerWidth % this.columnWidth;
35435 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35436 // round if off by 1 pixel, otherwise use ceil
35437 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35438 colSpan = Math.min( colSpan, this.cols );
35440 // normally this should be '1' as we dont' currently allow multi width columns..
35442 var colGroup = this._getColGroup( colSpan );
35443 // get the minimum Y value from the columns
35444 var minimumY = Math.min.apply( Math, colGroup );
35445 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35447 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35449 // position the brick
35451 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35452 y: this.currentSize.y + minimumY + this.padHeight
35456 // apply setHeight to necessary columns
35457 var setHeight = minimumY + sz.height + this.padHeight;
35458 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35460 var setSpan = this.cols + 1 - colGroup.length;
35461 for ( var i = 0; i < setSpan; i++ ) {
35462 this.colYs[ shortColIndex + i ] = setHeight ;
35469 * @param {Number} colSpan - number of columns the element spans
35470 * @returns {Array} colGroup
35472 _getColGroup : function( colSpan )
35474 if ( colSpan < 2 ) {
35475 // if brick spans only one column, use all the column Ys
35480 // how many different places could this brick fit horizontally
35481 var groupCount = this.cols + 1 - colSpan;
35482 // for each group potential horizontal position
35483 for ( var i = 0; i < groupCount; i++ ) {
35484 // make an array of colY values for that one group
35485 var groupColYs = this.colYs.slice( i, i + colSpan );
35486 // and get the max value of the array
35487 colGroup[i] = Math.max.apply( Math, groupColYs );
35492 _manageStamp : function( stamp )
35494 var stampSize = stamp.getSize();
35495 var offset = stamp.getBox();
35496 // get the columns that this stamp affects
35497 var firstX = this.isOriginLeft ? offset.x : offset.right;
35498 var lastX = firstX + stampSize.width;
35499 var firstCol = Math.floor( firstX / this.columnWidth );
35500 firstCol = Math.max( 0, firstCol );
35502 var lastCol = Math.floor( lastX / this.columnWidth );
35503 // lastCol should not go over if multiple of columnWidth #425
35504 lastCol -= lastX % this.columnWidth ? 0 : 1;
35505 lastCol = Math.min( this.cols - 1, lastCol );
35507 // set colYs to bottom of the stamp
35508 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35511 for ( var i = firstCol; i <= lastCol; i++ ) {
35512 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35517 _getContainerSize : function()
35519 this.maxY = Math.max.apply( Math, this.colYs );
35524 if ( this.isFitWidth ) {
35525 size.width = this._getContainerFitWidth();
35531 _getContainerFitWidth : function()
35533 var unusedCols = 0;
35534 // count unused columns
35537 if ( this.colYs[i] !== 0 ) {
35542 // fit container to columns that have been used
35543 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35546 needsResizeLayout : function()
35548 var previousWidth = this.containerWidth;
35549 this.getContainerWidth();
35550 return previousWidth !== this.containerWidth;
35565 * @class Roo.bootstrap.MasonryBrick
35566 * @extends Roo.bootstrap.Component
35567 * Bootstrap MasonryBrick class
35570 * Create a new MasonryBrick
35571 * @param {Object} config The config object
35574 Roo.bootstrap.MasonryBrick = function(config){
35576 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35578 Roo.bootstrap.MasonryBrick.register(this);
35584 * When a MasonryBrick is clcik
35585 * @param {Roo.bootstrap.MasonryBrick} this
35586 * @param {Roo.EventObject} e
35592 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35595 * @cfg {String} title
35599 * @cfg {String} html
35603 * @cfg {String} bgimage
35607 * @cfg {String} videourl
35611 * @cfg {String} cls
35615 * @cfg {String} href
35619 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35624 * @cfg {String} placetitle (center|bottom)
35629 * @cfg {Boolean} isFitContainer defalut true
35631 isFitContainer : true,
35634 * @cfg {Boolean} preventDefault defalut false
35636 preventDefault : false,
35639 * @cfg {Boolean} inverse defalut false
35641 maskInverse : false,
35643 getAutoCreate : function()
35645 if(!this.isFitContainer){
35646 return this.getSplitAutoCreate();
35649 var cls = 'masonry-brick masonry-brick-full';
35651 if(this.href.length){
35652 cls += ' masonry-brick-link';
35655 if(this.bgimage.length){
35656 cls += ' masonry-brick-image';
35659 if(this.maskInverse){
35660 cls += ' mask-inverse';
35663 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35664 cls += ' enable-mask';
35668 cls += ' masonry-' + this.size + '-brick';
35671 if(this.placetitle.length){
35673 switch (this.placetitle) {
35675 cls += ' masonry-center-title';
35678 cls += ' masonry-bottom-title';
35685 if(!this.html.length && !this.bgimage.length){
35686 cls += ' masonry-center-title';
35689 if(!this.html.length && this.bgimage.length){
35690 cls += ' masonry-bottom-title';
35695 cls += ' ' + this.cls;
35699 tag: (this.href.length) ? 'a' : 'div',
35704 cls: 'masonry-brick-mask'
35708 cls: 'masonry-brick-paragraph',
35714 if(this.href.length){
35715 cfg.href = this.href;
35718 var cn = cfg.cn[1].cn;
35720 if(this.title.length){
35723 cls: 'masonry-brick-title',
35728 if(this.html.length){
35731 cls: 'masonry-brick-text',
35736 if (!this.title.length && !this.html.length) {
35737 cfg.cn[1].cls += ' hide';
35740 if(this.bgimage.length){
35743 cls: 'masonry-brick-image-view',
35748 if(this.videourl.length){
35749 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35750 // youtube support only?
35753 cls: 'masonry-brick-image-view',
35756 allowfullscreen : true
35764 getSplitAutoCreate : function()
35766 var cls = 'masonry-brick masonry-brick-split';
35768 if(this.href.length){
35769 cls += ' masonry-brick-link';
35772 if(this.bgimage.length){
35773 cls += ' masonry-brick-image';
35777 cls += ' masonry-' + this.size + '-brick';
35780 switch (this.placetitle) {
35782 cls += ' masonry-center-title';
35785 cls += ' masonry-bottom-title';
35788 if(!this.bgimage.length){
35789 cls += ' masonry-center-title';
35792 if(this.bgimage.length){
35793 cls += ' masonry-bottom-title';
35799 cls += ' ' + this.cls;
35803 tag: (this.href.length) ? 'a' : 'div',
35808 cls: 'masonry-brick-split-head',
35812 cls: 'masonry-brick-paragraph',
35819 cls: 'masonry-brick-split-body',
35825 if(this.href.length){
35826 cfg.href = this.href;
35829 if(this.title.length){
35830 cfg.cn[0].cn[0].cn.push({
35832 cls: 'masonry-brick-title',
35837 if(this.html.length){
35838 cfg.cn[1].cn.push({
35840 cls: 'masonry-brick-text',
35845 if(this.bgimage.length){
35846 cfg.cn[0].cn.push({
35848 cls: 'masonry-brick-image-view',
35853 if(this.videourl.length){
35854 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35855 // youtube support only?
35856 cfg.cn[0].cn.cn.push({
35858 cls: 'masonry-brick-image-view',
35861 allowfullscreen : true
35868 initEvents: function()
35870 switch (this.size) {
35903 this.el.on('touchstart', this.onTouchStart, this);
35904 this.el.on('touchmove', this.onTouchMove, this);
35905 this.el.on('touchend', this.onTouchEnd, this);
35906 this.el.on('contextmenu', this.onContextMenu, this);
35908 this.el.on('mouseenter' ,this.enter, this);
35909 this.el.on('mouseleave', this.leave, this);
35910 this.el.on('click', this.onClick, this);
35913 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35914 this.parent().bricks.push(this);
35919 onClick: function(e, el)
35921 var time = this.endTimer - this.startTimer;
35922 // Roo.log(e.preventDefault());
35925 e.preventDefault();
35930 if(!this.preventDefault){
35934 e.preventDefault();
35936 if (this.activeClass != '') {
35937 this.selectBrick();
35940 this.fireEvent('click', this, e);
35943 enter: function(e, el)
35945 e.preventDefault();
35947 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35951 if(this.bgimage.length && this.html.length){
35952 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35956 leave: function(e, el)
35958 e.preventDefault();
35960 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35964 if(this.bgimage.length && this.html.length){
35965 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35969 onTouchStart: function(e, el)
35971 // e.preventDefault();
35973 this.touchmoved = false;
35975 if(!this.isFitContainer){
35979 if(!this.bgimage.length || !this.html.length){
35983 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35985 this.timer = new Date().getTime();
35989 onTouchMove: function(e, el)
35991 this.touchmoved = true;
35994 onContextMenu : function(e,el)
35996 e.preventDefault();
35997 e.stopPropagation();
36001 onTouchEnd: function(e, el)
36003 // e.preventDefault();
36005 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36012 if(!this.bgimage.length || !this.html.length){
36014 if(this.href.length){
36015 window.location.href = this.href;
36021 if(!this.isFitContainer){
36025 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36027 window.location.href = this.href;
36030 //selection on single brick only
36031 selectBrick : function() {
36033 if (!this.parentId) {
36037 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36038 var index = m.selectedBrick.indexOf(this.id);
36041 m.selectedBrick.splice(index,1);
36042 this.el.removeClass(this.activeClass);
36046 for(var i = 0; i < m.selectedBrick.length; i++) {
36047 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36048 b.el.removeClass(b.activeClass);
36051 m.selectedBrick = [];
36053 m.selectedBrick.push(this.id);
36054 this.el.addClass(this.activeClass);
36058 isSelected : function(){
36059 return this.el.hasClass(this.activeClass);
36064 Roo.apply(Roo.bootstrap.MasonryBrick, {
36067 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36069 * register a Masonry Brick
36070 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36073 register : function(brick)
36075 //this.groups[brick.id] = brick;
36076 this.groups.add(brick.id, brick);
36079 * fetch a masonry brick based on the masonry brick ID
36080 * @param {string} the masonry brick to add
36081 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36084 get: function(brick_id)
36086 // if (typeof(this.groups[brick_id]) == 'undefined') {
36089 // return this.groups[brick_id] ;
36091 if(this.groups.key(brick_id)) {
36092 return this.groups.key(brick_id);
36110 * @class Roo.bootstrap.Brick
36111 * @extends Roo.bootstrap.Component
36112 * Bootstrap Brick class
36115 * Create a new Brick
36116 * @param {Object} config The config object
36119 Roo.bootstrap.Brick = function(config){
36120 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36126 * When a Brick is click
36127 * @param {Roo.bootstrap.Brick} this
36128 * @param {Roo.EventObject} e
36134 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36137 * @cfg {String} title
36141 * @cfg {String} html
36145 * @cfg {String} bgimage
36149 * @cfg {String} cls
36153 * @cfg {String} href
36157 * @cfg {String} video
36161 * @cfg {Boolean} square
36165 getAutoCreate : function()
36167 var cls = 'roo-brick';
36169 if(this.href.length){
36170 cls += ' roo-brick-link';
36173 if(this.bgimage.length){
36174 cls += ' roo-brick-image';
36177 if(!this.html.length && !this.bgimage.length){
36178 cls += ' roo-brick-center-title';
36181 if(!this.html.length && this.bgimage.length){
36182 cls += ' roo-brick-bottom-title';
36186 cls += ' ' + this.cls;
36190 tag: (this.href.length) ? 'a' : 'div',
36195 cls: 'roo-brick-paragraph',
36201 if(this.href.length){
36202 cfg.href = this.href;
36205 var cn = cfg.cn[0].cn;
36207 if(this.title.length){
36210 cls: 'roo-brick-title',
36215 if(this.html.length){
36218 cls: 'roo-brick-text',
36225 if(this.bgimage.length){
36228 cls: 'roo-brick-image-view',
36236 initEvents: function()
36238 if(this.title.length || this.html.length){
36239 this.el.on('mouseenter' ,this.enter, this);
36240 this.el.on('mouseleave', this.leave, this);
36243 Roo.EventManager.onWindowResize(this.resize, this);
36245 if(this.bgimage.length){
36246 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36247 this.imageEl.on('load', this.onImageLoad, this);
36254 onImageLoad : function()
36259 resize : function()
36261 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36263 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36265 if(this.bgimage.length){
36266 var image = this.el.select('.roo-brick-image-view', true).first();
36268 image.setWidth(paragraph.getWidth());
36271 image.setHeight(paragraph.getWidth());
36274 this.el.setHeight(image.getHeight());
36275 paragraph.setHeight(image.getHeight());
36281 enter: function(e, el)
36283 e.preventDefault();
36285 if(this.bgimage.length){
36286 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36287 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36291 leave: function(e, el)
36293 e.preventDefault();
36295 if(this.bgimage.length){
36296 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36297 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36312 * @class Roo.bootstrap.NumberField
36313 * @extends Roo.bootstrap.Input
36314 * Bootstrap NumberField class
36320 * Create a new NumberField
36321 * @param {Object} config The config object
36324 Roo.bootstrap.NumberField = function(config){
36325 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36328 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36331 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36333 allowDecimals : true,
36335 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36337 decimalSeparator : ".",
36339 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36341 decimalPrecision : 2,
36343 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36345 allowNegative : true,
36348 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36352 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36354 minValue : Number.NEGATIVE_INFINITY,
36356 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36358 maxValue : Number.MAX_VALUE,
36360 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36362 minText : "The minimum value for this field is {0}",
36364 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36366 maxText : "The maximum value for this field is {0}",
36368 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36369 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36371 nanText : "{0} is not a valid number",
36373 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36375 thousandsDelimiter : false,
36377 * @cfg {String} valueAlign alignment of value
36379 valueAlign : "left",
36381 getAutoCreate : function()
36383 var hiddenInput = {
36387 cls: 'hidden-number-input'
36391 hiddenInput.name = this.name;
36396 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36398 this.name = hiddenInput.name;
36400 if(cfg.cn.length > 0) {
36401 cfg.cn.push(hiddenInput);
36408 initEvents : function()
36410 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36412 var allowed = "0123456789";
36414 if(this.allowDecimals){
36415 allowed += this.decimalSeparator;
36418 if(this.allowNegative){
36422 if(this.thousandsDelimiter) {
36426 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36428 var keyPress = function(e){
36430 var k = e.getKey();
36432 var c = e.getCharCode();
36435 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36436 allowed.indexOf(String.fromCharCode(c)) === -1
36442 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36446 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36451 this.el.on("keypress", keyPress, this);
36454 validateValue : function(value)
36457 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36461 var num = this.parseValue(value);
36464 this.markInvalid(String.format(this.nanText, value));
36468 if(num < this.minValue){
36469 this.markInvalid(String.format(this.minText, this.minValue));
36473 if(num > this.maxValue){
36474 this.markInvalid(String.format(this.maxText, this.maxValue));
36481 getValue : function()
36483 var v = this.hiddenEl().getValue();
36485 return this.fixPrecision(this.parseValue(v));
36488 parseValue : function(value)
36490 if(this.thousandsDelimiter) {
36492 r = new RegExp(",", "g");
36493 value = value.replace(r, "");
36496 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36497 return isNaN(value) ? '' : value;
36500 fixPrecision : function(value)
36502 if(this.thousandsDelimiter) {
36504 r = new RegExp(",", "g");
36505 value = value.replace(r, "");
36508 var nan = isNaN(value);
36510 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36511 return nan ? '' : value;
36513 return parseFloat(value).toFixed(this.decimalPrecision);
36516 setValue : function(v)
36518 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36524 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36526 this.inputEl().dom.value = (v == '') ? '' :
36527 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36529 if(!this.allowZero && v === '0') {
36530 this.hiddenEl().dom.value = '';
36531 this.inputEl().dom.value = '';
36538 decimalPrecisionFcn : function(v)
36540 return Math.floor(v);
36543 beforeBlur : function()
36545 var v = this.parseValue(this.getRawValue());
36547 if(v || v === 0 || v === ''){
36552 hiddenEl : function()
36554 return this.el.select('input.hidden-number-input',true).first();
36566 * @class Roo.bootstrap.DocumentSlider
36567 * @extends Roo.bootstrap.Component
36568 * Bootstrap DocumentSlider class
36571 * Create a new DocumentViewer
36572 * @param {Object} config The config object
36575 Roo.bootstrap.DocumentSlider = function(config){
36576 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36583 * Fire after initEvent
36584 * @param {Roo.bootstrap.DocumentSlider} this
36589 * Fire after update
36590 * @param {Roo.bootstrap.DocumentSlider} this
36596 * @param {Roo.bootstrap.DocumentSlider} this
36602 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36608 getAutoCreate : function()
36612 cls : 'roo-document-slider',
36616 cls : 'roo-document-slider-header',
36620 cls : 'roo-document-slider-header-title'
36626 cls : 'roo-document-slider-body',
36630 cls : 'roo-document-slider-prev',
36634 cls : 'fa fa-chevron-left'
36640 cls : 'roo-document-slider-thumb',
36644 cls : 'roo-document-slider-image'
36650 cls : 'roo-document-slider-next',
36654 cls : 'fa fa-chevron-right'
36666 initEvents : function()
36668 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36669 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36671 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36672 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36674 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36675 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36677 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36678 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36680 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36681 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36683 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36684 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36686 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36687 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36689 this.thumbEl.on('click', this.onClick, this);
36691 this.prevIndicator.on('click', this.prev, this);
36693 this.nextIndicator.on('click', this.next, this);
36697 initial : function()
36699 if(this.files.length){
36700 this.indicator = 1;
36704 this.fireEvent('initial', this);
36707 update : function()
36709 this.imageEl.attr('src', this.files[this.indicator - 1]);
36711 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36713 this.prevIndicator.show();
36715 if(this.indicator == 1){
36716 this.prevIndicator.hide();
36719 this.nextIndicator.show();
36721 if(this.indicator == this.files.length){
36722 this.nextIndicator.hide();
36725 this.thumbEl.scrollTo('top');
36727 this.fireEvent('update', this);
36730 onClick : function(e)
36732 e.preventDefault();
36734 this.fireEvent('click', this);
36739 e.preventDefault();
36741 this.indicator = Math.max(1, this.indicator - 1);
36748 e.preventDefault();
36750 this.indicator = Math.min(this.files.length, this.indicator + 1);
36764 * @class Roo.bootstrap.RadioSet
36765 * @extends Roo.bootstrap.Input
36766 * Bootstrap RadioSet class
36767 * @cfg {String} indicatorpos (left|right) default left
36768 * @cfg {Boolean} inline (true|false) inline the element (default true)
36769 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36771 * Create a new RadioSet
36772 * @param {Object} config The config object
36775 Roo.bootstrap.RadioSet = function(config){
36777 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36781 Roo.bootstrap.RadioSet.register(this);
36786 * Fires when the element is checked or unchecked.
36787 * @param {Roo.bootstrap.RadioSet} this This radio
36788 * @param {Roo.bootstrap.Radio} item The checked item
36793 * Fires when the element is click.
36794 * @param {Roo.bootstrap.RadioSet} this This radio set
36795 * @param {Roo.bootstrap.Radio} item The checked item
36796 * @param {Roo.EventObject} e The event object
36803 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36811 indicatorpos : 'left',
36813 getAutoCreate : function()
36817 cls : 'roo-radio-set-label',
36821 html : this.fieldLabel
36825 if (Roo.bootstrap.version == 3) {
36828 if(this.indicatorpos == 'left'){
36831 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36832 tooltip : 'This field is required'
36837 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36838 tooltip : 'This field is required'
36844 cls : 'roo-radio-set-items'
36847 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36849 if (align === 'left' && this.fieldLabel.length) {
36852 cls : "roo-radio-set-right",
36858 if(this.labelWidth > 12){
36859 label.style = "width: " + this.labelWidth + 'px';
36862 if(this.labelWidth < 13 && this.labelmd == 0){
36863 this.labelmd = this.labelWidth;
36866 if(this.labellg > 0){
36867 label.cls += ' col-lg-' + this.labellg;
36868 items.cls += ' col-lg-' + (12 - this.labellg);
36871 if(this.labelmd > 0){
36872 label.cls += ' col-md-' + this.labelmd;
36873 items.cls += ' col-md-' + (12 - this.labelmd);
36876 if(this.labelsm > 0){
36877 label.cls += ' col-sm-' + this.labelsm;
36878 items.cls += ' col-sm-' + (12 - this.labelsm);
36881 if(this.labelxs > 0){
36882 label.cls += ' col-xs-' + this.labelxs;
36883 items.cls += ' col-xs-' + (12 - this.labelxs);
36889 cls : 'roo-radio-set',
36893 cls : 'roo-radio-set-input',
36896 value : this.value ? this.value : ''
36903 if(this.weight.length){
36904 cfg.cls += ' roo-radio-' + this.weight;
36908 cfg.cls += ' roo-radio-set-inline';
36912 ['xs','sm','md','lg'].map(function(size){
36913 if (settings[size]) {
36914 cfg.cls += ' col-' + size + '-' + settings[size];
36922 initEvents : function()
36924 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36925 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36927 if(!this.fieldLabel.length){
36928 this.labelEl.hide();
36931 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36932 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36934 this.indicator = this.indicatorEl();
36936 if(this.indicator){
36937 this.indicator.addClass('invisible');
36940 this.originalValue = this.getValue();
36944 inputEl: function ()
36946 return this.el.select('.roo-radio-set-input', true).first();
36949 getChildContainer : function()
36951 return this.itemsEl;
36954 register : function(item)
36956 this.radioes.push(item);
36960 validate : function()
36962 if(this.getVisibilityEl().hasClass('hidden')){
36968 Roo.each(this.radioes, function(i){
36977 if(this.allowBlank) {
36981 if(this.disabled || valid){
36986 this.markInvalid();
36991 markValid : function()
36993 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36994 this.indicatorEl().removeClass('visible');
36995 this.indicatorEl().addClass('invisible');
36999 if (Roo.bootstrap.version == 3) {
37000 this.el.removeClass([this.invalidClass, this.validClass]);
37001 this.el.addClass(this.validClass);
37003 this.el.removeClass(['is-invalid','is-valid']);
37004 this.el.addClass(['is-valid']);
37006 this.fireEvent('valid', this);
37009 markInvalid : function(msg)
37011 if(this.allowBlank || this.disabled){
37015 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37016 this.indicatorEl().removeClass('invisible');
37017 this.indicatorEl().addClass('visible');
37019 if (Roo.bootstrap.version == 3) {
37020 this.el.removeClass([this.invalidClass, this.validClass]);
37021 this.el.addClass(this.invalidClass);
37023 this.el.removeClass(['is-invalid','is-valid']);
37024 this.el.addClass(['is-invalid']);
37027 this.fireEvent('invalid', this, msg);
37031 setValue : function(v, suppressEvent)
37033 if(this.value === v){
37040 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37043 Roo.each(this.radioes, function(i){
37045 i.el.removeClass('checked');
37048 Roo.each(this.radioes, function(i){
37050 if(i.value === v || i.value.toString() === v.toString()){
37052 i.el.addClass('checked');
37054 if(suppressEvent !== true){
37055 this.fireEvent('check', this, i);
37066 clearInvalid : function(){
37068 if(!this.el || this.preventMark){
37072 this.el.removeClass([this.invalidClass]);
37074 this.fireEvent('valid', this);
37079 Roo.apply(Roo.bootstrap.RadioSet, {
37083 register : function(set)
37085 this.groups[set.name] = set;
37088 get: function(name)
37090 if (typeof(this.groups[name]) == 'undefined') {
37094 return this.groups[name] ;
37100 * Ext JS Library 1.1.1
37101 * Copyright(c) 2006-2007, Ext JS, LLC.
37103 * Originally Released Under LGPL - original licence link has changed is not relivant.
37106 * <script type="text/javascript">
37111 * @class Roo.bootstrap.SplitBar
37112 * @extends Roo.util.Observable
37113 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37117 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37118 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37119 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37120 split.minSize = 100;
37121 split.maxSize = 600;
37122 split.animate = true;
37123 split.on('moved', splitterMoved);
37126 * Create a new SplitBar
37127 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37128 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37129 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37130 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37131 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37132 position of the SplitBar).
37134 Roo.bootstrap.SplitBar = function(cfg){
37139 // dragElement : elm
37140 // resizingElement: el,
37142 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37143 // placement : Roo.bootstrap.SplitBar.LEFT ,
37144 // existingProxy ???
37147 this.el = Roo.get(cfg.dragElement, true);
37148 this.el.dom.unselectable = "on";
37150 this.resizingEl = Roo.get(cfg.resizingElement, true);
37154 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37155 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37158 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37161 * The minimum size of the resizing element. (Defaults to 0)
37167 * The maximum size of the resizing element. (Defaults to 2000)
37170 this.maxSize = 2000;
37173 * Whether to animate the transition to the new size
37176 this.animate = false;
37179 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37182 this.useShim = false;
37187 if(!cfg.existingProxy){
37189 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37191 this.proxy = Roo.get(cfg.existingProxy).dom;
37194 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37197 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37200 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37203 this.dragSpecs = {};
37206 * @private The adapter to use to positon and resize elements
37208 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37209 this.adapter.init(this);
37211 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37213 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37214 this.el.addClass("roo-splitbar-h");
37217 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37218 this.el.addClass("roo-splitbar-v");
37224 * Fires when the splitter is moved (alias for {@link #event-moved})
37225 * @param {Roo.bootstrap.SplitBar} this
37226 * @param {Number} newSize the new width or height
37231 * Fires when the splitter is moved
37232 * @param {Roo.bootstrap.SplitBar} this
37233 * @param {Number} newSize the new width or height
37237 * @event beforeresize
37238 * Fires before the splitter is dragged
37239 * @param {Roo.bootstrap.SplitBar} this
37241 "beforeresize" : true,
37243 "beforeapply" : true
37246 Roo.util.Observable.call(this);
37249 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37250 onStartProxyDrag : function(x, y){
37251 this.fireEvent("beforeresize", this);
37253 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37255 o.enableDisplayMode("block");
37256 // all splitbars share the same overlay
37257 Roo.bootstrap.SplitBar.prototype.overlay = o;
37259 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37260 this.overlay.show();
37261 Roo.get(this.proxy).setDisplayed("block");
37262 var size = this.adapter.getElementSize(this);
37263 this.activeMinSize = this.getMinimumSize();;
37264 this.activeMaxSize = this.getMaximumSize();;
37265 var c1 = size - this.activeMinSize;
37266 var c2 = Math.max(this.activeMaxSize - size, 0);
37267 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37268 this.dd.resetConstraints();
37269 this.dd.setXConstraint(
37270 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37271 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37273 this.dd.setYConstraint(0, 0);
37275 this.dd.resetConstraints();
37276 this.dd.setXConstraint(0, 0);
37277 this.dd.setYConstraint(
37278 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37279 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37282 this.dragSpecs.startSize = size;
37283 this.dragSpecs.startPoint = [x, y];
37284 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37288 * @private Called after the drag operation by the DDProxy
37290 onEndProxyDrag : function(e){
37291 Roo.get(this.proxy).setDisplayed(false);
37292 var endPoint = Roo.lib.Event.getXY(e);
37294 this.overlay.hide();
37297 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37298 newSize = this.dragSpecs.startSize +
37299 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37300 endPoint[0] - this.dragSpecs.startPoint[0] :
37301 this.dragSpecs.startPoint[0] - endPoint[0]
37304 newSize = this.dragSpecs.startSize +
37305 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37306 endPoint[1] - this.dragSpecs.startPoint[1] :
37307 this.dragSpecs.startPoint[1] - endPoint[1]
37310 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37311 if(newSize != this.dragSpecs.startSize){
37312 if(this.fireEvent('beforeapply', this, newSize) !== false){
37313 this.adapter.setElementSize(this, newSize);
37314 this.fireEvent("moved", this, newSize);
37315 this.fireEvent("resize", this, newSize);
37321 * Get the adapter this SplitBar uses
37322 * @return The adapter object
37324 getAdapter : function(){
37325 return this.adapter;
37329 * Set the adapter this SplitBar uses
37330 * @param {Object} adapter A SplitBar adapter object
37332 setAdapter : function(adapter){
37333 this.adapter = adapter;
37334 this.adapter.init(this);
37338 * Gets the minimum size for the resizing element
37339 * @return {Number} The minimum size
37341 getMinimumSize : function(){
37342 return this.minSize;
37346 * Sets the minimum size for the resizing element
37347 * @param {Number} minSize The minimum size
37349 setMinimumSize : function(minSize){
37350 this.minSize = minSize;
37354 * Gets the maximum size for the resizing element
37355 * @return {Number} The maximum size
37357 getMaximumSize : function(){
37358 return this.maxSize;
37362 * Sets the maximum size for the resizing element
37363 * @param {Number} maxSize The maximum size
37365 setMaximumSize : function(maxSize){
37366 this.maxSize = maxSize;
37370 * Sets the initialize size for the resizing element
37371 * @param {Number} size The initial size
37373 setCurrentSize : function(size){
37374 var oldAnimate = this.animate;
37375 this.animate = false;
37376 this.adapter.setElementSize(this, size);
37377 this.animate = oldAnimate;
37381 * Destroy this splitbar.
37382 * @param {Boolean} removeEl True to remove the element
37384 destroy : function(removeEl){
37386 this.shim.remove();
37389 this.proxy.parentNode.removeChild(this.proxy);
37397 * @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.
37399 Roo.bootstrap.SplitBar.createProxy = function(dir){
37400 var proxy = new Roo.Element(document.createElement("div"));
37401 proxy.unselectable();
37402 var cls = 'roo-splitbar-proxy';
37403 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37404 document.body.appendChild(proxy.dom);
37409 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37410 * Default Adapter. It assumes the splitter and resizing element are not positioned
37411 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37413 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37416 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37417 // do nothing for now
37418 init : function(s){
37422 * Called before drag operations to get the current size of the resizing element.
37423 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37425 getElementSize : function(s){
37426 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37427 return s.resizingEl.getWidth();
37429 return s.resizingEl.getHeight();
37434 * Called after drag operations to set the size of the resizing element.
37435 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37436 * @param {Number} newSize The new size to set
37437 * @param {Function} onComplete A function to be invoked when resizing is complete
37439 setElementSize : function(s, newSize, onComplete){
37440 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37442 s.resizingEl.setWidth(newSize);
37444 onComplete(s, newSize);
37447 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37452 s.resizingEl.setHeight(newSize);
37454 onComplete(s, newSize);
37457 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37464 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37465 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37466 * Adapter that moves the splitter element to align with the resized sizing element.
37467 * Used with an absolute positioned SplitBar.
37468 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37469 * document.body, make sure you assign an id to the body element.
37471 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37472 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37473 this.container = Roo.get(container);
37476 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37477 init : function(s){
37478 this.basic.init(s);
37481 getElementSize : function(s){
37482 return this.basic.getElementSize(s);
37485 setElementSize : function(s, newSize, onComplete){
37486 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37489 moveSplitter : function(s){
37490 var yes = Roo.bootstrap.SplitBar;
37491 switch(s.placement){
37493 s.el.setX(s.resizingEl.getRight());
37496 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37499 s.el.setY(s.resizingEl.getBottom());
37502 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37509 * Orientation constant - Create a vertical SplitBar
37513 Roo.bootstrap.SplitBar.VERTICAL = 1;
37516 * Orientation constant - Create a horizontal SplitBar
37520 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37523 * Placement constant - The resizing element is to the left of the splitter element
37527 Roo.bootstrap.SplitBar.LEFT = 1;
37530 * Placement constant - The resizing element is to the right of the splitter element
37534 Roo.bootstrap.SplitBar.RIGHT = 2;
37537 * Placement constant - The resizing element is positioned above the splitter element
37541 Roo.bootstrap.SplitBar.TOP = 3;
37544 * Placement constant - The resizing element is positioned under splitter element
37548 Roo.bootstrap.SplitBar.BOTTOM = 4;
37549 Roo.namespace("Roo.bootstrap.layout");/*
37551 * Ext JS Library 1.1.1
37552 * Copyright(c) 2006-2007, Ext JS, LLC.
37554 * Originally Released Under LGPL - original licence link has changed is not relivant.
37557 * <script type="text/javascript">
37561 * @class Roo.bootstrap.layout.Manager
37562 * @extends Roo.bootstrap.Component
37563 * Base class for layout managers.
37565 Roo.bootstrap.layout.Manager = function(config)
37567 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37573 /** false to disable window resize monitoring @type Boolean */
37574 this.monitorWindowResize = true;
37579 * Fires when a layout is performed.
37580 * @param {Roo.LayoutManager} this
37584 * @event regionresized
37585 * Fires when the user resizes a region.
37586 * @param {Roo.LayoutRegion} region The resized region
37587 * @param {Number} newSize The new size (width for east/west, height for north/south)
37589 "regionresized" : true,
37591 * @event regioncollapsed
37592 * Fires when a region is collapsed.
37593 * @param {Roo.LayoutRegion} region The collapsed region
37595 "regioncollapsed" : true,
37597 * @event regionexpanded
37598 * Fires when a region is expanded.
37599 * @param {Roo.LayoutRegion} region The expanded region
37601 "regionexpanded" : true
37603 this.updating = false;
37606 this.el = Roo.get(config.el);
37612 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37617 monitorWindowResize : true,
37623 onRender : function(ct, position)
37626 this.el = Roo.get(ct);
37629 //this.fireEvent('render',this);
37633 initEvents: function()
37637 // ie scrollbar fix
37638 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37639 document.body.scroll = "no";
37640 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37641 this.el.position('relative');
37643 this.id = this.el.id;
37644 this.el.addClass("roo-layout-container");
37645 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37646 if(this.el.dom != document.body ) {
37647 this.el.on('resize', this.layout,this);
37648 this.el.on('show', this.layout,this);
37654 * Returns true if this layout is currently being updated
37655 * @return {Boolean}
37657 isUpdating : function(){
37658 return this.updating;
37662 * Suspend the LayoutManager from doing auto-layouts while
37663 * making multiple add or remove calls
37665 beginUpdate : function(){
37666 this.updating = true;
37670 * Restore auto-layouts and optionally disable the manager from performing a layout
37671 * @param {Boolean} noLayout true to disable a layout update
37673 endUpdate : function(noLayout){
37674 this.updating = false;
37680 layout: function(){
37684 onRegionResized : function(region, newSize){
37685 this.fireEvent("regionresized", region, newSize);
37689 onRegionCollapsed : function(region){
37690 this.fireEvent("regioncollapsed", region);
37693 onRegionExpanded : function(region){
37694 this.fireEvent("regionexpanded", region);
37698 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37699 * performs box-model adjustments.
37700 * @return {Object} The size as an object {width: (the width), height: (the height)}
37702 getViewSize : function()
37705 if(this.el.dom != document.body){
37706 size = this.el.getSize();
37708 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37710 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37711 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37716 * Returns the Element this layout is bound to.
37717 * @return {Roo.Element}
37719 getEl : function(){
37724 * Returns the specified region.
37725 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37726 * @return {Roo.LayoutRegion}
37728 getRegion : function(target){
37729 return this.regions[target.toLowerCase()];
37732 onWindowResize : function(){
37733 if(this.monitorWindowResize){
37740 * Ext JS Library 1.1.1
37741 * Copyright(c) 2006-2007, Ext JS, LLC.
37743 * Originally Released Under LGPL - original licence link has changed is not relivant.
37746 * <script type="text/javascript">
37749 * @class Roo.bootstrap.layout.Border
37750 * @extends Roo.bootstrap.layout.Manager
37751 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37752 * please see: examples/bootstrap/nested.html<br><br>
37754 <b>The container the layout is rendered into can be either the body element or any other element.
37755 If it is not the body element, the container needs to either be an absolute positioned element,
37756 or you will need to add "position:relative" to the css of the container. You will also need to specify
37757 the container size if it is not the body element.</b>
37760 * Create a new Border
37761 * @param {Object} config Configuration options
37763 Roo.bootstrap.layout.Border = function(config){
37764 config = config || {};
37765 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37769 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37770 if(config[region]){
37771 config[region].region = region;
37772 this.addRegion(config[region]);
37778 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37780 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37782 parent : false, // this might point to a 'nest' or a ???
37785 * Creates and adds a new region if it doesn't already exist.
37786 * @param {String} target The target region key (north, south, east, west or center).
37787 * @param {Object} config The regions config object
37788 * @return {BorderLayoutRegion} The new region
37790 addRegion : function(config)
37792 if(!this.regions[config.region]){
37793 var r = this.factory(config);
37794 this.bindRegion(r);
37796 return this.regions[config.region];
37800 bindRegion : function(r){
37801 this.regions[r.config.region] = r;
37803 r.on("visibilitychange", this.layout, this);
37804 r.on("paneladded", this.layout, this);
37805 r.on("panelremoved", this.layout, this);
37806 r.on("invalidated", this.layout, this);
37807 r.on("resized", this.onRegionResized, this);
37808 r.on("collapsed", this.onRegionCollapsed, this);
37809 r.on("expanded", this.onRegionExpanded, this);
37813 * Performs a layout update.
37815 layout : function()
37817 if(this.updating) {
37821 // render all the rebions if they have not been done alreayd?
37822 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37823 if(this.regions[region] && !this.regions[region].bodyEl){
37824 this.regions[region].onRender(this.el)
37828 var size = this.getViewSize();
37829 var w = size.width;
37830 var h = size.height;
37835 //var x = 0, y = 0;
37837 var rs = this.regions;
37838 var north = rs["north"];
37839 var south = rs["south"];
37840 var west = rs["west"];
37841 var east = rs["east"];
37842 var center = rs["center"];
37843 //if(this.hideOnLayout){ // not supported anymore
37844 //c.el.setStyle("display", "none");
37846 if(north && north.isVisible()){
37847 var b = north.getBox();
37848 var m = north.getMargins();
37849 b.width = w - (m.left+m.right);
37852 centerY = b.height + b.y + m.bottom;
37853 centerH -= centerY;
37854 north.updateBox(this.safeBox(b));
37856 if(south && south.isVisible()){
37857 var b = south.getBox();
37858 var m = south.getMargins();
37859 b.width = w - (m.left+m.right);
37861 var totalHeight = (b.height + m.top + m.bottom);
37862 b.y = h - totalHeight + m.top;
37863 centerH -= totalHeight;
37864 south.updateBox(this.safeBox(b));
37866 if(west && west.isVisible()){
37867 var b = west.getBox();
37868 var m = west.getMargins();
37869 b.height = centerH - (m.top+m.bottom);
37871 b.y = centerY + m.top;
37872 var totalWidth = (b.width + m.left + m.right);
37873 centerX += totalWidth;
37874 centerW -= totalWidth;
37875 west.updateBox(this.safeBox(b));
37877 if(east && east.isVisible()){
37878 var b = east.getBox();
37879 var m = east.getMargins();
37880 b.height = centerH - (m.top+m.bottom);
37881 var totalWidth = (b.width + m.left + m.right);
37882 b.x = w - totalWidth + m.left;
37883 b.y = centerY + m.top;
37884 centerW -= totalWidth;
37885 east.updateBox(this.safeBox(b));
37888 var m = center.getMargins();
37890 x: centerX + m.left,
37891 y: centerY + m.top,
37892 width: centerW - (m.left+m.right),
37893 height: centerH - (m.top+m.bottom)
37895 //if(this.hideOnLayout){
37896 //center.el.setStyle("display", "block");
37898 center.updateBox(this.safeBox(centerBox));
37901 this.fireEvent("layout", this);
37905 safeBox : function(box){
37906 box.width = Math.max(0, box.width);
37907 box.height = Math.max(0, box.height);
37912 * Adds a ContentPanel (or subclass) to this layout.
37913 * @param {String} target The target region key (north, south, east, west or center).
37914 * @param {Roo.ContentPanel} panel The panel to add
37915 * @return {Roo.ContentPanel} The added panel
37917 add : function(target, panel){
37919 target = target.toLowerCase();
37920 return this.regions[target].add(panel);
37924 * Remove a ContentPanel (or subclass) to this layout.
37925 * @param {String} target The target region key (north, south, east, west or center).
37926 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37927 * @return {Roo.ContentPanel} The removed panel
37929 remove : function(target, panel){
37930 target = target.toLowerCase();
37931 return this.regions[target].remove(panel);
37935 * Searches all regions for a panel with the specified id
37936 * @param {String} panelId
37937 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37939 findPanel : function(panelId){
37940 var rs = this.regions;
37941 for(var target in rs){
37942 if(typeof rs[target] != "function"){
37943 var p = rs[target].getPanel(panelId);
37953 * Searches all regions for a panel with the specified id and activates (shows) it.
37954 * @param {String/ContentPanel} panelId The panels id or the panel itself
37955 * @return {Roo.ContentPanel} The shown panel or null
37957 showPanel : function(panelId) {
37958 var rs = this.regions;
37959 for(var target in rs){
37960 var r = rs[target];
37961 if(typeof r != "function"){
37962 if(r.hasPanel(panelId)){
37963 return r.showPanel(panelId);
37971 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37972 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37975 restoreState : function(provider){
37977 provider = Roo.state.Manager;
37979 var sm = new Roo.LayoutStateManager();
37980 sm.init(this, provider);
37986 * Adds a xtype elements to the layout.
37990 xtype : 'ContentPanel',
37997 xtype : 'NestedLayoutPanel',
38003 items : [ ... list of content panels or nested layout panels.. ]
38007 * @param {Object} cfg Xtype definition of item to add.
38009 addxtype : function(cfg)
38011 // basically accepts a pannel...
38012 // can accept a layout region..!?!?
38013 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38016 // theory? children can only be panels??
38018 //if (!cfg.xtype.match(/Panel$/)) {
38023 if (typeof(cfg.region) == 'undefined') {
38024 Roo.log("Failed to add Panel, region was not set");
38028 var region = cfg.region;
38034 xitems = cfg.items;
38039 if ( region == 'center') {
38040 Roo.log("Center: " + cfg.title);
38046 case 'Content': // ContentPanel (el, cfg)
38047 case 'Scroll': // ContentPanel (el, cfg)
38049 cfg.autoCreate = cfg.autoCreate || true;
38050 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38052 // var el = this.el.createChild();
38053 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38056 this.add(region, ret);
38060 case 'TreePanel': // our new panel!
38061 cfg.el = this.el.createChild();
38062 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38063 this.add(region, ret);
38068 // create a new Layout (which is a Border Layout...
38070 var clayout = cfg.layout;
38071 clayout.el = this.el.createChild();
38072 clayout.items = clayout.items || [];
38076 // replace this exitems with the clayout ones..
38077 xitems = clayout.items;
38079 // force background off if it's in center...
38080 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38081 cfg.background = false;
38083 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38086 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38087 //console.log('adding nested layout panel ' + cfg.toSource());
38088 this.add(region, ret);
38089 nb = {}; /// find first...
38094 // needs grid and region
38096 //var el = this.getRegion(region).el.createChild();
38098 *var el = this.el.createChild();
38099 // create the grid first...
38100 cfg.grid.container = el;
38101 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38104 if (region == 'center' && this.active ) {
38105 cfg.background = false;
38108 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38110 this.add(region, ret);
38112 if (cfg.background) {
38113 // render grid on panel activation (if panel background)
38114 ret.on('activate', function(gp) {
38115 if (!gp.grid.rendered) {
38116 // gp.grid.render(el);
38120 // cfg.grid.render(el);
38126 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38127 // it was the old xcomponent building that caused this before.
38128 // espeically if border is the top element in the tree.
38138 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38140 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38141 this.add(region, ret);
38145 throw "Can not add '" + cfg.xtype + "' to Border";
38151 this.beginUpdate();
38155 Roo.each(xitems, function(i) {
38156 region = nb && i.region ? i.region : false;
38158 var add = ret.addxtype(i);
38161 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38162 if (!i.background) {
38163 abn[region] = nb[region] ;
38170 // make the last non-background panel active..
38171 //if (nb) { Roo.log(abn); }
38174 for(var r in abn) {
38175 region = this.getRegion(r);
38177 // tried using nb[r], but it does not work..
38179 region.showPanel(abn[r]);
38190 factory : function(cfg)
38193 var validRegions = Roo.bootstrap.layout.Border.regions;
38195 var target = cfg.region;
38198 var r = Roo.bootstrap.layout;
38202 return new r.North(cfg);
38204 return new r.South(cfg);
38206 return new r.East(cfg);
38208 return new r.West(cfg);
38210 return new r.Center(cfg);
38212 throw 'Layout region "'+target+'" not supported.';
38219 * Ext JS Library 1.1.1
38220 * Copyright(c) 2006-2007, Ext JS, LLC.
38222 * Originally Released Under LGPL - original licence link has changed is not relivant.
38225 * <script type="text/javascript">
38229 * @class Roo.bootstrap.layout.Basic
38230 * @extends Roo.util.Observable
38231 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38232 * and does not have a titlebar, tabs or any other features. All it does is size and position
38233 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38234 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38235 * @cfg {string} region the region that it inhabits..
38236 * @cfg {bool} skipConfig skip config?
38240 Roo.bootstrap.layout.Basic = function(config){
38242 this.mgr = config.mgr;
38244 this.position = config.region;
38246 var skipConfig = config.skipConfig;
38250 * @scope Roo.BasicLayoutRegion
38254 * @event beforeremove
38255 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38256 * @param {Roo.LayoutRegion} this
38257 * @param {Roo.ContentPanel} panel The panel
38258 * @param {Object} e The cancel event object
38260 "beforeremove" : true,
38262 * @event invalidated
38263 * Fires when the layout for this region is changed.
38264 * @param {Roo.LayoutRegion} this
38266 "invalidated" : true,
38268 * @event visibilitychange
38269 * Fires when this region is shown or hidden
38270 * @param {Roo.LayoutRegion} this
38271 * @param {Boolean} visibility true or false
38273 "visibilitychange" : true,
38275 * @event paneladded
38276 * Fires when a panel is added.
38277 * @param {Roo.LayoutRegion} this
38278 * @param {Roo.ContentPanel} panel The panel
38280 "paneladded" : true,
38282 * @event panelremoved
38283 * Fires when a panel is removed.
38284 * @param {Roo.LayoutRegion} this
38285 * @param {Roo.ContentPanel} panel The panel
38287 "panelremoved" : true,
38289 * @event beforecollapse
38290 * Fires when this region before collapse.
38291 * @param {Roo.LayoutRegion} this
38293 "beforecollapse" : true,
38296 * Fires when this region is collapsed.
38297 * @param {Roo.LayoutRegion} this
38299 "collapsed" : true,
38302 * Fires when this region is expanded.
38303 * @param {Roo.LayoutRegion} this
38308 * Fires when this region is slid into view.
38309 * @param {Roo.LayoutRegion} this
38311 "slideshow" : true,
38314 * Fires when this region slides out of view.
38315 * @param {Roo.LayoutRegion} this
38317 "slidehide" : true,
38319 * @event panelactivated
38320 * Fires when a panel is activated.
38321 * @param {Roo.LayoutRegion} this
38322 * @param {Roo.ContentPanel} panel The activated panel
38324 "panelactivated" : true,
38327 * Fires when the user resizes this region.
38328 * @param {Roo.LayoutRegion} this
38329 * @param {Number} newSize The new size (width for east/west, height for north/south)
38333 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38334 this.panels = new Roo.util.MixedCollection();
38335 this.panels.getKey = this.getPanelId.createDelegate(this);
38337 this.activePanel = null;
38338 // ensure listeners are added...
38340 if (config.listeners || config.events) {
38341 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38342 listeners : config.listeners || {},
38343 events : config.events || {}
38347 if(skipConfig !== true){
38348 this.applyConfig(config);
38352 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38354 getPanelId : function(p){
38358 applyConfig : function(config){
38359 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38360 this.config = config;
38365 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38366 * the width, for horizontal (north, south) the height.
38367 * @param {Number} newSize The new width or height
38369 resizeTo : function(newSize){
38370 var el = this.el ? this.el :
38371 (this.activePanel ? this.activePanel.getEl() : null);
38373 switch(this.position){
38376 el.setWidth(newSize);
38377 this.fireEvent("resized", this, newSize);
38381 el.setHeight(newSize);
38382 this.fireEvent("resized", this, newSize);
38388 getBox : function(){
38389 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38392 getMargins : function(){
38393 return this.margins;
38396 updateBox : function(box){
38398 var el = this.activePanel.getEl();
38399 el.dom.style.left = box.x + "px";
38400 el.dom.style.top = box.y + "px";
38401 this.activePanel.setSize(box.width, box.height);
38405 * Returns the container element for this region.
38406 * @return {Roo.Element}
38408 getEl : function(){
38409 return this.activePanel;
38413 * Returns true if this region is currently visible.
38414 * @return {Boolean}
38416 isVisible : function(){
38417 return this.activePanel ? true : false;
38420 setActivePanel : function(panel){
38421 panel = this.getPanel(panel);
38422 if(this.activePanel && this.activePanel != panel){
38423 this.activePanel.setActiveState(false);
38424 this.activePanel.getEl().setLeftTop(-10000,-10000);
38426 this.activePanel = panel;
38427 panel.setActiveState(true);
38429 panel.setSize(this.box.width, this.box.height);
38431 this.fireEvent("panelactivated", this, panel);
38432 this.fireEvent("invalidated");
38436 * Show the specified panel.
38437 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38438 * @return {Roo.ContentPanel} The shown panel or null
38440 showPanel : function(panel){
38441 panel = this.getPanel(panel);
38443 this.setActivePanel(panel);
38449 * Get the active panel for this region.
38450 * @return {Roo.ContentPanel} The active panel or null
38452 getActivePanel : function(){
38453 return this.activePanel;
38457 * Add the passed ContentPanel(s)
38458 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38459 * @return {Roo.ContentPanel} The panel added (if only one was added)
38461 add : function(panel){
38462 if(arguments.length > 1){
38463 for(var i = 0, len = arguments.length; i < len; i++) {
38464 this.add(arguments[i]);
38468 if(this.hasPanel(panel)){
38469 this.showPanel(panel);
38472 var el = panel.getEl();
38473 if(el.dom.parentNode != this.mgr.el.dom){
38474 this.mgr.el.dom.appendChild(el.dom);
38476 if(panel.setRegion){
38477 panel.setRegion(this);
38479 this.panels.add(panel);
38480 el.setStyle("position", "absolute");
38481 if(!panel.background){
38482 this.setActivePanel(panel);
38483 if(this.config.initialSize && this.panels.getCount()==1){
38484 this.resizeTo(this.config.initialSize);
38487 this.fireEvent("paneladded", this, panel);
38492 * Returns true if the panel is in this region.
38493 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38494 * @return {Boolean}
38496 hasPanel : function(panel){
38497 if(typeof panel == "object"){ // must be panel obj
38498 panel = panel.getId();
38500 return this.getPanel(panel) ? true : false;
38504 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38505 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38506 * @param {Boolean} preservePanel Overrides the config preservePanel option
38507 * @return {Roo.ContentPanel} The panel that was removed
38509 remove : function(panel, preservePanel){
38510 panel = this.getPanel(panel);
38515 this.fireEvent("beforeremove", this, panel, e);
38516 if(e.cancel === true){
38519 var panelId = panel.getId();
38520 this.panels.removeKey(panelId);
38525 * Returns the panel specified or null if it's not in this region.
38526 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38527 * @return {Roo.ContentPanel}
38529 getPanel : function(id){
38530 if(typeof id == "object"){ // must be panel obj
38533 return this.panels.get(id);
38537 * Returns this regions position (north/south/east/west/center).
38540 getPosition: function(){
38541 return this.position;
38545 * Ext JS Library 1.1.1
38546 * Copyright(c) 2006-2007, Ext JS, LLC.
38548 * Originally Released Under LGPL - original licence link has changed is not relivant.
38551 * <script type="text/javascript">
38555 * @class Roo.bootstrap.layout.Region
38556 * @extends Roo.bootstrap.layout.Basic
38557 * This class represents a region in a layout manager.
38559 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38560 * @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})
38561 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38562 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38563 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38564 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38565 * @cfg {String} title The title for the region (overrides panel titles)
38566 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38567 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38568 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38569 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38570 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38571 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38572 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38573 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38574 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38575 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38577 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38578 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38579 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38580 * @cfg {Number} width For East/West panels
38581 * @cfg {Number} height For North/South panels
38582 * @cfg {Boolean} split To show the splitter
38583 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38585 * @cfg {string} cls Extra CSS classes to add to region
38587 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38588 * @cfg {string} region the region that it inhabits..
38591 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38592 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38594 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38595 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38596 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38598 Roo.bootstrap.layout.Region = function(config)
38600 this.applyConfig(config);
38602 var mgr = config.mgr;
38603 var pos = config.region;
38604 config.skipConfig = true;
38605 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38608 this.onRender(mgr.el);
38611 this.visible = true;
38612 this.collapsed = false;
38613 this.unrendered_panels = [];
38616 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38618 position: '', // set by wrapper (eg. north/south etc..)
38619 unrendered_panels : null, // unrendered panels.
38621 tabPosition : false,
38623 mgr: false, // points to 'Border'
38626 createBody : function(){
38627 /** This region's body element
38628 * @type Roo.Element */
38629 this.bodyEl = this.el.createChild({
38631 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38635 onRender: function(ctr, pos)
38637 var dh = Roo.DomHelper;
38638 /** This region's container element
38639 * @type Roo.Element */
38640 this.el = dh.append(ctr.dom, {
38642 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38644 /** This region's title element
38645 * @type Roo.Element */
38647 this.titleEl = dh.append(this.el.dom, {
38649 unselectable: "on",
38650 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38652 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38653 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38657 this.titleEl.enableDisplayMode();
38658 /** This region's title text element
38659 * @type HTMLElement */
38660 this.titleTextEl = this.titleEl.dom.firstChild;
38661 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38663 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38664 this.closeBtn.enableDisplayMode();
38665 this.closeBtn.on("click", this.closeClicked, this);
38666 this.closeBtn.hide();
38668 this.createBody(this.config);
38669 if(this.config.hideWhenEmpty){
38671 this.on("paneladded", this.validateVisibility, this);
38672 this.on("panelremoved", this.validateVisibility, this);
38674 if(this.autoScroll){
38675 this.bodyEl.setStyle("overflow", "auto");
38677 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38679 //if(c.titlebar !== false){
38680 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38681 this.titleEl.hide();
38683 this.titleEl.show();
38684 if(this.config.title){
38685 this.titleTextEl.innerHTML = this.config.title;
38689 if(this.config.collapsed){
38690 this.collapse(true);
38692 if(this.config.hidden){
38696 if (this.unrendered_panels && this.unrendered_panels.length) {
38697 for (var i =0;i< this.unrendered_panels.length; i++) {
38698 this.add(this.unrendered_panels[i]);
38700 this.unrendered_panels = null;
38706 applyConfig : function(c)
38709 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38710 var dh = Roo.DomHelper;
38711 if(c.titlebar !== false){
38712 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38713 this.collapseBtn.on("click", this.collapse, this);
38714 this.collapseBtn.enableDisplayMode();
38716 if(c.showPin === true || this.showPin){
38717 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38718 this.stickBtn.enableDisplayMode();
38719 this.stickBtn.on("click", this.expand, this);
38720 this.stickBtn.hide();
38725 /** This region's collapsed element
38726 * @type Roo.Element */
38729 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38730 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38733 if(c.floatable !== false){
38734 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38735 this.collapsedEl.on("click", this.collapseClick, this);
38738 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38739 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38740 id: "message", unselectable: "on", style:{"float":"left"}});
38741 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38743 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38744 this.expandBtn.on("click", this.expand, this);
38748 if(this.collapseBtn){
38749 this.collapseBtn.setVisible(c.collapsible == true);
38752 this.cmargins = c.cmargins || this.cmargins ||
38753 (this.position == "west" || this.position == "east" ?
38754 {top: 0, left: 2, right:2, bottom: 0} :
38755 {top: 2, left: 0, right:0, bottom: 2});
38757 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38760 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38762 this.autoScroll = c.autoScroll || false;
38767 this.duration = c.duration || .30;
38768 this.slideDuration = c.slideDuration || .45;
38773 * Returns true if this region is currently visible.
38774 * @return {Boolean}
38776 isVisible : function(){
38777 return this.visible;
38781 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38782 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38784 //setCollapsedTitle : function(title){
38785 // title = title || " ";
38786 // if(this.collapsedTitleTextEl){
38787 // this.collapsedTitleTextEl.innerHTML = title;
38791 getBox : function(){
38793 // if(!this.collapsed){
38794 b = this.el.getBox(false, true);
38796 // b = this.collapsedEl.getBox(false, true);
38801 getMargins : function(){
38802 return this.margins;
38803 //return this.collapsed ? this.cmargins : this.margins;
38806 highlight : function(){
38807 this.el.addClass("x-layout-panel-dragover");
38810 unhighlight : function(){
38811 this.el.removeClass("x-layout-panel-dragover");
38814 updateBox : function(box)
38816 if (!this.bodyEl) {
38817 return; // not rendered yet..
38821 if(!this.collapsed){
38822 this.el.dom.style.left = box.x + "px";
38823 this.el.dom.style.top = box.y + "px";
38824 this.updateBody(box.width, box.height);
38826 this.collapsedEl.dom.style.left = box.x + "px";
38827 this.collapsedEl.dom.style.top = box.y + "px";
38828 this.collapsedEl.setSize(box.width, box.height);
38831 this.tabs.autoSizeTabs();
38835 updateBody : function(w, h)
38838 this.el.setWidth(w);
38839 w -= this.el.getBorderWidth("rl");
38840 if(this.config.adjustments){
38841 w += this.config.adjustments[0];
38844 if(h !== null && h > 0){
38845 this.el.setHeight(h);
38846 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38847 h -= this.el.getBorderWidth("tb");
38848 if(this.config.adjustments){
38849 h += this.config.adjustments[1];
38851 this.bodyEl.setHeight(h);
38853 h = this.tabs.syncHeight(h);
38856 if(this.panelSize){
38857 w = w !== null ? w : this.panelSize.width;
38858 h = h !== null ? h : this.panelSize.height;
38860 if(this.activePanel){
38861 var el = this.activePanel.getEl();
38862 w = w !== null ? w : el.getWidth();
38863 h = h !== null ? h : el.getHeight();
38864 this.panelSize = {width: w, height: h};
38865 this.activePanel.setSize(w, h);
38867 if(Roo.isIE && this.tabs){
38868 this.tabs.el.repaint();
38873 * Returns the container element for this region.
38874 * @return {Roo.Element}
38876 getEl : function(){
38881 * Hides this region.
38884 //if(!this.collapsed){
38885 this.el.dom.style.left = "-2000px";
38888 // this.collapsedEl.dom.style.left = "-2000px";
38889 // this.collapsedEl.hide();
38891 this.visible = false;
38892 this.fireEvent("visibilitychange", this, false);
38896 * Shows this region if it was previously hidden.
38899 //if(!this.collapsed){
38902 // this.collapsedEl.show();
38904 this.visible = true;
38905 this.fireEvent("visibilitychange", this, true);
38908 closeClicked : function(){
38909 if(this.activePanel){
38910 this.remove(this.activePanel);
38914 collapseClick : function(e){
38916 e.stopPropagation();
38919 e.stopPropagation();
38925 * Collapses this region.
38926 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38929 collapse : function(skipAnim, skipCheck = false){
38930 if(this.collapsed) {
38934 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38936 this.collapsed = true;
38938 this.split.el.hide();
38940 if(this.config.animate && skipAnim !== true){
38941 this.fireEvent("invalidated", this);
38942 this.animateCollapse();
38944 this.el.setLocation(-20000,-20000);
38946 this.collapsedEl.show();
38947 this.fireEvent("collapsed", this);
38948 this.fireEvent("invalidated", this);
38954 animateCollapse : function(){
38959 * Expands this region if it was previously collapsed.
38960 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38961 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38964 expand : function(e, skipAnim){
38966 e.stopPropagation();
38968 if(!this.collapsed || this.el.hasActiveFx()) {
38972 this.afterSlideIn();
38975 this.collapsed = false;
38976 if(this.config.animate && skipAnim !== true){
38977 this.animateExpand();
38981 this.split.el.show();
38983 this.collapsedEl.setLocation(-2000,-2000);
38984 this.collapsedEl.hide();
38985 this.fireEvent("invalidated", this);
38986 this.fireEvent("expanded", this);
38990 animateExpand : function(){
38994 initTabs : function()
38996 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38998 var ts = new Roo.bootstrap.panel.Tabs({
38999 el: this.bodyEl.dom,
39001 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39002 disableTooltips: this.config.disableTabTips,
39003 toolbar : this.config.toolbar
39006 if(this.config.hideTabs){
39007 ts.stripWrap.setDisplayed(false);
39010 ts.resizeTabs = this.config.resizeTabs === true;
39011 ts.minTabWidth = this.config.minTabWidth || 40;
39012 ts.maxTabWidth = this.config.maxTabWidth || 250;
39013 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39014 ts.monitorResize = false;
39015 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39016 ts.bodyEl.addClass('roo-layout-tabs-body');
39017 this.panels.each(this.initPanelAsTab, this);
39020 initPanelAsTab : function(panel){
39021 var ti = this.tabs.addTab(
39025 this.config.closeOnTab && panel.isClosable(),
39028 if(panel.tabTip !== undefined){
39029 ti.setTooltip(panel.tabTip);
39031 ti.on("activate", function(){
39032 this.setActivePanel(panel);
39035 if(this.config.closeOnTab){
39036 ti.on("beforeclose", function(t, e){
39038 this.remove(panel);
39042 panel.tabItem = ti;
39047 updatePanelTitle : function(panel, title)
39049 if(this.activePanel == panel){
39050 this.updateTitle(title);
39053 var ti = this.tabs.getTab(panel.getEl().id);
39055 if(panel.tabTip !== undefined){
39056 ti.setTooltip(panel.tabTip);
39061 updateTitle : function(title){
39062 if(this.titleTextEl && !this.config.title){
39063 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39067 setActivePanel : function(panel)
39069 panel = this.getPanel(panel);
39070 if(this.activePanel && this.activePanel != panel){
39071 if(this.activePanel.setActiveState(false) === false){
39075 this.activePanel = panel;
39076 panel.setActiveState(true);
39077 if(this.panelSize){
39078 panel.setSize(this.panelSize.width, this.panelSize.height);
39081 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39083 this.updateTitle(panel.getTitle());
39085 this.fireEvent("invalidated", this);
39087 this.fireEvent("panelactivated", this, panel);
39091 * Shows the specified panel.
39092 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39093 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39095 showPanel : function(panel)
39097 panel = this.getPanel(panel);
39100 var tab = this.tabs.getTab(panel.getEl().id);
39101 if(tab.isHidden()){
39102 this.tabs.unhideTab(tab.id);
39106 this.setActivePanel(panel);
39113 * Get the active panel for this region.
39114 * @return {Roo.ContentPanel} The active panel or null
39116 getActivePanel : function(){
39117 return this.activePanel;
39120 validateVisibility : function(){
39121 if(this.panels.getCount() < 1){
39122 this.updateTitle(" ");
39123 this.closeBtn.hide();
39126 if(!this.isVisible()){
39133 * Adds the passed ContentPanel(s) to this region.
39134 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39135 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39137 add : function(panel)
39139 if(arguments.length > 1){
39140 for(var i = 0, len = arguments.length; i < len; i++) {
39141 this.add(arguments[i]);
39146 // if we have not been rendered yet, then we can not really do much of this..
39147 if (!this.bodyEl) {
39148 this.unrendered_panels.push(panel);
39155 if(this.hasPanel(panel)){
39156 this.showPanel(panel);
39159 panel.setRegion(this);
39160 this.panels.add(panel);
39161 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39162 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39163 // and hide them... ???
39164 this.bodyEl.dom.appendChild(panel.getEl().dom);
39165 if(panel.background !== true){
39166 this.setActivePanel(panel);
39168 this.fireEvent("paneladded", this, panel);
39175 this.initPanelAsTab(panel);
39179 if(panel.background !== true){
39180 this.tabs.activate(panel.getEl().id);
39182 this.fireEvent("paneladded", this, panel);
39187 * Hides the tab for the specified panel.
39188 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39190 hidePanel : function(panel){
39191 if(this.tabs && (panel = this.getPanel(panel))){
39192 this.tabs.hideTab(panel.getEl().id);
39197 * Unhides the tab for a previously hidden panel.
39198 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39200 unhidePanel : function(panel){
39201 if(this.tabs && (panel = this.getPanel(panel))){
39202 this.tabs.unhideTab(panel.getEl().id);
39206 clearPanels : function(){
39207 while(this.panels.getCount() > 0){
39208 this.remove(this.panels.first());
39213 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39214 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39215 * @param {Boolean} preservePanel Overrides the config preservePanel option
39216 * @return {Roo.ContentPanel} The panel that was removed
39218 remove : function(panel, preservePanel)
39220 panel = this.getPanel(panel);
39225 this.fireEvent("beforeremove", this, panel, e);
39226 if(e.cancel === true){
39229 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39230 var panelId = panel.getId();
39231 this.panels.removeKey(panelId);
39233 document.body.appendChild(panel.getEl().dom);
39236 this.tabs.removeTab(panel.getEl().id);
39237 }else if (!preservePanel){
39238 this.bodyEl.dom.removeChild(panel.getEl().dom);
39240 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39241 var p = this.panels.first();
39242 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39243 tempEl.appendChild(p.getEl().dom);
39244 this.bodyEl.update("");
39245 this.bodyEl.dom.appendChild(p.getEl().dom);
39247 this.updateTitle(p.getTitle());
39249 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39250 this.setActivePanel(p);
39252 panel.setRegion(null);
39253 if(this.activePanel == panel){
39254 this.activePanel = null;
39256 if(this.config.autoDestroy !== false && preservePanel !== true){
39257 try{panel.destroy();}catch(e){}
39259 this.fireEvent("panelremoved", this, panel);
39264 * Returns the TabPanel component used by this region
39265 * @return {Roo.TabPanel}
39267 getTabs : function(){
39271 createTool : function(parentEl, className){
39272 var btn = Roo.DomHelper.append(parentEl, {
39274 cls: "x-layout-tools-button",
39277 cls: "roo-layout-tools-button-inner " + className,
39281 btn.addClassOnOver("roo-layout-tools-button-over");
39286 * Ext JS Library 1.1.1
39287 * Copyright(c) 2006-2007, Ext JS, LLC.
39289 * Originally Released Under LGPL - original licence link has changed is not relivant.
39292 * <script type="text/javascript">
39298 * @class Roo.SplitLayoutRegion
39299 * @extends Roo.LayoutRegion
39300 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39302 Roo.bootstrap.layout.Split = function(config){
39303 this.cursor = config.cursor;
39304 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39307 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39309 splitTip : "Drag to resize.",
39310 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39311 useSplitTips : false,
39313 applyConfig : function(config){
39314 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39317 onRender : function(ctr,pos) {
39319 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39320 if(!this.config.split){
39325 var splitEl = Roo.DomHelper.append(ctr.dom, {
39327 id: this.el.id + "-split",
39328 cls: "roo-layout-split roo-layout-split-"+this.position,
39331 /** The SplitBar for this region
39332 * @type Roo.SplitBar */
39333 // does not exist yet...
39334 Roo.log([this.position, this.orientation]);
39336 this.split = new Roo.bootstrap.SplitBar({
39337 dragElement : splitEl,
39338 resizingElement: this.el,
39339 orientation : this.orientation
39342 this.split.on("moved", this.onSplitMove, this);
39343 this.split.useShim = this.config.useShim === true;
39344 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39345 if(this.useSplitTips){
39346 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39348 //if(config.collapsible){
39349 // this.split.el.on("dblclick", this.collapse, this);
39352 if(typeof this.config.minSize != "undefined"){
39353 this.split.minSize = this.config.minSize;
39355 if(typeof this.config.maxSize != "undefined"){
39356 this.split.maxSize = this.config.maxSize;
39358 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39359 this.hideSplitter();
39364 getHMaxSize : function(){
39365 var cmax = this.config.maxSize || 10000;
39366 var center = this.mgr.getRegion("center");
39367 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39370 getVMaxSize : function(){
39371 var cmax = this.config.maxSize || 10000;
39372 var center = this.mgr.getRegion("center");
39373 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39376 onSplitMove : function(split, newSize){
39377 this.fireEvent("resized", this, newSize);
39381 * Returns the {@link Roo.SplitBar} for this region.
39382 * @return {Roo.SplitBar}
39384 getSplitBar : function(){
39389 this.hideSplitter();
39390 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39393 hideSplitter : function(){
39395 this.split.el.setLocation(-2000,-2000);
39396 this.split.el.hide();
39402 this.split.el.show();
39404 Roo.bootstrap.layout.Split.superclass.show.call(this);
39407 beforeSlide: function(){
39408 if(Roo.isGecko){// firefox overflow auto bug workaround
39409 this.bodyEl.clip();
39411 this.tabs.bodyEl.clip();
39413 if(this.activePanel){
39414 this.activePanel.getEl().clip();
39416 if(this.activePanel.beforeSlide){
39417 this.activePanel.beforeSlide();
39423 afterSlide : function(){
39424 if(Roo.isGecko){// firefox overflow auto bug workaround
39425 this.bodyEl.unclip();
39427 this.tabs.bodyEl.unclip();
39429 if(this.activePanel){
39430 this.activePanel.getEl().unclip();
39431 if(this.activePanel.afterSlide){
39432 this.activePanel.afterSlide();
39438 initAutoHide : function(){
39439 if(this.autoHide !== false){
39440 if(!this.autoHideHd){
39441 var st = new Roo.util.DelayedTask(this.slideIn, this);
39442 this.autoHideHd = {
39443 "mouseout": function(e){
39444 if(!e.within(this.el, true)){
39448 "mouseover" : function(e){
39454 this.el.on(this.autoHideHd);
39458 clearAutoHide : function(){
39459 if(this.autoHide !== false){
39460 this.el.un("mouseout", this.autoHideHd.mouseout);
39461 this.el.un("mouseover", this.autoHideHd.mouseover);
39465 clearMonitor : function(){
39466 Roo.get(document).un("click", this.slideInIf, this);
39469 // these names are backwards but not changed for compat
39470 slideOut : function(){
39471 if(this.isSlid || this.el.hasActiveFx()){
39474 this.isSlid = true;
39475 if(this.collapseBtn){
39476 this.collapseBtn.hide();
39478 this.closeBtnState = this.closeBtn.getStyle('display');
39479 this.closeBtn.hide();
39481 this.stickBtn.show();
39484 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39485 this.beforeSlide();
39486 this.el.setStyle("z-index", 10001);
39487 this.el.slideIn(this.getSlideAnchor(), {
39488 callback: function(){
39490 this.initAutoHide();
39491 Roo.get(document).on("click", this.slideInIf, this);
39492 this.fireEvent("slideshow", this);
39499 afterSlideIn : function(){
39500 this.clearAutoHide();
39501 this.isSlid = false;
39502 this.clearMonitor();
39503 this.el.setStyle("z-index", "");
39504 if(this.collapseBtn){
39505 this.collapseBtn.show();
39507 this.closeBtn.setStyle('display', this.closeBtnState);
39509 this.stickBtn.hide();
39511 this.fireEvent("slidehide", this);
39514 slideIn : function(cb){
39515 if(!this.isSlid || this.el.hasActiveFx()){
39519 this.isSlid = false;
39520 this.beforeSlide();
39521 this.el.slideOut(this.getSlideAnchor(), {
39522 callback: function(){
39523 this.el.setLeftTop(-10000, -10000);
39525 this.afterSlideIn();
39533 slideInIf : function(e){
39534 if(!e.within(this.el)){
39539 animateCollapse : function(){
39540 this.beforeSlide();
39541 this.el.setStyle("z-index", 20000);
39542 var anchor = this.getSlideAnchor();
39543 this.el.slideOut(anchor, {
39544 callback : function(){
39545 this.el.setStyle("z-index", "");
39546 this.collapsedEl.slideIn(anchor, {duration:.3});
39548 this.el.setLocation(-10000,-10000);
39550 this.fireEvent("collapsed", this);
39557 animateExpand : function(){
39558 this.beforeSlide();
39559 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39560 this.el.setStyle("z-index", 20000);
39561 this.collapsedEl.hide({
39564 this.el.slideIn(this.getSlideAnchor(), {
39565 callback : function(){
39566 this.el.setStyle("z-index", "");
39569 this.split.el.show();
39571 this.fireEvent("invalidated", this);
39572 this.fireEvent("expanded", this);
39600 getAnchor : function(){
39601 return this.anchors[this.position];
39604 getCollapseAnchor : function(){
39605 return this.canchors[this.position];
39608 getSlideAnchor : function(){
39609 return this.sanchors[this.position];
39612 getAlignAdj : function(){
39613 var cm = this.cmargins;
39614 switch(this.position){
39630 getExpandAdj : function(){
39631 var c = this.collapsedEl, cm = this.cmargins;
39632 switch(this.position){
39634 return [-(cm.right+c.getWidth()+cm.left), 0];
39637 return [cm.right+c.getWidth()+cm.left, 0];
39640 return [0, -(cm.top+cm.bottom+c.getHeight())];
39643 return [0, cm.top+cm.bottom+c.getHeight()];
39649 * Ext JS Library 1.1.1
39650 * Copyright(c) 2006-2007, Ext JS, LLC.
39652 * Originally Released Under LGPL - original licence link has changed is not relivant.
39655 * <script type="text/javascript">
39658 * These classes are private internal classes
39660 Roo.bootstrap.layout.Center = function(config){
39661 config.region = "center";
39662 Roo.bootstrap.layout.Region.call(this, config);
39663 this.visible = true;
39664 this.minWidth = config.minWidth || 20;
39665 this.minHeight = config.minHeight || 20;
39668 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39670 // center panel can't be hidden
39674 // center panel can't be hidden
39677 getMinWidth: function(){
39678 return this.minWidth;
39681 getMinHeight: function(){
39682 return this.minHeight;
39696 Roo.bootstrap.layout.North = function(config)
39698 config.region = 'north';
39699 config.cursor = 'n-resize';
39701 Roo.bootstrap.layout.Split.call(this, config);
39705 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39706 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39707 this.split.el.addClass("roo-layout-split-v");
39709 //var size = config.initialSize || config.height;
39710 //if(this.el && typeof size != "undefined"){
39711 // this.el.setHeight(size);
39714 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39716 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39719 onRender : function(ctr, pos)
39721 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39722 var size = this.config.initialSize || this.config.height;
39723 if(this.el && typeof size != "undefined"){
39724 this.el.setHeight(size);
39729 getBox : function(){
39730 if(this.collapsed){
39731 return this.collapsedEl.getBox();
39733 var box = this.el.getBox();
39735 box.height += this.split.el.getHeight();
39740 updateBox : function(box){
39741 if(this.split && !this.collapsed){
39742 box.height -= this.split.el.getHeight();
39743 this.split.el.setLeft(box.x);
39744 this.split.el.setTop(box.y+box.height);
39745 this.split.el.setWidth(box.width);
39747 if(this.collapsed){
39748 this.updateBody(box.width, null);
39750 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39758 Roo.bootstrap.layout.South = function(config){
39759 config.region = 'south';
39760 config.cursor = 's-resize';
39761 Roo.bootstrap.layout.Split.call(this, config);
39763 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39764 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39765 this.split.el.addClass("roo-layout-split-v");
39770 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39771 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39773 onRender : function(ctr, pos)
39775 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39776 var size = this.config.initialSize || this.config.height;
39777 if(this.el && typeof size != "undefined"){
39778 this.el.setHeight(size);
39783 getBox : function(){
39784 if(this.collapsed){
39785 return this.collapsedEl.getBox();
39787 var box = this.el.getBox();
39789 var sh = this.split.el.getHeight();
39796 updateBox : function(box){
39797 if(this.split && !this.collapsed){
39798 var sh = this.split.el.getHeight();
39801 this.split.el.setLeft(box.x);
39802 this.split.el.setTop(box.y-sh);
39803 this.split.el.setWidth(box.width);
39805 if(this.collapsed){
39806 this.updateBody(box.width, null);
39808 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39812 Roo.bootstrap.layout.East = function(config){
39813 config.region = "east";
39814 config.cursor = "e-resize";
39815 Roo.bootstrap.layout.Split.call(this, config);
39817 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39818 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39819 this.split.el.addClass("roo-layout-split-h");
39823 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39824 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39826 onRender : function(ctr, pos)
39828 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39829 var size = this.config.initialSize || this.config.width;
39830 if(this.el && typeof size != "undefined"){
39831 this.el.setWidth(size);
39836 getBox : function(){
39837 if(this.collapsed){
39838 return this.collapsedEl.getBox();
39840 var box = this.el.getBox();
39842 var sw = this.split.el.getWidth();
39849 updateBox : function(box){
39850 if(this.split && !this.collapsed){
39851 var sw = this.split.el.getWidth();
39853 this.split.el.setLeft(box.x);
39854 this.split.el.setTop(box.y);
39855 this.split.el.setHeight(box.height);
39858 if(this.collapsed){
39859 this.updateBody(null, box.height);
39861 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39865 Roo.bootstrap.layout.West = function(config){
39866 config.region = "west";
39867 config.cursor = "w-resize";
39869 Roo.bootstrap.layout.Split.call(this, config);
39871 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39872 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39873 this.split.el.addClass("roo-layout-split-h");
39877 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39878 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39880 onRender: function(ctr, pos)
39882 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39883 var size = this.config.initialSize || this.config.width;
39884 if(typeof size != "undefined"){
39885 this.el.setWidth(size);
39889 getBox : function(){
39890 if(this.collapsed){
39891 return this.collapsedEl.getBox();
39893 var box = this.el.getBox();
39894 if (box.width == 0) {
39895 box.width = this.config.width; // kludge?
39898 box.width += this.split.el.getWidth();
39903 updateBox : function(box){
39904 if(this.split && !this.collapsed){
39905 var sw = this.split.el.getWidth();
39907 this.split.el.setLeft(box.x+box.width);
39908 this.split.el.setTop(box.y);
39909 this.split.el.setHeight(box.height);
39911 if(this.collapsed){
39912 this.updateBody(null, box.height);
39914 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39916 });Roo.namespace("Roo.bootstrap.panel");/*
39918 * Ext JS Library 1.1.1
39919 * Copyright(c) 2006-2007, Ext JS, LLC.
39921 * Originally Released Under LGPL - original licence link has changed is not relivant.
39924 * <script type="text/javascript">
39927 * @class Roo.ContentPanel
39928 * @extends Roo.util.Observable
39929 * A basic ContentPanel element.
39930 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39931 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39932 * @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
39933 * @cfg {Boolean} closable True if the panel can be closed/removed
39934 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39935 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39936 * @cfg {Toolbar} toolbar A toolbar for this panel
39937 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39938 * @cfg {String} title The title for this panel
39939 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39940 * @cfg {String} url Calls {@link #setUrl} with this value
39941 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39942 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39943 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39944 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39945 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39946 * @cfg {Boolean} badges render the badges
39947 * @cfg {String} cls extra classes to use
39948 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39951 * Create a new ContentPanel.
39952 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39953 * @param {String/Object} config A string to set only the title or a config object
39954 * @param {String} content (optional) Set the HTML content for this panel
39955 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39957 Roo.bootstrap.panel.Content = function( config){
39959 this.tpl = config.tpl || false;
39961 var el = config.el;
39962 var content = config.content;
39964 if(config.autoCreate){ // xtype is available if this is called from factory
39967 this.el = Roo.get(el);
39968 if(!this.el && config && config.autoCreate){
39969 if(typeof config.autoCreate == "object"){
39970 if(!config.autoCreate.id){
39971 config.autoCreate.id = config.id||el;
39973 this.el = Roo.DomHelper.append(document.body,
39974 config.autoCreate, true);
39978 cls: (config.cls || '') +
39979 (config.background ? ' bg-' + config.background : '') +
39980 " roo-layout-inactive-content",
39983 if (config.iframe) {
39987 style : 'border: 0px',
39988 src : 'about:blank'
39994 elcfg.html = config.html;
39998 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39999 if (config.iframe) {
40000 this.iframeEl = this.el.select('iframe',true).first();
40005 this.closable = false;
40006 this.loaded = false;
40007 this.active = false;
40010 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40012 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40014 this.wrapEl = this.el; //this.el.wrap();
40016 if (config.toolbar.items) {
40017 ti = config.toolbar.items ;
40018 delete config.toolbar.items ;
40022 this.toolbar.render(this.wrapEl, 'before');
40023 for(var i =0;i < ti.length;i++) {
40024 // Roo.log(['add child', items[i]]);
40025 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40027 this.toolbar.items = nitems;
40028 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40029 delete config.toolbar;
40033 // xtype created footer. - not sure if will work as we normally have to render first..
40034 if (this.footer && !this.footer.el && this.footer.xtype) {
40035 if (!this.wrapEl) {
40036 this.wrapEl = this.el.wrap();
40039 this.footer.container = this.wrapEl.createChild();
40041 this.footer = Roo.factory(this.footer, Roo);
40046 if(typeof config == "string"){
40047 this.title = config;
40049 Roo.apply(this, config);
40053 this.resizeEl = Roo.get(this.resizeEl, true);
40055 this.resizeEl = this.el;
40057 // handle view.xtype
40065 * Fires when this panel is activated.
40066 * @param {Roo.ContentPanel} this
40070 * @event deactivate
40071 * Fires when this panel is activated.
40072 * @param {Roo.ContentPanel} this
40074 "deactivate" : true,
40078 * Fires when this panel is resized if fitToFrame is true.
40079 * @param {Roo.ContentPanel} this
40080 * @param {Number} width The width after any component adjustments
40081 * @param {Number} height The height after any component adjustments
40087 * Fires when this tab is created
40088 * @param {Roo.ContentPanel} this
40099 if(this.autoScroll && !this.iframe){
40100 this.resizeEl.setStyle("overflow", "auto");
40102 // fix randome scrolling
40103 //this.el.on('scroll', function() {
40104 // Roo.log('fix random scolling');
40105 // this.scrollTo('top',0);
40108 content = content || this.content;
40110 this.setContent(content);
40112 if(config && config.url){
40113 this.setUrl(this.url, this.params, this.loadOnce);
40118 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40120 if (this.view && typeof(this.view.xtype) != 'undefined') {
40121 this.view.el = this.el.appendChild(document.createElement("div"));
40122 this.view = Roo.factory(this.view);
40123 this.view.render && this.view.render(false, '');
40127 this.fireEvent('render', this);
40130 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40140 setRegion : function(region){
40141 this.region = region;
40142 this.setActiveClass(region && !this.background);
40146 setActiveClass: function(state)
40149 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40150 this.el.setStyle('position','relative');
40152 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40153 this.el.setStyle('position', 'absolute');
40158 * Returns the toolbar for this Panel if one was configured.
40159 * @return {Roo.Toolbar}
40161 getToolbar : function(){
40162 return this.toolbar;
40165 setActiveState : function(active)
40167 this.active = active;
40168 this.setActiveClass(active);
40170 if(this.fireEvent("deactivate", this) === false){
40175 this.fireEvent("activate", this);
40179 * Updates this panel's element (not for iframe)
40180 * @param {String} content The new content
40181 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40183 setContent : function(content, loadScripts){
40188 this.el.update(content, loadScripts);
40191 ignoreResize : function(w, h){
40192 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40195 this.lastSize = {width: w, height: h};
40200 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40201 * @return {Roo.UpdateManager} The UpdateManager
40203 getUpdateManager : function(){
40207 return this.el.getUpdateManager();
40210 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40211 * Does not work with IFRAME contents
40212 * @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:
40215 url: "your-url.php",
40216 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40217 callback: yourFunction,
40218 scope: yourObject, //(optional scope)
40221 text: "Loading...",
40227 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40228 * 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.
40229 * @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}
40230 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40231 * @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.
40232 * @return {Roo.ContentPanel} this
40240 var um = this.el.getUpdateManager();
40241 um.update.apply(um, arguments);
40247 * 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.
40248 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40249 * @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)
40250 * @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)
40251 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40253 setUrl : function(url, params, loadOnce){
40255 this.iframeEl.dom.src = url;
40259 if(this.refreshDelegate){
40260 this.removeListener("activate", this.refreshDelegate);
40262 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40263 this.on("activate", this.refreshDelegate);
40264 return this.el.getUpdateManager();
40267 _handleRefresh : function(url, params, loadOnce){
40268 if(!loadOnce || !this.loaded){
40269 var updater = this.el.getUpdateManager();
40270 updater.update(url, params, this._setLoaded.createDelegate(this));
40274 _setLoaded : function(){
40275 this.loaded = true;
40279 * Returns this panel's id
40282 getId : function(){
40287 * Returns this panel's element - used by regiosn to add.
40288 * @return {Roo.Element}
40290 getEl : function(){
40291 return this.wrapEl || this.el;
40296 adjustForComponents : function(width, height)
40298 //Roo.log('adjustForComponents ');
40299 if(this.resizeEl != this.el){
40300 width -= this.el.getFrameWidth('lr');
40301 height -= this.el.getFrameWidth('tb');
40304 var te = this.toolbar.getEl();
40305 te.setWidth(width);
40306 height -= te.getHeight();
40309 var te = this.footer.getEl();
40310 te.setWidth(width);
40311 height -= te.getHeight();
40315 if(this.adjustments){
40316 width += this.adjustments[0];
40317 height += this.adjustments[1];
40319 return {"width": width, "height": height};
40322 setSize : function(width, height){
40323 if(this.fitToFrame && !this.ignoreResize(width, height)){
40324 if(this.fitContainer && this.resizeEl != this.el){
40325 this.el.setSize(width, height);
40327 var size = this.adjustForComponents(width, height);
40329 this.iframeEl.setSize(width,height);
40332 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40333 this.fireEvent('resize', this, size.width, size.height);
40340 * Returns this panel's title
40343 getTitle : function(){
40345 if (typeof(this.title) != 'object') {
40350 for (var k in this.title) {
40351 if (!this.title.hasOwnProperty(k)) {
40355 if (k.indexOf('-') >= 0) {
40356 var s = k.split('-');
40357 for (var i = 0; i<s.length; i++) {
40358 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40361 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40368 * Set this panel's title
40369 * @param {String} title
40371 setTitle : function(title){
40372 this.title = title;
40374 this.region.updatePanelTitle(this, title);
40379 * Returns true is this panel was configured to be closable
40380 * @return {Boolean}
40382 isClosable : function(){
40383 return this.closable;
40386 beforeSlide : function(){
40388 this.resizeEl.clip();
40391 afterSlide : function(){
40393 this.resizeEl.unclip();
40397 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40398 * Will fail silently if the {@link #setUrl} method has not been called.
40399 * This does not activate the panel, just updates its content.
40401 refresh : function(){
40402 if(this.refreshDelegate){
40403 this.loaded = false;
40404 this.refreshDelegate();
40409 * Destroys this panel
40411 destroy : function(){
40412 this.el.removeAllListeners();
40413 var tempEl = document.createElement("span");
40414 tempEl.appendChild(this.el.dom);
40415 tempEl.innerHTML = "";
40421 * form - if the content panel contains a form - this is a reference to it.
40422 * @type {Roo.form.Form}
40426 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40427 * This contains a reference to it.
40433 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40443 * @param {Object} cfg Xtype definition of item to add.
40447 getChildContainer: function () {
40448 return this.getEl();
40453 var ret = new Roo.factory(cfg);
40458 if (cfg.xtype.match(/^Form$/)) {
40461 //if (this.footer) {
40462 // el = this.footer.container.insertSibling(false, 'before');
40464 el = this.el.createChild();
40467 this.form = new Roo.form.Form(cfg);
40470 if ( this.form.allItems.length) {
40471 this.form.render(el.dom);
40475 // should only have one of theses..
40476 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40477 // views.. should not be just added - used named prop 'view''
40479 cfg.el = this.el.appendChild(document.createElement("div"));
40482 var ret = new Roo.factory(cfg);
40484 ret.render && ret.render(false, ''); // render blank..
40494 * @class Roo.bootstrap.panel.Grid
40495 * @extends Roo.bootstrap.panel.Content
40497 * Create a new GridPanel.
40498 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40499 * @param {Object} config A the config object
40505 Roo.bootstrap.panel.Grid = function(config)
40509 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40510 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40512 config.el = this.wrapper;
40513 //this.el = this.wrapper;
40515 if (config.container) {
40516 // ctor'ed from a Border/panel.grid
40519 this.wrapper.setStyle("overflow", "hidden");
40520 this.wrapper.addClass('roo-grid-container');
40525 if(config.toolbar){
40526 var tool_el = this.wrapper.createChild();
40527 this.toolbar = Roo.factory(config.toolbar);
40529 if (config.toolbar.items) {
40530 ti = config.toolbar.items ;
40531 delete config.toolbar.items ;
40535 this.toolbar.render(tool_el);
40536 for(var i =0;i < ti.length;i++) {
40537 // Roo.log(['add child', items[i]]);
40538 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40540 this.toolbar.items = nitems;
40542 delete config.toolbar;
40545 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40546 config.grid.scrollBody = true;;
40547 config.grid.monitorWindowResize = false; // turn off autosizing
40548 config.grid.autoHeight = false;
40549 config.grid.autoWidth = false;
40551 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40553 if (config.background) {
40554 // render grid on panel activation (if panel background)
40555 this.on('activate', function(gp) {
40556 if (!gp.grid.rendered) {
40557 gp.grid.render(this.wrapper);
40558 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40563 this.grid.render(this.wrapper);
40564 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40567 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40568 // ??? needed ??? config.el = this.wrapper;
40573 // xtype created footer. - not sure if will work as we normally have to render first..
40574 if (this.footer && !this.footer.el && this.footer.xtype) {
40576 var ctr = this.grid.getView().getFooterPanel(true);
40577 this.footer.dataSource = this.grid.dataSource;
40578 this.footer = Roo.factory(this.footer, Roo);
40579 this.footer.render(ctr);
40589 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40590 getId : function(){
40591 return this.grid.id;
40595 * Returns the grid for this panel
40596 * @return {Roo.bootstrap.Table}
40598 getGrid : function(){
40602 setSize : function(width, height){
40603 if(!this.ignoreResize(width, height)){
40604 var grid = this.grid;
40605 var size = this.adjustForComponents(width, height);
40606 // tfoot is not a footer?
40609 var gridel = grid.getGridEl();
40610 gridel.setSize(size.width, size.height);
40612 var tbd = grid.getGridEl().select('tbody', true).first();
40613 var thd = grid.getGridEl().select('thead',true).first();
40614 var tbf= grid.getGridEl().select('tfoot', true).first();
40617 size.height -= tbf.getHeight();
40620 size.height -= thd.getHeight();
40623 tbd.setSize(size.width, size.height );
40624 // this is for the account management tab -seems to work there.
40625 var thd = grid.getGridEl().select('thead',true).first();
40627 // tbd.setSize(size.width, size.height - thd.getHeight());
40636 beforeSlide : function(){
40637 this.grid.getView().scroller.clip();
40640 afterSlide : function(){
40641 this.grid.getView().scroller.unclip();
40644 destroy : function(){
40645 this.grid.destroy();
40647 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40652 * @class Roo.bootstrap.panel.Nest
40653 * @extends Roo.bootstrap.panel.Content
40655 * Create a new Panel, that can contain a layout.Border.
40658 * @param {Roo.BorderLayout} layout The layout for this panel
40659 * @param {String/Object} config A string to set only the title or a config object
40661 Roo.bootstrap.panel.Nest = function(config)
40663 // construct with only one argument..
40664 /* FIXME - implement nicer consturctors
40665 if (layout.layout) {
40667 layout = config.layout;
40668 delete config.layout;
40670 if (layout.xtype && !layout.getEl) {
40671 // then layout needs constructing..
40672 layout = Roo.factory(layout, Roo);
40676 config.el = config.layout.getEl();
40678 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40680 config.layout.monitorWindowResize = false; // turn off autosizing
40681 this.layout = config.layout;
40682 this.layout.getEl().addClass("roo-layout-nested-layout");
40683 this.layout.parent = this;
40690 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40692 setSize : function(width, height){
40693 if(!this.ignoreResize(width, height)){
40694 var size = this.adjustForComponents(width, height);
40695 var el = this.layout.getEl();
40696 if (size.height < 1) {
40697 el.setWidth(size.width);
40699 el.setSize(size.width, size.height);
40701 var touch = el.dom.offsetWidth;
40702 this.layout.layout();
40703 // ie requires a double layout on the first pass
40704 if(Roo.isIE && !this.initialized){
40705 this.initialized = true;
40706 this.layout.layout();
40711 // activate all subpanels if not currently active..
40713 setActiveState : function(active){
40714 this.active = active;
40715 this.setActiveClass(active);
40718 this.fireEvent("deactivate", this);
40722 this.fireEvent("activate", this);
40723 // not sure if this should happen before or after..
40724 if (!this.layout) {
40725 return; // should not happen..
40728 for (var r in this.layout.regions) {
40729 reg = this.layout.getRegion(r);
40730 if (reg.getActivePanel()) {
40731 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40732 reg.setActivePanel(reg.getActivePanel());
40735 if (!reg.panels.length) {
40738 reg.showPanel(reg.getPanel(0));
40747 * Returns the nested BorderLayout for this panel
40748 * @return {Roo.BorderLayout}
40750 getLayout : function(){
40751 return this.layout;
40755 * Adds a xtype elements to the layout of the nested panel
40759 xtype : 'ContentPanel',
40766 xtype : 'NestedLayoutPanel',
40772 items : [ ... list of content panels or nested layout panels.. ]
40776 * @param {Object} cfg Xtype definition of item to add.
40778 addxtype : function(cfg) {
40779 return this.layout.addxtype(cfg);
40784 * Ext JS Library 1.1.1
40785 * Copyright(c) 2006-2007, Ext JS, LLC.
40787 * Originally Released Under LGPL - original licence link has changed is not relivant.
40790 * <script type="text/javascript">
40793 * @class Roo.TabPanel
40794 * @extends Roo.util.Observable
40795 * A lightweight tab container.
40799 // basic tabs 1, built from existing content
40800 var tabs = new Roo.TabPanel("tabs1");
40801 tabs.addTab("script", "View Script");
40802 tabs.addTab("markup", "View Markup");
40803 tabs.activate("script");
40805 // more advanced tabs, built from javascript
40806 var jtabs = new Roo.TabPanel("jtabs");
40807 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40809 // set up the UpdateManager
40810 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40811 var updater = tab2.getUpdateManager();
40812 updater.setDefaultUrl("ajax1.htm");
40813 tab2.on('activate', updater.refresh, updater, true);
40815 // Use setUrl for Ajax loading
40816 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40817 tab3.setUrl("ajax2.htm", null, true);
40820 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40823 jtabs.activate("jtabs-1");
40826 * Create a new TabPanel.
40827 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40828 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40830 Roo.bootstrap.panel.Tabs = function(config){
40832 * The container element for this TabPanel.
40833 * @type Roo.Element
40835 this.el = Roo.get(config.el);
40838 if(typeof config == "boolean"){
40839 this.tabPosition = config ? "bottom" : "top";
40841 Roo.apply(this, config);
40845 if(this.tabPosition == "bottom"){
40846 // if tabs are at the bottom = create the body first.
40847 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40848 this.el.addClass("roo-tabs-bottom");
40850 // next create the tabs holders
40852 if (this.tabPosition == "west"){
40854 var reg = this.region; // fake it..
40856 if (!reg.mgr.parent) {
40859 reg = reg.mgr.parent.region;
40861 Roo.log("got nest?");
40863 if (reg.mgr.getRegion('west')) {
40864 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40865 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40866 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40867 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40868 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40876 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40877 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40878 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40879 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40884 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40887 // finally - if tabs are at the top, then create the body last..
40888 if(this.tabPosition != "bottom"){
40889 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40890 * @type Roo.Element
40892 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40893 this.el.addClass("roo-tabs-top");
40897 this.bodyEl.setStyle("position", "relative");
40899 this.active = null;
40900 this.activateDelegate = this.activate.createDelegate(this);
40905 * Fires when the active tab changes
40906 * @param {Roo.TabPanel} this
40907 * @param {Roo.TabPanelItem} activePanel The new active tab
40911 * @event beforetabchange
40912 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40913 * @param {Roo.TabPanel} this
40914 * @param {Object} e Set cancel to true on this object to cancel the tab change
40915 * @param {Roo.TabPanelItem} tab The tab being changed to
40917 "beforetabchange" : true
40920 Roo.EventManager.onWindowResize(this.onResize, this);
40921 this.cpad = this.el.getPadding("lr");
40922 this.hiddenCount = 0;
40925 // toolbar on the tabbar support...
40926 if (this.toolbar) {
40927 alert("no toolbar support yet");
40928 this.toolbar = false;
40930 var tcfg = this.toolbar;
40931 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40932 this.toolbar = new Roo.Toolbar(tcfg);
40933 if (Roo.isSafari) {
40934 var tbl = tcfg.container.child('table', true);
40935 tbl.setAttribute('width', '100%');
40943 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40946 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40948 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40950 tabPosition : "top",
40952 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40954 currentTabWidth : 0,
40956 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40960 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40964 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40966 preferredTabWidth : 175,
40968 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40970 resizeTabs : false,
40972 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40974 monitorResize : true,
40976 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40978 toolbar : false, // set by caller..
40980 region : false, /// set by caller
40982 disableTooltips : true, // not used yet...
40985 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40986 * @param {String} id The id of the div to use <b>or create</b>
40987 * @param {String} text The text for the tab
40988 * @param {String} content (optional) Content to put in the TabPanelItem body
40989 * @param {Boolean} closable (optional) True to create a close icon on the tab
40990 * @return {Roo.TabPanelItem} The created TabPanelItem
40992 addTab : function(id, text, content, closable, tpl)
40994 var item = new Roo.bootstrap.panel.TabItem({
40998 closable : closable,
41001 this.addTabItem(item);
41003 item.setContent(content);
41009 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41010 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41011 * @return {Roo.TabPanelItem}
41013 getTab : function(id){
41014 return this.items[id];
41018 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41019 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41021 hideTab : function(id){
41022 var t = this.items[id];
41025 this.hiddenCount++;
41026 this.autoSizeTabs();
41031 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41032 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41034 unhideTab : function(id){
41035 var t = this.items[id];
41037 t.setHidden(false);
41038 this.hiddenCount--;
41039 this.autoSizeTabs();
41044 * Adds an existing {@link Roo.TabPanelItem}.
41045 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41047 addTabItem : function(item)
41049 this.items[item.id] = item;
41050 this.items.push(item);
41051 this.autoSizeTabs();
41052 // if(this.resizeTabs){
41053 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41054 // this.autoSizeTabs();
41056 // item.autoSize();
41061 * Removes a {@link Roo.TabPanelItem}.
41062 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41064 removeTab : function(id){
41065 var items = this.items;
41066 var tab = items[id];
41067 if(!tab) { return; }
41068 var index = items.indexOf(tab);
41069 if(this.active == tab && items.length > 1){
41070 var newTab = this.getNextAvailable(index);
41075 this.stripEl.dom.removeChild(tab.pnode.dom);
41076 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41077 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41079 items.splice(index, 1);
41080 delete this.items[tab.id];
41081 tab.fireEvent("close", tab);
41082 tab.purgeListeners();
41083 this.autoSizeTabs();
41086 getNextAvailable : function(start){
41087 var items = this.items;
41089 // look for a next tab that will slide over to
41090 // replace the one being removed
41091 while(index < items.length){
41092 var item = items[++index];
41093 if(item && !item.isHidden()){
41097 // if one isn't found select the previous tab (on the left)
41100 var item = items[--index];
41101 if(item && !item.isHidden()){
41109 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41110 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41112 disableTab : function(id){
41113 var tab = this.items[id];
41114 if(tab && this.active != tab){
41120 * Enables a {@link Roo.TabPanelItem} that is disabled.
41121 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41123 enableTab : function(id){
41124 var tab = this.items[id];
41129 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41130 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41131 * @return {Roo.TabPanelItem} The TabPanelItem.
41133 activate : function(id)
41135 //Roo.log('activite:' + id);
41137 var tab = this.items[id];
41141 if(tab == this.active || tab.disabled){
41145 this.fireEvent("beforetabchange", this, e, tab);
41146 if(e.cancel !== true && !tab.disabled){
41148 this.active.hide();
41150 this.active = this.items[id];
41151 this.active.show();
41152 this.fireEvent("tabchange", this, this.active);
41158 * Gets the active {@link Roo.TabPanelItem}.
41159 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41161 getActiveTab : function(){
41162 return this.active;
41166 * Updates the tab body element to fit the height of the container element
41167 * for overflow scrolling
41168 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41170 syncHeight : function(targetHeight){
41171 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41172 var bm = this.bodyEl.getMargins();
41173 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41174 this.bodyEl.setHeight(newHeight);
41178 onResize : function(){
41179 if(this.monitorResize){
41180 this.autoSizeTabs();
41185 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41187 beginUpdate : function(){
41188 this.updating = true;
41192 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41194 endUpdate : function(){
41195 this.updating = false;
41196 this.autoSizeTabs();
41200 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41202 autoSizeTabs : function()
41204 var count = this.items.length;
41205 var vcount = count - this.hiddenCount;
41208 this.stripEl.hide();
41210 this.stripEl.show();
41213 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41218 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41219 var availWidth = Math.floor(w / vcount);
41220 var b = this.stripBody;
41221 if(b.getWidth() > w){
41222 var tabs = this.items;
41223 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41224 if(availWidth < this.minTabWidth){
41225 /*if(!this.sleft){ // incomplete scrolling code
41226 this.createScrollButtons();
41229 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41232 if(this.currentTabWidth < this.preferredTabWidth){
41233 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41239 * Returns the number of tabs in this TabPanel.
41242 getCount : function(){
41243 return this.items.length;
41247 * Resizes all the tabs to the passed width
41248 * @param {Number} The new width
41250 setTabWidth : function(width){
41251 this.currentTabWidth = width;
41252 for(var i = 0, len = this.items.length; i < len; i++) {
41253 if(!this.items[i].isHidden()) {
41254 this.items[i].setWidth(width);
41260 * Destroys this TabPanel
41261 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41263 destroy : function(removeEl){
41264 Roo.EventManager.removeResizeListener(this.onResize, this);
41265 for(var i = 0, len = this.items.length; i < len; i++){
41266 this.items[i].purgeListeners();
41268 if(removeEl === true){
41269 this.el.update("");
41274 createStrip : function(container)
41276 var strip = document.createElement("nav");
41277 strip.className = Roo.bootstrap.version == 4 ?
41278 "navbar-light bg-light" :
41279 "navbar navbar-default"; //"x-tabs-wrap";
41280 container.appendChild(strip);
41284 createStripList : function(strip)
41286 // div wrapper for retard IE
41287 // returns the "tr" element.
41288 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41289 //'<div class="x-tabs-strip-wrap">'+
41290 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41291 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41292 return strip.firstChild; //.firstChild.firstChild.firstChild;
41294 createBody : function(container)
41296 var body = document.createElement("div");
41297 Roo.id(body, "tab-body");
41298 //Roo.fly(body).addClass("x-tabs-body");
41299 Roo.fly(body).addClass("tab-content");
41300 container.appendChild(body);
41303 createItemBody :function(bodyEl, id){
41304 var body = Roo.getDom(id);
41306 body = document.createElement("div");
41309 //Roo.fly(body).addClass("x-tabs-item-body");
41310 Roo.fly(body).addClass("tab-pane");
41311 bodyEl.insertBefore(body, bodyEl.firstChild);
41315 createStripElements : function(stripEl, text, closable, tpl)
41317 var td = document.createElement("li"); // was td..
41318 td.className = 'nav-item';
41320 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41323 stripEl.appendChild(td);
41325 td.className = "x-tabs-closable";
41326 if(!this.closeTpl){
41327 this.closeTpl = new Roo.Template(
41328 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41329 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41330 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41333 var el = this.closeTpl.overwrite(td, {"text": text});
41334 var close = el.getElementsByTagName("div")[0];
41335 var inner = el.getElementsByTagName("em")[0];
41336 return {"el": el, "close": close, "inner": inner};
41339 // not sure what this is..
41340 // if(!this.tabTpl){
41341 //this.tabTpl = new Roo.Template(
41342 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41343 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41345 // this.tabTpl = new Roo.Template(
41346 // '<a href="#">' +
41347 // '<span unselectable="on"' +
41348 // (this.disableTooltips ? '' : ' title="{text}"') +
41349 // ' >{text}</span></a>'
41355 var template = tpl || this.tabTpl || false;
41358 template = new Roo.Template(
41359 Roo.bootstrap.version == 4 ?
41361 '<a class="nav-link" href="#" unselectable="on"' +
41362 (this.disableTooltips ? '' : ' title="{text}"') +
41365 '<a class="nav-link" href="#">' +
41366 '<span unselectable="on"' +
41367 (this.disableTooltips ? '' : ' title="{text}"') +
41368 ' >{text}</span></a>'
41373 switch (typeof(template)) {
41377 template = new Roo.Template(template);
41383 var el = template.overwrite(td, {"text": text});
41385 var inner = el.getElementsByTagName("span")[0];
41387 return {"el": el, "inner": inner};
41395 * @class Roo.TabPanelItem
41396 * @extends Roo.util.Observable
41397 * Represents an individual item (tab plus body) in a TabPanel.
41398 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41399 * @param {String} id The id of this TabPanelItem
41400 * @param {String} text The text for the tab of this TabPanelItem
41401 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41403 Roo.bootstrap.panel.TabItem = function(config){
41405 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41406 * @type Roo.TabPanel
41408 this.tabPanel = config.panel;
41410 * The id for this TabPanelItem
41413 this.id = config.id;
41415 this.disabled = false;
41417 this.text = config.text;
41419 this.loaded = false;
41420 this.closable = config.closable;
41423 * The body element for this TabPanelItem.
41424 * @type Roo.Element
41426 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41427 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41428 this.bodyEl.setStyle("display", "block");
41429 this.bodyEl.setStyle("zoom", "1");
41430 //this.hideAction();
41432 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41434 this.el = Roo.get(els.el);
41435 this.inner = Roo.get(els.inner, true);
41436 this.textEl = Roo.bootstrap.version == 4 ?
41437 this.el : Roo.get(this.el.dom.firstChild, true);
41439 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41440 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41443 // this.el.on("mousedown", this.onTabMouseDown, this);
41444 this.el.on("click", this.onTabClick, this);
41446 if(config.closable){
41447 var c = Roo.get(els.close, true);
41448 c.dom.title = this.closeText;
41449 c.addClassOnOver("close-over");
41450 c.on("click", this.closeClick, this);
41456 * Fires when this tab becomes the active tab.
41457 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41458 * @param {Roo.TabPanelItem} this
41462 * @event beforeclose
41463 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41464 * @param {Roo.TabPanelItem} this
41465 * @param {Object} e Set cancel to true on this object to cancel the close.
41467 "beforeclose": true,
41470 * Fires when this tab is closed.
41471 * @param {Roo.TabPanelItem} this
41475 * @event deactivate
41476 * Fires when this tab is no longer the active tab.
41477 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41478 * @param {Roo.TabPanelItem} this
41480 "deactivate" : true
41482 this.hidden = false;
41484 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41487 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41489 purgeListeners : function(){
41490 Roo.util.Observable.prototype.purgeListeners.call(this);
41491 this.el.removeAllListeners();
41494 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41497 this.status_node.addClass("active");
41500 this.tabPanel.stripWrap.repaint();
41502 this.fireEvent("activate", this.tabPanel, this);
41506 * Returns true if this tab is the active tab.
41507 * @return {Boolean}
41509 isActive : function(){
41510 return this.tabPanel.getActiveTab() == this;
41514 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41517 this.status_node.removeClass("active");
41519 this.fireEvent("deactivate", this.tabPanel, this);
41522 hideAction : function(){
41523 this.bodyEl.hide();
41524 this.bodyEl.setStyle("position", "absolute");
41525 this.bodyEl.setLeft("-20000px");
41526 this.bodyEl.setTop("-20000px");
41529 showAction : function(){
41530 this.bodyEl.setStyle("position", "relative");
41531 this.bodyEl.setTop("");
41532 this.bodyEl.setLeft("");
41533 this.bodyEl.show();
41537 * Set the tooltip for the tab.
41538 * @param {String} tooltip The tab's tooltip
41540 setTooltip : function(text){
41541 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41542 this.textEl.dom.qtip = text;
41543 this.textEl.dom.removeAttribute('title');
41545 this.textEl.dom.title = text;
41549 onTabClick : function(e){
41550 e.preventDefault();
41551 this.tabPanel.activate(this.id);
41554 onTabMouseDown : function(e){
41555 e.preventDefault();
41556 this.tabPanel.activate(this.id);
41559 getWidth : function(){
41560 return this.inner.getWidth();
41563 setWidth : function(width){
41564 var iwidth = width - this.linode.getPadding("lr");
41565 this.inner.setWidth(iwidth);
41566 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41567 this.linode.setWidth(width);
41571 * Show or hide the tab
41572 * @param {Boolean} hidden True to hide or false to show.
41574 setHidden : function(hidden){
41575 this.hidden = hidden;
41576 this.linode.setStyle("display", hidden ? "none" : "");
41580 * Returns true if this tab is "hidden"
41581 * @return {Boolean}
41583 isHidden : function(){
41584 return this.hidden;
41588 * Returns the text for this tab
41591 getText : function(){
41595 autoSize : function(){
41596 //this.el.beginMeasure();
41597 this.textEl.setWidth(1);
41599 * #2804 [new] Tabs in Roojs
41600 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41602 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41603 //this.el.endMeasure();
41607 * Sets the text for the tab (Note: this also sets the tooltip text)
41608 * @param {String} text The tab's text and tooltip
41610 setText : function(text){
41612 this.textEl.update(text);
41613 this.setTooltip(text);
41614 //if(!this.tabPanel.resizeTabs){
41615 // this.autoSize();
41619 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41621 activate : function(){
41622 this.tabPanel.activate(this.id);
41626 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41628 disable : function(){
41629 if(this.tabPanel.active != this){
41630 this.disabled = true;
41631 this.status_node.addClass("disabled");
41636 * Enables this TabPanelItem if it was previously disabled.
41638 enable : function(){
41639 this.disabled = false;
41640 this.status_node.removeClass("disabled");
41644 * Sets the content for this TabPanelItem.
41645 * @param {String} content The content
41646 * @param {Boolean} loadScripts true to look for and load scripts
41648 setContent : function(content, loadScripts){
41649 this.bodyEl.update(content, loadScripts);
41653 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41654 * @return {Roo.UpdateManager} The UpdateManager
41656 getUpdateManager : function(){
41657 return this.bodyEl.getUpdateManager();
41661 * Set a URL to be used to load the content for this TabPanelItem.
41662 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41663 * @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)
41664 * @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)
41665 * @return {Roo.UpdateManager} The UpdateManager
41667 setUrl : function(url, params, loadOnce){
41668 if(this.refreshDelegate){
41669 this.un('activate', this.refreshDelegate);
41671 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41672 this.on("activate", this.refreshDelegate);
41673 return this.bodyEl.getUpdateManager();
41677 _handleRefresh : function(url, params, loadOnce){
41678 if(!loadOnce || !this.loaded){
41679 var updater = this.bodyEl.getUpdateManager();
41680 updater.update(url, params, this._setLoaded.createDelegate(this));
41685 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41686 * Will fail silently if the setUrl method has not been called.
41687 * This does not activate the panel, just updates its content.
41689 refresh : function(){
41690 if(this.refreshDelegate){
41691 this.loaded = false;
41692 this.refreshDelegate();
41697 _setLoaded : function(){
41698 this.loaded = true;
41702 closeClick : function(e){
41705 this.fireEvent("beforeclose", this, o);
41706 if(o.cancel !== true){
41707 this.tabPanel.removeTab(this.id);
41711 * The text displayed in the tooltip for the close icon.
41714 closeText : "Close this tab"
41717 * This script refer to:
41718 * Title: International Telephone Input
41719 * Author: Jack O'Connor
41720 * Code version: v12.1.12
41721 * Availability: https://github.com/jackocnr/intl-tel-input.git
41724 Roo.bootstrap.PhoneInputData = function() {
41727 "Afghanistan (افغانستان)",
41732 "Albania (Shqipëri)",
41737 "Algeria (الجزائر)",
41762 "Antigua and Barbuda",
41772 "Armenia (Հայաստան)",
41788 "Austria (Österreich)",
41793 "Azerbaijan (Azərbaycan)",
41803 "Bahrain (البحرين)",
41808 "Bangladesh (বাংলাদেশ)",
41818 "Belarus (Беларусь)",
41823 "Belgium (België)",
41853 "Bosnia and Herzegovina (Босна и Херцеговина)",
41868 "British Indian Ocean Territory",
41873 "British Virgin Islands",
41883 "Bulgaria (България)",
41893 "Burundi (Uburundi)",
41898 "Cambodia (កម្ពុជា)",
41903 "Cameroon (Cameroun)",
41912 ["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"]
41915 "Cape Verde (Kabu Verdi)",
41920 "Caribbean Netherlands",
41931 "Central African Republic (République centrafricaine)",
41951 "Christmas Island",
41957 "Cocos (Keeling) Islands",
41968 "Comoros (جزر القمر)",
41973 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41978 "Congo (Republic) (Congo-Brazzaville)",
41998 "Croatia (Hrvatska)",
42019 "Czech Republic (Česká republika)",
42024 "Denmark (Danmark)",
42039 "Dominican Republic (República Dominicana)",
42043 ["809", "829", "849"]
42061 "Equatorial Guinea (Guinea Ecuatorial)",
42081 "Falkland Islands (Islas Malvinas)",
42086 "Faroe Islands (Føroyar)",
42107 "French Guiana (Guyane française)",
42112 "French Polynesia (Polynésie française)",
42127 "Georgia (საქართველო)",
42132 "Germany (Deutschland)",
42152 "Greenland (Kalaallit Nunaat)",
42189 "Guinea-Bissau (Guiné Bissau)",
42214 "Hungary (Magyarország)",
42219 "Iceland (Ísland)",
42239 "Iraq (العراق)",
42255 "Israel (ישראל)",
42282 "Jordan (الأردن)",
42287 "Kazakhstan (Казахстан)",
42308 "Kuwait (الكويت)",
42313 "Kyrgyzstan (Кыргызстан)",
42323 "Latvia (Latvija)",
42328 "Lebanon (لبنان)",
42343 "Libya (ليبيا)",
42353 "Lithuania (Lietuva)",
42368 "Macedonia (FYROM) (Македонија)",
42373 "Madagascar (Madagasikara)",
42403 "Marshall Islands",
42413 "Mauritania (موريتانيا)",
42418 "Mauritius (Moris)",
42439 "Moldova (Republica Moldova)",
42449 "Mongolia (Монгол)",
42454 "Montenegro (Crna Gora)",
42464 "Morocco (المغرب)",
42470 "Mozambique (Moçambique)",
42475 "Myanmar (Burma) (မြန်မာ)",
42480 "Namibia (Namibië)",
42495 "Netherlands (Nederland)",
42500 "New Caledonia (Nouvelle-Calédonie)",
42535 "North Korea (조선 민주주의 인민 공화국)",
42540 "Northern Mariana Islands",
42556 "Pakistan (پاکستان)",
42566 "Palestine (فلسطين)",
42576 "Papua New Guinea",
42618 "Réunion (La Réunion)",
42624 "Romania (România)",
42640 "Saint Barthélemy",
42651 "Saint Kitts and Nevis",
42661 "Saint Martin (Saint-Martin (partie française))",
42667 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42672 "Saint Vincent and the Grenadines",
42687 "São Tomé and Príncipe (São Tomé e Príncipe)",
42692 "Saudi Arabia (المملكة العربية السعودية)",
42697 "Senegal (Sénégal)",
42727 "Slovakia (Slovensko)",
42732 "Slovenia (Slovenija)",
42742 "Somalia (Soomaaliya)",
42752 "South Korea (대한민국)",
42757 "South Sudan (جنوب السودان)",
42767 "Sri Lanka (ශ්රී ලංකාව)",
42772 "Sudan (السودان)",
42782 "Svalbard and Jan Mayen",
42793 "Sweden (Sverige)",
42798 "Switzerland (Schweiz)",
42803 "Syria (سوريا)",
42848 "Trinidad and Tobago",
42853 "Tunisia (تونس)",
42858 "Turkey (Türkiye)",
42868 "Turks and Caicos Islands",
42878 "U.S. Virgin Islands",
42888 "Ukraine (Україна)",
42893 "United Arab Emirates (الإمارات العربية المتحدة)",
42915 "Uzbekistan (Oʻzbekiston)",
42925 "Vatican City (Città del Vaticano)",
42936 "Vietnam (Việt Nam)",
42941 "Wallis and Futuna (Wallis-et-Futuna)",
42946 "Western Sahara (الصحراء الغربية)",
42952 "Yemen (اليمن)",
42976 * This script refer to:
42977 * Title: International Telephone Input
42978 * Author: Jack O'Connor
42979 * Code version: v12.1.12
42980 * Availability: https://github.com/jackocnr/intl-tel-input.git
42984 * @class Roo.bootstrap.PhoneInput
42985 * @extends Roo.bootstrap.TriggerField
42986 * An input with International dial-code selection
42988 * @cfg {String} defaultDialCode default '+852'
42989 * @cfg {Array} preferedCountries default []
42992 * Create a new PhoneInput.
42993 * @param {Object} config Configuration options
42996 Roo.bootstrap.PhoneInput = function(config) {
42997 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43000 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43002 listWidth: undefined,
43004 selectedClass: 'active',
43006 invalidClass : "has-warning",
43008 validClass: 'has-success',
43010 allowed: '0123456789',
43015 * @cfg {String} defaultDialCode The default dial code when initializing the input
43017 defaultDialCode: '+852',
43020 * @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
43022 preferedCountries: false,
43024 getAutoCreate : function()
43026 var data = Roo.bootstrap.PhoneInputData();
43027 var align = this.labelAlign || this.parentLabelAlign();
43030 this.allCountries = [];
43031 this.dialCodeMapping = [];
43033 for (var i = 0; i < data.length; i++) {
43035 this.allCountries[i] = {
43039 priority: c[3] || 0,
43040 areaCodes: c[4] || null
43042 this.dialCodeMapping[c[2]] = {
43045 priority: c[3] || 0,
43046 areaCodes: c[4] || null
43058 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43059 maxlength: this.max_length,
43060 cls : 'form-control tel-input',
43061 autocomplete: 'new-password'
43064 var hiddenInput = {
43067 cls: 'hidden-tel-input'
43071 hiddenInput.name = this.name;
43074 if (this.disabled) {
43075 input.disabled = true;
43078 var flag_container = {
43095 cls: this.hasFeedback ? 'has-feedback' : '',
43101 cls: 'dial-code-holder',
43108 cls: 'roo-select2-container input-group',
43115 if (this.fieldLabel.length) {
43118 tooltip: 'This field is required'
43124 cls: 'control-label',
43130 html: this.fieldLabel
43133 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43139 if(this.indicatorpos == 'right') {
43140 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43147 if(align == 'left') {
43155 if(this.labelWidth > 12){
43156 label.style = "width: " + this.labelWidth + 'px';
43158 if(this.labelWidth < 13 && this.labelmd == 0){
43159 this.labelmd = this.labelWidth;
43161 if(this.labellg > 0){
43162 label.cls += ' col-lg-' + this.labellg;
43163 input.cls += ' col-lg-' + (12 - this.labellg);
43165 if(this.labelmd > 0){
43166 label.cls += ' col-md-' + this.labelmd;
43167 container.cls += ' col-md-' + (12 - this.labelmd);
43169 if(this.labelsm > 0){
43170 label.cls += ' col-sm-' + this.labelsm;
43171 container.cls += ' col-sm-' + (12 - this.labelsm);
43173 if(this.labelxs > 0){
43174 label.cls += ' col-xs-' + this.labelxs;
43175 container.cls += ' col-xs-' + (12 - this.labelxs);
43185 var settings = this;
43187 ['xs','sm','md','lg'].map(function(size){
43188 if (settings[size]) {
43189 cfg.cls += ' col-' + size + '-' + settings[size];
43193 this.store = new Roo.data.Store({
43194 proxy : new Roo.data.MemoryProxy({}),
43195 reader : new Roo.data.JsonReader({
43206 'name' : 'dialCode',
43210 'name' : 'priority',
43214 'name' : 'areaCodes',
43221 if(!this.preferedCountries) {
43222 this.preferedCountries = [
43229 var p = this.preferedCountries.reverse();
43232 for (var i = 0; i < p.length; i++) {
43233 for (var j = 0; j < this.allCountries.length; j++) {
43234 if(this.allCountries[j].iso2 == p[i]) {
43235 var t = this.allCountries[j];
43236 this.allCountries.splice(j,1);
43237 this.allCountries.unshift(t);
43243 this.store.proxy.data = {
43245 data: this.allCountries
43251 initEvents : function()
43254 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43256 this.indicator = this.indicatorEl();
43257 this.flag = this.flagEl();
43258 this.dialCodeHolder = this.dialCodeHolderEl();
43260 this.trigger = this.el.select('div.flag-box',true).first();
43261 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43266 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43267 _this.list.setWidth(lw);
43270 this.list.on('mouseover', this.onViewOver, this);
43271 this.list.on('mousemove', this.onViewMove, this);
43272 this.inputEl().on("keyup", this.onKeyUp, this);
43273 this.inputEl().on("keypress", this.onKeyPress, this);
43275 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43277 this.view = new Roo.View(this.list, this.tpl, {
43278 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43281 this.view.on('click', this.onViewClick, this);
43282 this.setValue(this.defaultDialCode);
43285 onTriggerClick : function(e)
43287 Roo.log('trigger click');
43292 if(this.isExpanded()){
43294 this.hasFocus = false;
43296 this.store.load({});
43297 this.hasFocus = true;
43302 isExpanded : function()
43304 return this.list.isVisible();
43307 collapse : function()
43309 if(!this.isExpanded()){
43313 Roo.get(document).un('mousedown', this.collapseIf, this);
43314 Roo.get(document).un('mousewheel', this.collapseIf, this);
43315 this.fireEvent('collapse', this);
43319 expand : function()
43323 if(this.isExpanded() || !this.hasFocus){
43327 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43328 this.list.setWidth(lw);
43331 this.restrictHeight();
43333 Roo.get(document).on('mousedown', this.collapseIf, this);
43334 Roo.get(document).on('mousewheel', this.collapseIf, this);
43336 this.fireEvent('expand', this);
43339 restrictHeight : function()
43341 this.list.alignTo(this.inputEl(), this.listAlign);
43342 this.list.alignTo(this.inputEl(), this.listAlign);
43345 onViewOver : function(e, t)
43347 if(this.inKeyMode){
43350 var item = this.view.findItemFromChild(t);
43353 var index = this.view.indexOf(item);
43354 this.select(index, false);
43359 onViewClick : function(view, doFocus, el, e)
43361 var index = this.view.getSelectedIndexes()[0];
43363 var r = this.store.getAt(index);
43366 this.onSelect(r, index);
43368 if(doFocus !== false && !this.blockFocus){
43369 this.inputEl().focus();
43373 onViewMove : function(e, t)
43375 this.inKeyMode = false;
43378 select : function(index, scrollIntoView)
43380 this.selectedIndex = index;
43381 this.view.select(index);
43382 if(scrollIntoView !== false){
43383 var el = this.view.getNode(index);
43385 this.list.scrollChildIntoView(el, false);
43390 createList : function()
43392 this.list = Roo.get(document.body).createChild({
43394 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43395 style: 'display:none'
43398 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43401 collapseIf : function(e)
43403 var in_combo = e.within(this.el);
43404 var in_list = e.within(this.list);
43405 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43407 if (in_combo || in_list || is_list) {
43413 onSelect : function(record, index)
43415 if(this.fireEvent('beforeselect', this, record, index) !== false){
43417 this.setFlagClass(record.data.iso2);
43418 this.setDialCode(record.data.dialCode);
43419 this.hasFocus = false;
43421 this.fireEvent('select', this, record, index);
43425 flagEl : function()
43427 var flag = this.el.select('div.flag',true).first();
43434 dialCodeHolderEl : function()
43436 var d = this.el.select('input.dial-code-holder',true).first();
43443 setDialCode : function(v)
43445 this.dialCodeHolder.dom.value = '+'+v;
43448 setFlagClass : function(n)
43450 this.flag.dom.className = 'flag '+n;
43453 getValue : function()
43455 var v = this.inputEl().getValue();
43456 if(this.dialCodeHolder) {
43457 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43462 setValue : function(v)
43464 var d = this.getDialCode(v);
43466 //invalid dial code
43467 if(v.length == 0 || !d || d.length == 0) {
43469 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43470 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43476 this.setFlagClass(this.dialCodeMapping[d].iso2);
43477 this.setDialCode(d);
43478 this.inputEl().dom.value = v.replace('+'+d,'');
43479 this.hiddenEl().dom.value = this.getValue();
43484 getDialCode : function(v)
43488 if (v.length == 0) {
43489 return this.dialCodeHolder.dom.value;
43493 if (v.charAt(0) != "+") {
43496 var numericChars = "";
43497 for (var i = 1; i < v.length; i++) {
43498 var c = v.charAt(i);
43501 if (this.dialCodeMapping[numericChars]) {
43502 dialCode = v.substr(1, i);
43504 if (numericChars.length == 4) {
43514 this.setValue(this.defaultDialCode);
43518 hiddenEl : function()
43520 return this.el.select('input.hidden-tel-input',true).first();
43523 // after setting val
43524 onKeyUp : function(e){
43525 this.setValue(this.getValue());
43528 onKeyPress : function(e){
43529 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43536 * @class Roo.bootstrap.MoneyField
43537 * @extends Roo.bootstrap.ComboBox
43538 * Bootstrap MoneyField class
43541 * Create a new MoneyField.
43542 * @param {Object} config Configuration options
43545 Roo.bootstrap.MoneyField = function(config) {
43547 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43551 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43554 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43556 allowDecimals : true,
43558 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43560 decimalSeparator : ".",
43562 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43564 decimalPrecision : 0,
43566 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43568 allowNegative : true,
43570 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43574 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43576 minValue : Number.NEGATIVE_INFINITY,
43578 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43580 maxValue : Number.MAX_VALUE,
43582 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43584 minText : "The minimum value for this field is {0}",
43586 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43588 maxText : "The maximum value for this field is {0}",
43590 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43591 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43593 nanText : "{0} is not a valid number",
43595 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43599 * @cfg {String} defaults currency of the MoneyField
43600 * value should be in lkey
43602 defaultCurrency : false,
43604 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43606 thousandsDelimiter : false,
43608 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43619 getAutoCreate : function()
43621 var align = this.labelAlign || this.parentLabelAlign();
43633 cls : 'form-control roo-money-amount-input',
43634 autocomplete: 'new-password'
43637 var hiddenInput = {
43641 cls: 'hidden-number-input'
43644 if(this.max_length) {
43645 input.maxlength = this.max_length;
43649 hiddenInput.name = this.name;
43652 if (this.disabled) {
43653 input.disabled = true;
43656 var clg = 12 - this.inputlg;
43657 var cmd = 12 - this.inputmd;
43658 var csm = 12 - this.inputsm;
43659 var cxs = 12 - this.inputxs;
43663 cls : 'row roo-money-field',
43667 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43671 cls: 'roo-select2-container input-group',
43675 cls : 'form-control roo-money-currency-input',
43676 autocomplete: 'new-password',
43678 name : this.currencyName
43682 cls : 'input-group-addon',
43696 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43700 cls: this.hasFeedback ? 'has-feedback' : '',
43711 if (this.fieldLabel.length) {
43714 tooltip: 'This field is required'
43720 cls: 'control-label',
43726 html: this.fieldLabel
43729 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43735 if(this.indicatorpos == 'right') {
43736 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43743 if(align == 'left') {
43751 if(this.labelWidth > 12){
43752 label.style = "width: " + this.labelWidth + 'px';
43754 if(this.labelWidth < 13 && this.labelmd == 0){
43755 this.labelmd = this.labelWidth;
43757 if(this.labellg > 0){
43758 label.cls += ' col-lg-' + this.labellg;
43759 input.cls += ' col-lg-' + (12 - this.labellg);
43761 if(this.labelmd > 0){
43762 label.cls += ' col-md-' + this.labelmd;
43763 container.cls += ' col-md-' + (12 - this.labelmd);
43765 if(this.labelsm > 0){
43766 label.cls += ' col-sm-' + this.labelsm;
43767 container.cls += ' col-sm-' + (12 - this.labelsm);
43769 if(this.labelxs > 0){
43770 label.cls += ' col-xs-' + this.labelxs;
43771 container.cls += ' col-xs-' + (12 - this.labelxs);
43782 var settings = this;
43784 ['xs','sm','md','lg'].map(function(size){
43785 if (settings[size]) {
43786 cfg.cls += ' col-' + size + '-' + settings[size];
43793 initEvents : function()
43795 this.indicator = this.indicatorEl();
43797 this.initCurrencyEvent();
43799 this.initNumberEvent();
43802 initCurrencyEvent : function()
43805 throw "can not find store for combo";
43808 this.store = Roo.factory(this.store, Roo.data);
43809 this.store.parent = this;
43813 this.triggerEl = this.el.select('.input-group-addon', true).first();
43815 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43820 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43821 _this.list.setWidth(lw);
43824 this.list.on('mouseover', this.onViewOver, this);
43825 this.list.on('mousemove', this.onViewMove, this);
43826 this.list.on('scroll', this.onViewScroll, this);
43829 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43832 this.view = new Roo.View(this.list, this.tpl, {
43833 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43836 this.view.on('click', this.onViewClick, this);
43838 this.store.on('beforeload', this.onBeforeLoad, this);
43839 this.store.on('load', this.onLoad, this);
43840 this.store.on('loadexception', this.onLoadException, this);
43842 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43843 "up" : function(e){
43844 this.inKeyMode = true;
43848 "down" : function(e){
43849 if(!this.isExpanded()){
43850 this.onTriggerClick();
43852 this.inKeyMode = true;
43857 "enter" : function(e){
43860 if(this.fireEvent("specialkey", this, e)){
43861 this.onViewClick(false);
43867 "esc" : function(e){
43871 "tab" : function(e){
43874 if(this.fireEvent("specialkey", this, e)){
43875 this.onViewClick(false);
43883 doRelay : function(foo, bar, hname){
43884 if(hname == 'down' || this.scope.isExpanded()){
43885 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43893 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43897 initNumberEvent : function(e)
43899 this.inputEl().on("keydown" , this.fireKey, this);
43900 this.inputEl().on("focus", this.onFocus, this);
43901 this.inputEl().on("blur", this.onBlur, this);
43903 this.inputEl().relayEvent('keyup', this);
43905 if(this.indicator){
43906 this.indicator.addClass('invisible');
43909 this.originalValue = this.getValue();
43911 if(this.validationEvent == 'keyup'){
43912 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43913 this.inputEl().on('keyup', this.filterValidation, this);
43915 else if(this.validationEvent !== false){
43916 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43919 if(this.selectOnFocus){
43920 this.on("focus", this.preFocus, this);
43923 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43924 this.inputEl().on("keypress", this.filterKeys, this);
43926 this.inputEl().relayEvent('keypress', this);
43929 var allowed = "0123456789";
43931 if(this.allowDecimals){
43932 allowed += this.decimalSeparator;
43935 if(this.allowNegative){
43939 if(this.thousandsDelimiter) {
43943 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43945 var keyPress = function(e){
43947 var k = e.getKey();
43949 var c = e.getCharCode();
43952 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43953 allowed.indexOf(String.fromCharCode(c)) === -1
43959 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43963 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43968 this.inputEl().on("keypress", keyPress, this);
43972 onTriggerClick : function(e)
43979 this.loadNext = false;
43981 if(this.isExpanded()){
43986 this.hasFocus = true;
43988 if(this.triggerAction == 'all') {
43989 this.doQuery(this.allQuery, true);
43993 this.doQuery(this.getRawValue());
43996 getCurrency : function()
43998 var v = this.currencyEl().getValue();
44003 restrictHeight : function()
44005 this.list.alignTo(this.currencyEl(), this.listAlign);
44006 this.list.alignTo(this.currencyEl(), this.listAlign);
44009 onViewClick : function(view, doFocus, el, e)
44011 var index = this.view.getSelectedIndexes()[0];
44013 var r = this.store.getAt(index);
44016 this.onSelect(r, index);
44020 onSelect : function(record, index){
44022 if(this.fireEvent('beforeselect', this, record, index) !== false){
44024 this.setFromCurrencyData(index > -1 ? record.data : false);
44028 this.fireEvent('select', this, record, index);
44032 setFromCurrencyData : function(o)
44036 this.lastCurrency = o;
44038 if (this.currencyField) {
44039 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44041 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44044 this.lastSelectionText = currency;
44046 //setting default currency
44047 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44048 this.setCurrency(this.defaultCurrency);
44052 this.setCurrency(currency);
44055 setFromData : function(o)
44059 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44061 this.setFromCurrencyData(c);
44066 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44068 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44071 this.setValue(value);
44075 setCurrency : function(v)
44077 this.currencyValue = v;
44080 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44085 setValue : function(v)
44087 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44093 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44095 this.inputEl().dom.value = (v == '') ? '' :
44096 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44098 if(!this.allowZero && v === '0') {
44099 this.hiddenEl().dom.value = '';
44100 this.inputEl().dom.value = '';
44107 getRawValue : function()
44109 var v = this.inputEl().getValue();
44114 getValue : function()
44116 return this.fixPrecision(this.parseValue(this.getRawValue()));
44119 parseValue : function(value)
44121 if(this.thousandsDelimiter) {
44123 r = new RegExp(",", "g");
44124 value = value.replace(r, "");
44127 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44128 return isNaN(value) ? '' : value;
44132 fixPrecision : function(value)
44134 if(this.thousandsDelimiter) {
44136 r = new RegExp(",", "g");
44137 value = value.replace(r, "");
44140 var nan = isNaN(value);
44142 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44143 return nan ? '' : value;
44145 return parseFloat(value).toFixed(this.decimalPrecision);
44148 decimalPrecisionFcn : function(v)
44150 return Math.floor(v);
44153 validateValue : function(value)
44155 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44159 var num = this.parseValue(value);
44162 this.markInvalid(String.format(this.nanText, value));
44166 if(num < this.minValue){
44167 this.markInvalid(String.format(this.minText, this.minValue));
44171 if(num > this.maxValue){
44172 this.markInvalid(String.format(this.maxText, this.maxValue));
44179 validate : function()
44181 if(this.disabled || this.allowBlank){
44186 var currency = this.getCurrency();
44188 if(this.validateValue(this.getRawValue()) && currency.length){
44193 this.markInvalid();
44197 getName: function()
44202 beforeBlur : function()
44208 var v = this.parseValue(this.getRawValue());
44215 onBlur : function()
44219 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44220 //this.el.removeClass(this.focusClass);
44223 this.hasFocus = false;
44225 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44229 var v = this.getValue();
44231 if(String(v) !== String(this.startValue)){
44232 this.fireEvent('change', this, v, this.startValue);
44235 this.fireEvent("blur", this);
44238 inputEl : function()
44240 return this.el.select('.roo-money-amount-input', true).first();
44243 currencyEl : function()
44245 return this.el.select('.roo-money-currency-input', true).first();
44248 hiddenEl : function()
44250 return this.el.select('input.hidden-number-input',true).first();
44254 * @class Roo.bootstrap.BezierSignature
44255 * @extends Roo.bootstrap.Component
44256 * Bootstrap BezierSignature class
44257 * This script refer to:
44258 * Title: Signature Pad
44260 * Availability: https://github.com/szimek/signature_pad
44263 * Create a new BezierSignature
44264 * @param {Object} config The config object
44267 Roo.bootstrap.BezierSignature = function(config){
44268 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44274 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44281 mouse_btn_down: true,
44284 * @cfg {int} canvas height
44286 canvas_height: '200px',
44289 * @cfg {float|function} Radius of a single dot.
44294 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44299 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44304 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44309 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44314 * @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.
44316 bg_color: 'rgba(0, 0, 0, 0)',
44319 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44321 dot_color: 'black',
44324 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44326 velocity_filter_weight: 0.7,
44329 * @cfg {function} Callback when stroke begin.
44334 * @cfg {function} Callback when stroke end.
44338 getAutoCreate : function()
44340 var cls = 'roo-signature column';
44343 cls += ' ' + this.cls;
44353 for(var i = 0; i < col_sizes.length; i++) {
44354 if(this[col_sizes[i]]) {
44355 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44365 cls: 'roo-signature-body',
44369 cls: 'roo-signature-body-canvas',
44370 height: this.canvas_height,
44371 width: this.canvas_width
44378 style: 'display: none'
44386 initEvents: function()
44388 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44390 var canvas = this.canvasEl();
44392 // mouse && touch event swapping...
44393 canvas.dom.style.touchAction = 'none';
44394 canvas.dom.style.msTouchAction = 'none';
44396 this.mouse_btn_down = false;
44397 canvas.on('mousedown', this._handleMouseDown, this);
44398 canvas.on('mousemove', this._handleMouseMove, this);
44399 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44401 if (window.PointerEvent) {
44402 canvas.on('pointerdown', this._handleMouseDown, this);
44403 canvas.on('pointermove', this._handleMouseMove, this);
44404 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44407 if ('ontouchstart' in window) {
44408 canvas.on('touchstart', this._handleTouchStart, this);
44409 canvas.on('touchmove', this._handleTouchMove, this);
44410 canvas.on('touchend', this._handleTouchEnd, this);
44413 Roo.EventManager.onWindowResize(this.resize, this, true);
44415 // file input event
44416 this.fileEl().on('change', this.uploadImage, this);
44423 resize: function(){
44425 var canvas = this.canvasEl().dom;
44426 var ctx = this.canvasElCtx();
44427 var img_data = false;
44429 if(canvas.width > 0) {
44430 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44432 // setting canvas width will clean img data
44435 var style = window.getComputedStyle ?
44436 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44438 var padding_left = parseInt(style.paddingLeft) || 0;
44439 var padding_right = parseInt(style.paddingRight) || 0;
44441 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44444 ctx.putImageData(img_data, 0, 0);
44448 _handleMouseDown: function(e)
44450 if (e.browserEvent.which === 1) {
44451 this.mouse_btn_down = true;
44452 this.strokeBegin(e);
44456 _handleMouseMove: function (e)
44458 if (this.mouse_btn_down) {
44459 this.strokeMoveUpdate(e);
44463 _handleMouseUp: function (e)
44465 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44466 this.mouse_btn_down = false;
44471 _handleTouchStart: function (e) {
44473 e.preventDefault();
44474 if (e.browserEvent.targetTouches.length === 1) {
44475 // var touch = e.browserEvent.changedTouches[0];
44476 // this.strokeBegin(touch);
44478 this.strokeBegin(e); // assume e catching the correct xy...
44482 _handleTouchMove: function (e) {
44483 e.preventDefault();
44484 // var touch = event.targetTouches[0];
44485 // _this._strokeMoveUpdate(touch);
44486 this.strokeMoveUpdate(e);
44489 _handleTouchEnd: function (e) {
44490 var wasCanvasTouched = e.target === this.canvasEl().dom;
44491 if (wasCanvasTouched) {
44492 e.preventDefault();
44493 // var touch = event.changedTouches[0];
44494 // _this._strokeEnd(touch);
44499 reset: function () {
44500 this._lastPoints = [];
44501 this._lastVelocity = 0;
44502 this._lastWidth = (this.min_width + this.max_width) / 2;
44503 this.canvasElCtx().fillStyle = this.dot_color;
44506 strokeMoveUpdate: function(e)
44508 this.strokeUpdate(e);
44510 if (this.throttle) {
44511 this.throttleStroke(this.strokeUpdate, this.throttle);
44514 this.strokeUpdate(e);
44518 strokeBegin: function(e)
44520 var newPointGroup = {
44521 color: this.dot_color,
44525 if (typeof this.onBegin === 'function') {
44529 this.curve_data.push(newPointGroup);
44531 this.strokeUpdate(e);
44534 strokeUpdate: function(e)
44536 var rect = this.canvasEl().dom.getBoundingClientRect();
44537 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44538 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44539 var lastPoints = lastPointGroup.points;
44540 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44541 var isLastPointTooClose = lastPoint
44542 ? point.distanceTo(lastPoint) <= this.min_distance
44544 var color = lastPointGroup.color;
44545 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44546 var curve = this.addPoint(point);
44548 this.drawDot({color: color, point: point});
44551 this.drawCurve({color: color, curve: curve});
44561 strokeEnd: function(e)
44563 this.strokeUpdate(e);
44564 if (typeof this.onEnd === 'function') {
44569 addPoint: function (point) {
44570 var _lastPoints = this._lastPoints;
44571 _lastPoints.push(point);
44572 if (_lastPoints.length > 2) {
44573 if (_lastPoints.length === 3) {
44574 _lastPoints.unshift(_lastPoints[0]);
44576 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44577 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44578 _lastPoints.shift();
44584 calculateCurveWidths: function (startPoint, endPoint) {
44585 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44586 (1 - this.velocity_filter_weight) * this._lastVelocity;
44588 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44591 start: this._lastWidth
44594 this._lastVelocity = velocity;
44595 this._lastWidth = newWidth;
44599 drawDot: function (_a) {
44600 var color = _a.color, point = _a.point;
44601 var ctx = this.canvasElCtx();
44602 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44604 this.drawCurveSegment(point.x, point.y, width);
44606 ctx.fillStyle = color;
44610 drawCurve: function (_a) {
44611 var color = _a.color, curve = _a.curve;
44612 var ctx = this.canvasElCtx();
44613 var widthDelta = curve.endWidth - curve.startWidth;
44614 var drawSteps = Math.floor(curve.length()) * 2;
44616 ctx.fillStyle = color;
44617 for (var i = 0; i < drawSteps; i += 1) {
44618 var t = i / drawSteps;
44624 var x = uuu * curve.startPoint.x;
44625 x += 3 * uu * t * curve.control1.x;
44626 x += 3 * u * tt * curve.control2.x;
44627 x += ttt * curve.endPoint.x;
44628 var y = uuu * curve.startPoint.y;
44629 y += 3 * uu * t * curve.control1.y;
44630 y += 3 * u * tt * curve.control2.y;
44631 y += ttt * curve.endPoint.y;
44632 var width = curve.startWidth + ttt * widthDelta;
44633 this.drawCurveSegment(x, y, width);
44639 drawCurveSegment: function (x, y, width) {
44640 var ctx = this.canvasElCtx();
44642 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44643 this.is_empty = false;
44648 var ctx = this.canvasElCtx();
44649 var canvas = this.canvasEl().dom;
44650 ctx.fillStyle = this.bg_color;
44651 ctx.clearRect(0, 0, canvas.width, canvas.height);
44652 ctx.fillRect(0, 0, canvas.width, canvas.height);
44653 this.curve_data = [];
44655 this.is_empty = true;
44660 return this.el.select('input',true).first();
44663 canvasEl: function()
44665 return this.el.select('canvas',true).first();
44668 canvasElCtx: function()
44670 return this.el.select('canvas',true).first().dom.getContext('2d');
44673 getImage: function(type)
44675 if(this.is_empty) {
44680 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44683 drawFromImage: function(img_src)
44685 var img = new Image();
44687 img.onload = function(){
44688 this.canvasElCtx().drawImage(img, 0, 0);
44693 this.is_empty = false;
44696 selectImage: function()
44698 this.fileEl().dom.click();
44701 uploadImage: function(e)
44703 var reader = new FileReader();
44705 reader.onload = function(e){
44706 var img = new Image();
44707 img.onload = function(){
44709 this.canvasElCtx().drawImage(img, 0, 0);
44711 img.src = e.target.result;
44714 reader.readAsDataURL(e.target.files[0]);
44717 // Bezier Point Constructor
44718 Point: (function () {
44719 function Point(x, y, time) {
44722 this.time = time || Date.now();
44724 Point.prototype.distanceTo = function (start) {
44725 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44727 Point.prototype.equals = function (other) {
44728 return this.x === other.x && this.y === other.y && this.time === other.time;
44730 Point.prototype.velocityFrom = function (start) {
44731 return this.time !== start.time
44732 ? this.distanceTo(start) / (this.time - start.time)
44739 // Bezier Constructor
44740 Bezier: (function () {
44741 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44742 this.startPoint = startPoint;
44743 this.control2 = control2;
44744 this.control1 = control1;
44745 this.endPoint = endPoint;
44746 this.startWidth = startWidth;
44747 this.endWidth = endWidth;
44749 Bezier.fromPoints = function (points, widths, scope) {
44750 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44751 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44752 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44754 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44755 var dx1 = s1.x - s2.x;
44756 var dy1 = s1.y - s2.y;
44757 var dx2 = s2.x - s3.x;
44758 var dy2 = s2.y - s3.y;
44759 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44760 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44761 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44762 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44763 var dxm = m1.x - m2.x;
44764 var dym = m1.y - m2.y;
44765 var k = l2 / (l1 + l2);
44766 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44767 var tx = s2.x - cm.x;
44768 var ty = s2.y - cm.y;
44770 c1: new scope.Point(m1.x + tx, m1.y + ty),
44771 c2: new scope.Point(m2.x + tx, m2.y + ty)
44774 Bezier.prototype.length = function () {
44779 for (var i = 0; i <= steps; i += 1) {
44781 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44782 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44784 var xdiff = cx - px;
44785 var ydiff = cy - py;
44786 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44793 Bezier.prototype.point = function (t, start, c1, c2, end) {
44794 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44795 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44796 + (3.0 * c2 * (1.0 - t) * t * t)
44797 + (end * t * t * t);
44802 throttleStroke: function(fn, wait) {
44803 if (wait === void 0) { wait = 250; }
44805 var timeout = null;
44809 var later = function () {
44810 previous = Date.now();
44812 result = fn.apply(storedContext, storedArgs);
44814 storedContext = null;
44818 return function wrapper() {
44820 for (var _i = 0; _i < arguments.length; _i++) {
44821 args[_i] = arguments[_i];
44823 var now = Date.now();
44824 var remaining = wait - (now - previous);
44825 storedContext = this;
44827 if (remaining <= 0 || remaining > wait) {
44829 clearTimeout(timeout);
44833 result = fn.apply(storedContext, storedArgs);
44835 storedContext = null;
44839 else if (!timeout) {
44840 timeout = window.setTimeout(later, remaining);