2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
365 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367 return Roo.get(document.body);
371 * Fetch the element to display the tooltip on.
372 * @return {Roo.Element} defaults to this.el
374 tooltipEl : function()
379 addxtype : function(tree,cntr)
383 cn = Roo.factory(tree);
384 //Roo.log(['addxtype', cn]);
386 cn.parentType = this.xtype; //??
387 cn.parentId = this.id;
389 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390 if (typeof(cn.container_method) == 'string') {
391 cntr = cn.container_method;
395 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
397 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
399 var build_from_html = Roo.XComponent.build_from_html;
401 var is_body = (tree.xtype == 'Body') ;
403 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405 var self_cntr_el = Roo.get(this[cntr](false));
407 // do not try and build conditional elements
408 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414 return this.addxtypeChild(tree,cntr, is_body);
417 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
420 return this.addxtypeChild(Roo.apply({}, tree),cntr);
423 Roo.log('skipping render');
429 if (!build_from_html) {
433 // this i think handles overlaying multiple children of the same type
434 // with the sam eelement.. - which might be buggy..
436 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
442 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453 addxtypeChild : function (tree, cntr, is_body)
455 Roo.debug && Roo.log('addxtypeChild:' + cntr);
457 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
460 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461 (typeof(tree['flexy:foreach']) != 'undefined');
465 skip_children = false;
466 // render the element if it's not BODY.
469 // if parent was disabled, then do not try and create the children..
470 if(!this[cntr](true)){
475 cn = Roo.factory(tree);
477 cn.parentType = this.xtype; //??
478 cn.parentId = this.id;
480 var build_from_html = Roo.XComponent.build_from_html;
483 // does the container contain child eleemnts with 'xtype' attributes.
484 // that match this xtype..
485 // note - when we render we create these as well..
486 // so we should check to see if body has xtype set.
487 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489 var self_cntr_el = Roo.get(this[cntr](false));
490 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492 //Roo.log(Roo.XComponent.build_from_html);
493 //Roo.log("got echild:");
496 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497 // and are not displayed -this causes this to use up the wrong element when matching.
498 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
501 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
508 //echild.dom.removeAttribute('xtype');
510 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511 Roo.debug && Roo.log(self_cntr_el);
512 Roo.debug && Roo.log(echild);
513 Roo.debug && Roo.log(cn);
519 // if object has flexy:if - then it may or may not be rendered.
520 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
521 // skip a flexy if element.
522 Roo.debug && Roo.log('skipping render');
523 Roo.debug && Roo.log(tree);
525 Roo.debug && Roo.log('skipping all children');
526 skip_children = true;
531 // actually if flexy:foreach is found, we really want to create
532 // multiple copies here...
534 //Roo.log(this[cntr]());
535 // some elements do not have render methods.. like the layouts...
537 if(this[cntr](true) === false){
542 cn.render && cn.render(this[cntr](true));
545 // then add the element..
552 if (typeof (tree.menu) != 'undefined') {
553 tree.menu.parentType = cn.xtype;
554 tree.menu.triggerEl = cn.el;
555 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559 if (!tree.items || !tree.items.length) {
561 //Roo.log(["no children", this]);
566 var items = tree.items;
569 //Roo.log(items.length);
571 if (!skip_children) {
572 for(var i =0;i < items.length;i++) {
573 // Roo.log(['add child', items[i]]);
574 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
580 //Roo.log("fire childrenrendered");
582 cn.fireEvent('childrenrendered', this);
588 * Set the element that will be used to show or hide
590 setVisibilityEl : function(el)
592 this.visibilityEl = el;
596 * Get the element that will be used to show or hide
598 getVisibilityEl : function()
600 if (typeof(this.visibilityEl) == 'object') {
601 return this.visibilityEl;
604 if (typeof(this.visibilityEl) == 'string') {
605 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612 * Show a component - removes 'hidden' class
616 if(!this.getVisibilityEl()){
620 this.getVisibilityEl().removeClass(['hidden','d-none']);
622 this.fireEvent('show', this);
627 * Hide a component - adds 'hidden' class
631 if(!this.getVisibilityEl()){
635 this.getVisibilityEl().addClass(['hidden','d-none']);
637 this.fireEvent('hide', this);
650 * @class Roo.bootstrap.Element
651 * @extends Roo.bootstrap.Component
652 * Bootstrap Element class
653 * @cfg {String} html contents of the element
654 * @cfg {String} tag tag of the element
655 * @cfg {String} cls class of the element
656 * @cfg {Boolean} preventDefault (true|false) default false
657 * @cfg {Boolean} clickable (true|false) default false
658 * @cfg {String} role default blank - set to button to force cursor pointer
662 * Create a new Element
663 * @param {Object} config The config object
666 Roo.bootstrap.Element = function(config){
667 Roo.bootstrap.Element.superclass.constructor.call(this, config);
673 * When a element is chick
674 * @param {Roo.bootstrap.Element} this
675 * @param {Roo.EventObject} e
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
688 preventDefault: false,
693 getAutoCreate : function(){
697 // cls: this.cls, double assign in parent class Component.js :: onRender
700 if (this.role !== false) {
701 cfg.role = this.role;
707 initEvents: function()
709 Roo.bootstrap.Element.superclass.initEvents.call(this);
712 this.el.on('click', this.onClick, this);
718 onClick : function(e)
720 if(this.preventDefault){
724 this.fireEvent('click', this, e); // why was this double click before?
732 getValue : function()
734 return this.el.dom.innerHTML;
737 setValue : function(value)
739 this.el.dom.innerHTML = value;
754 * @class Roo.bootstrap.DropTarget
755 * @extends Roo.bootstrap.Element
756 * Bootstrap DropTarget class
758 * @cfg {string} name dropable name
761 * Create a new Dropable Area
762 * @param {Object} config The config object
765 Roo.bootstrap.DropTarget = function(config){
766 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
772 * When a element is chick
773 * @param {Roo.bootstrap.Element} this
774 * @param {Roo.EventObject} e
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
783 getAutoCreate : function(){
788 initEvents: function()
790 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
794 drop : this.dragDrop.createDelegate(this),
795 enter : this.dragEnter.createDelegate(this),
796 out : this.dragOut.createDelegate(this),
797 over : this.dragOver.createDelegate(this)
801 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
804 dragDrop : function(source,e,data)
806 // user has to decide how to impliment this.
809 //this.fireEvent('drop', this, source, e ,data);
813 dragEnter : function(n, dd, e, data)
815 // probably want to resize the element to match the dropped element..
817 this.originalSize = this.el.getSize();
818 this.el.setSize( n.el.getSize());
819 this.dropZone.DDM.refreshCache(this.name);
820 Roo.log([n, dd, e, data]);
823 dragOut : function(value)
825 // resize back to normal
827 this.el.setSize(this.originalSize);
828 this.dropZone.resetConstraints();
831 dragOver : function()
848 * @class Roo.bootstrap.Body
849 * @extends Roo.bootstrap.Component
850 * Bootstrap Body class
854 * @param {Object} config The config object
857 Roo.bootstrap.Body = function(config){
859 config = config || {};
861 Roo.bootstrap.Body.superclass.constructor.call(this, config);
862 this.el = Roo.get(config.el ? config.el : document.body );
863 if (this.cls && this.cls.length) {
864 Roo.get(document.body).addClass(this.cls);
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
870 is_body : true,// just to make sure it's constructed?
875 onRender : function(ct, position)
877 /* Roo.log("Roo.bootstrap.Body - onRender");
878 if (this.cls && this.cls.length) {
879 Roo.get(document.body).addClass(this.cls);
898 * @class Roo.bootstrap.ButtonGroup
899 * @extends Roo.bootstrap.Component
900 * Bootstrap ButtonGroup class
901 * @cfg {String} size lg | sm | xs (default empty normal)
902 * @cfg {String} align vertical | justified (default none)
903 * @cfg {String} direction up | down (default down)
904 * @cfg {Boolean} toolbar false | true
905 * @cfg {Boolean} btn true | false
910 * @param {Object} config The config object
913 Roo.bootstrap.ButtonGroup = function(config){
914 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
925 getAutoCreate : function(){
931 cfg.html = this.html || cfg.html;
942 if (['vertical','justified'].indexOf(this.align)!==-1) {
943 cfg.cls = 'btn-group-' + this.align;
945 if (this.align == 'justified') {
946 console.log(this.items);
950 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951 cfg.cls += ' btn-group-' + this.size;
954 if (this.direction == 'up') {
955 cfg.cls += ' dropup' ;
961 * Add a button to the group (similar to NavItem API.)
963 addItem : function(cfg)
965 var cn = new Roo.bootstrap.Button(cfg);
967 cn.parentId = this.id;
968 cn.onRender(this.el, null);
982 * @class Roo.bootstrap.Button
983 * @extends Roo.bootstrap.Component
984 * Bootstrap Button class
985 * @cfg {String} html The button content
986 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989 * @cfg {String} size (lg|sm|xs)
990 * @cfg {String} tag (a|input|submit)
991 * @cfg {String} href empty or href
992 * @cfg {Boolean} disabled default false;
993 * @cfg {Boolean} isClose default false;
994 * @cfg {String} glyphicon depricated - use fa
995 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996 * @cfg {String} badge text for badge
997 * @cfg {String} theme (default|glow)
998 * @cfg {Boolean} inverse dark themed version
999 * @cfg {Boolean} toggle is it a slidy toggle button
1000 * @cfg {Boolean} pressed default null - if the button ahs active state
1001 * @cfg {String} ontext text for on slidy toggle state
1002 * @cfg {String} offtext text for off slidy toggle state
1003 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1004 * @cfg {Boolean} removeClass remove the standard class..
1005 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1006 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1009 * Create a new button
1010 * @param {Object} config The config object
1014 Roo.bootstrap.Button = function(config){
1015 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1021 * When a button is pressed
1022 * @param {Roo.bootstrap.Button} btn
1023 * @param {Roo.EventObject} e
1028 * When a button is double clicked
1029 * @param {Roo.bootstrap.Button} btn
1030 * @param {Roo.EventObject} e
1035 * After the button has been toggles
1036 * @param {Roo.bootstrap.Button} btn
1037 * @param {Roo.EventObject} e
1038 * @param {boolean} pressed (also available as button.pressed)
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1065 preventDefault: true,
1074 getAutoCreate : function(){
1082 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084 this.tag = 'button';
1088 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090 if (this.toggle == true) {
1093 cls: 'slider-frame roo-button',
1097 'data-on-text':'ON',
1098 'data-off-text':'OFF',
1099 cls: 'slider-button',
1104 // why are we validating the weights?
1105 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106 cfg.cls += ' ' + this.weight;
1113 cfg.cls += ' close';
1115 cfg["aria-hidden"] = true;
1117 cfg.html = "×";
1123 if (this.theme==='default') {
1124 cfg.cls = 'btn roo-button';
1126 //if (this.parentType != 'Navbar') {
1127 this.weight = this.weight.length ? this.weight : 'default';
1129 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133 cfg.cls += ' btn-' + outline + weight;
1134 if (this.weight == 'default') {
1136 cfg.cls += ' btn-' + this.weight;
1139 } else if (this.theme==='glow') {
1142 cfg.cls = 'btn-glow roo-button';
1144 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146 cfg.cls += ' ' + this.weight;
1152 this.cls += ' inverse';
1156 if (this.active || this.pressed === true) {
1157 cfg.cls += ' active';
1160 if (this.disabled) {
1161 cfg.disabled = 'disabled';
1165 Roo.log('changing to ul' );
1167 this.glyphicon = 'caret';
1168 if (Roo.bootstrap.version == 4) {
1169 this.fa = 'caret-down';
1174 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176 //gsRoo.log(this.parentType);
1177 if (this.parentType === 'Navbar' && !this.parent().bar) {
1178 Roo.log('changing to li?');
1187 href : this.href || '#'
1190 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1191 cfg.cls += ' dropdown';
1198 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1200 if (this.glyphicon) {
1201 cfg.html = ' ' + cfg.html;
1206 cls: 'glyphicon glyphicon-' + this.glyphicon
1211 cfg.html = ' ' + cfg.html;
1216 cls: 'fa fas fa-' + this.fa
1226 // cfg.cls='btn roo-button';
1230 var value = cfg.html;
1235 cls: 'glyphicon glyphicon-' + this.glyphicon,
1242 cls: 'fa fas fa-' + this.fa,
1247 var bw = this.badge_weight.length ? this.badge_weight :
1248 (this.weight.length ? this.weight : 'secondary');
1249 bw = bw == 'default' ? 'secondary' : bw;
1255 cls: 'badge badge-' + bw,
1264 cfg.cls += ' dropdown';
1265 cfg.html = typeof(cfg.html) != 'undefined' ?
1266 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1269 if (cfg.tag !== 'a' && this.href !== '') {
1270 throw "Tag must be a to set href.";
1271 } else if (this.href.length > 0) {
1272 cfg.href = this.href;
1275 if(this.removeClass){
1280 cfg.target = this.target;
1285 initEvents: function() {
1286 // Roo.log('init events?');
1287 // Roo.log(this.el.dom);
1290 if (typeof (this.menu) != 'undefined') {
1291 this.menu.parentType = this.xtype;
1292 this.menu.triggerEl = this.el;
1293 this.addxtype(Roo.apply({}, this.menu));
1297 if (this.el.hasClass('roo-button')) {
1298 this.el.on('click', this.onClick, this);
1299 this.el.on('dblclick', this.onDblClick, this);
1301 this.el.select('.roo-button').on('click', this.onClick, this);
1302 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1306 if(this.removeClass){
1307 this.el.on('click', this.onClick, this);
1310 if (this.group === true) {
1311 if (this.pressed === false || this.pressed === true) {
1314 this.pressed = false;
1315 this.setActive(this.pressed);
1320 this.el.enableDisplayMode();
1323 onClick : function(e)
1325 if (this.disabled) {
1329 Roo.log('button on click ');
1330 if(this.preventDefault){
1339 this.setActive(true);
1340 var pi = this.parent().items;
1341 for (var i = 0;i < pi.length;i++) {
1342 if (this == pi[i]) {
1345 if (pi[i].el.hasClass('roo-button')) {
1346 pi[i].setActive(false);
1349 this.fireEvent('click', this, e);
1353 if (this.pressed === true || this.pressed === false) {
1354 this.toggleActive(e);
1358 this.fireEvent('click', this, e);
1360 onDblClick: function(e)
1362 if (this.disabled) {
1365 if(this.preventDefault){
1368 this.fireEvent('dblclick', this, e);
1371 * Enables this button
1375 this.disabled = false;
1376 this.el.removeClass('disabled');
1377 this.el.dom.removeAttribute("disabled");
1381 * Disable this button
1383 disable : function()
1385 this.disabled = true;
1386 this.el.addClass('disabled');
1387 this.el.attr("disabled", "disabled")
1390 * sets the active state on/off,
1391 * @param {Boolean} state (optional) Force a particular state
1393 setActive : function(v) {
1395 this.el[v ? 'addClass' : 'removeClass']('active');
1399 * toggles the current active state
1401 toggleActive : function(e)
1403 this.setActive(!this.pressed); // this modifies pressed...
1404 this.fireEvent('toggle', this, e, this.pressed);
1407 * get the current active state
1408 * @return {boolean} true if it's active
1410 isActive : function()
1412 return this.el.hasClass('active');
1415 * set the text of the first selected button
1417 setText : function(str)
1419 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1422 * get the text of the first selected button
1424 getText : function()
1426 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1429 setWeight : function(str)
1431 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434 var outline = this.outline ? 'outline-' : '';
1435 if (str == 'default') {
1436 this.el.addClass('btn-default btn-outline-secondary');
1439 this.el.addClass('btn-' + outline + str);
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446 Roo.bootstrap.Button.weights = [
1466 * @class Roo.bootstrap.Column
1467 * @extends Roo.bootstrap.Component
1468 * Bootstrap Column class
1469 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1479 * @cfg {Boolean} hidden (true|false) hide the element
1480 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481 * @cfg {String} fa (ban|check|...) font awesome icon
1482 * @cfg {Number} fasize (1|2|....) font awsome size
1484 * @cfg {String} icon (info-sign|check|...) glyphicon name
1486 * @cfg {String} html content of column.
1489 * Create a new Column
1490 * @param {Object} config The config object
1493 Roo.bootstrap.Column = function(config){
1494 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1515 getAutoCreate : function(){
1516 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1524 var sizes = ['xs','sm','md','lg'];
1525 sizes.map(function(size ,ix){
1526 //Roo.log( size + ':' + settings[size]);
1528 if (settings[size+'off'] !== false) {
1529 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1532 if (settings[size] === false) {
1536 if (!settings[size]) { // 0 = hidden
1537 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539 for (var i = ix; i > -1; i--) {
1540 cfg.cls += ' d-' + sizes[i] + '-none';
1546 cfg.cls += ' col-' + size + '-' + settings[size] + (
1547 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1553 cfg.cls += ' hidden';
1556 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557 cfg.cls +=' alert alert-' + this.alert;
1561 if (this.html.length) {
1562 cfg.html = this.html;
1566 if (this.fasize > 1) {
1567 fasize = ' fa-' + this.fasize + 'x';
1569 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1574 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1593 * @class Roo.bootstrap.Container
1594 * @extends Roo.bootstrap.Component
1595 * Bootstrap Container class
1596 * @cfg {Boolean} jumbotron is it a jumbotron element
1597 * @cfg {String} html content of element
1598 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1600 * @cfg {String} header content of header (for panel)
1601 * @cfg {String} footer content of footer (for panel)
1602 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603 * @cfg {String} tag (header|aside|section) type of HTML tag.
1604 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605 * @cfg {String} fa font awesome icon
1606 * @cfg {String} icon (info-sign|check|...) glyphicon name
1607 * @cfg {Boolean} hidden (true|false) hide the element
1608 * @cfg {Boolean} expandable (true|false) default false
1609 * @cfg {Boolean} expanded (true|false) default true
1610 * @cfg {String} rheader contet on the right of header
1611 * @cfg {Boolean} clickable (true|false) default false
1615 * Create a new Container
1616 * @param {Object} config The config object
1619 Roo.bootstrap.Container = function(config){
1620 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1626 * After the panel has been expand
1628 * @param {Roo.bootstrap.Container} this
1633 * After the panel has been collapsed
1635 * @param {Roo.bootstrap.Container} this
1640 * When a element is chick
1641 * @param {Roo.bootstrap.Container} this
1642 * @param {Roo.EventObject} e
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1666 getChildContainer : function() {
1672 if (this.panel.length) {
1673 return this.el.select('.panel-body',true).first();
1680 getAutoCreate : function(){
1683 tag : this.tag || 'div',
1687 if (this.jumbotron) {
1688 cfg.cls = 'jumbotron';
1693 // - this is applied by the parent..
1695 // cfg.cls = this.cls + '';
1698 if (this.sticky.length) {
1700 var bd = Roo.get(document.body);
1701 if (!bd.hasClass('bootstrap-sticky')) {
1702 bd.addClass('bootstrap-sticky');
1703 Roo.select('html',true).setStyle('height', '100%');
1706 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1710 if (this.well.length) {
1711 switch (this.well) {
1714 cfg.cls +=' well well-' +this.well;
1723 cfg.cls += ' hidden';
1727 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728 cfg.cls +=' alert alert-' + this.alert;
1733 if (this.panel.length) {
1734 cfg.cls += ' panel panel-' + this.panel;
1736 if (this.header.length) {
1740 if(this.expandable){
1742 cfg.cls = cfg.cls + ' expandable';
1746 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1754 cls : 'panel-title',
1755 html : (this.expandable ? ' ' : '') + this.header
1759 cls: 'panel-header-right',
1765 cls : 'panel-heading',
1766 style : this.expandable ? 'cursor: pointer' : '',
1774 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1779 if (this.footer.length) {
1781 cls : 'panel-footer',
1790 body.html = this.html || cfg.html;
1791 // prefix with the icons..
1793 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1796 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1801 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802 cfg.cls = 'container';
1808 initEvents: function()
1810 if(this.expandable){
1811 var headerEl = this.headerEl();
1814 headerEl.on('click', this.onToggleClick, this);
1819 this.el.on('click', this.onClick, this);
1824 onToggleClick : function()
1826 var headerEl = this.headerEl();
1842 if(this.fireEvent('expand', this)) {
1844 this.expanded = true;
1846 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848 this.el.select('.panel-body',true).first().removeClass('hide');
1850 var toggleEl = this.toggleEl();
1856 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1861 collapse : function()
1863 if(this.fireEvent('collapse', this)) {
1865 this.expanded = false;
1867 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868 this.el.select('.panel-body',true).first().addClass('hide');
1870 var toggleEl = this.toggleEl();
1876 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1880 toggleEl : function()
1882 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1886 return this.el.select('.panel-heading .fa',true).first();
1889 headerEl : function()
1891 if(!this.el || !this.panel.length || !this.header.length){
1895 return this.el.select('.panel-heading',true).first()
1900 if(!this.el || !this.panel.length){
1904 return this.el.select('.panel-body',true).first()
1907 titleEl : function()
1909 if(!this.el || !this.panel.length || !this.header.length){
1913 return this.el.select('.panel-title',true).first();
1916 setTitle : function(v)
1918 var titleEl = this.titleEl();
1924 titleEl.dom.innerHTML = v;
1927 getTitle : function()
1930 var titleEl = this.titleEl();
1936 return titleEl.dom.innerHTML;
1939 setRightTitle : function(v)
1941 var t = this.el.select('.panel-header-right',true).first();
1947 t.dom.innerHTML = v;
1950 onClick : function(e)
1954 this.fireEvent('click', this, e);
1961 * This is BS4's Card element.. - similar to our containers probably..
1965 * @class Roo.bootstrap.Card
1966 * @extends Roo.bootstrap.Component
1967 * Bootstrap Card class
1970 * possible... may not be implemented..
1971 * @cfg {String} header_image src url of image.
1972 * @cfg {String|Object} header
1973 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1976 * @cfg {String} title
1977 * @cfg {String} subtitle
1978 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979 * @cfg {String} footer
1981 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983 * @cfg {String} margin (0|1|2|3|4|5|auto)
1984 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991 * @cfg {String} padding (0|1|2|3|4|5)
1992 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994 * @cfg {String} padding_left (0|1|2|3|4|5)
1995 * @cfg {String} padding_right (0|1|2|3|4|5)
1996 * @cfg {String} padding_x (0|1|2|3|4|5)
1997 * @cfg {String} padding_y (0|1|2|3|4|5)
1999 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005 * @config {Boolean} dragable if this card can be dragged.
2006 * @config {String} drag_group group for drag
2007 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2008 * @config {String} drop_group group for drag
2010 * @config {Boolean} collapsable can the body be collapsed.
2011 * @config {Boolean} collapsed is the body collapsed when rendered...
2012 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013 * @config {Boolean} rotated is the body rotated when rendered...
2016 * Create a new Container
2017 * @param {Object} config The config object
2020 Roo.bootstrap.Card = function(config){
2021 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2027 * When a element a card is dropped
2028 * @param {Roo.bootstrap.Card} this
2031 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032 * @param {String} position 'above' or 'below'
2033 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2039 * When a element a card is rotate
2040 * @param {Roo.bootstrap.Card} this
2041 * @param {Roo.Element} n the node being dropped?
2042 * @param {Boolean} rotate status
2047 * When a card element is dragged over ready to drop (return false to block dropable)
2048 * @param {Roo.bootstrap.Card} this
2049 * @param {Object} data from dragdrop
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2062 margin: '', /// may be better in component?
2092 collapsable : false,
2101 childContainer : false,
2102 dropEl : false, /// the dom placeholde element that indicates drop location.
2103 containerEl: false, // body container
2104 bodyEl: false, // card-body
2105 headerContainerEl : false, //
2107 header_imageEl : false,
2110 layoutCls : function()
2114 Roo.log(this.margin_bottom.length);
2115 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2121 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2126 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2132 // more generic support?
2140 // Roo.log("Call onRender: " + this.xtype);
2141 /* We are looking at something like this.
2143 <img src="..." class="card-img-top" alt="...">
2144 <div class="card-body">
2145 <h5 class="card-title">Card title</h5>
2146 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148 >> this bit is really the body...
2149 <div> << we will ad dthis in hopefully it will not break shit.
2151 ** card text does not actually have any styling...
2153 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2156 <a href="#" class="card-link">Card link</a>
2159 <div class="card-footer">
2160 <small class="text-muted">Last updated 3 mins ago</small>
2164 getAutoCreate : function(){
2172 if (this.weight.length && this.weight != 'light') {
2173 cfg.cls += ' text-white';
2175 cfg.cls += ' text-dark'; // need as it's nested..
2177 if (this.weight.length) {
2178 cfg.cls += ' bg-' + this.weight;
2181 cfg.cls += ' ' + this.layoutCls();
2184 var hdr_ctr = false;
2185 if (this.header.length) {
2187 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2196 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2202 if (this.collapsable) {
2205 cls : 'd-block user-select-none',
2209 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2214 hdr.cn.push(hdr_ctr);
2219 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2224 if (this.header_image.length) {
2227 cls : 'card-img-top',
2228 src: this.header_image // escape?
2233 cls : 'card-img-top d-none'
2239 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2243 if (this.collapsable || this.rotateable) {
2246 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2253 if (this.title.length) {
2257 src: this.title // escape?
2261 if (this.subtitle.length) {
2265 src: this.subtitle // escape?
2271 cls : 'roo-card-body-ctr'
2274 if (this.html.length) {
2280 // fixme ? handle objects?
2282 if (this.footer.length) {
2285 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2290 cfg.cn.push({cls : 'card-footer d-none'});
2299 getCardHeader : function()
2301 var ret = this.el.select('.card-header',true).first();
2302 if (ret.hasClass('d-none')) {
2303 ret.removeClass('d-none');
2308 getCardFooter : function()
2310 var ret = this.el.select('.card-footer',true).first();
2311 if (ret.hasClass('d-none')) {
2312 ret.removeClass('d-none');
2317 getCardImageTop : function()
2319 var ret = this.header_imageEl;
2320 if (ret.hasClass('d-none')) {
2321 ret.removeClass('d-none');
2327 getChildContainer : function()
2333 return this.el.select('.roo-card-body-ctr',true).first();
2336 initEvents: function()
2338 this.bodyEl = this.el.select('.card-body',true).first();
2339 this.containerEl = this.getChildContainer();
2341 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342 containerScroll: true,
2343 ddGroup: this.drag_group || 'default_card_drag_group'
2345 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347 if (this.dropable) {
2348 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349 containerScroll: true,
2350 ddGroup: this.drop_group || 'default_card_drag_group'
2352 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2359 if (this.collapsable) {
2360 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362 if (this.rotateable) {
2363 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367 this.footerEl = this.el.select('.card-footer',true).first();
2368 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370 this.headerEl = this.el.select('.card-header',true).first();
2373 this.el.addClass('roo-card-rotated');
2374 this.fireEvent('rotate', this, true);
2376 this.header_imageEl = this.el.select('.card-img-top',true).first();
2377 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2380 getDragData : function(e)
2382 var target = this.getEl();
2384 //this.handleSelection(e);
2389 nodes: this.getEl(),
2394 dragData.ddel = target.dom ; // the div element
2395 Roo.log(target.getWidth( ));
2396 dragData.ddel.style.width = target.getWidth() + 'px';
2403 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2404 * whole Element becomes the target, and this causes the drop gesture to append.
2406 * Returns an object:
2409 position : 'below' or 'above'
2410 card : relateive to card OBJECT (or true for no cards listed)
2411 items_n : relative to nth item in list
2412 card_n : relative to nth card in list
2417 getTargetFromEvent : function(e, dragged_card_el)
2419 var target = e.getTarget();
2420 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421 target = target.parentNode;
2432 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433 // see if target is one of the 'cards'...
2436 //Roo.log(this.items.length);
2439 var last_card_n = 0;
2441 for (var i = 0;i< this.items.length;i++) {
2443 if (!this.items[i].el.hasClass('card')) {
2446 pos = this.getDropPoint(e, this.items[i].el.dom);
2448 cards_len = ret.cards.length;
2449 //Roo.log(this.items[i].el.dom.id);
2450 ret.cards.push(this.items[i]);
2452 if (ret.card_n < 0 && pos == 'above') {
2453 ret.position = cards_len > 0 ? 'below' : pos;
2454 ret.items_n = i > 0 ? i - 1 : 0;
2455 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2456 ret.card = ret.cards[ret.card_n];
2459 if (!ret.cards.length) {
2461 ret.position = 'below';
2465 // could not find a card.. stick it at the end..
2466 if (ret.card_n < 0) {
2467 ret.card_n = last_card_n;
2468 ret.card = ret.cards[last_card_n];
2469 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470 ret.position = 'below';
2473 if (this.items[ret.items_n].el == dragged_card_el) {
2477 if (ret.position == 'below') {
2478 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480 if (card_after && card_after.el == dragged_card_el) {
2487 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489 if (card_before && card_before.el == dragged_card_el) {
2496 onNodeEnter : function(n, dd, e, data){
2499 onNodeOver : function(n, dd, e, data)
2502 var target_info = this.getTargetFromEvent(e,data.source.el);
2503 if (target_info === false) {
2504 this.dropPlaceHolder('hide');
2507 Roo.log(['getTargetFromEvent', target_info ]);
2510 if (this.fireEvent('cardover', this, [ data ]) === false) {
2514 this.dropPlaceHolder('show', target_info,data);
2518 onNodeOut : function(n, dd, e, data){
2519 this.dropPlaceHolder('hide');
2522 onNodeDrop : function(n, dd, e, data)
2525 // call drop - return false if
2527 // this could actually fail - if the Network drops..
2528 // we will ignore this at present..- client should probably reload
2529 // the whole set of cards if stuff like that fails.
2532 var info = this.getTargetFromEvent(e,data.source.el);
2533 if (info === false) {
2536 this.dropPlaceHolder('hide');
2540 this.acceptCard(data.source, info.position, info.card, info.items_n);
2544 firstChildCard : function()
2546 for (var i = 0;i< this.items.length;i++) {
2548 if (!this.items[i].el.hasClass('card')) {
2551 return this.items[i];
2553 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2558 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2560 acceptCard : function(move_card, position, next_to_card )
2562 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2566 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568 move_card.parent().removeCard(move_card);
2571 var dom = move_card.el.dom;
2572 dom.style.width = ''; // clear with - which is set by drag.
2574 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575 var cardel = next_to_card.el.dom;
2577 if (position == 'above' ) {
2578 cardel.parentNode.insertBefore(dom, cardel);
2579 } else if (cardel.nextSibling) {
2580 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582 cardel.parentNode.append(dom);
2585 // card container???
2586 this.containerEl.dom.append(dom);
2589 //FIXME HANDLE card = true
2591 // add this to the correct place in items.
2593 // remove Card from items.
2596 if (this.items.length) {
2598 //Roo.log([info.items_n, info.position, this.items.length]);
2599 for (var i =0; i < this.items.length; i++) {
2600 if (i == to_items_n && position == 'above') {
2601 nitems.push(move_card);
2603 nitems.push(this.items[i]);
2604 if (i == to_items_n && position == 'below') {
2605 nitems.push(move_card);
2608 this.items = nitems;
2609 Roo.log(this.items);
2611 this.items.push(move_card);
2614 move_card.parentId = this.id;
2620 removeCard : function(c)
2622 this.items = this.items.filter(function(e) { return e != c });
2625 dom.parentNode.removeChild(dom);
2626 dom.style.width = ''; // clear with - which is set by drag.
2631 /** Decide whether to drop above or below a View node. */
2632 getDropPoint : function(e, n, dd)
2637 if (n == this.containerEl.dom) {
2640 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641 var c = t + (b - t) / 2;
2642 var y = Roo.lib.Event.getPageY(e);
2649 onToggleCollapse : function(e)
2651 if (this.collapsed) {
2652 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653 this.collapsableEl.addClass('show');
2654 this.collapsed = false;
2657 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658 this.collapsableEl.removeClass('show');
2659 this.collapsed = true;
2664 onToggleRotate : function(e)
2666 this.collapsableEl.removeClass('show');
2667 this.footerEl.removeClass('d-none');
2668 this.el.removeClass('roo-card-rotated');
2669 this.el.removeClass('d-none');
2672 this.collapsableEl.addClass('show');
2673 this.rotated = false;
2674 this.fireEvent('rotate', this, this.rotated);
2677 this.el.addClass('roo-card-rotated');
2678 this.footerEl.addClass('d-none');
2679 this.el.select('.roo-collapsable').removeClass('show');
2681 this.rotated = true;
2682 this.fireEvent('rotate', this, this.rotated);
2686 dropPlaceHolder: function (action, info, data)
2688 if (this.dropEl === false) {
2689 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2693 this.dropEl.removeClass(['d-none', 'd-block']);
2694 if (action == 'hide') {
2696 this.dropEl.addClass('d-none');
2699 // FIXME - info.card == true!!!
2700 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702 if (info.card !== true) {
2703 var cardel = info.card.el.dom;
2705 if (info.position == 'above') {
2706 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707 } else if (cardel.nextSibling) {
2708 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710 cardel.parentNode.append(this.dropEl.dom);
2713 // card container???
2714 this.containerEl.dom.append(this.dropEl.dom);
2717 this.dropEl.addClass('d-block roo-card-dropzone');
2719 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2726 setHeaderText: function(html)
2729 if (this.headerContainerEl) {
2730 this.headerContainerEl.dom.innerHTML = html;
2733 onHeaderImageLoad : function(ev, he)
2735 if (!this.header_image_fit_square) {
2739 var hw = he.naturalHeight / he.naturalWidth;
2742 //var w = he.dom.naturalWidth;
2745 he.style.position = 'relative';
2747 var nw = (ww * (1/hw));
2748 Roo.get(he).setSize( ww * (1/hw), ww);
2749 he.style.left = ((ww - nw)/ 2) + 'px';
2750 he.style.position = 'relative';
2761 * Card header - holder for the card header elements.
2766 * @class Roo.bootstrap.CardHeader
2767 * @extends Roo.bootstrap.Element
2768 * Bootstrap CardHeader class
2770 * Create a new Card Header - that you can embed children into
2771 * @param {Object} config The config object
2774 Roo.bootstrap.CardHeader = function(config){
2775 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2781 container_method : 'getCardHeader'
2794 * Card footer - holder for the card footer elements.
2799 * @class Roo.bootstrap.CardFooter
2800 * @extends Roo.bootstrap.Element
2801 * Bootstrap CardFooter class
2803 * Create a new Card Footer - that you can embed children into
2804 * @param {Object} config The config object
2807 Roo.bootstrap.CardFooter = function(config){
2808 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2814 container_method : 'getCardFooter'
2827 * Card header - holder for the card header elements.
2832 * @class Roo.bootstrap.CardImageTop
2833 * @extends Roo.bootstrap.Element
2834 * Bootstrap CardImageTop class
2836 * Create a new Card Image Top container
2837 * @param {Object} config The config object
2840 Roo.bootstrap.CardImageTop = function(config){
2841 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2847 container_method : 'getCardImageTop'
2862 * @class Roo.bootstrap.ButtonUploader
2863 * @extends Roo.bootstrap.Button
2864 * Bootstrap Button Uploader class - it's a button which when you add files to it
2867 * @cfg {Number} errorTimeout default 3000
2868 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2869 * @cfg {Array} html The button text.
2870 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2873 * Create a new CardUploader
2874 * @param {Object} config The config object
2877 Roo.bootstrap.ButtonUploader = function(config){
2881 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2887 * @event beforeselect
2888 * When button is pressed, before show upload files dialog is shown
2889 * @param {Roo.bootstrap.UploaderButton} this
2892 'beforeselect' : true,
2894 * @event fired when files have been selected,
2895 * When a the download link is clicked
2896 * @param {Roo.bootstrap.UploaderButton} this
2897 * @param {Array} Array of files that have been uploaded
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2907 errorTimeout : 3000,
2911 fileCollection : false,
2916 getAutoCreate : function()
2921 cls : 'd-none roo-card-upload-selector'
2924 if (this.multiple) {
2925 im.multiple = 'multiple';
2931 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2941 initEvents : function()
2944 Roo.bootstrap.Button.prototype.initEvents.call(this);
2950 this.urlAPI = (window.createObjectURL && window) ||
2951 (window.URL && URL.revokeObjectURL && URL) ||
2952 (window.webkitURL && webkitURL);
2957 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959 this.selectorEl.on('change', this.onFileSelected, this);
2966 onClick : function(e)
2970 if ( this.fireEvent('beforeselect', this) === false) {
2974 this.selectorEl.dom.click();
2978 onFileSelected : function(e)
2982 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2985 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986 this.selectorEl.dom.value = '';// hopefully reset..
2988 this.fireEvent('uploaded', this, files );
2996 * addCard - add an Attachment to the uploader
2997 * @param data - the data about the image to upload
3001 title : "Title of file",
3002 is_uploaded : false,
3003 src : "http://.....",
3004 srcfile : { the File upload object },
3005 mimetype : file.type,
3008 .. any other data...
3033 * @class Roo.bootstrap.Img
3034 * @extends Roo.bootstrap.Component
3035 * Bootstrap Img class
3036 * @cfg {Boolean} imgResponsive false | true
3037 * @cfg {String} border rounded | circle | thumbnail
3038 * @cfg {String} src image source
3039 * @cfg {String} alt image alternative text
3040 * @cfg {String} href a tag href
3041 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042 * @cfg {String} xsUrl xs image source
3043 * @cfg {String} smUrl sm image source
3044 * @cfg {String} mdUrl md image source
3045 * @cfg {String} lgUrl lg image source
3048 * Create a new Input
3049 * @param {Object} config The config object
3052 Roo.bootstrap.Img = function(config){
3053 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3059 * The img click event for the img.
3060 * @param {Roo.EventObject} e
3066 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3068 imgResponsive: true,
3078 getAutoCreate : function()
3080 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3081 return this.createSingleImg();
3086 cls: 'roo-image-responsive-group',
3091 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3093 if(!_this[size + 'Url']){
3099 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3100 html: _this.html || cfg.html,
3101 src: _this[size + 'Url']
3104 img.cls += ' roo-image-responsive-' + size;
3106 var s = ['xs', 'sm', 'md', 'lg'];
3108 s.splice(s.indexOf(size), 1);
3110 Roo.each(s, function(ss){
3111 img.cls += ' hidden-' + ss;
3114 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3115 cfg.cls += ' img-' + _this.border;
3119 cfg.alt = _this.alt;
3132 a.target = _this.target;
3136 cfg.cn.push((_this.href) ? a : img);
3143 createSingleImg : function()
3147 cls: (this.imgResponsive) ? 'img-responsive' : '',
3149 src : 'about:blank' // just incase src get's set to undefined?!?
3152 cfg.html = this.html || cfg.html;
3154 cfg.src = this.src || cfg.src;
3156 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3157 cfg.cls += ' img-' + this.border;
3174 a.target = this.target;
3179 return (this.href) ? a : cfg;
3182 initEvents: function()
3185 this.el.on('click', this.onClick, this);
3190 onClick : function(e)
3192 Roo.log('img onclick');
3193 this.fireEvent('click', this, e);
3196 * Sets the url of the image - used to update it
3197 * @param {String} url the url of the image
3200 setSrc : function(url)
3204 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205 this.el.dom.src = url;
3209 this.el.select('img', true).first().dom.src = url;
3225 * @class Roo.bootstrap.Link
3226 * @extends Roo.bootstrap.Component
3227 * Bootstrap Link Class
3228 * @cfg {String} alt image alternative text
3229 * @cfg {String} href a tag href
3230 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3231 * @cfg {String} html the content of the link.
3232 * @cfg {String} anchor name for the anchor link
3233 * @cfg {String} fa - favicon
3235 * @cfg {Boolean} preventDefault (true | false) default false
3239 * Create a new Input
3240 * @param {Object} config The config object
3243 Roo.bootstrap.Link = function(config){
3244 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3250 * The img click event for the img.
3251 * @param {Roo.EventObject} e
3257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3261 preventDefault: false,
3267 getAutoCreate : function()
3269 var html = this.html || '';
3271 if (this.fa !== false) {
3272 html = '<i class="fa fa-' + this.fa + '"></i>';
3277 // anchor's do not require html/href...
3278 if (this.anchor === false) {
3280 cfg.href = this.href || '#';
3282 cfg.name = this.anchor;
3283 if (this.html !== false || this.fa !== false) {
3286 if (this.href !== false) {
3287 cfg.href = this.href;
3291 if(this.alt !== false){
3296 if(this.target !== false) {
3297 cfg.target = this.target;
3303 initEvents: function() {
3305 if(!this.href || this.preventDefault){
3306 this.el.on('click', this.onClick, this);
3310 onClick : function(e)
3312 if(this.preventDefault){
3315 //Roo.log('img onclick');
3316 this.fireEvent('click', this, e);
3329 * @class Roo.bootstrap.Header
3330 * @extends Roo.bootstrap.Component
3331 * Bootstrap Header class
3332 * @cfg {String} html content of header
3333 * @cfg {Number} level (1|2|3|4|5|6) default 1
3336 * Create a new Header
3337 * @param {Object} config The config object
3341 Roo.bootstrap.Header = function(config){
3342 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3345 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3353 getAutoCreate : function(){
3358 tag: 'h' + (1 *this.level),
3359 html: this.html || ''
3371 * Ext JS Library 1.1.1
3372 * Copyright(c) 2006-2007, Ext JS, LLC.
3374 * Originally Released Under LGPL - original licence link has changed is not relivant.
3377 * <script type="text/javascript">
3381 * @class Roo.bootstrap.MenuMgr
3382 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3385 Roo.bootstrap.MenuMgr = function(){
3386 var menus, active, groups = {}, attached = false, lastShow = new Date();
3388 // private - called when first menu is created
3391 active = new Roo.util.MixedCollection();
3392 Roo.get(document).addKeyListener(27, function(){
3393 if(active.length > 0){
3401 if(active && active.length > 0){
3402 var c = active.clone();
3412 if(active.length < 1){
3413 Roo.get(document).un("mouseup", onMouseDown);
3421 var last = active.last();
3422 lastShow = new Date();
3425 Roo.get(document).on("mouseup", onMouseDown);
3430 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3431 m.parentMenu.activeChild = m;
3432 }else if(last && last.isVisible()){
3433 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3438 function onBeforeHide(m){
3440 m.activeChild.hide();
3442 if(m.autoHideTimer){
3443 clearTimeout(m.autoHideTimer);
3444 delete m.autoHideTimer;
3449 function onBeforeShow(m){
3450 var pm = m.parentMenu;
3451 if(!pm && !m.allowOtherMenus){
3453 }else if(pm && pm.activeChild && active != m){
3454 pm.activeChild.hide();
3458 // private this should really trigger on mouseup..
3459 function onMouseDown(e){
3460 Roo.log("on Mouse Up");
3462 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3463 Roo.log("MenuManager hideAll");
3472 function onBeforeCheck(mi, state){
3474 var g = groups[mi.group];
3475 for(var i = 0, l = g.length; i < l; i++){
3477 g[i].setChecked(false);
3486 * Hides all menus that are currently visible
3488 hideAll : function(){
3493 register : function(menu){
3497 menus[menu.id] = menu;
3498 menu.on("beforehide", onBeforeHide);
3499 menu.on("hide", onHide);
3500 menu.on("beforeshow", onBeforeShow);
3501 menu.on("show", onShow);
3503 if(g && menu.events["checkchange"]){
3507 groups[g].push(menu);
3508 menu.on("checkchange", onCheck);
3513 * Returns a {@link Roo.menu.Menu} object
3514 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3515 * be used to generate and return a new Menu instance.
3517 get : function(menu){
3518 if(typeof menu == "string"){ // menu id
3520 }else if(menu.events){ // menu instance
3523 /*else if(typeof menu.length == 'number'){ // array of menu items?
3524 return new Roo.bootstrap.Menu({items:menu});
3525 }else{ // otherwise, must be a config
3526 return new Roo.bootstrap.Menu(menu);
3533 unregister : function(menu){
3534 delete menus[menu.id];
3535 menu.un("beforehide", onBeforeHide);
3536 menu.un("hide", onHide);
3537 menu.un("beforeshow", onBeforeShow);
3538 menu.un("show", onShow);
3540 if(g && menu.events["checkchange"]){
3541 groups[g].remove(menu);
3542 menu.un("checkchange", onCheck);
3547 registerCheckable : function(menuItem){
3548 var g = menuItem.group;
3553 groups[g].push(menuItem);
3554 menuItem.on("beforecheckchange", onBeforeCheck);
3559 unregisterCheckable : function(menuItem){
3560 var g = menuItem.group;
3562 groups[g].remove(menuItem);
3563 menuItem.un("beforecheckchange", onBeforeCheck);
3575 * @class Roo.bootstrap.Menu
3576 * @extends Roo.bootstrap.Component
3577 * Bootstrap Menu class - container for MenuItems
3578 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3579 * @cfg {bool} hidden if the menu should be hidden when rendered.
3580 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3581 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3582 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3583 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3587 * @param {Object} config The config object
3591 Roo.bootstrap.Menu = function(config){
3593 if (config.type == 'treeview') {
3594 // normally menu's are drawn attached to the document to handle layering etc..
3595 // however treeview (used by the docs menu is drawn into the parent element)
3596 this.container_method = 'getChildContainer';
3599 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3600 if (this.registerMenu && this.type != 'treeview') {
3601 Roo.bootstrap.MenuMgr.register(this);
3608 * Fires before this menu is displayed (return false to block)
3609 * @param {Roo.menu.Menu} this
3614 * Fires before this menu is hidden (return false to block)
3615 * @param {Roo.menu.Menu} this
3620 * Fires after this menu is displayed
3621 * @param {Roo.menu.Menu} this
3626 * Fires after this menu is hidden
3627 * @param {Roo.menu.Menu} this
3632 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3633 * @param {Roo.menu.Menu} this
3634 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3635 * @param {Roo.EventObject} e
3640 * Fires when the mouse is hovering over this menu
3641 * @param {Roo.menu.Menu} this
3642 * @param {Roo.EventObject} e
3643 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3648 * Fires when the mouse exits this menu
3649 * @param {Roo.menu.Menu} this
3650 * @param {Roo.EventObject} e
3651 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3656 * Fires when a menu item contained in this menu is clicked
3657 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3658 * @param {Roo.EventObject} e
3662 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3665 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3669 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3672 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3674 registerMenu : true,
3676 menuItems :false, // stores the menu items..
3686 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3688 hideTrigger : false,
3693 getChildContainer : function() {
3697 getAutoCreate : function(){
3699 //if (['right'].indexOf(this.align)!==-1) {
3700 // cfg.cn[1].cls += ' pull-right'
3705 cls : 'dropdown-menu shadow' ,
3706 style : 'z-index:1000'
3710 if (this.type === 'submenu') {
3711 cfg.cls = 'submenu active';
3713 if (this.type === 'treeview') {
3714 cfg.cls = 'treeview-menu';
3719 initEvents : function() {
3721 // Roo.log("ADD event");
3722 // Roo.log(this.triggerEl.dom);
3723 if (this.triggerEl) {
3725 this.triggerEl.on('click', this.onTriggerClick, this);
3727 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3729 if (!this.hideTrigger) {
3730 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3731 // dropdown toggle on the 'a' in BS4?
3732 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3734 this.triggerEl.addClass('dropdown-toggle');
3740 this.el.on('touchstart' , this.onTouch, this);
3742 this.el.on('click' , this.onClick, this);
3744 this.el.on("mouseover", this.onMouseOver, this);
3745 this.el.on("mouseout", this.onMouseOut, this);
3749 findTargetItem : function(e)
3751 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3755 //Roo.log(t); Roo.log(t.id);
3757 //Roo.log(this.menuitems);
3758 return this.menuitems.get(t.id);
3760 //return this.items.get(t.menuItemId);
3766 onTouch : function(e)
3768 Roo.log("menu.onTouch");
3769 //e.stopEvent(); this make the user popdown broken
3773 onClick : function(e)
3775 Roo.log("menu.onClick");
3777 var t = this.findTargetItem(e);
3778 if(!t || t.isContainer){
3783 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3784 if(t == this.activeItem && t.shouldDeactivate(e)){
3785 this.activeItem.deactivate();
3786 delete this.activeItem;
3790 this.setActiveItem(t, true);
3798 Roo.log('pass click event');
3802 this.fireEvent("click", this, t, e);
3806 if(!t.href.length || t.href == '#'){
3807 (function() { _this.hide(); }).defer(100);
3812 onMouseOver : function(e){
3813 var t = this.findTargetItem(e);
3816 // if(t.canActivate && !t.disabled){
3817 // this.setActiveItem(t, true);
3821 this.fireEvent("mouseover", this, e, t);
3823 isVisible : function(){
3824 return !this.hidden;
3826 onMouseOut : function(e){
3827 var t = this.findTargetItem(e);
3830 // if(t == this.activeItem && t.shouldDeactivate(e)){
3831 // this.activeItem.deactivate();
3832 // delete this.activeItem;
3835 this.fireEvent("mouseout", this, e, t);
3840 * Displays this menu relative to another element
3841 * @param {String/HTMLElement/Roo.Element} element The element to align to
3842 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3843 * the element (defaults to this.defaultAlign)
3844 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3846 show : function(el, pos, parentMenu)
3848 if (false === this.fireEvent("beforeshow", this)) {
3849 Roo.log("show canceled");
3852 this.parentMenu = parentMenu;
3856 this.el.addClass('show'); // show otherwise we do not know how big we are..
3858 var xy = this.el.getAlignToXY(el, pos);
3860 // bl-tl << left align below
3861 // tl-bl << left align
3863 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3864 // if it goes to far to the right.. -> align left.
3865 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3868 // was left align - go right?
3869 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3872 // goes down the bottom
3873 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3875 var a = this.align.replace('?', '').split('-');
3876 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3880 this.showAt( xy , parentMenu, false);
3883 * Displays this menu at a specific xy position
3884 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3885 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3887 showAt : function(xy, parentMenu, /* private: */_e){
3888 this.parentMenu = parentMenu;
3893 this.fireEvent("beforeshow", this);
3894 //xy = this.el.adjustForConstraints(xy);
3898 this.hideMenuItems();
3899 this.hidden = false;
3900 if (this.triggerEl) {
3901 this.triggerEl.addClass('open');
3904 this.el.addClass('show');
3908 // reassign x when hitting right
3910 // reassign y when hitting bottom
3912 // but the list may align on trigger left or trigger top... should it be a properity?
3914 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3919 this.fireEvent("show", this);
3925 this.doFocus.defer(50, this);
3929 doFocus : function(){
3931 this.focusEl.focus();
3936 * Hides this menu and optionally all parent menus
3937 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3939 hide : function(deep)
3941 if (false === this.fireEvent("beforehide", this)) {
3942 Roo.log("hide canceled");
3945 this.hideMenuItems();
3946 if(this.el && this.isVisible()){
3948 if(this.activeItem){
3949 this.activeItem.deactivate();
3950 this.activeItem = null;
3952 if (this.triggerEl) {
3953 this.triggerEl.removeClass('open');
3956 this.el.removeClass('show');
3958 this.fireEvent("hide", this);
3960 if(deep === true && this.parentMenu){
3961 this.parentMenu.hide(true);
3965 onTriggerClick : function(e)
3967 Roo.log('trigger click');
3969 var target = e.getTarget();
3971 Roo.log(target.nodeName.toLowerCase());
3973 if(target.nodeName.toLowerCase() === 'i'){
3979 onTriggerPress : function(e)
3981 Roo.log('trigger press');
3982 //Roo.log(e.getTarget());
3983 // Roo.log(this.triggerEl.dom);
3985 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3986 var pel = Roo.get(e.getTarget());
3987 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3988 Roo.log('is treeview or dropdown?');
3992 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3996 if (this.isVisible()) {
4002 this.show(this.triggerEl, this.align, false);
4005 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4012 hideMenuItems : function()
4014 Roo.log("hide Menu Items");
4019 this.el.select('.open',true).each(function(aa) {
4021 aa.removeClass('open');
4025 addxtypeChild : function (tree, cntr) {
4026 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4028 this.menuitems.add(comp);
4040 this.getEl().dom.innerHTML = '';
4041 this.menuitems.clear();
4055 * @class Roo.bootstrap.MenuItem
4056 * @extends Roo.bootstrap.Component
4057 * Bootstrap MenuItem class
4058 * @cfg {String} html the menu label
4059 * @cfg {String} href the link
4060 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4061 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4062 * @cfg {Boolean} active used on sidebars to highlight active itesm
4063 * @cfg {String} fa favicon to show on left of menu item.
4064 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4068 * Create a new MenuItem
4069 * @param {Object} config The config object
4073 Roo.bootstrap.MenuItem = function(config){
4074 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4079 * The raw click event for the entire grid.
4080 * @param {Roo.bootstrap.MenuItem} this
4081 * @param {Roo.EventObject} e
4087 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4091 preventDefault: false,
4092 isContainer : false,
4096 getAutoCreate : function(){
4098 if(this.isContainer){
4101 cls: 'dropdown-menu-item '
4111 cls : 'dropdown-item',
4116 if (this.fa !== false) {
4119 cls : 'fa fa-' + this.fa
4128 cls: 'dropdown-menu-item',
4131 if (this.parent().type == 'treeview') {
4132 cfg.cls = 'treeview-menu';
4135 cfg.cls += ' active';
4140 anc.href = this.href || cfg.cn[0].href ;
4141 ctag.html = this.html || cfg.cn[0].html ;
4145 initEvents: function()
4147 if (this.parent().type == 'treeview') {
4148 this.el.select('a').on('click', this.onClick, this);
4152 this.menu.parentType = this.xtype;
4153 this.menu.triggerEl = this.el;
4154 this.menu = this.addxtype(Roo.apply({}, this.menu));
4158 onClick : function(e)
4160 Roo.log('item on click ');
4162 if(this.preventDefault){
4165 //this.parent().hideMenuItems();
4167 this.fireEvent('click', this, e);
4186 * @class Roo.bootstrap.MenuSeparator
4187 * @extends Roo.bootstrap.Component
4188 * Bootstrap MenuSeparator class
4191 * Create a new MenuItem
4192 * @param {Object} config The config object
4196 Roo.bootstrap.MenuSeparator = function(config){
4197 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4200 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4202 getAutoCreate : function(){
4221 * @class Roo.bootstrap.Modal
4222 * @extends Roo.bootstrap.Component
4223 * Bootstrap Modal class
4224 * @cfg {String} title Title of dialog
4225 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4226 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4227 * @cfg {Boolean} specificTitle default false
4228 * @cfg {Array} buttons Array of buttons or standard button set..
4229 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4230 * @cfg {Boolean} animate default true
4231 * @cfg {Boolean} allow_close default true
4232 * @cfg {Boolean} fitwindow default false
4233 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4234 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4235 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4236 * @cfg {String} size (sm|lg|xl) default empty
4237 * @cfg {Number} max_width set the max width of modal
4238 * @cfg {Boolean} editableTitle can the title be edited
4243 * Create a new Modal Dialog
4244 * @param {Object} config The config object
4247 Roo.bootstrap.Modal = function(config){
4248 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4253 * The raw btnclick event for the button
4254 * @param {Roo.EventObject} e
4259 * Fire when dialog resize
4260 * @param {Roo.bootstrap.Modal} this
4261 * @param {Roo.EventObject} e
4265 * @event titlechanged
4266 * Fire when the editable title has been changed
4267 * @param {Roo.bootstrap.Modal} this
4268 * @param {Roo.EventObject} value
4270 "titlechanged" : true
4273 this.buttons = this.buttons || [];
4276 this.tmpl = Roo.factory(this.tmpl);
4281 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4283 title : 'test dialog',
4293 specificTitle: false,
4295 buttonPosition: 'right',
4317 editableTitle : false,
4319 onRender : function(ct, position)
4321 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4324 var cfg = Roo.apply({}, this.getAutoCreate());
4327 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4329 //if (!cfg.name.length) {
4333 cfg.cls += ' ' + this.cls;
4336 cfg.style = this.style;
4338 this.el = Roo.get(document.body).createChild(cfg, position);
4340 //var type = this.el.dom.type;
4343 if(this.tabIndex !== undefined){
4344 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4347 this.dialogEl = this.el.select('.modal-dialog',true).first();
4348 this.bodyEl = this.el.select('.modal-body',true).first();
4349 this.closeEl = this.el.select('.modal-header .close', true).first();
4350 this.headerEl = this.el.select('.modal-header',true).first();
4351 this.titleEl = this.el.select('.modal-title',true).first();
4352 this.footerEl = this.el.select('.modal-footer',true).first();
4354 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4356 //this.el.addClass("x-dlg-modal");
4358 if (this.buttons.length) {
4359 Roo.each(this.buttons, function(bb) {
4360 var b = Roo.apply({}, bb);
4361 b.xns = b.xns || Roo.bootstrap;
4362 b.xtype = b.xtype || 'Button';
4363 if (typeof(b.listeners) == 'undefined') {
4364 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4367 var btn = Roo.factory(b);
4369 btn.render(this.getButtonContainer());
4373 // render the children.
4376 if(typeof(this.items) != 'undefined'){
4377 var items = this.items;
4380 for(var i =0;i < items.length;i++) {
4381 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4385 this.items = nitems;
4387 // where are these used - they used to be body/close/footer
4391 //this.el.addClass([this.fieldClass, this.cls]);
4395 getAutoCreate : function()
4397 // we will default to modal-body-overflow - might need to remove or make optional later.
4399 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4400 html : this.html || ''
4405 cls : 'modal-title',
4409 if(this.specificTitle){ // WTF is this?
4414 if (this.allow_close && Roo.bootstrap.version == 3) {
4424 if (this.editableTitle) {
4426 cls: 'form-control roo-editable-title d-none',
4432 if (this.allow_close && Roo.bootstrap.version == 4) {
4442 if(this.size.length){
4443 size = 'modal-' + this.size;
4446 var footer = Roo.bootstrap.version == 3 ?
4448 cls : 'modal-footer',
4452 cls: 'btn-' + this.buttonPosition
4457 { // BS4 uses mr-auto on left buttons....
4458 cls : 'modal-footer'
4469 cls: "modal-dialog " + size,
4472 cls : "modal-content",
4475 cls : 'modal-header',
4490 modal.cls += ' fade';
4496 getChildContainer : function() {
4501 getButtonContainer : function() {
4503 return Roo.bootstrap.version == 4 ?
4504 this.el.select('.modal-footer',true).first()
4505 : this.el.select('.modal-footer div',true).first();
4508 initEvents : function()
4510 if (this.allow_close) {
4511 this.closeEl.on('click', this.hide, this);
4513 Roo.EventManager.onWindowResize(this.resize, this, true);
4514 if (this.editableTitle) {
4515 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4516 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4517 this.headerEditEl.on('keyup', function(e) {
4518 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4519 this.toggleHeaderInput(false)
4522 this.headerEditEl.on('blur', function(e) {
4523 this.toggleHeaderInput(false)
4532 this.maskEl.setSize(
4533 Roo.lib.Dom.getViewWidth(true),
4534 Roo.lib.Dom.getViewHeight(true)
4537 if (this.fitwindow) {
4539 this.dialogEl.setStyle( { 'max-width' : '100%' });
4541 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4542 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4547 if(this.max_width !== 0) {
4549 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4552 this.setSize(w, this.height);
4556 if(this.max_height) {
4557 this.setSize(w,Math.min(
4559 Roo.lib.Dom.getViewportHeight(true) - 60
4565 if(!this.fit_content) {
4566 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4570 this.setSize(w, Math.min(
4572 this.headerEl.getHeight() +
4573 this.footerEl.getHeight() +
4574 this.getChildHeight(this.bodyEl.dom.childNodes),
4575 Roo.lib.Dom.getViewportHeight(true) - 60)
4581 setSize : function(w,h)
4592 if (!this.rendered) {
4595 this.toggleHeaderInput(false);
4596 //this.el.setStyle('display', 'block');
4597 this.el.removeClass('hideing');
4598 this.el.dom.style.display='block';
4600 Roo.get(document.body).addClass('modal-open');
4602 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4605 this.el.addClass('show');
4606 this.el.addClass('in');
4609 this.el.addClass('show');
4610 this.el.addClass('in');
4613 // not sure how we can show data in here..
4615 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4618 Roo.get(document.body).addClass("x-body-masked");
4620 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4621 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4622 this.maskEl.dom.style.display = 'block';
4623 this.maskEl.addClass('show');
4628 this.fireEvent('show', this);
4630 // set zindex here - otherwise it appears to be ignored...
4631 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4634 this.items.forEach( function(e) {
4635 e.layout ? e.layout() : false;
4643 if(this.fireEvent("beforehide", this) !== false){
4645 this.maskEl.removeClass('show');
4647 this.maskEl.dom.style.display = '';
4648 Roo.get(document.body).removeClass("x-body-masked");
4649 this.el.removeClass('in');
4650 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4652 if(this.animate){ // why
4653 this.el.addClass('hideing');
4654 this.el.removeClass('show');
4656 if (!this.el.hasClass('hideing')) {
4657 return; // it's been shown again...
4660 this.el.dom.style.display='';
4662 Roo.get(document.body).removeClass('modal-open');
4663 this.el.removeClass('hideing');
4667 this.el.removeClass('show');
4668 this.el.dom.style.display='';
4669 Roo.get(document.body).removeClass('modal-open');
4672 this.fireEvent('hide', this);
4675 isVisible : function()
4678 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4682 addButton : function(str, cb)
4686 var b = Roo.apply({}, { html : str } );
4687 b.xns = b.xns || Roo.bootstrap;
4688 b.xtype = b.xtype || 'Button';
4689 if (typeof(b.listeners) == 'undefined') {
4690 b.listeners = { click : cb.createDelegate(this) };
4693 var btn = Roo.factory(b);
4695 btn.render(this.getButtonContainer());
4701 setDefaultButton : function(btn)
4703 //this.el.select('.modal-footer').()
4706 resizeTo: function(w,h)
4708 this.dialogEl.setWidth(w);
4710 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4712 this.bodyEl.setHeight(h - diff);
4714 this.fireEvent('resize', this);
4717 setContentSize : function(w, h)
4721 onButtonClick: function(btn,e)
4724 this.fireEvent('btnclick', btn.name, e);
4727 * Set the title of the Dialog
4728 * @param {String} str new Title
4730 setTitle: function(str) {
4731 this.titleEl.dom.innerHTML = str;
4735 * Set the body of the Dialog
4736 * @param {String} str new Title
4738 setBody: function(str) {
4739 this.bodyEl.dom.innerHTML = str;
4742 * Set the body of the Dialog using the template
4743 * @param {Obj} data - apply this data to the template and replace the body contents.
4745 applyBody: function(obj)
4748 Roo.log("Error - using apply Body without a template");
4751 this.tmpl.overwrite(this.bodyEl, obj);
4754 getChildHeight : function(child_nodes)
4758 child_nodes.length == 0
4763 var child_height = 0;
4765 for(var i = 0; i < child_nodes.length; i++) {
4768 * for modal with tabs...
4769 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4771 var layout_childs = child_nodes[i].childNodes;
4773 for(var j = 0; j < layout_childs.length; j++) {
4775 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4777 var layout_body_childs = layout_childs[j].childNodes;
4779 for(var k = 0; k < layout_body_childs.length; k++) {
4781 if(layout_body_childs[k].classList.contains('navbar')) {
4782 child_height += layout_body_childs[k].offsetHeight;
4786 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4788 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4790 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4792 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4793 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4808 child_height += child_nodes[i].offsetHeight;
4809 // Roo.log(child_nodes[i].offsetHeight);
4812 return child_height;
4814 toggleHeaderInput : function(is_edit)
4816 if (!this.editableTitle) {
4817 return; // not editable.
4819 if (is_edit && this.is_header_editing) {
4820 return; // already editing..
4824 this.headerEditEl.dom.value = this.title;
4825 this.headerEditEl.removeClass('d-none');
4826 this.headerEditEl.dom.focus();
4827 this.titleEl.addClass('d-none');
4829 this.is_header_editing = true;
4832 // flip back to not editing.
4833 this.title = this.headerEditEl.dom.value;
4834 this.headerEditEl.addClass('d-none');
4835 this.titleEl.removeClass('d-none');
4836 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4837 this.is_header_editing = false;
4838 this.fireEvent('titlechanged', this, this.title);
4847 Roo.apply(Roo.bootstrap.Modal, {
4849 * Button config that displays a single OK button
4858 * Button config that displays Yes and No buttons
4874 * Button config that displays OK and Cancel buttons
4889 * Button config that displays Yes, No and Cancel buttons
4914 * messagebox - can be used as a replace
4918 * @class Roo.MessageBox
4919 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4923 Roo.Msg.alert('Status', 'Changes saved successfully.');
4925 // Prompt for user data:
4926 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4928 // process text value...
4932 // Show a dialog using config options:
4934 title:'Save Changes?',
4935 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4936 buttons: Roo.Msg.YESNOCANCEL,
4943 Roo.bootstrap.MessageBox = function(){
4944 var dlg, opt, mask, waitTimer;
4945 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4946 var buttons, activeTextEl, bwidth;
4950 var handleButton = function(button){
4952 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4956 var handleHide = function(){
4958 dlg.el.removeClass(opt.cls);
4961 // Roo.TaskMgr.stop(waitTimer);
4962 // waitTimer = null;
4967 var updateButtons = function(b){
4970 buttons["ok"].hide();
4971 buttons["cancel"].hide();
4972 buttons["yes"].hide();
4973 buttons["no"].hide();
4974 dlg.footerEl.hide();
4978 dlg.footerEl.show();
4979 for(var k in buttons){
4980 if(typeof buttons[k] != "function"){
4983 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4984 width += buttons[k].el.getWidth()+15;
4994 var handleEsc = function(d, k, e){
4995 if(opt && opt.closable !== false){
5005 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5006 * @return {Roo.BasicDialog} The BasicDialog element
5008 getDialog : function(){
5010 dlg = new Roo.bootstrap.Modal( {
5013 //constraintoviewport:false,
5015 //collapsible : false,
5020 //buttonAlign:"center",
5021 closeClick : function(){
5022 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5025 handleButton("cancel");
5030 dlg.on("hide", handleHide);
5032 //dlg.addKeyListener(27, handleEsc);
5034 this.buttons = buttons;
5035 var bt = this.buttonText;
5036 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5037 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5038 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5039 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5041 bodyEl = dlg.bodyEl.createChild({
5043 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5044 '<textarea class="roo-mb-textarea"></textarea>' +
5045 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5047 msgEl = bodyEl.dom.firstChild;
5048 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5049 textboxEl.enableDisplayMode();
5050 textboxEl.addKeyListener([10,13], function(){
5051 if(dlg.isVisible() && opt && opt.buttons){
5054 }else if(opt.buttons.yes){
5055 handleButton("yes");
5059 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5060 textareaEl.enableDisplayMode();
5061 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5062 progressEl.enableDisplayMode();
5064 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5065 var pf = progressEl.dom.firstChild;
5067 pp = Roo.get(pf.firstChild);
5068 pp.setHeight(pf.offsetHeight);
5076 * Updates the message box body text
5077 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5078 * the XHTML-compliant non-breaking space character '&#160;')
5079 * @return {Roo.MessageBox} This message box
5081 updateText : function(text)
5083 if(!dlg.isVisible() && !opt.width){
5084 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5085 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5087 msgEl.innerHTML = text || ' ';
5089 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5090 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5092 Math.min(opt.width || cw , this.maxWidth),
5093 Math.max(opt.minWidth || this.minWidth, bwidth)
5096 activeTextEl.setWidth(w);
5098 if(dlg.isVisible()){
5099 dlg.fixedcenter = false;
5101 // to big, make it scroll. = But as usual stupid IE does not support
5104 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5105 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5106 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5108 bodyEl.dom.style.height = '';
5109 bodyEl.dom.style.overflowY = '';
5112 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5114 bodyEl.dom.style.overflowX = '';
5117 dlg.setContentSize(w, bodyEl.getHeight());
5118 if(dlg.isVisible()){
5119 dlg.fixedcenter = true;
5125 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5126 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5127 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5128 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5129 * @return {Roo.MessageBox} This message box
5131 updateProgress : function(value, text){
5133 this.updateText(text);
5136 if (pp) { // weird bug on my firefox - for some reason this is not defined
5137 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5138 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5144 * Returns true if the message box is currently displayed
5145 * @return {Boolean} True if the message box is visible, else false
5147 isVisible : function(){
5148 return dlg && dlg.isVisible();
5152 * Hides the message box if it is displayed
5155 if(this.isVisible()){
5161 * Displays a new message box, or reinitializes an existing message box, based on the config options
5162 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5163 * The following config object properties are supported:
5165 Property Type Description
5166 ---------- --------------- ------------------------------------------------------------------------------------
5167 animEl String/Element An id or Element from which the message box should animate as it opens and
5168 closes (defaults to undefined)
5169 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5170 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5171 closable Boolean False to hide the top-right close button (defaults to true). Note that
5172 progress and wait dialogs will ignore this property and always hide the
5173 close button as they can only be closed programmatically.
5174 cls String A custom CSS class to apply to the message box element
5175 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5176 displayed (defaults to 75)
5177 fn Function A callback function to execute after closing the dialog. The arguments to the
5178 function will be btn (the name of the button that was clicked, if applicable,
5179 e.g. "ok"), and text (the value of the active text field, if applicable).
5180 Progress and wait dialogs will ignore this option since they do not respond to
5181 user actions and can only be closed programmatically, so any required function
5182 should be called by the same code after it closes the dialog.
5183 icon String A CSS class that provides a background image to be used as an icon for
5184 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5185 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5186 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5187 modal Boolean False to allow user interaction with the page while the message box is
5188 displayed (defaults to true)
5189 msg String A string that will replace the existing message box body text (defaults
5190 to the XHTML-compliant non-breaking space character ' ')
5191 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5192 progress Boolean True to display a progress bar (defaults to false)
5193 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5194 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5195 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5196 title String The title text
5197 value String The string value to set into the active textbox element if displayed
5198 wait Boolean True to display a progress bar (defaults to false)
5199 width Number The width of the dialog in pixels
5206 msg: 'Please enter your address:',
5208 buttons: Roo.MessageBox.OKCANCEL,
5211 animEl: 'addAddressBtn'
5214 * @param {Object} config Configuration options
5215 * @return {Roo.MessageBox} This message box
5217 show : function(options)
5220 // this causes nightmares if you show one dialog after another
5221 // especially on callbacks..
5223 if(this.isVisible()){
5226 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5227 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5228 Roo.log("New Dialog Message:" + options.msg )
5229 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5230 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5233 var d = this.getDialog();
5235 d.setTitle(opt.title || " ");
5236 d.closeEl.setDisplayed(opt.closable !== false);
5237 activeTextEl = textboxEl;
5238 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5243 textareaEl.setHeight(typeof opt.multiline == "number" ?
5244 opt.multiline : this.defaultTextHeight);
5245 activeTextEl = textareaEl;
5254 progressEl.setDisplayed(opt.progress === true);
5256 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5258 this.updateProgress(0);
5259 activeTextEl.dom.value = opt.value || "";
5261 dlg.setDefaultButton(activeTextEl);
5263 var bs = opt.buttons;
5267 }else if(bs && bs.yes){
5268 db = buttons["yes"];
5270 dlg.setDefaultButton(db);
5272 bwidth = updateButtons(opt.buttons);
5273 this.updateText(opt.msg);
5275 d.el.addClass(opt.cls);
5277 d.proxyDrag = opt.proxyDrag === true;
5278 d.modal = opt.modal !== false;
5279 d.mask = opt.modal !== false ? mask : false;
5281 // force it to the end of the z-index stack so it gets a cursor in FF
5282 document.body.appendChild(dlg.el.dom);
5283 d.animateTarget = null;
5284 d.show(options.animEl);
5290 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5291 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5292 * and closing the message box when the process is complete.
5293 * @param {String} title The title bar text
5294 * @param {String} msg The message box body text
5295 * @return {Roo.MessageBox} This message box
5297 progress : function(title, msg){
5304 minWidth: this.minProgressWidth,
5311 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5312 * If a callback function is passed it will be called after the user clicks the button, and the
5313 * id of the button that was clicked will be passed as the only parameter to the callback
5314 * (could also be the top-right close button).
5315 * @param {String} title The title bar text
5316 * @param {String} msg The message box body text
5317 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5318 * @param {Object} scope (optional) The scope of the callback function
5319 * @return {Roo.MessageBox} This message box
5321 alert : function(title, msg, fn, scope)
5336 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5337 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5338 * You are responsible for closing the message box when the process is complete.
5339 * @param {String} msg The message box body text
5340 * @param {String} title (optional) The title bar text
5341 * @return {Roo.MessageBox} This message box
5343 wait : function(msg, title){
5354 waitTimer = Roo.TaskMgr.start({
5356 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5364 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5365 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5366 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5367 * @param {String} title The title bar text
5368 * @param {String} msg The message box body text
5369 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5370 * @param {Object} scope (optional) The scope of the callback function
5371 * @return {Roo.MessageBox} This message box
5373 confirm : function(title, msg, fn, scope){
5377 buttons: this.YESNO,
5386 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5387 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5388 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5389 * (could also be the top-right close button) and the text that was entered will be passed as the two
5390 * parameters to the callback.
5391 * @param {String} title The title bar text
5392 * @param {String} msg The message box body text
5393 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5394 * @param {Object} scope (optional) The scope of the callback function
5395 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5396 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5397 * @return {Roo.MessageBox} This message box
5399 prompt : function(title, msg, fn, scope, multiline){
5403 buttons: this.OKCANCEL,
5408 multiline: multiline,
5415 * Button config that displays a single OK button
5420 * Button config that displays Yes and No buttons
5423 YESNO : {yes:true, no:true},
5425 * Button config that displays OK and Cancel buttons
5428 OKCANCEL : {ok:true, cancel:true},
5430 * Button config that displays Yes, No and Cancel buttons
5433 YESNOCANCEL : {yes:true, no:true, cancel:true},
5436 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5439 defaultTextHeight : 75,
5441 * The maximum width in pixels of the message box (defaults to 600)
5446 * The minimum width in pixels of the message box (defaults to 100)
5451 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5452 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5455 minProgressWidth : 250,
5457 * An object containing the default button text strings that can be overriden for localized language support.
5458 * Supported properties are: ok, cancel, yes and no.
5459 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5472 * Shorthand for {@link Roo.MessageBox}
5474 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5475 Roo.Msg = Roo.Msg || Roo.MessageBox;
5484 * @class Roo.bootstrap.Navbar
5485 * @extends Roo.bootstrap.Component
5486 * Bootstrap Navbar class
5489 * Create a new Navbar
5490 * @param {Object} config The config object
5494 Roo.bootstrap.Navbar = function(config){
5495 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5499 * @event beforetoggle
5500 * Fire before toggle the menu
5501 * @param {Roo.EventObject} e
5503 "beforetoggle" : true
5507 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5516 getAutoCreate : function(){
5519 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5523 initEvents :function ()
5525 //Roo.log(this.el.select('.navbar-toggle',true));
5526 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5533 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5535 var size = this.el.getSize();
5536 this.maskEl.setSize(size.width, size.height);
5537 this.maskEl.enableDisplayMode("block");
5546 getChildContainer : function()
5548 if (this.el && this.el.select('.collapse').getCount()) {
5549 return this.el.select('.collapse',true).first();
5564 onToggle : function()
5567 if(this.fireEvent('beforetoggle', this) === false){
5570 var ce = this.el.select('.navbar-collapse',true).first();
5572 if (!ce.hasClass('show')) {
5582 * Expand the navbar pulldown
5584 expand : function ()
5587 var ce = this.el.select('.navbar-collapse',true).first();
5588 if (ce.hasClass('collapsing')) {
5591 ce.dom.style.height = '';
5593 ce.addClass('in'); // old...
5594 ce.removeClass('collapse');
5595 ce.addClass('show');
5596 var h = ce.getHeight();
5598 ce.removeClass('show');
5599 // at this point we should be able to see it..
5600 ce.addClass('collapsing');
5602 ce.setHeight(0); // resize it ...
5603 ce.on('transitionend', function() {
5604 //Roo.log('done transition');
5605 ce.removeClass('collapsing');
5606 ce.addClass('show');
5607 ce.removeClass('collapse');
5609 ce.dom.style.height = '';
5610 }, this, { single: true} );
5612 ce.dom.scrollTop = 0;
5615 * Collapse the navbar pulldown
5617 collapse : function()
5619 var ce = this.el.select('.navbar-collapse',true).first();
5621 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5622 // it's collapsed or collapsing..
5625 ce.removeClass('in'); // old...
5626 ce.setHeight(ce.getHeight());
5627 ce.removeClass('show');
5628 ce.addClass('collapsing');
5630 ce.on('transitionend', function() {
5631 ce.dom.style.height = '';
5632 ce.removeClass('collapsing');
5633 ce.addClass('collapse');
5634 }, this, { single: true} );
5654 * @class Roo.bootstrap.NavSimplebar
5655 * @extends Roo.bootstrap.Navbar
5656 * Bootstrap Sidebar class
5658 * @cfg {Boolean} inverse is inverted color
5660 * @cfg {String} type (nav | pills | tabs)
5661 * @cfg {Boolean} arrangement stacked | justified
5662 * @cfg {String} align (left | right) alignment
5664 * @cfg {Boolean} main (true|false) main nav bar? default false
5665 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5667 * @cfg {String} tag (header|footer|nav|div) default is nav
5669 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5673 * Create a new Sidebar
5674 * @param {Object} config The config object
5678 Roo.bootstrap.NavSimplebar = function(config){
5679 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5682 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5698 getAutoCreate : function(){
5702 tag : this.tag || 'div',
5703 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5705 if (['light','white'].indexOf(this.weight) > -1) {
5706 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5708 cfg.cls += ' bg-' + this.weight;
5711 cfg.cls += ' navbar-inverse';
5715 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5717 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5726 cls: 'nav nav-' + this.xtype,
5732 this.type = this.type || 'nav';
5733 if (['tabs','pills'].indexOf(this.type) != -1) {
5734 cfg.cn[0].cls += ' nav-' + this.type
5738 if (this.type!=='nav') {
5739 Roo.log('nav type must be nav/tabs/pills')
5741 cfg.cn[0].cls += ' navbar-nav'
5747 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5748 cfg.cn[0].cls += ' nav-' + this.arrangement;
5752 if (this.align === 'right') {
5753 cfg.cn[0].cls += ' navbar-right';
5778 * navbar-expand-md fixed-top
5782 * @class Roo.bootstrap.NavHeaderbar
5783 * @extends Roo.bootstrap.NavSimplebar
5784 * Bootstrap Sidebar class
5786 * @cfg {String} brand what is brand
5787 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5788 * @cfg {String} brand_href href of the brand
5789 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5790 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5791 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5792 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5795 * Create a new Sidebar
5796 * @param {Object} config The config object
5800 Roo.bootstrap.NavHeaderbar = function(config){
5801 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5805 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5812 desktopCenter : false,
5815 getAutoCreate : function(){
5818 tag: this.nav || 'nav',
5819 cls: 'navbar navbar-expand-md',
5825 if (this.desktopCenter) {
5826 cn.push({cls : 'container', cn : []});
5834 cls: 'navbar-toggle navbar-toggler',
5835 'data-toggle': 'collapse',
5840 html: 'Toggle navigation'
5844 cls: 'icon-bar navbar-toggler-icon'
5857 cn.push( Roo.bootstrap.version == 4 ? btn : {
5859 cls: 'navbar-header',
5868 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5872 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5874 if (['light','white'].indexOf(this.weight) > -1) {
5875 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5877 cfg.cls += ' bg-' + this.weight;
5880 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5881 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5883 // tag can override this..
5885 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5888 if (this.brand !== '') {
5889 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5890 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5892 href: this.brand_href ? this.brand_href : '#',
5893 cls: 'navbar-brand',
5901 cfg.cls += ' main-nav';
5909 getHeaderChildContainer : function()
5911 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5912 return this.el.select('.navbar-header',true).first();
5915 return this.getChildContainer();
5918 getChildContainer : function()
5921 return this.el.select('.roo-navbar-collapse',true).first();
5926 initEvents : function()
5928 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5930 if (this.autohide) {
5935 Roo.get(document).on('scroll',function(e) {
5936 var ns = Roo.get(document).getScroll().top;
5937 var os = prevScroll;
5941 ft.removeClass('slideDown');
5942 ft.addClass('slideUp');
5945 ft.removeClass('slideUp');
5946 ft.addClass('slideDown');
5967 * @class Roo.bootstrap.NavSidebar
5968 * @extends Roo.bootstrap.Navbar
5969 * Bootstrap Sidebar class
5972 * Create a new Sidebar
5973 * @param {Object} config The config object
5977 Roo.bootstrap.NavSidebar = function(config){
5978 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5981 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5983 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5985 getAutoCreate : function(){
5990 cls: 'sidebar sidebar-nav'
6012 * @class Roo.bootstrap.NavGroup
6013 * @extends Roo.bootstrap.Component
6014 * Bootstrap NavGroup class
6015 * @cfg {String} align (left|right)
6016 * @cfg {Boolean} inverse
6017 * @cfg {String} type (nav|pills|tab) default nav
6018 * @cfg {String} navId - reference Id for navbar.
6019 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6022 * Create a new nav group
6023 * @param {Object} config The config object
6026 Roo.bootstrap.NavGroup = function(config){
6027 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6030 Roo.bootstrap.NavGroup.register(this);
6034 * Fires when the active item changes
6035 * @param {Roo.bootstrap.NavGroup} this
6036 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6037 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6044 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6056 getAutoCreate : function()
6058 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6064 if (Roo.bootstrap.version == 4) {
6065 if (['tabs','pills'].indexOf(this.type) != -1) {
6066 cfg.cls += ' nav-' + this.type;
6068 // trying to remove so header bar can right align top?
6069 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6070 // do not use on header bar...
6071 cfg.cls += ' navbar-nav';
6076 if (['tabs','pills'].indexOf(this.type) != -1) {
6077 cfg.cls += ' nav-' + this.type
6079 if (this.type !== 'nav') {
6080 Roo.log('nav type must be nav/tabs/pills')
6082 cfg.cls += ' navbar-nav'
6086 if (this.parent() && this.parent().sidebar) {
6089 cls: 'dashboard-menu sidebar-menu'
6095 if (this.form === true) {
6098 cls: 'navbar-form form-inline'
6100 //nav navbar-right ml-md-auto
6101 if (this.align === 'right') {
6102 cfg.cls += ' navbar-right ml-md-auto';
6104 cfg.cls += ' navbar-left';
6108 if (this.align === 'right') {
6109 cfg.cls += ' navbar-right ml-md-auto';
6111 cfg.cls += ' mr-auto';
6115 cfg.cls += ' navbar-inverse';
6123 * sets the active Navigation item
6124 * @param {Roo.bootstrap.NavItem} the new current navitem
6126 setActiveItem : function(item)
6129 Roo.each(this.navItems, function(v){
6134 v.setActive(false, true);
6141 item.setActive(true, true);
6142 this.fireEvent('changed', this, item, prev);
6147 * gets the active Navigation item
6148 * @return {Roo.bootstrap.NavItem} the current navitem
6150 getActive : function()
6154 Roo.each(this.navItems, function(v){
6165 indexOfNav : function()
6169 Roo.each(this.navItems, function(v,i){
6180 * adds a Navigation item
6181 * @param {Roo.bootstrap.NavItem} the navitem to add
6183 addItem : function(cfg)
6185 if (this.form && Roo.bootstrap.version == 4) {
6188 var cn = new Roo.bootstrap.NavItem(cfg);
6190 cn.parentId = this.id;
6191 cn.onRender(this.el, null);
6195 * register a Navigation item
6196 * @param {Roo.bootstrap.NavItem} the navitem to add
6198 register : function(item)
6200 this.navItems.push( item);
6201 item.navId = this.navId;
6206 * clear all the Navigation item
6209 clearAll : function()
6212 this.el.dom.innerHTML = '';
6215 getNavItem: function(tabId)
6218 Roo.each(this.navItems, function(e) {
6219 if (e.tabId == tabId) {
6229 setActiveNext : function()
6231 var i = this.indexOfNav(this.getActive());
6232 if (i > this.navItems.length) {
6235 this.setActiveItem(this.navItems[i+1]);
6237 setActivePrev : function()
6239 var i = this.indexOfNav(this.getActive());
6243 this.setActiveItem(this.navItems[i-1]);
6245 clearWasActive : function(except) {
6246 Roo.each(this.navItems, function(e) {
6247 if (e.tabId != except.tabId && e.was_active) {
6248 e.was_active = false;
6255 getWasActive : function ()
6258 Roo.each(this.navItems, function(e) {
6273 Roo.apply(Roo.bootstrap.NavGroup, {
6277 * register a Navigation Group
6278 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6280 register : function(navgrp)
6282 this.groups[navgrp.navId] = navgrp;
6286 * fetch a Navigation Group based on the navigation ID
6287 * @param {string} the navgroup to add
6288 * @returns {Roo.bootstrap.NavGroup} the navgroup
6290 get: function(navId) {
6291 if (typeof(this.groups[navId]) == 'undefined') {
6293 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6295 return this.groups[navId] ;
6310 * @class Roo.bootstrap.NavItem
6311 * @extends Roo.bootstrap.Component
6312 * Bootstrap Navbar.NavItem class
6313 * @cfg {String} href link to
6314 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6315 * @cfg {Boolean} button_outline show and outlined button
6316 * @cfg {String} html content of button
6317 * @cfg {String} badge text inside badge
6318 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6319 * @cfg {String} glyphicon DEPRICATED - use fa
6320 * @cfg {String} icon DEPRICATED - use fa
6321 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6322 * @cfg {Boolean} active Is item active
6323 * @cfg {Boolean} disabled Is item disabled
6324 * @cfg {String} linkcls Link Class
6325 * @cfg {Boolean} preventDefault (true | false) default false
6326 * @cfg {String} tabId the tab that this item activates.
6327 * @cfg {String} tagtype (a|span) render as a href or span?
6328 * @cfg {Boolean} animateRef (true|false) link to element default false
6331 * Create a new Navbar Item
6332 * @param {Object} config The config object
6334 Roo.bootstrap.NavItem = function(config){
6335 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6340 * The raw click event for the entire grid.
6341 * @param {Roo.EventObject} e
6346 * Fires when the active item active state changes
6347 * @param {Roo.bootstrap.NavItem} this
6348 * @param {boolean} state the new state
6354 * Fires when scroll to element
6355 * @param {Roo.bootstrap.NavItem} this
6356 * @param {Object} options
6357 * @param {Roo.EventObject} e
6365 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6374 preventDefault : false,
6382 button_outline : false,
6386 getAutoCreate : function(){
6393 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6396 cfg.cls += ' active' ;
6398 if (this.disabled) {
6399 cfg.cls += ' disabled';
6403 if (this.button_weight.length) {
6404 cfg.tag = this.href ? 'a' : 'button';
6405 cfg.html = this.html || '';
6406 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6408 cfg.href = this.href;
6411 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6413 cfg.cls += " nav-html";
6416 // menu .. should add dropdown-menu class - so no need for carat..
6418 if (this.badge !== '') {
6420 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6425 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6429 href : this.href || "#",
6430 html: this.html || '',
6434 if (this.tagtype == 'a') {
6435 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6439 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6440 } else if (this.fa) {
6441 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6442 } else if(this.glyphicon) {
6443 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6445 cfg.cn[0].cls += " nav-html";
6449 cfg.cn[0].html += " <span class='caret'></span>";
6453 if (this.badge !== '') {
6454 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6462 onRender : function(ct, position)
6464 // Roo.log("Call onRender: " + this.xtype);
6465 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6469 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6470 this.navLink = this.el.select('.nav-link',true).first();
6471 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6476 initEvents: function()
6478 if (typeof (this.menu) != 'undefined') {
6479 this.menu.parentType = this.xtype;
6480 this.menu.triggerEl = this.el;
6481 this.menu = this.addxtype(Roo.apply({}, this.menu));
6484 this.el.on('click', this.onClick, this);
6486 //if(this.tagtype == 'span'){
6487 // this.el.select('span',true).on('click', this.onClick, this);
6490 // at this point parent should be available..
6491 this.parent().register(this);
6494 onClick : function(e)
6496 if (e.getTarget('.dropdown-menu-item')) {
6497 // did you click on a menu itemm.... - then don't trigger onclick..
6502 this.preventDefault ||
6505 Roo.log("NavItem - prevent Default?");
6509 if (this.disabled) {
6513 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6514 if (tg && tg.transition) {
6515 Roo.log("waiting for the transitionend");
6521 //Roo.log("fire event clicked");
6522 if(this.fireEvent('click', this, e) === false){
6526 if(this.tagtype == 'span'){
6530 //Roo.log(this.href);
6531 var ael = this.el.select('a',true).first();
6534 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6535 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6536 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6537 return; // ignore... - it's a 'hash' to another page.
6539 Roo.log("NavItem - prevent Default?");
6541 this.scrollToElement(e);
6545 var p = this.parent();
6547 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6548 if (typeof(p.setActiveItem) !== 'undefined') {
6549 p.setActiveItem(this);
6553 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6554 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6555 // remove the collapsed menu expand...
6556 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6560 isActive: function () {
6563 setActive : function(state, fire, is_was_active)
6565 if (this.active && !state && this.navId) {
6566 this.was_active = true;
6567 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6569 nv.clearWasActive(this);
6573 this.active = state;
6576 this.el.removeClass('active');
6577 this.navLink ? this.navLink.removeClass('active') : false;
6578 } else if (!this.el.hasClass('active')) {
6580 this.el.addClass('active');
6581 if (Roo.bootstrap.version == 4 && this.navLink ) {
6582 this.navLink.addClass('active');
6587 this.fireEvent('changed', this, state);
6590 // show a panel if it's registered and related..
6592 if (!this.navId || !this.tabId || !state || is_was_active) {
6596 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6600 var pan = tg.getPanelByName(this.tabId);
6604 // if we can not flip to new panel - go back to old nav highlight..
6605 if (false == tg.showPanel(pan)) {
6606 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6608 var onav = nv.getWasActive();
6610 onav.setActive(true, false, true);
6619 // this should not be here...
6620 setDisabled : function(state)
6622 this.disabled = state;
6624 this.el.removeClass('disabled');
6625 } else if (!this.el.hasClass('disabled')) {
6626 this.el.addClass('disabled');
6632 * Fetch the element to display the tooltip on.
6633 * @return {Roo.Element} defaults to this.el
6635 tooltipEl : function()
6637 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6640 scrollToElement : function(e)
6642 var c = document.body;
6645 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6647 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6648 c = document.documentElement;
6651 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6657 var o = target.calcOffsetsTo(c);
6664 this.fireEvent('scrollto', this, options, e);
6666 Roo.get(c).scrollTo('top', options.value, true);
6671 * Set the HTML (text content) of the item
6672 * @param {string} html content for the nav item
6674 setHtml : function(html)
6677 this.htmlEl.dom.innerHTML = html;
6689 * <span> icon </span>
6690 * <span> text </span>
6691 * <span>badge </span>
6695 * @class Roo.bootstrap.NavSidebarItem
6696 * @extends Roo.bootstrap.NavItem
6697 * Bootstrap Navbar.NavSidebarItem class
6698 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6699 * {Boolean} open is the menu open
6700 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6701 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6702 * {String} buttonSize (sm|md|lg)the extra classes for the button
6703 * {Boolean} showArrow show arrow next to the text (default true)
6705 * Create a new Navbar Button
6706 * @param {Object} config The config object
6708 Roo.bootstrap.NavSidebarItem = function(config){
6709 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6714 * The raw click event for the entire grid.
6715 * @param {Roo.EventObject} e
6720 * Fires when the active item active state changes
6721 * @param {Roo.bootstrap.NavSidebarItem} this
6722 * @param {boolean} state the new state
6730 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6732 badgeWeight : 'default',
6738 buttonWeight : 'default',
6744 getAutoCreate : function(){
6749 href : this.href || '#',
6755 if(this.buttonView){
6758 href : this.href || '#',
6759 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6772 cfg.cls += ' active';
6775 if (this.disabled) {
6776 cfg.cls += ' disabled';
6779 cfg.cls += ' open x-open';
6782 if (this.glyphicon || this.icon) {
6783 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6784 a.cn.push({ tag : 'i', cls : c }) ;
6787 if(!this.buttonView){
6790 html : this.html || ''
6797 if (this.badge !== '') {
6798 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6804 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6807 a.cls += ' dropdown-toggle treeview' ;
6813 initEvents : function()
6815 if (typeof (this.menu) != 'undefined') {
6816 this.menu.parentType = this.xtype;
6817 this.menu.triggerEl = this.el;
6818 this.menu = this.addxtype(Roo.apply({}, this.menu));
6821 this.el.on('click', this.onClick, this);
6823 if(this.badge !== ''){
6824 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6829 onClick : function(e)
6836 if(this.preventDefault){
6840 this.fireEvent('click', this, e);
6843 disable : function()
6845 this.setDisabled(true);
6850 this.setDisabled(false);
6853 setDisabled : function(state)
6855 if(this.disabled == state){
6859 this.disabled = state;
6862 this.el.addClass('disabled');
6866 this.el.removeClass('disabled');
6871 setActive : function(state)
6873 if(this.active == state){
6877 this.active = state;
6880 this.el.addClass('active');
6884 this.el.removeClass('active');
6889 isActive: function ()
6894 setBadge : function(str)
6900 this.badgeEl.dom.innerHTML = str;
6915 Roo.namespace('Roo.bootstrap.breadcrumb');
6919 * @class Roo.bootstrap.breadcrumb.Nav
6920 * @extends Roo.bootstrap.Component
6921 * Bootstrap Breadcrumb Nav Class
6923 * @children Roo.bootstrap.breadcrumb.Item
6926 * Create a new breadcrumb.Nav
6927 * @param {Object} config The config object
6931 Roo.bootstrap.breadcrumb.Nav = function(config){
6932 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6937 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6939 getAutoCreate : function()
6956 initEvents: function()
6958 this.olEl = this.el.select('ol',true).first();
6960 getChildContainer : function()
6976 * @class Roo.bootstrap.breadcrumb.Nav
6977 * @extends Roo.bootstrap.Component
6978 * Bootstrap Breadcrumb Nav Class
6980 * @children Roo.bootstrap.breadcrumb.Component
6981 * @cfg {String} html the content of the link.
6982 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6983 * @cfg {Boolean} active is it active
6987 * Create a new breadcrumb.Nav
6988 * @param {Object} config The config object
6991 Roo.bootstrap.breadcrumb.Item = function(config){
6992 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6997 * The img click event for the img.
6998 * @param {Roo.EventObject} e
7005 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7010 getAutoCreate : function()
7015 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7017 if (this.href !== false) {
7024 cfg.html = this.html;
7030 initEvents: function()
7033 this.el.select('a', true).first().on('click',this.onClick, this)
7037 onClick : function(e)
7040 this.fireEvent('click',this, e);
7053 * @class Roo.bootstrap.Row
7054 * @extends Roo.bootstrap.Component
7055 * Bootstrap Row class (contains columns...)
7059 * @param {Object} config The config object
7062 Roo.bootstrap.Row = function(config){
7063 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7066 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7068 getAutoCreate : function(){
7087 * @class Roo.bootstrap.Pagination
7088 * @extends Roo.bootstrap.Component
7089 * Bootstrap Pagination class
7090 * @cfg {String} size xs | sm | md | lg
7091 * @cfg {Boolean} inverse false | true
7094 * Create a new Pagination
7095 * @param {Object} config The config object
7098 Roo.bootstrap.Pagination = function(config){
7099 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7102 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7108 getAutoCreate : function(){
7114 cfg.cls += ' inverse';
7120 cfg.cls += " " + this.cls;
7138 * @class Roo.bootstrap.PaginationItem
7139 * @extends Roo.bootstrap.Component
7140 * Bootstrap PaginationItem class
7141 * @cfg {String} html text
7142 * @cfg {String} href the link
7143 * @cfg {Boolean} preventDefault (true | false) default true
7144 * @cfg {Boolean} active (true | false) default false
7145 * @cfg {Boolean} disabled default false
7149 * Create a new PaginationItem
7150 * @param {Object} config The config object
7154 Roo.bootstrap.PaginationItem = function(config){
7155 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7160 * The raw click event for the entire grid.
7161 * @param {Roo.EventObject} e
7167 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7171 preventDefault: true,
7176 getAutoCreate : function(){
7182 href : this.href ? this.href : '#',
7183 html : this.html ? this.html : ''
7193 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7197 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7203 initEvents: function() {
7205 this.el.on('click', this.onClick, this);
7208 onClick : function(e)
7210 Roo.log('PaginationItem on click ');
7211 if(this.preventDefault){
7219 this.fireEvent('click', this, e);
7235 * @class Roo.bootstrap.Slider
7236 * @extends Roo.bootstrap.Component
7237 * Bootstrap Slider class
7240 * Create a new Slider
7241 * @param {Object} config The config object
7244 Roo.bootstrap.Slider = function(config){
7245 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7248 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7250 getAutoCreate : function(){
7254 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7258 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7270 * Ext JS Library 1.1.1
7271 * Copyright(c) 2006-2007, Ext JS, LLC.
7273 * Originally Released Under LGPL - original licence link has changed is not relivant.
7276 * <script type="text/javascript">
7281 * @class Roo.grid.ColumnModel
7282 * @extends Roo.util.Observable
7283 * This is the default implementation of a ColumnModel used by the Grid. It defines
7284 * the columns in the grid.
7287 var colModel = new Roo.grid.ColumnModel([
7288 {header: "Ticker", width: 60, sortable: true, locked: true},
7289 {header: "Company Name", width: 150, sortable: true},
7290 {header: "Market Cap.", width: 100, sortable: true},
7291 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7292 {header: "Employees", width: 100, sortable: true, resizable: false}
7297 * The config options listed for this class are options which may appear in each
7298 * individual column definition.
7299 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7301 * @param {Object} config An Array of column config objects. See this class's
7302 * config objects for details.
7304 Roo.grid.ColumnModel = function(config){
7306 * The config passed into the constructor
7308 this.config = []; //config;
7311 // if no id, create one
7312 // if the column does not have a dataIndex mapping,
7313 // map it to the order it is in the config
7314 for(var i = 0, len = config.length; i < len; i++){
7315 this.addColumn(config[i]);
7320 * The width of columns which have no width specified (defaults to 100)
7323 this.defaultWidth = 100;
7326 * Default sortable of columns which have no sortable specified (defaults to false)
7329 this.defaultSortable = false;
7333 * @event widthchange
7334 * Fires when the width of a column changes.
7335 * @param {ColumnModel} this
7336 * @param {Number} columnIndex The column index
7337 * @param {Number} newWidth The new width
7339 "widthchange": true,
7341 * @event headerchange
7342 * Fires when the text of a header changes.
7343 * @param {ColumnModel} this
7344 * @param {Number} columnIndex The column index
7345 * @param {Number} newText The new header text
7347 "headerchange": true,
7349 * @event hiddenchange
7350 * Fires when a column is hidden or "unhidden".
7351 * @param {ColumnModel} this
7352 * @param {Number} columnIndex The column index
7353 * @param {Boolean} hidden true if hidden, false otherwise
7355 "hiddenchange": true,
7357 * @event columnmoved
7358 * Fires when a column is moved.
7359 * @param {ColumnModel} this
7360 * @param {Number} oldIndex
7361 * @param {Number} newIndex
7363 "columnmoved" : true,
7365 * @event columlockchange
7366 * Fires when a column's locked state is changed
7367 * @param {ColumnModel} this
7368 * @param {Number} colIndex
7369 * @param {Boolean} locked true if locked
7371 "columnlockchange" : true
7373 Roo.grid.ColumnModel.superclass.constructor.call(this);
7375 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7377 * @cfg {String} header The header text to display in the Grid view.
7380 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7381 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7382 * specified, the column's index is used as an index into the Record's data Array.
7385 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7386 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7389 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7390 * Defaults to the value of the {@link #defaultSortable} property.
7391 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7394 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7397 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7400 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7403 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7406 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7407 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7408 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7409 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7412 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7415 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7418 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7421 * @cfg {String} cursor (Optional)
7424 * @cfg {String} tooltip (Optional)
7427 * @cfg {Number} xs (Optional)
7430 * @cfg {Number} sm (Optional)
7433 * @cfg {Number} md (Optional)
7436 * @cfg {Number} lg (Optional)
7439 * Returns the id of the column at the specified index.
7440 * @param {Number} index The column index
7441 * @return {String} the id
7443 getColumnId : function(index){
7444 return this.config[index].id;
7448 * Returns the column for a specified id.
7449 * @param {String} id The column id
7450 * @return {Object} the column
7452 getColumnById : function(id){
7453 return this.lookup[id];
7458 * Returns the column Object for a specified dataIndex.
7459 * @param {String} dataIndex The column dataIndex
7460 * @return {Object|Boolean} the column or false if not found
7462 getColumnByDataIndex: function(dataIndex){
7463 var index = this.findColumnIndex(dataIndex);
7464 return index > -1 ? this.config[index] : false;
7468 * Returns the index for a specified column id.
7469 * @param {String} id The column id
7470 * @return {Number} the index, or -1 if not found
7472 getIndexById : function(id){
7473 for(var i = 0, len = this.config.length; i < len; i++){
7474 if(this.config[i].id == id){
7482 * Returns the index for a specified column dataIndex.
7483 * @param {String} dataIndex The column dataIndex
7484 * @return {Number} the index, or -1 if not found
7487 findColumnIndex : function(dataIndex){
7488 for(var i = 0, len = this.config.length; i < len; i++){
7489 if(this.config[i].dataIndex == dataIndex){
7497 moveColumn : function(oldIndex, newIndex){
7498 var c = this.config[oldIndex];
7499 this.config.splice(oldIndex, 1);
7500 this.config.splice(newIndex, 0, c);
7501 this.dataMap = null;
7502 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7505 isLocked : function(colIndex){
7506 return this.config[colIndex].locked === true;
7509 setLocked : function(colIndex, value, suppressEvent){
7510 if(this.isLocked(colIndex) == value){
7513 this.config[colIndex].locked = value;
7515 this.fireEvent("columnlockchange", this, colIndex, value);
7519 getTotalLockedWidth : function(){
7521 for(var i = 0; i < this.config.length; i++){
7522 if(this.isLocked(i) && !this.isHidden(i)){
7523 this.totalWidth += this.getColumnWidth(i);
7529 getLockedCount : function(){
7530 for(var i = 0, len = this.config.length; i < len; i++){
7531 if(!this.isLocked(i)){
7536 return this.config.length;
7540 * Returns the number of columns.
7543 getColumnCount : function(visibleOnly){
7544 if(visibleOnly === true){
7546 for(var i = 0, len = this.config.length; i < len; i++){
7547 if(!this.isHidden(i)){
7553 return this.config.length;
7557 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7558 * @param {Function} fn
7559 * @param {Object} scope (optional)
7560 * @return {Array} result
7562 getColumnsBy : function(fn, scope){
7564 for(var i = 0, len = this.config.length; i < len; i++){
7565 var c = this.config[i];
7566 if(fn.call(scope||this, c, i) === true){
7574 * Returns true if the specified column is sortable.
7575 * @param {Number} col The column index
7578 isSortable : function(col){
7579 if(typeof this.config[col].sortable == "undefined"){
7580 return this.defaultSortable;
7582 return this.config[col].sortable;
7586 * Returns the rendering (formatting) function defined for the column.
7587 * @param {Number} col The column index.
7588 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7590 getRenderer : function(col){
7591 if(!this.config[col].renderer){
7592 return Roo.grid.ColumnModel.defaultRenderer;
7594 return this.config[col].renderer;
7598 * Sets the rendering (formatting) function for a column.
7599 * @param {Number} col The column index
7600 * @param {Function} fn The function to use to process the cell's raw data
7601 * to return HTML markup for the grid view. The render function is called with
7602 * the following parameters:<ul>
7603 * <li>Data value.</li>
7604 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7605 * <li>css A CSS style string to apply to the table cell.</li>
7606 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7607 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7608 * <li>Row index</li>
7609 * <li>Column index</li>
7610 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7612 setRenderer : function(col, fn){
7613 this.config[col].renderer = fn;
7617 * Returns the width for the specified column.
7618 * @param {Number} col The column index
7621 getColumnWidth : function(col){
7622 return this.config[col].width * 1 || this.defaultWidth;
7626 * Sets the width for a column.
7627 * @param {Number} col The column index
7628 * @param {Number} width The new width
7630 setColumnWidth : function(col, width, suppressEvent){
7631 this.config[col].width = width;
7632 this.totalWidth = null;
7634 this.fireEvent("widthchange", this, col, width);
7639 * Returns the total width of all columns.
7640 * @param {Boolean} includeHidden True to include hidden column widths
7643 getTotalWidth : function(includeHidden){
7644 if(!this.totalWidth){
7645 this.totalWidth = 0;
7646 for(var i = 0, len = this.config.length; i < len; i++){
7647 if(includeHidden || !this.isHidden(i)){
7648 this.totalWidth += this.getColumnWidth(i);
7652 return this.totalWidth;
7656 * Returns the header for the specified column.
7657 * @param {Number} col The column index
7660 getColumnHeader : function(col){
7661 return this.config[col].header;
7665 * Sets the header for a column.
7666 * @param {Number} col The column index
7667 * @param {String} header The new header
7669 setColumnHeader : function(col, header){
7670 this.config[col].header = header;
7671 this.fireEvent("headerchange", this, col, header);
7675 * Returns the tooltip for the specified column.
7676 * @param {Number} col The column index
7679 getColumnTooltip : function(col){
7680 return this.config[col].tooltip;
7683 * Sets the tooltip for a column.
7684 * @param {Number} col The column index
7685 * @param {String} tooltip The new tooltip
7687 setColumnTooltip : function(col, tooltip){
7688 this.config[col].tooltip = tooltip;
7692 * Returns the dataIndex for the specified column.
7693 * @param {Number} col The column index
7696 getDataIndex : function(col){
7697 return this.config[col].dataIndex;
7701 * Sets the dataIndex for a column.
7702 * @param {Number} col The column index
7703 * @param {Number} dataIndex The new dataIndex
7705 setDataIndex : function(col, dataIndex){
7706 this.config[col].dataIndex = dataIndex;
7712 * Returns true if the cell is editable.
7713 * @param {Number} colIndex The column index
7714 * @param {Number} rowIndex The row index - this is nto actually used..?
7717 isCellEditable : function(colIndex, rowIndex){
7718 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7722 * Returns the editor defined for the cell/column.
7723 * return false or null to disable editing.
7724 * @param {Number} colIndex The column index
7725 * @param {Number} rowIndex The row index
7728 getCellEditor : function(colIndex, rowIndex){
7729 return this.config[colIndex].editor;
7733 * Sets if a column is editable.
7734 * @param {Number} col The column index
7735 * @param {Boolean} editable True if the column is editable
7737 setEditable : function(col, editable){
7738 this.config[col].editable = editable;
7743 * Returns true if the column is hidden.
7744 * @param {Number} colIndex The column index
7747 isHidden : function(colIndex){
7748 return this.config[colIndex].hidden;
7753 * Returns true if the column width cannot be changed
7755 isFixed : function(colIndex){
7756 return this.config[colIndex].fixed;
7760 * Returns true if the column can be resized
7763 isResizable : function(colIndex){
7764 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7767 * Sets if a column is hidden.
7768 * @param {Number} colIndex The column index
7769 * @param {Boolean} hidden True if the column is hidden
7771 setHidden : function(colIndex, hidden){
7772 this.config[colIndex].hidden = hidden;
7773 this.totalWidth = null;
7774 this.fireEvent("hiddenchange", this, colIndex, hidden);
7778 * Sets the editor for a column.
7779 * @param {Number} col The column index
7780 * @param {Object} editor The editor object
7782 setEditor : function(col, editor){
7783 this.config[col].editor = editor;
7786 * Add a column (experimental...) - defaults to adding to the end..
7787 * @param {Object} config
7789 addColumn : function(c)
7792 var i = this.config.length;
7795 if(typeof c.dataIndex == "undefined"){
7798 if(typeof c.renderer == "string"){
7799 c.renderer = Roo.util.Format[c.renderer];
7801 if(typeof c.id == "undefined"){
7804 if(c.editor && c.editor.xtype){
7805 c.editor = Roo.factory(c.editor, Roo.grid);
7807 if(c.editor && c.editor.isFormField){
7808 c.editor = new Roo.grid.GridEditor(c.editor);
7810 this.lookup[c.id] = c;
7815 Roo.grid.ColumnModel.defaultRenderer = function(value)
7817 if(typeof value == "object") {
7820 if(typeof value == "string" && value.length < 1){
7824 return String.format("{0}", value);
7827 // Alias for backwards compatibility
7828 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7831 * Ext JS Library 1.1.1
7832 * Copyright(c) 2006-2007, Ext JS, LLC.
7834 * Originally Released Under LGPL - original licence link has changed is not relivant.
7837 * <script type="text/javascript">
7841 * @class Roo.LoadMask
7842 * A simple utility class for generically masking elements while loading data. If the element being masked has
7843 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7844 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7845 * element's UpdateManager load indicator and will be destroyed after the initial load.
7847 * Create a new LoadMask
7848 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7849 * @param {Object} config The config object
7851 Roo.LoadMask = function(el, config){
7852 this.el = Roo.get(el);
7853 Roo.apply(this, config);
7855 this.store.on('beforeload', this.onBeforeLoad, this);
7856 this.store.on('load', this.onLoad, this);
7857 this.store.on('loadexception', this.onLoadException, this);
7858 this.removeMask = false;
7860 var um = this.el.getUpdateManager();
7861 um.showLoadIndicator = false; // disable the default indicator
7862 um.on('beforeupdate', this.onBeforeLoad, this);
7863 um.on('update', this.onLoad, this);
7864 um.on('failure', this.onLoad, this);
7865 this.removeMask = true;
7869 Roo.LoadMask.prototype = {
7871 * @cfg {Boolean} removeMask
7872 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7873 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7877 * The text to display in a centered loading message box (defaults to 'Loading...')
7881 * @cfg {String} msgCls
7882 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7884 msgCls : 'x-mask-loading',
7887 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7893 * Disables the mask to prevent it from being displayed
7895 disable : function(){
7896 this.disabled = true;
7900 * Enables the mask so that it can be displayed
7902 enable : function(){
7903 this.disabled = false;
7906 onLoadException : function()
7910 if (typeof(arguments[3]) != 'undefined') {
7911 Roo.MessageBox.alert("Error loading",arguments[3]);
7915 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7916 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7923 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7928 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7932 onBeforeLoad : function(){
7934 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7939 destroy : function(){
7941 this.store.un('beforeload', this.onBeforeLoad, this);
7942 this.store.un('load', this.onLoad, this);
7943 this.store.un('loadexception', this.onLoadException, this);
7945 var um = this.el.getUpdateManager();
7946 um.un('beforeupdate', this.onBeforeLoad, this);
7947 um.un('update', this.onLoad, this);
7948 um.un('failure', this.onLoad, this);
7959 * @class Roo.bootstrap.Table
7960 * @extends Roo.bootstrap.Component
7961 * Bootstrap Table class
7962 * @cfg {String} cls table class
7963 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7964 * @cfg {String} bgcolor Specifies the background color for a table
7965 * @cfg {Number} border Specifies whether the table cells should have borders or not
7966 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7967 * @cfg {Number} cellspacing Specifies the space between cells
7968 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7969 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7970 * @cfg {String} sortable Specifies that the table should be sortable
7971 * @cfg {String} summary Specifies a summary of the content of a table
7972 * @cfg {Number} width Specifies the width of a table
7973 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7975 * @cfg {boolean} striped Should the rows be alternative striped
7976 * @cfg {boolean} bordered Add borders to the table
7977 * @cfg {boolean} hover Add hover highlighting
7978 * @cfg {boolean} condensed Format condensed
7979 * @cfg {boolean} responsive Format condensed
7980 * @cfg {Boolean} loadMask (true|false) default false
7981 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7982 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7983 * @cfg {Boolean} rowSelection (true|false) default false
7984 * @cfg {Boolean} cellSelection (true|false) default false
7985 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7986 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7987 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7988 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7992 * Create a new Table
7993 * @param {Object} config The config object
7996 Roo.bootstrap.Table = function(config){
7997 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8002 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8003 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8004 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8005 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8007 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8009 this.sm.grid = this;
8010 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
8011 this.sm = this.selModel;
8012 this.sm.xmodule = this.xmodule || false;
8015 if (this.cm && typeof(this.cm.config) == 'undefined') {
8016 this.colModel = new Roo.grid.ColumnModel(this.cm);
8017 this.cm = this.colModel;
8018 this.cm.xmodule = this.xmodule || false;
8021 this.store= Roo.factory(this.store, Roo.data);
8022 this.ds = this.store;
8023 this.ds.xmodule = this.xmodule || false;
8026 if (this.footer && this.store) {
8027 this.footer.dataSource = this.ds;
8028 this.footer = Roo.factory(this.footer);
8035 * Fires when a cell is clicked
8036 * @param {Roo.bootstrap.Table} this
8037 * @param {Roo.Element} el
8038 * @param {Number} rowIndex
8039 * @param {Number} columnIndex
8040 * @param {Roo.EventObject} e
8044 * @event celldblclick
8045 * Fires when a cell is double clicked
8046 * @param {Roo.bootstrap.Table} this
8047 * @param {Roo.Element} el
8048 * @param {Number} rowIndex
8049 * @param {Number} columnIndex
8050 * @param {Roo.EventObject} e
8052 "celldblclick" : true,
8055 * Fires when a row is clicked
8056 * @param {Roo.bootstrap.Table} this
8057 * @param {Roo.Element} el
8058 * @param {Number} rowIndex
8059 * @param {Roo.EventObject} e
8063 * @event rowdblclick
8064 * Fires when a row is double clicked
8065 * @param {Roo.bootstrap.Table} this
8066 * @param {Roo.Element} el
8067 * @param {Number} rowIndex
8068 * @param {Roo.EventObject} e
8070 "rowdblclick" : true,
8073 * Fires when a mouseover occur
8074 * @param {Roo.bootstrap.Table} this
8075 * @param {Roo.Element} el
8076 * @param {Number} rowIndex
8077 * @param {Number} columnIndex
8078 * @param {Roo.EventObject} e
8083 * Fires when a mouseout occur
8084 * @param {Roo.bootstrap.Table} this
8085 * @param {Roo.Element} el
8086 * @param {Number} rowIndex
8087 * @param {Number} columnIndex
8088 * @param {Roo.EventObject} e
8093 * Fires when a row is rendered, so you can change add a style to it.
8094 * @param {Roo.bootstrap.Table} this
8095 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8099 * @event rowsrendered
8100 * Fires when all the rows have been rendered
8101 * @param {Roo.bootstrap.Table} this
8103 'rowsrendered' : true,
8105 * @event contextmenu
8106 * The raw contextmenu event for the entire grid.
8107 * @param {Roo.EventObject} e
8109 "contextmenu" : true,
8111 * @event rowcontextmenu
8112 * Fires when a row is right clicked
8113 * @param {Roo.bootstrap.Table} this
8114 * @param {Number} rowIndex
8115 * @param {Roo.EventObject} e
8117 "rowcontextmenu" : true,
8119 * @event cellcontextmenu
8120 * Fires when a cell is right clicked
8121 * @param {Roo.bootstrap.Table} this
8122 * @param {Number} rowIndex
8123 * @param {Number} cellIndex
8124 * @param {Roo.EventObject} e
8126 "cellcontextmenu" : true,
8128 * @event headercontextmenu
8129 * Fires when a header is right clicked
8130 * @param {Roo.bootstrap.Table} this
8131 * @param {Number} columnIndex
8132 * @param {Roo.EventObject} e
8134 "headercontextmenu" : true
8138 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8164 rowSelection : false,
8165 cellSelection : false,
8168 // Roo.Element - the tbody
8170 // Roo.Element - thead element
8173 container: false, // used by gridpanel...
8179 auto_hide_footer : false,
8181 getAutoCreate : function()
8183 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8190 if (this.scrollBody) {
8191 cfg.cls += ' table-body-fixed';
8194 cfg.cls += ' table-striped';
8198 cfg.cls += ' table-hover';
8200 if (this.bordered) {
8201 cfg.cls += ' table-bordered';
8203 if (this.condensed) {
8204 cfg.cls += ' table-condensed';
8206 if (this.responsive) {
8207 cfg.cls += ' table-responsive';
8211 cfg.cls+= ' ' +this.cls;
8214 // this lot should be simplifed...
8227 ].forEach(function(k) {
8235 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8238 if(this.store || this.cm){
8239 if(this.headerShow){
8240 cfg.cn.push(this.renderHeader());
8243 cfg.cn.push(this.renderBody());
8245 if(this.footerShow){
8246 cfg.cn.push(this.renderFooter());
8248 // where does this come from?
8249 //cfg.cls+= ' TableGrid';
8252 return { cn : [ cfg ] };
8255 initEvents : function()
8257 if(!this.store || !this.cm){
8260 if (this.selModel) {
8261 this.selModel.initEvents();
8265 //Roo.log('initEvents with ds!!!!');
8267 this.mainBody = this.el.select('tbody', true).first();
8268 this.mainHead = this.el.select('thead', true).first();
8269 this.mainFoot = this.el.select('tfoot', true).first();
8274 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8275 e.on('click', this.sort, this);
8278 this.mainBody.on("click", this.onClick, this);
8279 this.mainBody.on("dblclick", this.onDblClick, this);
8281 // why is this done????? = it breaks dialogs??
8282 //this.parent().el.setStyle('position', 'relative');
8286 this.footer.parentId = this.id;
8287 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8290 this.el.select('tfoot tr td').first().addClass('hide');
8295 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8298 this.store.on('load', this.onLoad, this);
8299 this.store.on('beforeload', this.onBeforeLoad, this);
8300 this.store.on('update', this.onUpdate, this);
8301 this.store.on('add', this.onAdd, this);
8302 this.store.on("clear", this.clear, this);
8304 this.el.on("contextmenu", this.onContextMenu, this);
8306 this.mainBody.on('scroll', this.onBodyScroll, this);
8308 this.cm.on("headerchange", this.onHeaderChange, this);
8310 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8314 onContextMenu : function(e, t)
8316 this.processEvent("contextmenu", e);
8319 processEvent : function(name, e)
8321 if (name != 'touchstart' ) {
8322 this.fireEvent(name, e);
8325 var t = e.getTarget();
8327 var cell = Roo.get(t);
8333 if(cell.findParent('tfoot', false, true)){
8337 if(cell.findParent('thead', false, true)){
8339 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8340 cell = Roo.get(t).findParent('th', false, true);
8342 Roo.log("failed to find th in thead?");
8343 Roo.log(e.getTarget());
8348 var cellIndex = cell.dom.cellIndex;
8350 var ename = name == 'touchstart' ? 'click' : name;
8351 this.fireEvent("header" + ename, this, cellIndex, e);
8356 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8357 cell = Roo.get(t).findParent('td', false, true);
8359 Roo.log("failed to find th in tbody?");
8360 Roo.log(e.getTarget());
8365 var row = cell.findParent('tr', false, true);
8366 var cellIndex = cell.dom.cellIndex;
8367 var rowIndex = row.dom.rowIndex - 1;
8371 this.fireEvent("row" + name, this, rowIndex, e);
8375 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8381 onMouseover : function(e, el)
8383 var cell = Roo.get(el);
8389 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8390 cell = cell.findParent('td', false, true);
8393 var row = cell.findParent('tr', false, true);
8394 var cellIndex = cell.dom.cellIndex;
8395 var rowIndex = row.dom.rowIndex - 1; // start from 0
8397 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8401 onMouseout : function(e, el)
8403 var cell = Roo.get(el);
8409 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8410 cell = cell.findParent('td', false, true);
8413 var row = cell.findParent('tr', false, true);
8414 var cellIndex = cell.dom.cellIndex;
8415 var rowIndex = row.dom.rowIndex - 1; // start from 0
8417 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8421 onClick : function(e, el)
8423 var cell = Roo.get(el);
8425 if(!cell || (!this.cellSelection && !this.rowSelection)){
8429 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8430 cell = cell.findParent('td', false, true);
8433 if(!cell || typeof(cell) == 'undefined'){
8437 var row = cell.findParent('tr', false, true);
8439 if(!row || typeof(row) == 'undefined'){
8443 var cellIndex = cell.dom.cellIndex;
8444 var rowIndex = this.getRowIndex(row);
8446 // why??? - should these not be based on SelectionModel?
8447 if(this.cellSelection){
8448 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8451 if(this.rowSelection){
8452 this.fireEvent('rowclick', this, row, rowIndex, e);
8458 onDblClick : function(e,el)
8460 var cell = Roo.get(el);
8462 if(!cell || (!this.cellSelection && !this.rowSelection)){
8466 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8467 cell = cell.findParent('td', false, true);
8470 if(!cell || typeof(cell) == 'undefined'){
8474 var row = cell.findParent('tr', false, true);
8476 if(!row || typeof(row) == 'undefined'){
8480 var cellIndex = cell.dom.cellIndex;
8481 var rowIndex = this.getRowIndex(row);
8483 if(this.cellSelection){
8484 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8487 if(this.rowSelection){
8488 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8492 sort : function(e,el)
8494 var col = Roo.get(el);
8496 if(!col.hasClass('sortable')){
8500 var sort = col.attr('sort');
8503 if(col.select('i', true).first().hasClass('fa-arrow-up')){
8507 this.store.sortInfo = {field : sort, direction : dir};
8510 Roo.log("calling footer first");
8511 this.footer.onClick('first');
8514 this.store.load({ params : { start : 0 } });
8518 renderHeader : function()
8526 this.totalWidth = 0;
8528 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8530 var config = cm.config[i];
8534 cls : 'x-hcol-' + i,
8536 tooltip : cm.getColumnTooltip(i) || '',
8537 html: cm.getColumnHeader(i)
8542 if(typeof(config.sortable) != 'undefined' && config.sortable){
8544 c.html = '<i class="fa"></i>' + c.html;
8547 // could use BS4 hidden-..-down
8549 if(typeof(config.lgHeader) != 'undefined'){
8550 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8553 if(typeof(config.mdHeader) != 'undefined'){
8554 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8557 if(typeof(config.smHeader) != 'undefined'){
8558 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8561 if(typeof(config.xsHeader) != 'undefined'){
8562 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8569 if(typeof(config.tooltip) != 'undefined'){
8570 c.tooltip = config.tooltip;
8573 if(typeof(config.colspan) != 'undefined'){
8574 c.colspan = config.colspan;
8577 if(typeof(config.hidden) != 'undefined' && config.hidden){
8578 c.style += ' display:none;';
8581 if(typeof(config.dataIndex) != 'undefined'){
8582 c.sort = config.dataIndex;
8587 if(typeof(config.align) != 'undefined' && config.align.length){
8588 c.style += ' text-align:' + config.align + ';';
8591 if(typeof(config.width) != 'undefined'){
8592 c.style += ' width:' + config.width + 'px;';
8593 this.totalWidth += config.width;
8595 this.totalWidth += 100; // assume minimum of 100 per column?
8598 if(typeof(config.cls) != 'undefined'){
8599 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8602 ['xs','sm','md','lg'].map(function(size){
8604 if(typeof(config[size]) == 'undefined'){
8608 if (!config[size]) { // 0 = hidden
8609 // BS 4 '0' is treated as hide that column and below.
8610 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8614 c.cls += ' col-' + size + '-' + config[size] + (
8615 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8627 renderBody : function()
8637 colspan : this.cm.getColumnCount()
8647 renderFooter : function()
8657 colspan : this.cm.getColumnCount()
8671 // Roo.log('ds onload');
8676 var ds = this.store;
8678 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8679 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8680 if (_this.store.sortInfo) {
8682 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8683 e.select('i', true).addClass(['fa-arrow-up']);
8686 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8687 e.select('i', true).addClass(['fa-arrow-down']);
8692 var tbody = this.mainBody;
8694 if(ds.getCount() > 0){
8695 ds.data.each(function(d,rowIndex){
8696 var row = this.renderRow(cm, ds, rowIndex);
8698 tbody.createChild(row);
8702 if(row.cellObjects.length){
8703 Roo.each(row.cellObjects, function(r){
8704 _this.renderCellObject(r);
8711 var tfoot = this.el.select('tfoot', true).first();
8713 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8715 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8717 var total = this.ds.getTotalCount();
8719 if(this.footer.pageSize < total){
8720 this.mainFoot.show();
8724 Roo.each(this.el.select('tbody td', true).elements, function(e){
8725 e.on('mouseover', _this.onMouseover, _this);
8728 Roo.each(this.el.select('tbody td', true).elements, function(e){
8729 e.on('mouseout', _this.onMouseout, _this);
8731 this.fireEvent('rowsrendered', this);
8737 onUpdate : function(ds,record)
8739 this.refreshRow(record);
8743 onRemove : function(ds, record, index, isUpdate){
8744 if(isUpdate !== true){
8745 this.fireEvent("beforerowremoved", this, index, record);
8747 var bt = this.mainBody.dom;
8749 var rows = this.el.select('tbody > tr', true).elements;
8751 if(typeof(rows[index]) != 'undefined'){
8752 bt.removeChild(rows[index].dom);
8755 // if(bt.rows[index]){
8756 // bt.removeChild(bt.rows[index]);
8759 if(isUpdate !== true){
8760 //this.stripeRows(index);
8761 //this.syncRowHeights(index, index);
8763 this.fireEvent("rowremoved", this, index, record);
8767 onAdd : function(ds, records, rowIndex)
8769 //Roo.log('on Add called');
8770 // - note this does not handle multiple adding very well..
8771 var bt = this.mainBody.dom;
8772 for (var i =0 ; i < records.length;i++) {
8773 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8774 //Roo.log(records[i]);
8775 //Roo.log(this.store.getAt(rowIndex+i));
8776 this.insertRow(this.store, rowIndex + i, false);
8783 refreshRow : function(record){
8784 var ds = this.store, index;
8785 if(typeof record == 'number'){
8787 record = ds.getAt(index);
8789 index = ds.indexOf(record);
8791 return; // should not happen - but seems to
8794 this.insertRow(ds, index, true);
8796 this.onRemove(ds, record, index+1, true);
8798 //this.syncRowHeights(index, index);
8800 this.fireEvent("rowupdated", this, index, record);
8803 insertRow : function(dm, rowIndex, isUpdate){
8806 this.fireEvent("beforerowsinserted", this, rowIndex);
8808 //var s = this.getScrollState();
8809 var row = this.renderRow(this.cm, this.store, rowIndex);
8810 // insert before rowIndex..
8811 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8815 if(row.cellObjects.length){
8816 Roo.each(row.cellObjects, function(r){
8817 _this.renderCellObject(r);
8822 this.fireEvent("rowsinserted", this, rowIndex);
8823 //this.syncRowHeights(firstRow, lastRow);
8824 //this.stripeRows(firstRow);
8831 getRowDom : function(rowIndex)
8833 var rows = this.el.select('tbody > tr', true).elements;
8835 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8838 // returns the object tree for a tr..
8841 renderRow : function(cm, ds, rowIndex)
8843 var d = ds.getAt(rowIndex);
8847 cls : 'x-row-' + rowIndex,
8851 var cellObjects = [];
8853 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8854 var config = cm.config[i];
8856 var renderer = cm.getRenderer(i);
8860 if(typeof(renderer) !== 'undefined'){
8861 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8863 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8864 // and are rendered into the cells after the row is rendered - using the id for the element.
8866 if(typeof(value) === 'object'){
8876 rowIndex : rowIndex,
8881 this.fireEvent('rowclass', this, rowcfg);
8885 // this might end up displaying HTML?
8886 // this is too messy... - better to only do it on columsn you know are going to be too long
8887 //tooltip : (typeof(value) === 'object') ? '' : value,
8888 cls : rowcfg.rowClass + ' x-col-' + i,
8890 html: (typeof(value) === 'object') ? '' : value
8897 if(typeof(config.colspan) != 'undefined'){
8898 td.colspan = config.colspan;
8901 if(typeof(config.hidden) != 'undefined' && config.hidden){
8902 td.style += ' display:none;';
8905 if(typeof(config.align) != 'undefined' && config.align.length){
8906 td.style += ' text-align:' + config.align + ';';
8908 if(typeof(config.valign) != 'undefined' && config.valign.length){
8909 td.style += ' vertical-align:' + config.valign + ';';
8912 if(typeof(config.width) != 'undefined'){
8913 td.style += ' width:' + config.width + 'px;';
8916 if(typeof(config.cursor) != 'undefined'){
8917 td.style += ' cursor:' + config.cursor + ';';
8920 if(typeof(config.cls) != 'undefined'){
8921 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8924 ['xs','sm','md','lg'].map(function(size){
8926 if(typeof(config[size]) == 'undefined'){
8932 if (!config[size]) { // 0 = hidden
8933 // BS 4 '0' is treated as hide that column and below.
8934 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8938 td.cls += ' col-' + size + '-' + config[size] + (
8939 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8949 row.cellObjects = cellObjects;
8957 onBeforeLoad : function()
8966 this.el.select('tbody', true).first().dom.innerHTML = '';
8969 * Show or hide a row.
8970 * @param {Number} rowIndex to show or hide
8971 * @param {Boolean} state hide
8973 setRowVisibility : function(rowIndex, state)
8975 var bt = this.mainBody.dom;
8977 var rows = this.el.select('tbody > tr', true).elements;
8979 if(typeof(rows[rowIndex]) == 'undefined'){
8982 rows[rowIndex].dom.style.display = state ? '' : 'none';
8986 getSelectionModel : function(){
8988 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8990 return this.selModel;
8993 * Render the Roo.bootstrap object from renderder
8995 renderCellObject : function(r)
8999 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9001 var t = r.cfg.render(r.container);
9004 Roo.each(r.cfg.cn, function(c){
9006 container: t.getChildContainer(),
9009 _this.renderCellObject(child);
9014 getRowIndex : function(row)
9018 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9029 * Returns the grid's underlying element = used by panel.Grid
9030 * @return {Element} The element
9032 getGridEl : function(){
9036 * Forces a resize - used by panel.Grid
9037 * @return {Element} The element
9039 autoSize : function()
9041 //var ctr = Roo.get(this.container.dom.parentElement);
9042 var ctr = Roo.get(this.el.dom);
9044 var thd = this.getGridEl().select('thead',true).first();
9045 var tbd = this.getGridEl().select('tbody', true).first();
9046 var tfd = this.getGridEl().select('tfoot', true).first();
9048 var cw = ctr.getWidth();
9049 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9053 tbd.setWidth(ctr.getWidth());
9054 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9055 // this needs fixing for various usage - currently only hydra job advers I think..
9057 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9059 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9062 cw = Math.max(cw, this.totalWidth);
9063 this.getGridEl().select('tbody tr',true).setWidth(cw);
9065 // resize 'expandable coloumn?
9067 return; // we doe not have a view in this design..
9070 onBodyScroll: function()
9072 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9074 this.mainHead.setStyle({
9075 'position' : 'relative',
9076 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9082 var scrollHeight = this.mainBody.dom.scrollHeight;
9084 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9086 var height = this.mainBody.getHeight();
9088 if(scrollHeight - height == scrollTop) {
9090 var total = this.ds.getTotalCount();
9092 if(this.footer.cursor + this.footer.pageSize < total){
9094 this.footer.ds.load({
9096 start : this.footer.cursor + this.footer.pageSize,
9097 limit : this.footer.pageSize
9107 onHeaderChange : function()
9109 var header = this.renderHeader();
9110 var table = this.el.select('table', true).first();
9112 this.mainHead.remove();
9113 this.mainHead = table.createChild(header, this.mainBody, false);
9115 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9116 e.on('click', this.sort, this);
9122 onHiddenChange : function(colModel, colIndex, hidden)
9124 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9125 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9127 this.CSS.updateRule(thSelector, "display", "");
9128 this.CSS.updateRule(tdSelector, "display", "");
9131 this.CSS.updateRule(thSelector, "display", "none");
9132 this.CSS.updateRule(tdSelector, "display", "none");
9135 this.onHeaderChange();
9139 setColumnWidth: function(col_index, width)
9141 // width = "md-2 xs-2..."
9142 if(!this.colModel.config[col_index]) {
9146 var w = width.split(" ");
9148 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9150 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9153 for(var j = 0; j < w.length; j++) {
9159 var size_cls = w[j].split("-");
9161 if(!Number.isInteger(size_cls[1] * 1)) {
9165 if(!this.colModel.config[col_index][size_cls[0]]) {
9169 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9173 h_row[0].classList.replace(
9174 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9175 "col-"+size_cls[0]+"-"+size_cls[1]
9178 for(var i = 0; i < rows.length; i++) {
9180 var size_cls = w[j].split("-");
9182 if(!Number.isInteger(size_cls[1] * 1)) {
9186 if(!this.colModel.config[col_index][size_cls[0]]) {
9190 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9194 rows[i].classList.replace(
9195 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9196 "col-"+size_cls[0]+"-"+size_cls[1]
9200 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9215 * @class Roo.bootstrap.TableCell
9216 * @extends Roo.bootstrap.Component
9217 * Bootstrap TableCell class
9218 * @cfg {String} html cell contain text
9219 * @cfg {String} cls cell class
9220 * @cfg {String} tag cell tag (td|th) default td
9221 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9222 * @cfg {String} align Aligns the content in a cell
9223 * @cfg {String} axis Categorizes cells
9224 * @cfg {String} bgcolor Specifies the background color of a cell
9225 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9226 * @cfg {Number} colspan Specifies the number of columns a cell should span
9227 * @cfg {String} headers Specifies one or more header cells a cell is related to
9228 * @cfg {Number} height Sets the height of a cell
9229 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9230 * @cfg {Number} rowspan Sets the number of rows a cell should span
9231 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9232 * @cfg {String} valign Vertical aligns the content in a cell
9233 * @cfg {Number} width Specifies the width of a cell
9236 * Create a new TableCell
9237 * @param {Object} config The config object
9240 Roo.bootstrap.TableCell = function(config){
9241 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9244 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9264 getAutoCreate : function(){
9265 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9285 cfg.align=this.align
9291 cfg.bgcolor=this.bgcolor
9294 cfg.charoff=this.charoff
9297 cfg.colspan=this.colspan
9300 cfg.headers=this.headers
9303 cfg.height=this.height
9306 cfg.nowrap=this.nowrap
9309 cfg.rowspan=this.rowspan
9312 cfg.scope=this.scope
9315 cfg.valign=this.valign
9318 cfg.width=this.width
9337 * @class Roo.bootstrap.TableRow
9338 * @extends Roo.bootstrap.Component
9339 * Bootstrap TableRow class
9340 * @cfg {String} cls row class
9341 * @cfg {String} align Aligns the content in a table row
9342 * @cfg {String} bgcolor Specifies a background color for a table row
9343 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9344 * @cfg {String} valign Vertical aligns the content in a table row
9347 * Create a new TableRow
9348 * @param {Object} config The config object
9351 Roo.bootstrap.TableRow = function(config){
9352 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9355 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9363 getAutoCreate : function(){
9364 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9374 cfg.align = this.align;
9377 cfg.bgcolor = this.bgcolor;
9380 cfg.charoff = this.charoff;
9383 cfg.valign = this.valign;
9401 * @class Roo.bootstrap.TableBody
9402 * @extends Roo.bootstrap.Component
9403 * Bootstrap TableBody class
9404 * @cfg {String} cls element class
9405 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9406 * @cfg {String} align Aligns the content inside the element
9407 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9408 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9411 * Create a new TableBody
9412 * @param {Object} config The config object
9415 Roo.bootstrap.TableBody = function(config){
9416 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9419 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9427 getAutoCreate : function(){
9428 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9442 cfg.align = this.align;
9445 cfg.charoff = this.charoff;
9448 cfg.valign = this.valign;
9455 // initEvents : function()
9462 // this.store = Roo.factory(this.store, Roo.data);
9463 // this.store.on('load', this.onLoad, this);
9465 // this.store.load();
9469 // onLoad: function ()
9471 // this.fireEvent('load', this);
9481 * Ext JS Library 1.1.1
9482 * Copyright(c) 2006-2007, Ext JS, LLC.
9484 * Originally Released Under LGPL - original licence link has changed is not relivant.
9487 * <script type="text/javascript">
9490 // as we use this in bootstrap.
9491 Roo.namespace('Roo.form');
9493 * @class Roo.form.Action
9494 * Internal Class used to handle form actions
9496 * @param {Roo.form.BasicForm} el The form element or its id
9497 * @param {Object} config Configuration options
9502 // define the action interface
9503 Roo.form.Action = function(form, options){
9505 this.options = options || {};
9508 * Client Validation Failed
9511 Roo.form.Action.CLIENT_INVALID = 'client';
9513 * Server Validation Failed
9516 Roo.form.Action.SERVER_INVALID = 'server';
9518 * Connect to Server Failed
9521 Roo.form.Action.CONNECT_FAILURE = 'connect';
9523 * Reading Data from Server Failed
9526 Roo.form.Action.LOAD_FAILURE = 'load';
9528 Roo.form.Action.prototype = {
9530 failureType : undefined,
9531 response : undefined,
9535 run : function(options){
9540 success : function(response){
9545 handleResponse : function(response){
9549 // default connection failure
9550 failure : function(response){
9552 this.response = response;
9553 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9554 this.form.afterAction(this, false);
9557 processResponse : function(response){
9558 this.response = response;
9559 if(!response.responseText){
9562 this.result = this.handleResponse(response);
9566 // utility functions used internally
9567 getUrl : function(appendParams){
9568 var url = this.options.url || this.form.url || this.form.el.dom.action;
9570 var p = this.getParams();
9572 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9578 getMethod : function(){
9579 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9582 getParams : function(){
9583 var bp = this.form.baseParams;
9584 var p = this.options.params;
9586 if(typeof p == "object"){
9587 p = Roo.urlEncode(Roo.applyIf(p, bp));
9588 }else if(typeof p == 'string' && bp){
9589 p += '&' + Roo.urlEncode(bp);
9592 p = Roo.urlEncode(bp);
9597 createCallback : function(){
9599 success: this.success,
9600 failure: this.failure,
9602 timeout: (this.form.timeout*1000),
9603 upload: this.form.fileUpload ? this.success : undefined
9608 Roo.form.Action.Submit = function(form, options){
9609 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9612 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9615 haveProgress : false,
9616 uploadComplete : false,
9618 // uploadProgress indicator.
9619 uploadProgress : function()
9621 if (!this.form.progressUrl) {
9625 if (!this.haveProgress) {
9626 Roo.MessageBox.progress("Uploading", "Uploading");
9628 if (this.uploadComplete) {
9629 Roo.MessageBox.hide();
9633 this.haveProgress = true;
9635 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9637 var c = new Roo.data.Connection();
9639 url : this.form.progressUrl,
9644 success : function(req){
9645 //console.log(data);
9649 rdata = Roo.decode(req.responseText)
9651 Roo.log("Invalid data from server..");
9655 if (!rdata || !rdata.success) {
9657 Roo.MessageBox.alert(Roo.encode(rdata));
9660 var data = rdata.data;
9662 if (this.uploadComplete) {
9663 Roo.MessageBox.hide();
9668 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9669 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9672 this.uploadProgress.defer(2000,this);
9675 failure: function(data) {
9676 Roo.log('progress url failed ');
9687 // run get Values on the form, so it syncs any secondary forms.
9688 this.form.getValues();
9690 var o = this.options;
9691 var method = this.getMethod();
9692 var isPost = method == 'POST';
9693 if(o.clientValidation === false || this.form.isValid()){
9695 if (this.form.progressUrl) {
9696 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9697 (new Date() * 1) + '' + Math.random());
9702 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9703 form:this.form.el.dom,
9704 url:this.getUrl(!isPost),
9706 params:isPost ? this.getParams() : null,
9707 isUpload: this.form.fileUpload,
9708 formData : this.form.formData
9711 this.uploadProgress();
9713 }else if (o.clientValidation !== false){ // client validation failed
9714 this.failureType = Roo.form.Action.CLIENT_INVALID;
9715 this.form.afterAction(this, false);
9719 success : function(response)
9721 this.uploadComplete= true;
9722 if (this.haveProgress) {
9723 Roo.MessageBox.hide();
9727 var result = this.processResponse(response);
9728 if(result === true || result.success){
9729 this.form.afterAction(this, true);
9733 this.form.markInvalid(result.errors);
9734 this.failureType = Roo.form.Action.SERVER_INVALID;
9736 this.form.afterAction(this, false);
9738 failure : function(response)
9740 this.uploadComplete= true;
9741 if (this.haveProgress) {
9742 Roo.MessageBox.hide();
9745 this.response = response;
9746 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9747 this.form.afterAction(this, false);
9750 handleResponse : function(response){
9751 if(this.form.errorReader){
9752 var rs = this.form.errorReader.read(response);
9755 for(var i = 0, len = rs.records.length; i < len; i++) {
9756 var r = rs.records[i];
9760 if(errors.length < 1){
9764 success : rs.success,
9770 ret = Roo.decode(response.responseText);
9774 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9784 Roo.form.Action.Load = function(form, options){
9785 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9786 this.reader = this.form.reader;
9789 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9794 Roo.Ajax.request(Roo.apply(
9795 this.createCallback(), {
9796 method:this.getMethod(),
9797 url:this.getUrl(false),
9798 params:this.getParams()
9802 success : function(response){
9804 var result = this.processResponse(response);
9805 if(result === true || !result.success || !result.data){
9806 this.failureType = Roo.form.Action.LOAD_FAILURE;
9807 this.form.afterAction(this, false);
9810 this.form.clearInvalid();
9811 this.form.setValues(result.data);
9812 this.form.afterAction(this, true);
9815 handleResponse : function(response){
9816 if(this.form.reader){
9817 var rs = this.form.reader.read(response);
9818 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9820 success : rs.success,
9824 return Roo.decode(response.responseText);
9828 Roo.form.Action.ACTION_TYPES = {
9829 'load' : Roo.form.Action.Load,
9830 'submit' : Roo.form.Action.Submit
9839 * @class Roo.bootstrap.Form
9840 * @extends Roo.bootstrap.Component
9841 * Bootstrap Form class
9842 * @cfg {String} method GET | POST (default POST)
9843 * @cfg {String} labelAlign top | left (default top)
9844 * @cfg {String} align left | right - for navbars
9845 * @cfg {Boolean} loadMask load mask when submit (default true)
9850 * @param {Object} config The config object
9854 Roo.bootstrap.Form = function(config){
9856 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9858 Roo.bootstrap.Form.popover.apply();
9862 * @event clientvalidation
9863 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9864 * @param {Form} this
9865 * @param {Boolean} valid true if the form has passed client-side validation
9867 clientvalidation: true,
9869 * @event beforeaction
9870 * Fires before any action is performed. Return false to cancel the action.
9871 * @param {Form} this
9872 * @param {Action} action The action to be performed
9876 * @event actionfailed
9877 * Fires when an action fails.
9878 * @param {Form} this
9879 * @param {Action} action The action that failed
9881 actionfailed : true,
9883 * @event actioncomplete
9884 * Fires when an action is completed.
9885 * @param {Form} this
9886 * @param {Action} action The action that completed
9888 actioncomplete : true
9892 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9895 * @cfg {String} method
9896 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9901 * The URL to use for form actions if one isn't supplied in the action options.
9904 * @cfg {Boolean} fileUpload
9905 * Set to true if this form is a file upload.
9909 * @cfg {Object} baseParams
9910 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9914 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9918 * @cfg {Sting} align (left|right) for navbar forms
9923 activeAction : null,
9926 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9927 * element by passing it or its id or mask the form itself by passing in true.
9930 waitMsgTarget : false,
9935 * @cfg {Boolean} errorMask (true|false) default false
9940 * @cfg {Number} maskOffset Default 100
9945 * @cfg {Boolean} maskBody
9949 getAutoCreate : function(){
9953 method : this.method || 'POST',
9954 id : this.id || Roo.id(),
9957 if (this.parent().xtype.match(/^Nav/)) {
9958 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9962 if (this.labelAlign == 'left' ) {
9963 cfg.cls += ' form-horizontal';
9969 initEvents : function()
9971 this.el.on('submit', this.onSubmit, this);
9972 // this was added as random key presses on the form where triggering form submit.
9973 this.el.on('keypress', function(e) {
9974 if (e.getCharCode() != 13) {
9977 // we might need to allow it for textareas.. and some other items.
9978 // check e.getTarget().
9980 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9984 Roo.log("keypress blocked");
9992 onSubmit : function(e){
9997 * Returns true if client-side validation on the form is successful.
10000 isValid : function(){
10001 var items = this.getItems();
10003 var target = false;
10005 items.each(function(f){
10011 Roo.log('invalid field: ' + f.name);
10015 if(!target && f.el.isVisible(true)){
10021 if(this.errorMask && !valid){
10022 Roo.bootstrap.Form.popover.mask(this, target);
10029 * Returns true if any fields in this form have changed since their original load.
10032 isDirty : function(){
10034 var items = this.getItems();
10035 items.each(function(f){
10045 * Performs a predefined action (submit or load) or custom actions you define on this form.
10046 * @param {String} actionName The name of the action type
10047 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10048 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10049 * accept other config options):
10051 Property Type Description
10052 ---------------- --------------- ----------------------------------------------------------------------------------
10053 url String The url for the action (defaults to the form's url)
10054 method String The form method to use (defaults to the form's method, or POST if not defined)
10055 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10056 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10057 validate the form on the client (defaults to false)
10059 * @return {BasicForm} this
10061 doAction : function(action, options){
10062 if(typeof action == 'string'){
10063 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10065 if(this.fireEvent('beforeaction', this, action) !== false){
10066 this.beforeAction(action);
10067 action.run.defer(100, action);
10073 beforeAction : function(action){
10074 var o = action.options;
10079 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10081 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10084 // not really supported yet.. ??
10086 //if(this.waitMsgTarget === true){
10087 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10088 //}else if(this.waitMsgTarget){
10089 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10090 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10092 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10098 afterAction : function(action, success){
10099 this.activeAction = null;
10100 var o = action.options;
10105 Roo.get(document.body).unmask();
10111 //if(this.waitMsgTarget === true){
10112 // this.el.unmask();
10113 //}else if(this.waitMsgTarget){
10114 // this.waitMsgTarget.unmask();
10116 // Roo.MessageBox.updateProgress(1);
10117 // Roo.MessageBox.hide();
10124 Roo.callback(o.success, o.scope, [this, action]);
10125 this.fireEvent('actioncomplete', this, action);
10129 // failure condition..
10130 // we have a scenario where updates need confirming.
10131 // eg. if a locking scenario exists..
10132 // we look for { errors : { needs_confirm : true }} in the response.
10134 (typeof(action.result) != 'undefined') &&
10135 (typeof(action.result.errors) != 'undefined') &&
10136 (typeof(action.result.errors.needs_confirm) != 'undefined')
10139 Roo.log("not supported yet");
10142 Roo.MessageBox.confirm(
10143 "Change requires confirmation",
10144 action.result.errorMsg,
10149 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10159 Roo.callback(o.failure, o.scope, [this, action]);
10160 // show an error message if no failed handler is set..
10161 if (!this.hasListener('actionfailed')) {
10162 Roo.log("need to add dialog support");
10164 Roo.MessageBox.alert("Error",
10165 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10166 action.result.errorMsg :
10167 "Saving Failed, please check your entries or try again"
10172 this.fireEvent('actionfailed', this, action);
10177 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10178 * @param {String} id The value to search for
10181 findField : function(id){
10182 var items = this.getItems();
10183 var field = items.get(id);
10185 items.each(function(f){
10186 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10193 return field || null;
10196 * Mark fields in this form invalid in bulk.
10197 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10198 * @return {BasicForm} this
10200 markInvalid : function(errors){
10201 if(errors instanceof Array){
10202 for(var i = 0, len = errors.length; i < len; i++){
10203 var fieldError = errors[i];
10204 var f = this.findField(fieldError.id);
10206 f.markInvalid(fieldError.msg);
10212 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10213 field.markInvalid(errors[id]);
10217 //Roo.each(this.childForms || [], function (f) {
10218 // f.markInvalid(errors);
10225 * Set values for fields in this form in bulk.
10226 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10227 * @return {BasicForm} this
10229 setValues : function(values){
10230 if(values instanceof Array){ // array of objects
10231 for(var i = 0, len = values.length; i < len; i++){
10233 var f = this.findField(v.id);
10235 f.setValue(v.value);
10236 if(this.trackResetOnLoad){
10237 f.originalValue = f.getValue();
10241 }else{ // object hash
10244 if(typeof values[id] != 'function' && (field = this.findField(id))){
10246 if (field.setFromData &&
10247 field.valueField &&
10248 field.displayField &&
10249 // combos' with local stores can
10250 // be queried via setValue()
10251 // to set their value..
10252 (field.store && !field.store.isLocal)
10256 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10257 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10258 field.setFromData(sd);
10260 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10262 field.setFromData(values);
10265 field.setValue(values[id]);
10269 if(this.trackResetOnLoad){
10270 field.originalValue = field.getValue();
10276 //Roo.each(this.childForms || [], function (f) {
10277 // f.setValues(values);
10284 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10285 * they are returned as an array.
10286 * @param {Boolean} asString
10289 getValues : function(asString){
10290 //if (this.childForms) {
10291 // copy values from the child forms
10292 // Roo.each(this.childForms, function (f) {
10293 // this.setValues(f.getValues());
10299 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10300 if(asString === true){
10303 return Roo.urlDecode(fs);
10307 * Returns the fields in this form as an object with key/value pairs.
10308 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10311 getFieldValues : function(with_hidden)
10313 var items = this.getItems();
10315 items.each(function(f){
10317 if (!f.getName()) {
10321 var v = f.getValue();
10323 if (f.inputType =='radio') {
10324 if (typeof(ret[f.getName()]) == 'undefined') {
10325 ret[f.getName()] = ''; // empty..
10328 if (!f.el.dom.checked) {
10332 v = f.el.dom.value;
10336 if(f.xtype == 'MoneyField'){
10337 ret[f.currencyName] = f.getCurrency();
10340 // not sure if this supported any more..
10341 if ((typeof(v) == 'object') && f.getRawValue) {
10342 v = f.getRawValue() ; // dates..
10344 // combo boxes where name != hiddenName...
10345 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10346 ret[f.name] = f.getRawValue();
10348 ret[f.getName()] = v;
10355 * Clears all invalid messages in this form.
10356 * @return {BasicForm} this
10358 clearInvalid : function(){
10359 var items = this.getItems();
10361 items.each(function(f){
10369 * Resets this form.
10370 * @return {BasicForm} this
10372 reset : function(){
10373 var items = this.getItems();
10374 items.each(function(f){
10378 Roo.each(this.childForms || [], function (f) {
10386 getItems : function()
10388 var r=new Roo.util.MixedCollection(false, function(o){
10389 return o.id || (o.id = Roo.id());
10391 var iter = function(el) {
10398 Roo.each(el.items,function(e) {
10407 hideFields : function(items)
10409 Roo.each(items, function(i){
10411 var f = this.findField(i);
10422 showFields : function(items)
10424 Roo.each(items, function(i){
10426 var f = this.findField(i);
10439 Roo.apply(Roo.bootstrap.Form, {
10455 intervalID : false,
10461 if(this.isApplied){
10466 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10467 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10468 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10469 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10472 this.maskEl.top.enableDisplayMode("block");
10473 this.maskEl.left.enableDisplayMode("block");
10474 this.maskEl.bottom.enableDisplayMode("block");
10475 this.maskEl.right.enableDisplayMode("block");
10477 this.toolTip = new Roo.bootstrap.Tooltip({
10478 cls : 'roo-form-error-popover',
10480 'left' : ['r-l', [-2,0], 'right'],
10481 'right' : ['l-r', [2,0], 'left'],
10482 'bottom' : ['tl-bl', [0,2], 'top'],
10483 'top' : [ 'bl-tl', [0,-2], 'bottom']
10487 this.toolTip.render(Roo.get(document.body));
10489 this.toolTip.el.enableDisplayMode("block");
10491 Roo.get(document.body).on('click', function(){
10495 Roo.get(document.body).on('touchstart', function(){
10499 this.isApplied = true
10502 mask : function(form, target)
10506 this.target = target;
10508 if(!this.form.errorMask || !target.el){
10512 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10514 Roo.log(scrollable);
10516 var ot = this.target.el.calcOffsetsTo(scrollable);
10518 var scrollTo = ot[1] - this.form.maskOffset;
10520 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10522 scrollable.scrollTo('top', scrollTo);
10524 var box = this.target.el.getBox();
10526 var zIndex = Roo.bootstrap.Modal.zIndex++;
10529 this.maskEl.top.setStyle('position', 'absolute');
10530 this.maskEl.top.setStyle('z-index', zIndex);
10531 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10532 this.maskEl.top.setLeft(0);
10533 this.maskEl.top.setTop(0);
10534 this.maskEl.top.show();
10536 this.maskEl.left.setStyle('position', 'absolute');
10537 this.maskEl.left.setStyle('z-index', zIndex);
10538 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10539 this.maskEl.left.setLeft(0);
10540 this.maskEl.left.setTop(box.y - this.padding);
10541 this.maskEl.left.show();
10543 this.maskEl.bottom.setStyle('position', 'absolute');
10544 this.maskEl.bottom.setStyle('z-index', zIndex);
10545 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10546 this.maskEl.bottom.setLeft(0);
10547 this.maskEl.bottom.setTop(box.bottom + this.padding);
10548 this.maskEl.bottom.show();
10550 this.maskEl.right.setStyle('position', 'absolute');
10551 this.maskEl.right.setStyle('z-index', zIndex);
10552 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10553 this.maskEl.right.setLeft(box.right + this.padding);
10554 this.maskEl.right.setTop(box.y - this.padding);
10555 this.maskEl.right.show();
10557 this.toolTip.bindEl = this.target.el;
10559 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10561 var tip = this.target.blankText;
10563 if(this.target.getValue() !== '' ) {
10565 if (this.target.invalidText.length) {
10566 tip = this.target.invalidText;
10567 } else if (this.target.regexText.length){
10568 tip = this.target.regexText;
10572 this.toolTip.show(tip);
10574 this.intervalID = window.setInterval(function() {
10575 Roo.bootstrap.Form.popover.unmask();
10578 window.onwheel = function(){ return false;};
10580 (function(){ this.isMasked = true; }).defer(500, this);
10584 unmask : function()
10586 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10590 this.maskEl.top.setStyle('position', 'absolute');
10591 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10592 this.maskEl.top.hide();
10594 this.maskEl.left.setStyle('position', 'absolute');
10595 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10596 this.maskEl.left.hide();
10598 this.maskEl.bottom.setStyle('position', 'absolute');
10599 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10600 this.maskEl.bottom.hide();
10602 this.maskEl.right.setStyle('position', 'absolute');
10603 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10604 this.maskEl.right.hide();
10606 this.toolTip.hide();
10608 this.toolTip.el.hide();
10610 window.onwheel = function(){ return true;};
10612 if(this.intervalID){
10613 window.clearInterval(this.intervalID);
10614 this.intervalID = false;
10617 this.isMasked = false;
10627 * Ext JS Library 1.1.1
10628 * Copyright(c) 2006-2007, Ext JS, LLC.
10630 * Originally Released Under LGPL - original licence link has changed is not relivant.
10633 * <script type="text/javascript">
10636 * @class Roo.form.VTypes
10637 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10640 Roo.form.VTypes = function(){
10641 // closure these in so they are only created once.
10642 var alpha = /^[a-zA-Z_]+$/;
10643 var alphanum = /^[a-zA-Z0-9_]+$/;
10644 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10645 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10647 // All these messages and functions are configurable
10650 * The function used to validate email addresses
10651 * @param {String} value The email address
10653 'email' : function(v){
10654 return email.test(v);
10657 * The error text to display when the email validation function returns false
10660 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10662 * The keystroke filter mask to be applied on email input
10665 'emailMask' : /[a-z0-9_\.\-@]/i,
10668 * The function used to validate URLs
10669 * @param {String} value The URL
10671 'url' : function(v){
10672 return url.test(v);
10675 * The error text to display when the url validation function returns false
10678 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10681 * The function used to validate alpha values
10682 * @param {String} value The value
10684 'alpha' : function(v){
10685 return alpha.test(v);
10688 * The error text to display when the alpha validation function returns false
10691 'alphaText' : 'This field should only contain letters and _',
10693 * The keystroke filter mask to be applied on alpha input
10696 'alphaMask' : /[a-z_]/i,
10699 * The function used to validate alphanumeric values
10700 * @param {String} value The value
10702 'alphanum' : function(v){
10703 return alphanum.test(v);
10706 * The error text to display when the alphanumeric validation function returns false
10709 'alphanumText' : 'This field should only contain letters, numbers and _',
10711 * The keystroke filter mask to be applied on alphanumeric input
10714 'alphanumMask' : /[a-z0-9_]/i
10724 * @class Roo.bootstrap.Input
10725 * @extends Roo.bootstrap.Component
10726 * Bootstrap Input class
10727 * @cfg {Boolean} disabled is it disabled
10728 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10729 * @cfg {String} name name of the input
10730 * @cfg {string} fieldLabel - the label associated
10731 * @cfg {string} placeholder - placeholder to put in text.
10732 * @cfg {string} before - input group add on before
10733 * @cfg {string} after - input group add on after
10734 * @cfg {string} size - (lg|sm) or leave empty..
10735 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10736 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10737 * @cfg {Number} md colspan out of 12 for computer-sized screens
10738 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10739 * @cfg {string} value default value of the input
10740 * @cfg {Number} labelWidth set the width of label
10741 * @cfg {Number} labellg set the width of label (1-12)
10742 * @cfg {Number} labelmd set the width of label (1-12)
10743 * @cfg {Number} labelsm set the width of label (1-12)
10744 * @cfg {Number} labelxs set the width of label (1-12)
10745 * @cfg {String} labelAlign (top|left)
10746 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10747 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10748 * @cfg {String} indicatorpos (left|right) default left
10749 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10750 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10751 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10753 * @cfg {String} align (left|center|right) Default left
10754 * @cfg {Boolean} forceFeedback (true|false) Default false
10757 * Create a new Input
10758 * @param {Object} config The config object
10761 Roo.bootstrap.Input = function(config){
10763 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10768 * Fires when this field receives input focus.
10769 * @param {Roo.form.Field} this
10774 * Fires when this field loses input focus.
10775 * @param {Roo.form.Field} this
10779 * @event specialkey
10780 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10781 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10782 * @param {Roo.form.Field} this
10783 * @param {Roo.EventObject} e The event object
10788 * Fires just before the field blurs if the field value has changed.
10789 * @param {Roo.form.Field} this
10790 * @param {Mixed} newValue The new value
10791 * @param {Mixed} oldValue The original value
10796 * Fires after the field has been marked as invalid.
10797 * @param {Roo.form.Field} this
10798 * @param {String} msg The validation message
10803 * Fires after the field has been validated with no errors.
10804 * @param {Roo.form.Field} this
10809 * Fires after the key up
10810 * @param {Roo.form.Field} this
10811 * @param {Roo.EventObject} e The event Object
10816 * Fires after the user pastes into input
10817 * @param {Roo.form.Field} this
10818 * @param {Roo.EventObject} e The event Object
10824 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10826 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10827 automatic validation (defaults to "keyup").
10829 validationEvent : "keyup",
10831 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10833 validateOnBlur : true,
10835 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10837 validationDelay : 250,
10839 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10841 focusClass : "x-form-focus", // not needed???
10845 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10847 invalidClass : "has-warning",
10850 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10852 validClass : "has-success",
10855 * @cfg {Boolean} hasFeedback (true|false) default true
10857 hasFeedback : true,
10860 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10862 invalidFeedbackClass : "glyphicon-warning-sign",
10865 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10867 validFeedbackClass : "glyphicon-ok",
10870 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10872 selectOnFocus : false,
10875 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10879 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10884 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10886 disableKeyFilter : false,
10889 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10893 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10897 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10899 blankText : "Please complete this mandatory field",
10902 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10906 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10908 maxLength : Number.MAX_VALUE,
10910 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10912 minLengthText : "The minimum length for this field is {0}",
10914 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10916 maxLengthText : "The maximum length for this field is {0}",
10920 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10921 * If available, this function will be called only after the basic validators all return true, and will be passed the
10922 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10926 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10927 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10928 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10932 * @cfg {String} regexText -- Depricated - use Invalid Text
10937 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10943 autocomplete: false,
10947 inputType : 'text',
10950 placeholder: false,
10955 preventMark: false,
10956 isFormField : true,
10959 labelAlign : false,
10962 formatedValue : false,
10963 forceFeedback : false,
10965 indicatorpos : 'left',
10975 parentLabelAlign : function()
10978 while (parent.parent()) {
10979 parent = parent.parent();
10980 if (typeof(parent.labelAlign) !='undefined') {
10981 return parent.labelAlign;
10988 getAutoCreate : function()
10990 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10996 if(this.inputType != 'hidden'){
10997 cfg.cls = 'form-group' //input-group
11003 type : this.inputType,
11004 value : this.value,
11005 cls : 'form-control',
11006 placeholder : this.placeholder || '',
11007 autocomplete : this.autocomplete || 'new-password'
11009 if (this.inputType == 'file') {
11010 input.style = 'overflow:hidden'; // why not in CSS?
11013 if(this.capture.length){
11014 input.capture = this.capture;
11017 if(this.accept.length){
11018 input.accept = this.accept + "/*";
11022 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11025 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11026 input.maxLength = this.maxLength;
11029 if (this.disabled) {
11030 input.disabled=true;
11033 if (this.readOnly) {
11034 input.readonly=true;
11038 input.name = this.name;
11042 input.cls += ' input-' + this.size;
11046 ['xs','sm','md','lg'].map(function(size){
11047 if (settings[size]) {
11048 cfg.cls += ' col-' + size + '-' + settings[size];
11052 var inputblock = input;
11056 cls: 'glyphicon form-control-feedback'
11059 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11062 cls : 'has-feedback',
11070 if (this.before || this.after) {
11073 cls : 'input-group',
11077 if (this.before && typeof(this.before) == 'string') {
11079 inputblock.cn.push({
11081 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11085 if (this.before && typeof(this.before) == 'object') {
11086 this.before = Roo.factory(this.before);
11088 inputblock.cn.push({
11090 cls : 'roo-input-before input-group-prepend input-group-' +
11091 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11095 inputblock.cn.push(input);
11097 if (this.after && typeof(this.after) == 'string') {
11098 inputblock.cn.push({
11100 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11104 if (this.after && typeof(this.after) == 'object') {
11105 this.after = Roo.factory(this.after);
11107 inputblock.cn.push({
11109 cls : 'roo-input-after input-group-append input-group-' +
11110 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11114 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11115 inputblock.cls += ' has-feedback';
11116 inputblock.cn.push(feedback);
11121 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11122 tooltip : 'This field is required'
11124 if (this.allowBlank ) {
11125 indicator.style = this.allowBlank ? ' display:none' : '';
11127 if (align ==='left' && this.fieldLabel.length) {
11129 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11136 cls : 'control-label col-form-label',
11137 html : this.fieldLabel
11148 var labelCfg = cfg.cn[1];
11149 var contentCfg = cfg.cn[2];
11151 if(this.indicatorpos == 'right'){
11156 cls : 'control-label col-form-label',
11160 html : this.fieldLabel
11174 labelCfg = cfg.cn[0];
11175 contentCfg = cfg.cn[1];
11179 if(this.labelWidth > 12){
11180 labelCfg.style = "width: " + this.labelWidth + 'px';
11183 if(this.labelWidth < 13 && this.labelmd == 0){
11184 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11187 if(this.labellg > 0){
11188 labelCfg.cls += ' col-lg-' + this.labellg;
11189 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11192 if(this.labelmd > 0){
11193 labelCfg.cls += ' col-md-' + this.labelmd;
11194 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11197 if(this.labelsm > 0){
11198 labelCfg.cls += ' col-sm-' + this.labelsm;
11199 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11202 if(this.labelxs > 0){
11203 labelCfg.cls += ' col-xs-' + this.labelxs;
11204 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11208 } else if ( this.fieldLabel.length) {
11215 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11216 tooltip : 'This field is required',
11217 style : this.allowBlank ? ' display:none' : ''
11221 //cls : 'input-group-addon',
11222 html : this.fieldLabel
11230 if(this.indicatorpos == 'right'){
11235 //cls : 'input-group-addon',
11236 html : this.fieldLabel
11241 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11242 tooltip : 'This field is required',
11243 style : this.allowBlank ? ' display:none' : ''
11263 if (this.parentType === 'Navbar' && this.parent().bar) {
11264 cfg.cls += ' navbar-form';
11267 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11268 // on BS4 we do this only if not form
11269 cfg.cls += ' navbar-form';
11277 * return the real input element.
11279 inputEl: function ()
11281 return this.el.select('input.form-control',true).first();
11284 tooltipEl : function()
11286 return this.inputEl();
11289 indicatorEl : function()
11291 if (Roo.bootstrap.version == 4) {
11292 return false; // not enabled in v4 yet.
11295 var indicator = this.el.select('i.roo-required-indicator',true).first();
11305 setDisabled : function(v)
11307 var i = this.inputEl().dom;
11309 i.removeAttribute('disabled');
11313 i.setAttribute('disabled','true');
11315 initEvents : function()
11318 this.inputEl().on("keydown" , this.fireKey, this);
11319 this.inputEl().on("focus", this.onFocus, this);
11320 this.inputEl().on("blur", this.onBlur, this);
11322 this.inputEl().relayEvent('keyup', this);
11323 this.inputEl().relayEvent('paste', this);
11325 this.indicator = this.indicatorEl();
11327 if(this.indicator){
11328 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11331 // reference to original value for reset
11332 this.originalValue = this.getValue();
11333 //Roo.form.TextField.superclass.initEvents.call(this);
11334 if(this.validationEvent == 'keyup'){
11335 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11336 this.inputEl().on('keyup', this.filterValidation, this);
11338 else if(this.validationEvent !== false){
11339 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11342 if(this.selectOnFocus){
11343 this.on("focus", this.preFocus, this);
11346 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11347 this.inputEl().on("keypress", this.filterKeys, this);
11349 this.inputEl().relayEvent('keypress', this);
11352 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11353 this.el.on("click", this.autoSize, this);
11356 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11357 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11360 if (typeof(this.before) == 'object') {
11361 this.before.render(this.el.select('.roo-input-before',true).first());
11363 if (typeof(this.after) == 'object') {
11364 this.after.render(this.el.select('.roo-input-after',true).first());
11367 this.inputEl().on('change', this.onChange, this);
11370 filterValidation : function(e){
11371 if(!e.isNavKeyPress()){
11372 this.validationTask.delay(this.validationDelay);
11376 * Validates the field value
11377 * @return {Boolean} True if the value is valid, else false
11379 validate : function(){
11380 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11381 if(this.disabled || this.validateValue(this.getRawValue())){
11386 this.markInvalid();
11392 * Validates a value according to the field's validation rules and marks the field as invalid
11393 * if the validation fails
11394 * @param {Mixed} value The value to validate
11395 * @return {Boolean} True if the value is valid, else false
11397 validateValue : function(value)
11399 if(this.getVisibilityEl().hasClass('hidden')){
11403 if(value.length < 1) { // if it's blank
11404 if(this.allowBlank){
11410 if(value.length < this.minLength){
11413 if(value.length > this.maxLength){
11417 var vt = Roo.form.VTypes;
11418 if(!vt[this.vtype](value, this)){
11422 if(typeof this.validator == "function"){
11423 var msg = this.validator(value);
11427 if (typeof(msg) == 'string') {
11428 this.invalidText = msg;
11432 if(this.regex && !this.regex.test(value)){
11440 fireKey : function(e){
11441 //Roo.log('field ' + e.getKey());
11442 if(e.isNavKeyPress()){
11443 this.fireEvent("specialkey", this, e);
11446 focus : function (selectText){
11448 this.inputEl().focus();
11449 if(selectText === true){
11450 this.inputEl().dom.select();
11456 onFocus : function(){
11457 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11458 // this.el.addClass(this.focusClass);
11460 if(!this.hasFocus){
11461 this.hasFocus = true;
11462 this.startValue = this.getValue();
11463 this.fireEvent("focus", this);
11467 beforeBlur : Roo.emptyFn,
11471 onBlur : function(){
11473 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11474 //this.el.removeClass(this.focusClass);
11476 this.hasFocus = false;
11477 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11480 var v = this.getValue();
11481 if(String(v) !== String(this.startValue)){
11482 this.fireEvent('change', this, v, this.startValue);
11484 this.fireEvent("blur", this);
11487 onChange : function(e)
11489 var v = this.getValue();
11490 if(String(v) !== String(this.startValue)){
11491 this.fireEvent('change', this, v, this.startValue);
11497 * Resets the current field value to the originally loaded value and clears any validation messages
11499 reset : function(){
11500 this.setValue(this.originalValue);
11504 * Returns the name of the field
11505 * @return {Mixed} name The name field
11507 getName: function(){
11511 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11512 * @return {Mixed} value The field value
11514 getValue : function(){
11516 var v = this.inputEl().getValue();
11521 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11522 * @return {Mixed} value The field value
11524 getRawValue : function(){
11525 var v = this.inputEl().getValue();
11531 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11532 * @param {Mixed} value The value to set
11534 setRawValue : function(v){
11535 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11538 selectText : function(start, end){
11539 var v = this.getRawValue();
11541 start = start === undefined ? 0 : start;
11542 end = end === undefined ? v.length : end;
11543 var d = this.inputEl().dom;
11544 if(d.setSelectionRange){
11545 d.setSelectionRange(start, end);
11546 }else if(d.createTextRange){
11547 var range = d.createTextRange();
11548 range.moveStart("character", start);
11549 range.moveEnd("character", v.length-end);
11556 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11557 * @param {Mixed} value The value to set
11559 setValue : function(v){
11562 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11568 processValue : function(value){
11569 if(this.stripCharsRe){
11570 var newValue = value.replace(this.stripCharsRe, '');
11571 if(newValue !== value){
11572 this.setRawValue(newValue);
11579 preFocus : function(){
11581 if(this.selectOnFocus){
11582 this.inputEl().dom.select();
11585 filterKeys : function(e){
11586 var k = e.getKey();
11587 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11590 var c = e.getCharCode(), cc = String.fromCharCode(c);
11591 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11594 if(!this.maskRe.test(cc)){
11599 * Clear any invalid styles/messages for this field
11601 clearInvalid : function(){
11603 if(!this.el || this.preventMark){ // not rendered
11608 this.el.removeClass([this.invalidClass, 'is-invalid']);
11610 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11612 var feedback = this.el.select('.form-control-feedback', true).first();
11615 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11620 if(this.indicator){
11621 this.indicator.removeClass('visible');
11622 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11625 this.fireEvent('valid', this);
11629 * Mark this field as valid
11631 markValid : function()
11633 if(!this.el || this.preventMark){ // not rendered...
11637 this.el.removeClass([this.invalidClass, this.validClass]);
11638 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11640 var feedback = this.el.select('.form-control-feedback', true).first();
11643 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11646 if(this.indicator){
11647 this.indicator.removeClass('visible');
11648 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11656 if(this.allowBlank && !this.getRawValue().length){
11659 if (Roo.bootstrap.version == 3) {
11660 this.el.addClass(this.validClass);
11662 this.inputEl().addClass('is-valid');
11665 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11667 var feedback = this.el.select('.form-control-feedback', true).first();
11670 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11671 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11676 this.fireEvent('valid', this);
11680 * Mark this field as invalid
11681 * @param {String} msg The validation message
11683 markInvalid : function(msg)
11685 if(!this.el || this.preventMark){ // not rendered
11689 this.el.removeClass([this.invalidClass, this.validClass]);
11690 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11692 var feedback = this.el.select('.form-control-feedback', true).first();
11695 this.el.select('.form-control-feedback', true).first().removeClass(
11696 [this.invalidFeedbackClass, this.validFeedbackClass]);
11703 if(this.allowBlank && !this.getRawValue().length){
11707 if(this.indicator){
11708 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11709 this.indicator.addClass('visible');
11711 if (Roo.bootstrap.version == 3) {
11712 this.el.addClass(this.invalidClass);
11714 this.inputEl().addClass('is-invalid');
11719 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11721 var feedback = this.el.select('.form-control-feedback', true).first();
11724 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11726 if(this.getValue().length || this.forceFeedback){
11727 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11734 this.fireEvent('invalid', this, msg);
11737 SafariOnKeyDown : function(event)
11739 // this is a workaround for a password hang bug on chrome/ webkit.
11740 if (this.inputEl().dom.type != 'password') {
11744 var isSelectAll = false;
11746 if(this.inputEl().dom.selectionEnd > 0){
11747 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11749 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11750 event.preventDefault();
11755 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11757 event.preventDefault();
11758 // this is very hacky as keydown always get's upper case.
11760 var cc = String.fromCharCode(event.getCharCode());
11761 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11765 adjustWidth : function(tag, w){
11766 tag = tag.toLowerCase();
11767 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11768 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11769 if(tag == 'input'){
11772 if(tag == 'textarea'){
11775 }else if(Roo.isOpera){
11776 if(tag == 'input'){
11779 if(tag == 'textarea'){
11787 setFieldLabel : function(v)
11789 if(!this.rendered){
11793 if(this.indicatorEl()){
11794 var ar = this.el.select('label > span',true);
11796 if (ar.elements.length) {
11797 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11798 this.fieldLabel = v;
11802 var br = this.el.select('label',true);
11804 if(br.elements.length) {
11805 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11806 this.fieldLabel = v;
11810 Roo.log('Cannot Found any of label > span || label in input');
11814 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11815 this.fieldLabel = v;
11830 * @class Roo.bootstrap.TextArea
11831 * @extends Roo.bootstrap.Input
11832 * Bootstrap TextArea class
11833 * @cfg {Number} cols Specifies the visible width of a text area
11834 * @cfg {Number} rows Specifies the visible number of lines in a text area
11835 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11836 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11837 * @cfg {string} html text
11840 * Create a new TextArea
11841 * @param {Object} config The config object
11844 Roo.bootstrap.TextArea = function(config){
11845 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11849 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11859 getAutoCreate : function(){
11861 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11867 if(this.inputType != 'hidden'){
11868 cfg.cls = 'form-group' //input-group
11876 value : this.value || '',
11877 html: this.html || '',
11878 cls : 'form-control',
11879 placeholder : this.placeholder || ''
11883 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11884 input.maxLength = this.maxLength;
11888 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11892 input.cols = this.cols;
11895 if (this.readOnly) {
11896 input.readonly = true;
11900 input.name = this.name;
11904 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11908 ['xs','sm','md','lg'].map(function(size){
11909 if (settings[size]) {
11910 cfg.cls += ' col-' + size + '-' + settings[size];
11914 var inputblock = input;
11916 if(this.hasFeedback && !this.allowBlank){
11920 cls: 'glyphicon form-control-feedback'
11924 cls : 'has-feedback',
11933 if (this.before || this.after) {
11936 cls : 'input-group',
11940 inputblock.cn.push({
11942 cls : 'input-group-addon',
11947 inputblock.cn.push(input);
11949 if(this.hasFeedback && !this.allowBlank){
11950 inputblock.cls += ' has-feedback';
11951 inputblock.cn.push(feedback);
11955 inputblock.cn.push({
11957 cls : 'input-group-addon',
11964 if (align ==='left' && this.fieldLabel.length) {
11969 cls : 'control-label',
11970 html : this.fieldLabel
11981 if(this.labelWidth > 12){
11982 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11985 if(this.labelWidth < 13 && this.labelmd == 0){
11986 this.labelmd = this.labelWidth;
11989 if(this.labellg > 0){
11990 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11991 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11994 if(this.labelmd > 0){
11995 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11996 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11999 if(this.labelsm > 0){
12000 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12001 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12004 if(this.labelxs > 0){
12005 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12006 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12009 } else if ( this.fieldLabel.length) {
12014 //cls : 'input-group-addon',
12015 html : this.fieldLabel
12033 if (this.disabled) {
12034 input.disabled=true;
12041 * return the real textarea element.
12043 inputEl: function ()
12045 return this.el.select('textarea.form-control',true).first();
12049 * Clear any invalid styles/messages for this field
12051 clearInvalid : function()
12054 if(!this.el || this.preventMark){ // not rendered
12058 var label = this.el.select('label', true).first();
12059 var icon = this.el.select('i.fa-star', true).first();
12064 this.el.removeClass( this.validClass);
12065 this.inputEl().removeClass('is-invalid');
12067 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12069 var feedback = this.el.select('.form-control-feedback', true).first();
12072 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12077 this.fireEvent('valid', this);
12081 * Mark this field as valid
12083 markValid : function()
12085 if(!this.el || this.preventMark){ // not rendered
12089 this.el.removeClass([this.invalidClass, this.validClass]);
12090 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12092 var feedback = this.el.select('.form-control-feedback', true).first();
12095 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12098 if(this.disabled || this.allowBlank){
12102 var label = this.el.select('label', true).first();
12103 var icon = this.el.select('i.fa-star', true).first();
12108 if (Roo.bootstrap.version == 3) {
12109 this.el.addClass(this.validClass);
12111 this.inputEl().addClass('is-valid');
12115 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12117 var feedback = this.el.select('.form-control-feedback', true).first();
12120 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12121 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12126 this.fireEvent('valid', this);
12130 * Mark this field as invalid
12131 * @param {String} msg The validation message
12133 markInvalid : function(msg)
12135 if(!this.el || this.preventMark){ // not rendered
12139 this.el.removeClass([this.invalidClass, this.validClass]);
12140 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12142 var feedback = this.el.select('.form-control-feedback', true).first();
12145 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12148 if(this.disabled || this.allowBlank){
12152 var label = this.el.select('label', true).first();
12153 var icon = this.el.select('i.fa-star', true).first();
12155 if(!this.getValue().length && label && !icon){
12156 this.el.createChild({
12158 cls : 'text-danger fa fa-lg fa-star',
12159 tooltip : 'This field is required',
12160 style : 'margin-right:5px;'
12164 if (Roo.bootstrap.version == 3) {
12165 this.el.addClass(this.invalidClass);
12167 this.inputEl().addClass('is-invalid');
12170 // fixme ... this may be depricated need to test..
12171 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12173 var feedback = this.el.select('.form-control-feedback', true).first();
12176 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12178 if(this.getValue().length || this.forceFeedback){
12179 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12186 this.fireEvent('invalid', this, msg);
12194 * trigger field - base class for combo..
12199 * @class Roo.bootstrap.TriggerField
12200 * @extends Roo.bootstrap.Input
12201 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12202 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12203 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12204 * for which you can provide a custom implementation. For example:
12206 var trigger = new Roo.bootstrap.TriggerField();
12207 trigger.onTriggerClick = myTriggerFn;
12208 trigger.applyTo('my-field');
12211 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12212 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12213 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12214 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12215 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12218 * Create a new TriggerField.
12219 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12220 * to the base TextField)
12222 Roo.bootstrap.TriggerField = function(config){
12223 this.mimicing = false;
12224 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12227 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12229 * @cfg {String} triggerClass A CSS class to apply to the trigger
12232 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12237 * @cfg {Boolean} removable (true|false) special filter default false
12241 /** @cfg {Boolean} grow @hide */
12242 /** @cfg {Number} growMin @hide */
12243 /** @cfg {Number} growMax @hide */
12249 autoSize: Roo.emptyFn,
12253 deferHeight : true,
12256 actionMode : 'wrap',
12261 getAutoCreate : function(){
12263 var align = this.labelAlign || this.parentLabelAlign();
12268 cls: 'form-group' //input-group
12275 type : this.inputType,
12276 cls : 'form-control',
12277 autocomplete: 'new-password',
12278 placeholder : this.placeholder || ''
12282 input.name = this.name;
12285 input.cls += ' input-' + this.size;
12288 if (this.disabled) {
12289 input.disabled=true;
12292 var inputblock = input;
12294 if(this.hasFeedback && !this.allowBlank){
12298 cls: 'glyphicon form-control-feedback'
12301 if(this.removable && !this.editable ){
12303 cls : 'has-feedback',
12309 cls : 'roo-combo-removable-btn close'
12316 cls : 'has-feedback',
12325 if(this.removable && !this.editable ){
12327 cls : 'roo-removable',
12333 cls : 'roo-combo-removable-btn close'
12340 if (this.before || this.after) {
12343 cls : 'input-group',
12347 inputblock.cn.push({
12349 cls : 'input-group-addon input-group-prepend input-group-text',
12354 inputblock.cn.push(input);
12356 if(this.hasFeedback && !this.allowBlank){
12357 inputblock.cls += ' has-feedback';
12358 inputblock.cn.push(feedback);
12362 inputblock.cn.push({
12364 cls : 'input-group-addon input-group-append input-group-text',
12373 var ibwrap = inputblock;
12378 cls: 'roo-select2-choices',
12382 cls: 'roo-select2-search-field',
12394 cls: 'roo-select2-container input-group',
12399 cls: 'form-hidden-field'
12405 if(!this.multiple && this.showToggleBtn){
12411 if (this.caret != false) {
12414 cls: 'fa fa-' + this.caret
12421 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12423 Roo.bootstrap.version == 3 ? caret : '',
12426 cls: 'combobox-clear',
12440 combobox.cls += ' roo-select2-container-multi';
12444 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12445 tooltip : 'This field is required'
12447 if (Roo.bootstrap.version == 4) {
12450 style : 'display:none'
12455 if (align ==='left' && this.fieldLabel.length) {
12457 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12464 cls : 'control-label',
12465 html : this.fieldLabel
12477 var labelCfg = cfg.cn[1];
12478 var contentCfg = cfg.cn[2];
12480 if(this.indicatorpos == 'right'){
12485 cls : 'control-label',
12489 html : this.fieldLabel
12503 labelCfg = cfg.cn[0];
12504 contentCfg = cfg.cn[1];
12507 if(this.labelWidth > 12){
12508 labelCfg.style = "width: " + this.labelWidth + 'px';
12511 if(this.labelWidth < 13 && this.labelmd == 0){
12512 this.labelmd = this.labelWidth;
12515 if(this.labellg > 0){
12516 labelCfg.cls += ' col-lg-' + this.labellg;
12517 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12520 if(this.labelmd > 0){
12521 labelCfg.cls += ' col-md-' + this.labelmd;
12522 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12525 if(this.labelsm > 0){
12526 labelCfg.cls += ' col-sm-' + this.labelsm;
12527 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12530 if(this.labelxs > 0){
12531 labelCfg.cls += ' col-xs-' + this.labelxs;
12532 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12535 } else if ( this.fieldLabel.length) {
12536 // Roo.log(" label");
12541 //cls : 'input-group-addon',
12542 html : this.fieldLabel
12550 if(this.indicatorpos == 'right'){
12558 html : this.fieldLabel
12572 // Roo.log(" no label && no align");
12579 ['xs','sm','md','lg'].map(function(size){
12580 if (settings[size]) {
12581 cfg.cls += ' col-' + size + '-' + settings[size];
12592 onResize : function(w, h){
12593 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12594 // if(typeof w == 'number'){
12595 // var x = w - this.trigger.getWidth();
12596 // this.inputEl().setWidth(this.adjustWidth('input', x));
12597 // this.trigger.setStyle('left', x+'px');
12602 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12605 getResizeEl : function(){
12606 return this.inputEl();
12610 getPositionEl : function(){
12611 return this.inputEl();
12615 alignErrorIcon : function(){
12616 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12620 initEvents : function(){
12624 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12625 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12626 if(!this.multiple && this.showToggleBtn){
12627 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12628 if(this.hideTrigger){
12629 this.trigger.setDisplayed(false);
12631 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12635 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12638 if(this.removable && !this.editable && !this.tickable){
12639 var close = this.closeTriggerEl();
12642 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12643 close.on('click', this.removeBtnClick, this, close);
12647 //this.trigger.addClassOnOver('x-form-trigger-over');
12648 //this.trigger.addClassOnClick('x-form-trigger-click');
12651 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12655 closeTriggerEl : function()
12657 var close = this.el.select('.roo-combo-removable-btn', true).first();
12658 return close ? close : false;
12661 removeBtnClick : function(e, h, el)
12663 e.preventDefault();
12665 if(this.fireEvent("remove", this) !== false){
12667 this.fireEvent("afterremove", this)
12671 createList : function()
12673 this.list = Roo.get(document.body).createChild({
12674 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12675 cls: 'typeahead typeahead-long dropdown-menu shadow',
12676 style: 'display:none'
12679 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12684 initTrigger : function(){
12689 onDestroy : function(){
12691 this.trigger.removeAllListeners();
12692 // this.trigger.remove();
12695 // this.wrap.remove();
12697 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12701 onFocus : function(){
12702 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12704 if(!this.mimicing){
12705 this.wrap.addClass('x-trigger-wrap-focus');
12706 this.mimicing = true;
12707 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12708 if(this.monitorTab){
12709 this.el.on("keydown", this.checkTab, this);
12716 checkTab : function(e){
12717 if(e.getKey() == e.TAB){
12718 this.triggerBlur();
12723 onBlur : function(){
12728 mimicBlur : function(e, t){
12730 if(!this.wrap.contains(t) && this.validateBlur()){
12731 this.triggerBlur();
12737 triggerBlur : function(){
12738 this.mimicing = false;
12739 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12740 if(this.monitorTab){
12741 this.el.un("keydown", this.checkTab, this);
12743 //this.wrap.removeClass('x-trigger-wrap-focus');
12744 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12748 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12749 validateBlur : function(e, t){
12754 onDisable : function(){
12755 this.inputEl().dom.disabled = true;
12756 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12758 // this.wrap.addClass('x-item-disabled');
12763 onEnable : function(){
12764 this.inputEl().dom.disabled = false;
12765 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12767 // this.el.removeClass('x-item-disabled');
12772 onShow : function(){
12773 var ae = this.getActionEl();
12776 ae.dom.style.display = '';
12777 ae.dom.style.visibility = 'visible';
12783 onHide : function(){
12784 var ae = this.getActionEl();
12785 ae.dom.style.display = 'none';
12789 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12790 * by an implementing function.
12792 * @param {EventObject} e
12794 onTriggerClick : Roo.emptyFn
12802 * @class Roo.bootstrap.CardUploader
12803 * @extends Roo.bootstrap.Button
12804 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12805 * @cfg {Number} errorTimeout default 3000
12806 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12807 * @cfg {Array} html The button text.
12811 * Create a new CardUploader
12812 * @param {Object} config The config object
12815 Roo.bootstrap.CardUploader = function(config){
12819 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12822 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12830 * When a image is clicked on - and needs to display a slideshow or similar..
12831 * @param {Roo.bootstrap.Card} this
12832 * @param {Object} The image information data
12838 * When a the download link is clicked
12839 * @param {Roo.bootstrap.Card} this
12840 * @param {Object} The image information data contains
12847 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12850 errorTimeout : 3000,
12854 fileCollection : false,
12857 getAutoCreate : function()
12861 cls :'form-group' ,
12866 //cls : 'input-group-addon',
12867 html : this.fieldLabel
12875 value : this.value,
12876 cls : 'd-none form-control'
12881 multiple : 'multiple',
12883 cls : 'd-none roo-card-upload-selector'
12887 cls : 'roo-card-uploader-button-container w-100 mb-2'
12890 cls : 'card-columns roo-card-uploader-container'
12900 getChildContainer : function() /// what children are added to.
12902 return this.containerEl;
12905 getButtonContainer : function() /// what children are added to.
12907 return this.el.select(".roo-card-uploader-button-container").first();
12910 initEvents : function()
12913 Roo.bootstrap.Input.prototype.initEvents.call(this);
12917 xns: Roo.bootstrap,
12920 container_method : 'getButtonContainer' ,
12921 html : this.html, // fix changable?
12924 'click' : function(btn, e) {
12933 this.urlAPI = (window.createObjectURL && window) ||
12934 (window.URL && URL.revokeObjectURL && URL) ||
12935 (window.webkitURL && webkitURL);
12940 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12942 this.selectorEl.on('change', this.onFileSelected, this);
12945 this.images.forEach(function(img) {
12948 this.images = false;
12950 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12956 onClick : function(e)
12958 e.preventDefault();
12960 this.selectorEl.dom.click();
12964 onFileSelected : function(e)
12966 e.preventDefault();
12968 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12972 Roo.each(this.selectorEl.dom.files, function(file){
12973 this.addFile(file);
12982 addFile : function(file)
12985 if(typeof(file) === 'string'){
12986 throw "Add file by name?"; // should not happen
12990 if(!file || !this.urlAPI){
13000 var url = _this.urlAPI.createObjectURL( file);
13003 id : Roo.bootstrap.CardUploader.ID--,
13004 is_uploaded : false,
13008 mimetype : file.type,
13016 * addCard - add an Attachment to the uploader
13017 * @param data - the data about the image to upload
13021 title : "Title of file",
13022 is_uploaded : false,
13023 src : "http://.....",
13024 srcfile : { the File upload object },
13025 mimetype : file.type,
13028 .. any other data...
13034 addCard : function (data)
13036 // hidden input element?
13037 // if the file is not an image...
13038 //then we need to use something other that and header_image
13043 xns : Roo.bootstrap,
13044 xtype : 'CardFooter',
13047 xns : Roo.bootstrap,
13053 xns : Roo.bootstrap,
13055 html : String.format("<small>{0}</small>", data.title),
13056 cls : 'col-10 text-left',
13061 click : function() {
13063 t.fireEvent( "download", t, data );
13069 xns : Roo.bootstrap,
13071 style: 'max-height: 28px; ',
13077 click : function() {
13078 t.removeCard(data.id)
13090 var cn = this.addxtype(
13093 xns : Roo.bootstrap,
13096 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13097 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13098 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13103 initEvents : function() {
13104 Roo.bootstrap.Card.prototype.initEvents.call(this);
13106 this.imgEl = this.el.select('.card-img-top').first();
13108 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13109 this.imgEl.set({ 'pointer' : 'cursor' });
13112 this.getCardFooter().addClass('p-1');
13119 // dont' really need ot update items.
13120 // this.items.push(cn);
13121 this.fileCollection.add(cn);
13123 if (!data.srcfile) {
13124 this.updateInput();
13129 var reader = new FileReader();
13130 reader.addEventListener("load", function() {
13131 data.srcdata = reader.result;
13134 reader.readAsDataURL(data.srcfile);
13139 removeCard : function(id)
13142 var card = this.fileCollection.get(id);
13143 card.data.is_deleted = 1;
13144 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13145 //this.fileCollection.remove(card);
13146 //this.items = this.items.filter(function(e) { return e != card });
13147 // dont' really need ot update items.
13148 card.el.dom.parentNode.removeChild(card.el.dom);
13149 this.updateInput();
13155 this.fileCollection.each(function(card) {
13156 if (card.el.dom && card.el.dom.parentNode) {
13157 card.el.dom.parentNode.removeChild(card.el.dom);
13160 this.fileCollection.clear();
13161 this.updateInput();
13164 updateInput : function()
13167 this.fileCollection.each(function(e) {
13171 this.inputEl().dom.value = JSON.stringify(data);
13181 Roo.bootstrap.CardUploader.ID = -1;/*
13183 * Ext JS Library 1.1.1
13184 * Copyright(c) 2006-2007, Ext JS, LLC.
13186 * Originally Released Under LGPL - original licence link has changed is not relivant.
13189 * <script type="text/javascript">
13194 * @class Roo.data.SortTypes
13196 * Defines the default sorting (casting?) comparison functions used when sorting data.
13198 Roo.data.SortTypes = {
13200 * Default sort that does nothing
13201 * @param {Mixed} s The value being converted
13202 * @return {Mixed} The comparison value
13204 none : function(s){
13209 * The regular expression used to strip tags
13213 stripTagsRE : /<\/?[^>]+>/gi,
13216 * Strips all HTML tags to sort on text only
13217 * @param {Mixed} s The value being converted
13218 * @return {String} The comparison value
13220 asText : function(s){
13221 return String(s).replace(this.stripTagsRE, "");
13225 * Strips all HTML tags to sort on text only - Case insensitive
13226 * @param {Mixed} s The value being converted
13227 * @return {String} The comparison value
13229 asUCText : function(s){
13230 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13234 * Case insensitive string
13235 * @param {Mixed} s The value being converted
13236 * @return {String} The comparison value
13238 asUCString : function(s) {
13239 return String(s).toUpperCase();
13244 * @param {Mixed} s The value being converted
13245 * @return {Number} The comparison value
13247 asDate : function(s) {
13251 if(s instanceof Date){
13252 return s.getTime();
13254 return Date.parse(String(s));
13259 * @param {Mixed} s The value being converted
13260 * @return {Float} The comparison value
13262 asFloat : function(s) {
13263 var val = parseFloat(String(s).replace(/,/g, ""));
13272 * @param {Mixed} s The value being converted
13273 * @return {Number} The comparison value
13275 asInt : function(s) {
13276 var val = parseInt(String(s).replace(/,/g, ""));
13284 * Ext JS Library 1.1.1
13285 * Copyright(c) 2006-2007, Ext JS, LLC.
13287 * Originally Released Under LGPL - original licence link has changed is not relivant.
13290 * <script type="text/javascript">
13294 * @class Roo.data.Record
13295 * Instances of this class encapsulate both record <em>definition</em> information, and record
13296 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13297 * to access Records cached in an {@link Roo.data.Store} object.<br>
13299 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13300 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13303 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13305 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13306 * {@link #create}. The parameters are the same.
13307 * @param {Array} data An associative Array of data values keyed by the field name.
13308 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13309 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13310 * not specified an integer id is generated.
13312 Roo.data.Record = function(data, id){
13313 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13318 * Generate a constructor for a specific record layout.
13319 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13320 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13321 * Each field definition object may contain the following properties: <ul>
13322 * <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,
13323 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13324 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13325 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13326 * is being used, then this is a string containing the javascript expression to reference the data relative to
13327 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13328 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13329 * this may be omitted.</p></li>
13330 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13331 * <ul><li>auto (Default, implies no conversion)</li>
13336 * <li>date</li></ul></p></li>
13337 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13338 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13339 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13340 * by the Reader into an object that will be stored in the Record. It is passed the
13341 * following parameters:<ul>
13342 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13344 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13346 * <br>usage:<br><pre><code>
13347 var TopicRecord = Roo.data.Record.create(
13348 {name: 'title', mapping: 'topic_title'},
13349 {name: 'author', mapping: 'username'},
13350 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13351 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13352 {name: 'lastPoster', mapping: 'user2'},
13353 {name: 'excerpt', mapping: 'post_text'}
13356 var myNewRecord = new TopicRecord({
13357 title: 'Do my job please',
13360 lastPost: new Date(),
13361 lastPoster: 'Animal',
13362 excerpt: 'No way dude!'
13364 myStore.add(myNewRecord);
13369 Roo.data.Record.create = function(o){
13370 var f = function(){
13371 f.superclass.constructor.apply(this, arguments);
13373 Roo.extend(f, Roo.data.Record);
13374 var p = f.prototype;
13375 p.fields = new Roo.util.MixedCollection(false, function(field){
13378 for(var i = 0, len = o.length; i < len; i++){
13379 p.fields.add(new Roo.data.Field(o[i]));
13381 f.getField = function(name){
13382 return p.fields.get(name);
13387 Roo.data.Record.AUTO_ID = 1000;
13388 Roo.data.Record.EDIT = 'edit';
13389 Roo.data.Record.REJECT = 'reject';
13390 Roo.data.Record.COMMIT = 'commit';
13392 Roo.data.Record.prototype = {
13394 * Readonly flag - true if this record has been modified.
13403 join : function(store){
13404 this.store = store;
13408 * Set the named field to the specified value.
13409 * @param {String} name The name of the field to set.
13410 * @param {Object} value The value to set the field to.
13412 set : function(name, value){
13413 if(this.data[name] == value){
13417 if(!this.modified){
13418 this.modified = {};
13420 if(typeof this.modified[name] == 'undefined'){
13421 this.modified[name] = this.data[name];
13423 this.data[name] = value;
13424 if(!this.editing && this.store){
13425 this.store.afterEdit(this);
13430 * Get the value of the named field.
13431 * @param {String} name The name of the field to get the value of.
13432 * @return {Object} The value of the field.
13434 get : function(name){
13435 return this.data[name];
13439 beginEdit : function(){
13440 this.editing = true;
13441 this.modified = {};
13445 cancelEdit : function(){
13446 this.editing = false;
13447 delete this.modified;
13451 endEdit : function(){
13452 this.editing = false;
13453 if(this.dirty && this.store){
13454 this.store.afterEdit(this);
13459 * Usually called by the {@link Roo.data.Store} which owns the Record.
13460 * Rejects all changes made to the Record since either creation, or the last commit operation.
13461 * Modified fields are reverted to their original values.
13463 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13464 * of reject operations.
13466 reject : function(){
13467 var m = this.modified;
13469 if(typeof m[n] != "function"){
13470 this.data[n] = m[n];
13473 this.dirty = false;
13474 delete this.modified;
13475 this.editing = false;
13477 this.store.afterReject(this);
13482 * Usually called by the {@link Roo.data.Store} which owns the Record.
13483 * Commits all changes made to the Record since either creation, or the last commit operation.
13485 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13486 * of commit operations.
13488 commit : function(){
13489 this.dirty = false;
13490 delete this.modified;
13491 this.editing = false;
13493 this.store.afterCommit(this);
13498 hasError : function(){
13499 return this.error != null;
13503 clearError : function(){
13508 * Creates a copy of this record.
13509 * @param {String} id (optional) A new record id if you don't want to use this record's id
13512 copy : function(newId) {
13513 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13517 * Ext JS Library 1.1.1
13518 * Copyright(c) 2006-2007, Ext JS, LLC.
13520 * Originally Released Under LGPL - original licence link has changed is not relivant.
13523 * <script type="text/javascript">
13529 * @class Roo.data.Store
13530 * @extends Roo.util.Observable
13531 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13532 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13534 * 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
13535 * has no knowledge of the format of the data returned by the Proxy.<br>
13537 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13538 * instances from the data object. These records are cached and made available through accessor functions.
13540 * Creates a new Store.
13541 * @param {Object} config A config object containing the objects needed for the Store to access data,
13542 * and read the data into Records.
13544 Roo.data.Store = function(config){
13545 this.data = new Roo.util.MixedCollection(false);
13546 this.data.getKey = function(o){
13549 this.baseParams = {};
13551 this.paramNames = {
13556 "multisort" : "_multisort"
13559 if(config && config.data){
13560 this.inlineData = config.data;
13561 delete config.data;
13564 Roo.apply(this, config);
13566 if(this.reader){ // reader passed
13567 this.reader = Roo.factory(this.reader, Roo.data);
13568 this.reader.xmodule = this.xmodule || false;
13569 if(!this.recordType){
13570 this.recordType = this.reader.recordType;
13572 if(this.reader.onMetaChange){
13573 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13577 if(this.recordType){
13578 this.fields = this.recordType.prototype.fields;
13580 this.modified = [];
13584 * @event datachanged
13585 * Fires when the data cache has changed, and a widget which is using this Store
13586 * as a Record cache should refresh its view.
13587 * @param {Store} this
13589 datachanged : true,
13591 * @event metachange
13592 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13593 * @param {Store} this
13594 * @param {Object} meta The JSON metadata
13599 * Fires when Records have been added to the Store
13600 * @param {Store} this
13601 * @param {Roo.data.Record[]} records The array of Records added
13602 * @param {Number} index The index at which the record(s) were added
13607 * Fires when a Record has been removed from the Store
13608 * @param {Store} this
13609 * @param {Roo.data.Record} record The Record that was removed
13610 * @param {Number} index The index at which the record was removed
13615 * Fires when a Record has been updated
13616 * @param {Store} this
13617 * @param {Roo.data.Record} record The Record that was updated
13618 * @param {String} operation The update operation being performed. Value may be one of:
13620 Roo.data.Record.EDIT
13621 Roo.data.Record.REJECT
13622 Roo.data.Record.COMMIT
13628 * Fires when the data cache has been cleared.
13629 * @param {Store} this
13633 * @event beforeload
13634 * Fires before a request is made for a new data object. If the beforeload handler returns false
13635 * the load action will be canceled.
13636 * @param {Store} this
13637 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13641 * @event beforeloadadd
13642 * Fires after a new set of Records has been loaded.
13643 * @param {Store} this
13644 * @param {Roo.data.Record[]} records The Records that were loaded
13645 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13647 beforeloadadd : true,
13650 * Fires after a new set of Records has been loaded, before they are added to the store.
13651 * @param {Store} this
13652 * @param {Roo.data.Record[]} records The Records that were loaded
13653 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13654 * @params {Object} return from reader
13658 * @event loadexception
13659 * Fires if an exception occurs in the Proxy during loading.
13660 * Called with the signature of the Proxy's "loadexception" event.
13661 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13664 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13665 * @param {Object} load options
13666 * @param {Object} jsonData from your request (normally this contains the Exception)
13668 loadexception : true
13672 this.proxy = Roo.factory(this.proxy, Roo.data);
13673 this.proxy.xmodule = this.xmodule || false;
13674 this.relayEvents(this.proxy, ["loadexception"]);
13676 this.sortToggle = {};
13677 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13679 Roo.data.Store.superclass.constructor.call(this);
13681 if(this.inlineData){
13682 this.loadData(this.inlineData);
13683 delete this.inlineData;
13687 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13689 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13690 * without a remote query - used by combo/forms at present.
13694 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13697 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13700 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13701 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13704 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13705 * on any HTTP request
13708 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13711 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13715 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13716 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13718 remoteSort : false,
13721 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13722 * loaded or when a record is removed. (defaults to false).
13724 pruneModifiedRecords : false,
13727 lastOptions : null,
13730 * Add Records to the Store and fires the add event.
13731 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13733 add : function(records){
13734 records = [].concat(records);
13735 for(var i = 0, len = records.length; i < len; i++){
13736 records[i].join(this);
13738 var index = this.data.length;
13739 this.data.addAll(records);
13740 this.fireEvent("add", this, records, index);
13744 * Remove a Record from the Store and fires the remove event.
13745 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13747 remove : function(record){
13748 var index = this.data.indexOf(record);
13749 this.data.removeAt(index);
13751 if(this.pruneModifiedRecords){
13752 this.modified.remove(record);
13754 this.fireEvent("remove", this, record, index);
13758 * Remove all Records from the Store and fires the clear event.
13760 removeAll : function(){
13762 if(this.pruneModifiedRecords){
13763 this.modified = [];
13765 this.fireEvent("clear", this);
13769 * Inserts Records to the Store at the given index and fires the add event.
13770 * @param {Number} index The start index at which to insert the passed Records.
13771 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13773 insert : function(index, records){
13774 records = [].concat(records);
13775 for(var i = 0, len = records.length; i < len; i++){
13776 this.data.insert(index, records[i]);
13777 records[i].join(this);
13779 this.fireEvent("add", this, records, index);
13783 * Get the index within the cache of the passed Record.
13784 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13785 * @return {Number} The index of the passed Record. Returns -1 if not found.
13787 indexOf : function(record){
13788 return this.data.indexOf(record);
13792 * Get the index within the cache of the Record with the passed id.
13793 * @param {String} id The id of the Record to find.
13794 * @return {Number} The index of the Record. Returns -1 if not found.
13796 indexOfId : function(id){
13797 return this.data.indexOfKey(id);
13801 * Get the Record with the specified id.
13802 * @param {String} id The id of the Record to find.
13803 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13805 getById : function(id){
13806 return this.data.key(id);
13810 * Get the Record at the specified index.
13811 * @param {Number} index The index of the Record to find.
13812 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13814 getAt : function(index){
13815 return this.data.itemAt(index);
13819 * Returns a range of Records between specified indices.
13820 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13821 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13822 * @return {Roo.data.Record[]} An array of Records
13824 getRange : function(start, end){
13825 return this.data.getRange(start, end);
13829 storeOptions : function(o){
13830 o = Roo.apply({}, o);
13833 this.lastOptions = o;
13837 * Loads the Record cache from the configured Proxy using the configured Reader.
13839 * If using remote paging, then the first load call must specify the <em>start</em>
13840 * and <em>limit</em> properties in the options.params property to establish the initial
13841 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13843 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13844 * and this call will return before the new data has been loaded. Perform any post-processing
13845 * in a callback function, or in a "load" event handler.</strong>
13847 * @param {Object} options An object containing properties which control loading options:<ul>
13848 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13849 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13850 * passed the following arguments:<ul>
13851 * <li>r : Roo.data.Record[]</li>
13852 * <li>options: Options object from the load call</li>
13853 * <li>success: Boolean success indicator</li></ul></li>
13854 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13855 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13858 load : function(options){
13859 options = options || {};
13860 if(this.fireEvent("beforeload", this, options) !== false){
13861 this.storeOptions(options);
13862 var p = Roo.apply(options.params || {}, this.baseParams);
13863 // if meta was not loaded from remote source.. try requesting it.
13864 if (!this.reader.metaFromRemote) {
13865 p._requestMeta = 1;
13867 if(this.sortInfo && this.remoteSort){
13868 var pn = this.paramNames;
13869 p[pn["sort"]] = this.sortInfo.field;
13870 p[pn["dir"]] = this.sortInfo.direction;
13872 if (this.multiSort) {
13873 var pn = this.paramNames;
13874 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13877 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13882 * Reloads the Record cache from the configured Proxy using the configured Reader and
13883 * the options from the last load operation performed.
13884 * @param {Object} options (optional) An object containing properties which may override the options
13885 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13886 * the most recently used options are reused).
13888 reload : function(options){
13889 this.load(Roo.applyIf(options||{}, this.lastOptions));
13893 // Called as a callback by the Reader during a load operation.
13894 loadRecords : function(o, options, success){
13895 if(!o || success === false){
13896 if(success !== false){
13897 this.fireEvent("load", this, [], options, o);
13899 if(options.callback){
13900 options.callback.call(options.scope || this, [], options, false);
13904 // if data returned failure - throw an exception.
13905 if (o.success === false) {
13906 // show a message if no listener is registered.
13907 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13908 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13910 // loadmask wil be hooked into this..
13911 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13914 var r = o.records, t = o.totalRecords || r.length;
13916 this.fireEvent("beforeloadadd", this, r, options, o);
13918 if(!options || options.add !== true){
13919 if(this.pruneModifiedRecords){
13920 this.modified = [];
13922 for(var i = 0, len = r.length; i < len; i++){
13926 this.data = this.snapshot;
13927 delete this.snapshot;
13930 this.data.addAll(r);
13931 this.totalLength = t;
13933 this.fireEvent("datachanged", this);
13935 this.totalLength = Math.max(t, this.data.length+r.length);
13939 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13941 var e = new Roo.data.Record({});
13943 e.set(this.parent.displayField, this.parent.emptyTitle);
13944 e.set(this.parent.valueField, '');
13949 this.fireEvent("load", this, r, options, o);
13950 if(options.callback){
13951 options.callback.call(options.scope || this, r, options, true);
13957 * Loads data from a passed data block. A Reader which understands the format of the data
13958 * must have been configured in the constructor.
13959 * @param {Object} data The data block from which to read the Records. The format of the data expected
13960 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13961 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13963 loadData : function(o, append){
13964 var r = this.reader.readRecords(o);
13965 this.loadRecords(r, {add: append}, true);
13969 * using 'cn' the nested child reader read the child array into it's child stores.
13970 * @param {Object} rec The record with a 'children array
13972 loadDataFromChildren : function(rec)
13974 this.loadData(this.reader.toLoadData(rec));
13979 * Gets the number of cached records.
13981 * <em>If using paging, this may not be the total size of the dataset. If the data object
13982 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13983 * the data set size</em>
13985 getCount : function(){
13986 return this.data.length || 0;
13990 * Gets the total number of records in the dataset as returned by the server.
13992 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13993 * the dataset size</em>
13995 getTotalCount : function(){
13996 return this.totalLength || 0;
14000 * Returns the sort state of the Store as an object with two properties:
14002 field {String} The name of the field by which the Records are sorted
14003 direction {String} The sort order, "ASC" or "DESC"
14006 getSortState : function(){
14007 return this.sortInfo;
14011 applySort : function(){
14012 if(this.sortInfo && !this.remoteSort){
14013 var s = this.sortInfo, f = s.field;
14014 var st = this.fields.get(f).sortType;
14015 var fn = function(r1, r2){
14016 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14017 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14019 this.data.sort(s.direction, fn);
14020 if(this.snapshot && this.snapshot != this.data){
14021 this.snapshot.sort(s.direction, fn);
14027 * Sets the default sort column and order to be used by the next load operation.
14028 * @param {String} fieldName The name of the field to sort by.
14029 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14031 setDefaultSort : function(field, dir){
14032 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14036 * Sort the Records.
14037 * If remote sorting is used, the sort is performed on the server, and the cache is
14038 * reloaded. If local sorting is used, the cache is sorted internally.
14039 * @param {String} fieldName The name of the field to sort by.
14040 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14042 sort : function(fieldName, dir){
14043 var f = this.fields.get(fieldName);
14045 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14047 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14048 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14053 this.sortToggle[f.name] = dir;
14054 this.sortInfo = {field: f.name, direction: dir};
14055 if(!this.remoteSort){
14057 this.fireEvent("datachanged", this);
14059 this.load(this.lastOptions);
14064 * Calls the specified function for each of the Records in the cache.
14065 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14066 * Returning <em>false</em> aborts and exits the iteration.
14067 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14069 each : function(fn, scope){
14070 this.data.each(fn, scope);
14074 * Gets all records modified since the last commit. Modified records are persisted across load operations
14075 * (e.g., during paging).
14076 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14078 getModifiedRecords : function(){
14079 return this.modified;
14083 createFilterFn : function(property, value, anyMatch){
14084 if(!value.exec){ // not a regex
14085 value = String(value);
14086 if(value.length == 0){
14089 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14091 return function(r){
14092 return value.test(r.data[property]);
14097 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14098 * @param {String} property A field on your records
14099 * @param {Number} start The record index to start at (defaults to 0)
14100 * @param {Number} end The last record index to include (defaults to length - 1)
14101 * @return {Number} The sum
14103 sum : function(property, start, end){
14104 var rs = this.data.items, v = 0;
14105 start = start || 0;
14106 end = (end || end === 0) ? end : rs.length-1;
14108 for(var i = start; i <= end; i++){
14109 v += (rs[i].data[property] || 0);
14115 * Filter the records by a specified property.
14116 * @param {String} field A field on your records
14117 * @param {String/RegExp} value Either a string that the field
14118 * should start with or a RegExp to test against the field
14119 * @param {Boolean} anyMatch True to match any part not just the beginning
14121 filter : function(property, value, anyMatch){
14122 var fn = this.createFilterFn(property, value, anyMatch);
14123 return fn ? this.filterBy(fn) : this.clearFilter();
14127 * Filter by a function. The specified function will be called with each
14128 * record in this data source. If the function returns true the record is included,
14129 * otherwise it is filtered.
14130 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14131 * @param {Object} scope (optional) The scope of the function (defaults to this)
14133 filterBy : function(fn, scope){
14134 this.snapshot = this.snapshot || this.data;
14135 this.data = this.queryBy(fn, scope||this);
14136 this.fireEvent("datachanged", this);
14140 * Query the records by a specified property.
14141 * @param {String} field A field on your records
14142 * @param {String/RegExp} value Either a string that the field
14143 * should start with or a RegExp to test against the field
14144 * @param {Boolean} anyMatch True to match any part not just the beginning
14145 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14147 query : function(property, value, anyMatch){
14148 var fn = this.createFilterFn(property, value, anyMatch);
14149 return fn ? this.queryBy(fn) : this.data.clone();
14153 * Query by a function. The specified function will be called with each
14154 * record in this data source. If the function returns true the record is included
14156 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14157 * @param {Object} scope (optional) The scope of the function (defaults to this)
14158 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14160 queryBy : function(fn, scope){
14161 var data = this.snapshot || this.data;
14162 return data.filterBy(fn, scope||this);
14166 * Collects unique values for a particular dataIndex from this store.
14167 * @param {String} dataIndex The property to collect
14168 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14169 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14170 * @return {Array} An array of the unique values
14172 collect : function(dataIndex, allowNull, bypassFilter){
14173 var d = (bypassFilter === true && this.snapshot) ?
14174 this.snapshot.items : this.data.items;
14175 var v, sv, r = [], l = {};
14176 for(var i = 0, len = d.length; i < len; i++){
14177 v = d[i].data[dataIndex];
14179 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14188 * Revert to a view of the Record cache with no filtering applied.
14189 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14191 clearFilter : function(suppressEvent){
14192 if(this.snapshot && this.snapshot != this.data){
14193 this.data = this.snapshot;
14194 delete this.snapshot;
14195 if(suppressEvent !== true){
14196 this.fireEvent("datachanged", this);
14202 afterEdit : function(record){
14203 if(this.modified.indexOf(record) == -1){
14204 this.modified.push(record);
14206 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14210 afterReject : function(record){
14211 this.modified.remove(record);
14212 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14216 afterCommit : function(record){
14217 this.modified.remove(record);
14218 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14222 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14223 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14225 commitChanges : function(){
14226 var m = this.modified.slice(0);
14227 this.modified = [];
14228 for(var i = 0, len = m.length; i < len; i++){
14234 * Cancel outstanding changes on all changed records.
14236 rejectChanges : function(){
14237 var m = this.modified.slice(0);
14238 this.modified = [];
14239 for(var i = 0, len = m.length; i < len; i++){
14244 onMetaChange : function(meta, rtype, o){
14245 this.recordType = rtype;
14246 this.fields = rtype.prototype.fields;
14247 delete this.snapshot;
14248 this.sortInfo = meta.sortInfo || this.sortInfo;
14249 this.modified = [];
14250 this.fireEvent('metachange', this, this.reader.meta);
14253 moveIndex : function(data, type)
14255 var index = this.indexOf(data);
14257 var newIndex = index + type;
14261 this.insert(newIndex, data);
14266 * Ext JS Library 1.1.1
14267 * Copyright(c) 2006-2007, Ext JS, LLC.
14269 * Originally Released Under LGPL - original licence link has changed is not relivant.
14272 * <script type="text/javascript">
14276 * @class Roo.data.SimpleStore
14277 * @extends Roo.data.Store
14278 * Small helper class to make creating Stores from Array data easier.
14279 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14280 * @cfg {Array} fields An array of field definition objects, or field name strings.
14281 * @cfg {Object} an existing reader (eg. copied from another store)
14282 * @cfg {Array} data The multi-dimensional array of data
14284 * @param {Object} config
14286 Roo.data.SimpleStore = function(config)
14288 Roo.data.SimpleStore.superclass.constructor.call(this, {
14290 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14293 Roo.data.Record.create(config.fields)
14295 proxy : new Roo.data.MemoryProxy(config.data)
14299 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14301 * Ext JS Library 1.1.1
14302 * Copyright(c) 2006-2007, Ext JS, LLC.
14304 * Originally Released Under LGPL - original licence link has changed is not relivant.
14307 * <script type="text/javascript">
14312 * @extends Roo.data.Store
14313 * @class Roo.data.JsonStore
14314 * Small helper class to make creating Stores for JSON data easier. <br/>
14316 var store = new Roo.data.JsonStore({
14317 url: 'get-images.php',
14319 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14322 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14323 * JsonReader and HttpProxy (unless inline data is provided).</b>
14324 * @cfg {Array} fields An array of field definition objects, or field name strings.
14326 * @param {Object} config
14328 Roo.data.JsonStore = function(c){
14329 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14330 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14331 reader: new Roo.data.JsonReader(c, c.fields)
14334 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14336 * Ext JS Library 1.1.1
14337 * Copyright(c) 2006-2007, Ext JS, LLC.
14339 * Originally Released Under LGPL - original licence link has changed is not relivant.
14342 * <script type="text/javascript">
14346 Roo.data.Field = function(config){
14347 if(typeof config == "string"){
14348 config = {name: config};
14350 Roo.apply(this, config);
14353 this.type = "auto";
14356 var st = Roo.data.SortTypes;
14357 // named sortTypes are supported, here we look them up
14358 if(typeof this.sortType == "string"){
14359 this.sortType = st[this.sortType];
14362 // set default sortType for strings and dates
14363 if(!this.sortType){
14366 this.sortType = st.asUCString;
14369 this.sortType = st.asDate;
14372 this.sortType = st.none;
14377 var stripRe = /[\$,%]/g;
14379 // prebuilt conversion function for this field, instead of
14380 // switching every time we're reading a value
14382 var cv, dateFormat = this.dateFormat;
14387 cv = function(v){ return v; };
14390 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14394 return v !== undefined && v !== null && v !== '' ?
14395 parseInt(String(v).replace(stripRe, ""), 10) : '';
14400 return v !== undefined && v !== null && v !== '' ?
14401 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14406 cv = function(v){ return v === true || v === "true" || v == 1; };
14413 if(v instanceof Date){
14417 if(dateFormat == "timestamp"){
14418 return new Date(v*1000);
14420 return Date.parseDate(v, dateFormat);
14422 var parsed = Date.parse(v);
14423 return parsed ? new Date(parsed) : null;
14432 Roo.data.Field.prototype = {
14440 * Ext JS Library 1.1.1
14441 * Copyright(c) 2006-2007, Ext JS, LLC.
14443 * Originally Released Under LGPL - original licence link has changed is not relivant.
14446 * <script type="text/javascript">
14449 // Base class for reading structured data from a data source. This class is intended to be
14450 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14453 * @class Roo.data.DataReader
14454 * Base class for reading structured data from a data source. This class is intended to be
14455 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14458 Roo.data.DataReader = function(meta, recordType){
14462 this.recordType = recordType instanceof Array ?
14463 Roo.data.Record.create(recordType) : recordType;
14466 Roo.data.DataReader.prototype = {
14469 readerType : 'Data',
14471 * Create an empty record
14472 * @param {Object} data (optional) - overlay some values
14473 * @return {Roo.data.Record} record created.
14475 newRow : function(d) {
14477 this.recordType.prototype.fields.each(function(c) {
14479 case 'int' : da[c.name] = 0; break;
14480 case 'date' : da[c.name] = new Date(); break;
14481 case 'float' : da[c.name] = 0.0; break;
14482 case 'boolean' : da[c.name] = false; break;
14483 default : da[c.name] = ""; break;
14487 return new this.recordType(Roo.apply(da, d));
14493 * Ext JS Library 1.1.1
14494 * Copyright(c) 2006-2007, Ext JS, LLC.
14496 * Originally Released Under LGPL - original licence link has changed is not relivant.
14499 * <script type="text/javascript">
14503 * @class Roo.data.DataProxy
14504 * @extends Roo.data.Observable
14505 * This class is an abstract base class for implementations which provide retrieval of
14506 * unformatted data objects.<br>
14508 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14509 * (of the appropriate type which knows how to parse the data object) to provide a block of
14510 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14512 * Custom implementations must implement the load method as described in
14513 * {@link Roo.data.HttpProxy#load}.
14515 Roo.data.DataProxy = function(){
14518 * @event beforeload
14519 * Fires before a network request is made to retrieve a data object.
14520 * @param {Object} This DataProxy object.
14521 * @param {Object} params The params parameter to the load function.
14526 * Fires before the load method's callback is called.
14527 * @param {Object} This DataProxy object.
14528 * @param {Object} o The data object.
14529 * @param {Object} arg The callback argument object passed to the load function.
14533 * @event loadexception
14534 * Fires if an Exception occurs during data retrieval.
14535 * @param {Object} This DataProxy object.
14536 * @param {Object} o The data object.
14537 * @param {Object} arg The callback argument object passed to the load function.
14538 * @param {Object} e The Exception.
14540 loadexception : true
14542 Roo.data.DataProxy.superclass.constructor.call(this);
14545 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14548 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14552 * Ext JS Library 1.1.1
14553 * Copyright(c) 2006-2007, Ext JS, LLC.
14555 * Originally Released Under LGPL - original licence link has changed is not relivant.
14558 * <script type="text/javascript">
14561 * @class Roo.data.MemoryProxy
14562 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14563 * to the Reader when its load method is called.
14565 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14567 Roo.data.MemoryProxy = function(data){
14571 Roo.data.MemoryProxy.superclass.constructor.call(this);
14575 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14578 * Load data from the requested source (in this case an in-memory
14579 * data object passed to the constructor), read the data object into
14580 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14581 * process that block using the passed callback.
14582 * @param {Object} params This parameter is not used by the MemoryProxy class.
14583 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14584 * object into a block of Roo.data.Records.
14585 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14586 * The function must be passed <ul>
14587 * <li>The Record block object</li>
14588 * <li>The "arg" argument from the load function</li>
14589 * <li>A boolean success indicator</li>
14591 * @param {Object} scope The scope in which to call the callback
14592 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14594 load : function(params, reader, callback, scope, arg){
14595 params = params || {};
14598 result = reader.readRecords(params.data ? params.data :this.data);
14600 this.fireEvent("loadexception", this, arg, null, e);
14601 callback.call(scope, null, arg, false);
14604 callback.call(scope, result, arg, true);
14608 update : function(params, records){
14613 * Ext JS Library 1.1.1
14614 * Copyright(c) 2006-2007, Ext JS, LLC.
14616 * Originally Released Under LGPL - original licence link has changed is not relivant.
14619 * <script type="text/javascript">
14622 * @class Roo.data.HttpProxy
14623 * @extends Roo.data.DataProxy
14624 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14625 * configured to reference a certain URL.<br><br>
14627 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14628 * from which the running page was served.<br><br>
14630 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14632 * Be aware that to enable the browser to parse an XML document, the server must set
14633 * the Content-Type header in the HTTP response to "text/xml".
14635 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14636 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14637 * will be used to make the request.
14639 Roo.data.HttpProxy = function(conn){
14640 Roo.data.HttpProxy.superclass.constructor.call(this);
14641 // is conn a conn config or a real conn?
14643 this.useAjax = !conn || !conn.events;
14647 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14648 // thse are take from connection...
14651 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14654 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14655 * extra parameters to each request made by this object. (defaults to undefined)
14658 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14659 * to each request made by this object. (defaults to undefined)
14662 * @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)
14665 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14668 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14674 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14678 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14679 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14680 * a finer-grained basis than the DataProxy events.
14682 getConnection : function(){
14683 return this.useAjax ? Roo.Ajax : this.conn;
14687 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14688 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14689 * process that block using the passed callback.
14690 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14691 * for the request to the remote server.
14692 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14693 * object into a block of Roo.data.Records.
14694 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14695 * The function must be passed <ul>
14696 * <li>The Record block object</li>
14697 * <li>The "arg" argument from the load function</li>
14698 * <li>A boolean success indicator</li>
14700 * @param {Object} scope The scope in which to call the callback
14701 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14703 load : function(params, reader, callback, scope, arg){
14704 if(this.fireEvent("beforeload", this, params) !== false){
14706 params : params || {},
14708 callback : callback,
14713 callback : this.loadResponse,
14717 Roo.applyIf(o, this.conn);
14718 if(this.activeRequest){
14719 Roo.Ajax.abort(this.activeRequest);
14721 this.activeRequest = Roo.Ajax.request(o);
14723 this.conn.request(o);
14726 callback.call(scope||this, null, arg, false);
14731 loadResponse : function(o, success, response){
14732 delete this.activeRequest;
14734 this.fireEvent("loadexception", this, o, response);
14735 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14740 result = o.reader.read(response);
14742 this.fireEvent("loadexception", this, o, response, e);
14743 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14747 this.fireEvent("load", this, o, o.request.arg);
14748 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14752 update : function(dataSet){
14757 updateResponse : function(dataSet){
14762 * Ext JS Library 1.1.1
14763 * Copyright(c) 2006-2007, Ext JS, LLC.
14765 * Originally Released Under LGPL - original licence link has changed is not relivant.
14768 * <script type="text/javascript">
14772 * @class Roo.data.ScriptTagProxy
14773 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14774 * other than the originating domain of the running page.<br><br>
14776 * <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
14777 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14779 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14780 * source code that is used as the source inside a <script> tag.<br><br>
14782 * In order for the browser to process the returned data, the server must wrap the data object
14783 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14784 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14785 * depending on whether the callback name was passed:
14788 boolean scriptTag = false;
14789 String cb = request.getParameter("callback");
14792 response.setContentType("text/javascript");
14794 response.setContentType("application/x-json");
14796 Writer out = response.getWriter();
14798 out.write(cb + "(");
14800 out.print(dataBlock.toJsonString());
14807 * @param {Object} config A configuration object.
14809 Roo.data.ScriptTagProxy = function(config){
14810 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14811 Roo.apply(this, config);
14812 this.head = document.getElementsByTagName("head")[0];
14815 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14817 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14819 * @cfg {String} url The URL from which to request the data object.
14822 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14826 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14827 * the server the name of the callback function set up by the load call to process the returned data object.
14828 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14829 * javascript output which calls this named function passing the data object as its only parameter.
14831 callbackParam : "callback",
14833 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14834 * name to the request.
14839 * Load data from the configured URL, read the data object into
14840 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14841 * process that block using the passed callback.
14842 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14843 * for the request to the remote server.
14844 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14845 * object into a block of Roo.data.Records.
14846 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14847 * The function must be passed <ul>
14848 * <li>The Record block object</li>
14849 * <li>The "arg" argument from the load function</li>
14850 * <li>A boolean success indicator</li>
14852 * @param {Object} scope The scope in which to call the callback
14853 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14855 load : function(params, reader, callback, scope, arg){
14856 if(this.fireEvent("beforeload", this, params) !== false){
14858 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14860 var url = this.url;
14861 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14863 url += "&_dc=" + (new Date().getTime());
14865 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14868 cb : "stcCallback"+transId,
14869 scriptId : "stcScript"+transId,
14873 callback : callback,
14879 window[trans.cb] = function(o){
14880 conn.handleResponse(o, trans);
14883 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14885 if(this.autoAbort !== false){
14889 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14891 var script = document.createElement("script");
14892 script.setAttribute("src", url);
14893 script.setAttribute("type", "text/javascript");
14894 script.setAttribute("id", trans.scriptId);
14895 this.head.appendChild(script);
14897 this.trans = trans;
14899 callback.call(scope||this, null, arg, false);
14904 isLoading : function(){
14905 return this.trans ? true : false;
14909 * Abort the current server request.
14911 abort : function(){
14912 if(this.isLoading()){
14913 this.destroyTrans(this.trans);
14918 destroyTrans : function(trans, isLoaded){
14919 this.head.removeChild(document.getElementById(trans.scriptId));
14920 clearTimeout(trans.timeoutId);
14922 window[trans.cb] = undefined;
14924 delete window[trans.cb];
14927 // if hasn't been loaded, wait for load to remove it to prevent script error
14928 window[trans.cb] = function(){
14929 window[trans.cb] = undefined;
14931 delete window[trans.cb];
14938 handleResponse : function(o, trans){
14939 this.trans = false;
14940 this.destroyTrans(trans, true);
14943 result = trans.reader.readRecords(o);
14945 this.fireEvent("loadexception", this, o, trans.arg, e);
14946 trans.callback.call(trans.scope||window, null, trans.arg, false);
14949 this.fireEvent("load", this, o, trans.arg);
14950 trans.callback.call(trans.scope||window, result, trans.arg, true);
14954 handleFailure : function(trans){
14955 this.trans = false;
14956 this.destroyTrans(trans, false);
14957 this.fireEvent("loadexception", this, null, trans.arg);
14958 trans.callback.call(trans.scope||window, null, trans.arg, false);
14962 * Ext JS Library 1.1.1
14963 * Copyright(c) 2006-2007, Ext JS, LLC.
14965 * Originally Released Under LGPL - original licence link has changed is not relivant.
14968 * <script type="text/javascript">
14972 * @class Roo.data.JsonReader
14973 * @extends Roo.data.DataReader
14974 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14975 * based on mappings in a provided Roo.data.Record constructor.
14977 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14978 * in the reply previously.
14983 var RecordDef = Roo.data.Record.create([
14984 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14985 {name: 'occupation'} // This field will use "occupation" as the mapping.
14987 var myReader = new Roo.data.JsonReader({
14988 totalProperty: "results", // The property which contains the total dataset size (optional)
14989 root: "rows", // The property which contains an Array of row objects
14990 id: "id" // The property within each row object that provides an ID for the record (optional)
14994 * This would consume a JSON file like this:
14996 { 'results': 2, 'rows': [
14997 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14998 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15001 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15002 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15003 * paged from the remote server.
15004 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15005 * @cfg {String} root name of the property which contains the Array of row objects.
15006 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15007 * @cfg {Array} fields Array of field definition objects
15009 * Create a new JsonReader
15010 * @param {Object} meta Metadata configuration options
15011 * @param {Object} recordType Either an Array of field definition objects,
15012 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15014 Roo.data.JsonReader = function(meta, recordType){
15017 // set some defaults:
15018 Roo.applyIf(meta, {
15019 totalProperty: 'total',
15020 successProperty : 'success',
15025 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15027 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15029 readerType : 'Json',
15032 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15033 * Used by Store query builder to append _requestMeta to params.
15036 metaFromRemote : false,
15038 * This method is only used by a DataProxy which has retrieved data from a remote server.
15039 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15040 * @return {Object} data A data block which is used by an Roo.data.Store object as
15041 * a cache of Roo.data.Records.
15043 read : function(response){
15044 var json = response.responseText;
15046 var o = /* eval:var:o */ eval("("+json+")");
15048 throw {message: "JsonReader.read: Json object not found"};
15054 this.metaFromRemote = true;
15055 this.meta = o.metaData;
15056 this.recordType = Roo.data.Record.create(o.metaData.fields);
15057 this.onMetaChange(this.meta, this.recordType, o);
15059 return this.readRecords(o);
15062 // private function a store will implement
15063 onMetaChange : function(meta, recordType, o){
15070 simpleAccess: function(obj, subsc) {
15077 getJsonAccessor: function(){
15079 return function(expr) {
15081 return(re.test(expr))
15082 ? new Function("obj", "return obj." + expr)
15087 return Roo.emptyFn;
15092 * Create a data block containing Roo.data.Records from an XML document.
15093 * @param {Object} o An object which contains an Array of row objects in the property specified
15094 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15095 * which contains the total size of the dataset.
15096 * @return {Object} data A data block which is used by an Roo.data.Store object as
15097 * a cache of Roo.data.Records.
15099 readRecords : function(o){
15101 * After any data loads, the raw JSON data is available for further custom processing.
15105 var s = this.meta, Record = this.recordType,
15106 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15108 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15110 if(s.totalProperty) {
15111 this.getTotal = this.getJsonAccessor(s.totalProperty);
15113 if(s.successProperty) {
15114 this.getSuccess = this.getJsonAccessor(s.successProperty);
15116 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15118 var g = this.getJsonAccessor(s.id);
15119 this.getId = function(rec) {
15121 return (r === undefined || r === "") ? null : r;
15124 this.getId = function(){return null;};
15127 for(var jj = 0; jj < fl; jj++){
15129 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15130 this.ef[jj] = this.getJsonAccessor(map);
15134 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15135 if(s.totalProperty){
15136 var vt = parseInt(this.getTotal(o), 10);
15141 if(s.successProperty){
15142 var vs = this.getSuccess(o);
15143 if(vs === false || vs === 'false'){
15148 for(var i = 0; i < c; i++){
15151 var id = this.getId(n);
15152 for(var j = 0; j < fl; j++){
15154 var v = this.ef[j](n);
15156 Roo.log('missing convert for ' + f.name);
15160 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15162 var record = new Record(values, id);
15164 records[i] = record;
15170 totalRecords : totalRecords
15173 // used when loading children.. @see loadDataFromChildren
15174 toLoadData: function(rec)
15176 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15177 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15178 return { data : data, total : data.length };
15183 * Ext JS Library 1.1.1
15184 * Copyright(c) 2006-2007, Ext JS, LLC.
15186 * Originally Released Under LGPL - original licence link has changed is not relivant.
15189 * <script type="text/javascript">
15193 * @class Roo.data.ArrayReader
15194 * @extends Roo.data.DataReader
15195 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15196 * Each element of that Array represents a row of data fields. The
15197 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15198 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15202 var RecordDef = Roo.data.Record.create([
15203 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15204 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15206 var myReader = new Roo.data.ArrayReader({
15207 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15211 * This would consume an Array like this:
15213 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15217 * Create a new JsonReader
15218 * @param {Object} meta Metadata configuration options.
15219 * @param {Object|Array} recordType Either an Array of field definition objects
15221 * @cfg {Array} fields Array of field definition objects
15222 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15223 * as specified to {@link Roo.data.Record#create},
15224 * or an {@link Roo.data.Record} object
15227 * created using {@link Roo.data.Record#create}.
15229 Roo.data.ArrayReader = function(meta, recordType)
15231 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15234 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15237 * Create a data block containing Roo.data.Records from an XML document.
15238 * @param {Object} o An Array of row objects which represents the dataset.
15239 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15240 * a cache of Roo.data.Records.
15242 readRecords : function(o)
15244 var sid = this.meta ? this.meta.id : null;
15245 var recordType = this.recordType, fields = recordType.prototype.fields;
15248 for(var i = 0; i < root.length; i++){
15251 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15252 for(var j = 0, jlen = fields.length; j < jlen; j++){
15253 var f = fields.items[j];
15254 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15255 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15257 values[f.name] = v;
15259 var record = new recordType(values, id);
15261 records[records.length] = record;
15265 totalRecords : records.length
15268 // used when loading children.. @see loadDataFromChildren
15269 toLoadData: function(rec)
15271 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15272 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15283 * @class Roo.bootstrap.ComboBox
15284 * @extends Roo.bootstrap.TriggerField
15285 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15286 * @cfg {Boolean} append (true|false) default false
15287 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15288 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15289 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15290 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15291 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15292 * @cfg {Boolean} animate default true
15293 * @cfg {Boolean} emptyResultText only for touch device
15294 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15295 * @cfg {String} emptyTitle default ''
15296 * @cfg {Number} width fixed with? experimental
15298 * Create a new ComboBox.
15299 * @param {Object} config Configuration options
15301 Roo.bootstrap.ComboBox = function(config){
15302 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15306 * Fires when the dropdown list is expanded
15307 * @param {Roo.bootstrap.ComboBox} combo This combo box
15312 * Fires when the dropdown list is collapsed
15313 * @param {Roo.bootstrap.ComboBox} combo This combo box
15317 * @event beforeselect
15318 * Fires before a list item is selected. Return false to cancel the selection.
15319 * @param {Roo.bootstrap.ComboBox} combo This combo box
15320 * @param {Roo.data.Record} record The data record returned from the underlying store
15321 * @param {Number} index The index of the selected item in the dropdown list
15323 'beforeselect' : true,
15326 * Fires when a list item is selected
15327 * @param {Roo.bootstrap.ComboBox} combo This combo box
15328 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15329 * @param {Number} index The index of the selected item in the dropdown list
15333 * @event beforequery
15334 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15335 * The event object passed has these properties:
15336 * @param {Roo.bootstrap.ComboBox} combo This combo box
15337 * @param {String} query The query
15338 * @param {Boolean} forceAll true to force "all" query
15339 * @param {Boolean} cancel true to cancel the query
15340 * @param {Object} e The query event object
15342 'beforequery': true,
15345 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15346 * @param {Roo.bootstrap.ComboBox} combo This combo box
15351 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15352 * @param {Roo.bootstrap.ComboBox} combo This combo box
15353 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15358 * Fires when the remove value from the combobox array
15359 * @param {Roo.bootstrap.ComboBox} combo This combo box
15363 * @event afterremove
15364 * Fires when the remove value from the combobox array
15365 * @param {Roo.bootstrap.ComboBox} combo This combo box
15367 'afterremove' : true,
15369 * @event specialfilter
15370 * Fires when specialfilter
15371 * @param {Roo.bootstrap.ComboBox} combo This combo box
15373 'specialfilter' : true,
15376 * Fires when tick the element
15377 * @param {Roo.bootstrap.ComboBox} combo This combo box
15381 * @event touchviewdisplay
15382 * Fires when touch view require special display (default is using displayField)
15383 * @param {Roo.bootstrap.ComboBox} combo This combo box
15384 * @param {Object} cfg set html .
15386 'touchviewdisplay' : true
15391 this.tickItems = [];
15393 this.selectedIndex = -1;
15394 if(this.mode == 'local'){
15395 if(config.queryDelay === undefined){
15396 this.queryDelay = 10;
15398 if(config.minChars === undefined){
15404 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15407 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15408 * rendering into an Roo.Editor, defaults to false)
15411 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15412 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15415 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15418 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15419 * the dropdown list (defaults to undefined, with no header element)
15423 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15427 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15429 listWidth: undefined,
15431 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15432 * mode = 'remote' or 'text' if mode = 'local')
15434 displayField: undefined,
15437 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15438 * mode = 'remote' or 'value' if mode = 'local').
15439 * Note: use of a valueField requires the user make a selection
15440 * in order for a value to be mapped.
15442 valueField: undefined,
15444 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15449 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15450 * field's data value (defaults to the underlying DOM element's name)
15452 hiddenName: undefined,
15454 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15458 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15460 selectedClass: 'active',
15463 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15467 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15468 * anchor positions (defaults to 'tl-bl')
15470 listAlign: 'tl-bl?',
15472 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15476 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15477 * query specified by the allQuery config option (defaults to 'query')
15479 triggerAction: 'query',
15481 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15482 * (defaults to 4, does not apply if editable = false)
15486 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15487 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15491 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15492 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15496 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15497 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15501 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15502 * when editable = true (defaults to false)
15504 selectOnFocus:false,
15506 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15508 queryParam: 'query',
15510 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15511 * when mode = 'remote' (defaults to 'Loading...')
15513 loadingText: 'Loading...',
15515 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15519 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15523 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15524 * traditional select (defaults to true)
15528 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15532 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15536 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15537 * listWidth has a higher value)
15541 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15542 * allow the user to set arbitrary text into the field (defaults to false)
15544 forceSelection:false,
15546 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15547 * if typeAhead = true (defaults to 250)
15549 typeAheadDelay : 250,
15551 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15552 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15554 valueNotFoundText : undefined,
15556 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15558 blockFocus : false,
15561 * @cfg {Boolean} disableClear Disable showing of clear button.
15563 disableClear : false,
15565 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15567 alwaysQuery : false,
15570 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15575 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15577 invalidClass : "has-warning",
15580 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15582 validClass : "has-success",
15585 * @cfg {Boolean} specialFilter (true|false) special filter default false
15587 specialFilter : false,
15590 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15592 mobileTouchView : true,
15595 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15597 useNativeIOS : false,
15600 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15602 mobile_restrict_height : false,
15604 ios_options : false,
15616 btnPosition : 'right',
15617 triggerList : true,
15618 showToggleBtn : true,
15620 emptyResultText: 'Empty',
15621 triggerText : 'Select',
15625 // element that contains real text value.. (when hidden is used..)
15627 getAutoCreate : function()
15632 * Render classic select for iso
15635 if(Roo.isIOS && this.useNativeIOS){
15636 cfg = this.getAutoCreateNativeIOS();
15644 if(Roo.isTouch && this.mobileTouchView){
15645 cfg = this.getAutoCreateTouchView();
15652 if(!this.tickable){
15653 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15658 * ComboBox with tickable selections
15661 var align = this.labelAlign || this.parentLabelAlign();
15664 cls : 'form-group roo-combobox-tickable' //input-group
15667 var btn_text_select = '';
15668 var btn_text_done = '';
15669 var btn_text_cancel = '';
15671 if (this.btn_text_show) {
15672 btn_text_select = 'Select';
15673 btn_text_done = 'Done';
15674 btn_text_cancel = 'Cancel';
15679 cls : 'tickable-buttons',
15684 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15685 //html : this.triggerText
15686 html: btn_text_select
15692 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15694 html: btn_text_done
15700 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15702 html: btn_text_cancel
15708 buttons.cn.unshift({
15710 cls: 'roo-select2-search-field-input'
15716 Roo.each(buttons.cn, function(c){
15718 c.cls += ' btn-' + _this.size;
15721 if (_this.disabled) {
15728 style : 'display: contents',
15733 cls: 'form-hidden-field'
15737 cls: 'roo-select2-choices',
15741 cls: 'roo-select2-search-field',
15752 cls: 'roo-select2-container input-group roo-select2-container-multi',
15758 // cls: 'typeahead typeahead-long dropdown-menu',
15759 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15764 if(this.hasFeedback && !this.allowBlank){
15768 cls: 'glyphicon form-control-feedback'
15771 combobox.cn.push(feedback);
15778 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15779 tooltip : 'This field is required'
15781 if (Roo.bootstrap.version == 4) {
15784 style : 'display:none'
15787 if (align ==='left' && this.fieldLabel.length) {
15789 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15796 cls : 'control-label col-form-label',
15797 html : this.fieldLabel
15809 var labelCfg = cfg.cn[1];
15810 var contentCfg = cfg.cn[2];
15813 if(this.indicatorpos == 'right'){
15819 cls : 'control-label col-form-label',
15823 html : this.fieldLabel
15839 labelCfg = cfg.cn[0];
15840 contentCfg = cfg.cn[1];
15844 if(this.labelWidth > 12){
15845 labelCfg.style = "width: " + this.labelWidth + 'px';
15847 if(this.width * 1 > 0){
15848 contentCfg.style = "width: " + this.width + 'px';
15850 if(this.labelWidth < 13 && this.labelmd == 0){
15851 this.labelmd = this.labelWidth;
15854 if(this.labellg > 0){
15855 labelCfg.cls += ' col-lg-' + this.labellg;
15856 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15859 if(this.labelmd > 0){
15860 labelCfg.cls += ' col-md-' + this.labelmd;
15861 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15864 if(this.labelsm > 0){
15865 labelCfg.cls += ' col-sm-' + this.labelsm;
15866 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15869 if(this.labelxs > 0){
15870 labelCfg.cls += ' col-xs-' + this.labelxs;
15871 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15875 } else if ( this.fieldLabel.length) {
15876 // Roo.log(" label");
15881 //cls : 'input-group-addon',
15882 html : this.fieldLabel
15887 if(this.indicatorpos == 'right'){
15891 //cls : 'input-group-addon',
15892 html : this.fieldLabel
15902 // Roo.log(" no label && no align");
15909 ['xs','sm','md','lg'].map(function(size){
15910 if (settings[size]) {
15911 cfg.cls += ' col-' + size + '-' + settings[size];
15919 _initEventsCalled : false,
15922 initEvents: function()
15924 if (this._initEventsCalled) { // as we call render... prevent looping...
15927 this._initEventsCalled = true;
15930 throw "can not find store for combo";
15933 this.indicator = this.indicatorEl();
15935 this.store = Roo.factory(this.store, Roo.data);
15936 this.store.parent = this;
15938 // if we are building from html. then this element is so complex, that we can not really
15939 // use the rendered HTML.
15940 // so we have to trash and replace the previous code.
15941 if (Roo.XComponent.build_from_html) {
15942 // remove this element....
15943 var e = this.el.dom, k=0;
15944 while (e ) { e = e.previousSibling; ++k;}
15949 this.rendered = false;
15951 this.render(this.parent().getChildContainer(true), k);
15954 if(Roo.isIOS && this.useNativeIOS){
15955 this.initIOSView();
15963 if(Roo.isTouch && this.mobileTouchView){
15964 this.initTouchView();
15969 this.initTickableEvents();
15973 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15975 if(this.hiddenName){
15977 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15979 this.hiddenField.dom.value =
15980 this.hiddenValue !== undefined ? this.hiddenValue :
15981 this.value !== undefined ? this.value : '';
15983 // prevent input submission
15984 this.el.dom.removeAttribute('name');
15985 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15990 // this.el.dom.setAttribute('autocomplete', 'off');
15993 var cls = 'x-combo-list';
15995 //this.list = new Roo.Layer({
15996 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16002 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16003 _this.list.setWidth(lw);
16006 this.list.on('mouseover', this.onViewOver, this);
16007 this.list.on('mousemove', this.onViewMove, this);
16008 this.list.on('scroll', this.onViewScroll, this);
16011 this.list.swallowEvent('mousewheel');
16012 this.assetHeight = 0;
16015 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16016 this.assetHeight += this.header.getHeight();
16019 this.innerList = this.list.createChild({cls:cls+'-inner'});
16020 this.innerList.on('mouseover', this.onViewOver, this);
16021 this.innerList.on('mousemove', this.onViewMove, this);
16022 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16024 if(this.allowBlank && !this.pageSize && !this.disableClear){
16025 this.footer = this.list.createChild({cls:cls+'-ft'});
16026 this.pageTb = new Roo.Toolbar(this.footer);
16030 this.footer = this.list.createChild({cls:cls+'-ft'});
16031 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16032 {pageSize: this.pageSize});
16036 if (this.pageTb && this.allowBlank && !this.disableClear) {
16038 this.pageTb.add(new Roo.Toolbar.Fill(), {
16039 cls: 'x-btn-icon x-btn-clear',
16041 handler: function()
16044 _this.clearValue();
16045 _this.onSelect(false, -1);
16050 this.assetHeight += this.footer.getHeight();
16055 this.tpl = Roo.bootstrap.version == 4 ?
16056 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16057 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16060 this.view = new Roo.View(this.list, this.tpl, {
16061 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16063 //this.view.wrapEl.setDisplayed(false);
16064 this.view.on('click', this.onViewClick, this);
16067 this.store.on('beforeload', this.onBeforeLoad, this);
16068 this.store.on('load', this.onLoad, this);
16069 this.store.on('loadexception', this.onLoadException, this);
16071 if(this.resizable){
16072 this.resizer = new Roo.Resizable(this.list, {
16073 pinned:true, handles:'se'
16075 this.resizer.on('resize', function(r, w, h){
16076 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16077 this.listWidth = w;
16078 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16079 this.restrictHeight();
16081 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16084 if(!this.editable){
16085 this.editable = true;
16086 this.setEditable(false);
16091 if (typeof(this.events.add.listeners) != 'undefined') {
16093 this.addicon = this.wrap.createChild(
16094 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16096 this.addicon.on('click', function(e) {
16097 this.fireEvent('add', this);
16100 if (typeof(this.events.edit.listeners) != 'undefined') {
16102 this.editicon = this.wrap.createChild(
16103 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16104 if (this.addicon) {
16105 this.editicon.setStyle('margin-left', '40px');
16107 this.editicon.on('click', function(e) {
16109 // we fire even if inothing is selected..
16110 this.fireEvent('edit', this, this.lastData );
16116 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16117 "up" : function(e){
16118 this.inKeyMode = true;
16122 "down" : function(e){
16123 if(!this.isExpanded()){
16124 this.onTriggerClick();
16126 this.inKeyMode = true;
16131 "enter" : function(e){
16132 // this.onViewClick();
16136 if(this.fireEvent("specialkey", this, e)){
16137 this.onViewClick(false);
16143 "esc" : function(e){
16147 "tab" : function(e){
16150 if(this.fireEvent("specialkey", this, e)){
16151 this.onViewClick(false);
16159 doRelay : function(foo, bar, hname){
16160 if(hname == 'down' || this.scope.isExpanded()){
16161 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16170 this.queryDelay = Math.max(this.queryDelay || 10,
16171 this.mode == 'local' ? 10 : 250);
16174 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16176 if(this.typeAhead){
16177 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16179 if(this.editable !== false){
16180 this.inputEl().on("keyup", this.onKeyUp, this);
16182 if(this.forceSelection){
16183 this.inputEl().on('blur', this.doForce, this);
16187 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16188 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16192 initTickableEvents: function()
16196 if(this.hiddenName){
16198 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16200 this.hiddenField.dom.value =
16201 this.hiddenValue !== undefined ? this.hiddenValue :
16202 this.value !== undefined ? this.value : '';
16204 // prevent input submission
16205 this.el.dom.removeAttribute('name');
16206 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16211 // this.list = this.el.select('ul.dropdown-menu',true).first();
16213 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16214 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16215 if(this.triggerList){
16216 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16219 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16220 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16222 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16223 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16225 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16226 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16228 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16229 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16230 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16233 this.cancelBtn.hide();
16238 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16239 _this.list.setWidth(lw);
16242 this.list.on('mouseover', this.onViewOver, this);
16243 this.list.on('mousemove', this.onViewMove, this);
16245 this.list.on('scroll', this.onViewScroll, this);
16248 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16249 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16252 this.view = new Roo.View(this.list, this.tpl, {
16257 selectedClass: this.selectedClass
16260 //this.view.wrapEl.setDisplayed(false);
16261 this.view.on('click', this.onViewClick, this);
16265 this.store.on('beforeload', this.onBeforeLoad, this);
16266 this.store.on('load', this.onLoad, this);
16267 this.store.on('loadexception', this.onLoadException, this);
16270 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16271 "up" : function(e){
16272 this.inKeyMode = true;
16276 "down" : function(e){
16277 this.inKeyMode = true;
16281 "enter" : function(e){
16282 if(this.fireEvent("specialkey", this, e)){
16283 this.onViewClick(false);
16289 "esc" : function(e){
16290 this.onTickableFooterButtonClick(e, false, false);
16293 "tab" : function(e){
16294 this.fireEvent("specialkey", this, e);
16296 this.onTickableFooterButtonClick(e, false, false);
16303 doRelay : function(e, fn, key){
16304 if(this.scope.isExpanded()){
16305 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16314 this.queryDelay = Math.max(this.queryDelay || 10,
16315 this.mode == 'local' ? 10 : 250);
16318 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16320 if(this.typeAhead){
16321 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16324 if(this.editable !== false){
16325 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16328 this.indicator = this.indicatorEl();
16330 if(this.indicator){
16331 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16332 this.indicator.hide();
16337 onDestroy : function(){
16339 this.view.setStore(null);
16340 this.view.el.removeAllListeners();
16341 this.view.el.remove();
16342 this.view.purgeListeners();
16345 this.list.dom.innerHTML = '';
16349 this.store.un('beforeload', this.onBeforeLoad, this);
16350 this.store.un('load', this.onLoad, this);
16351 this.store.un('loadexception', this.onLoadException, this);
16353 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16357 fireKey : function(e){
16358 if(e.isNavKeyPress() && !this.list.isVisible()){
16359 this.fireEvent("specialkey", this, e);
16364 onResize: function(w, h)
16368 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16370 // if(typeof w != 'number'){
16371 // // we do not handle it!?!?
16374 // var tw = this.trigger.getWidth();
16375 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16376 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16378 // this.inputEl().setWidth( this.adjustWidth('input', x));
16380 // //this.trigger.setStyle('left', x+'px');
16382 // if(this.list && this.listWidth === undefined){
16383 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16384 // this.list.setWidth(lw);
16385 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16393 * Allow or prevent the user from directly editing the field text. If false is passed,
16394 * the user will only be able to select from the items defined in the dropdown list. This method
16395 * is the runtime equivalent of setting the 'editable' config option at config time.
16396 * @param {Boolean} value True to allow the user to directly edit the field text
16398 setEditable : function(value){
16399 if(value == this.editable){
16402 this.editable = value;
16404 this.inputEl().dom.setAttribute('readOnly', true);
16405 this.inputEl().on('mousedown', this.onTriggerClick, this);
16406 this.inputEl().addClass('x-combo-noedit');
16408 this.inputEl().dom.removeAttribute('readOnly');
16409 this.inputEl().un('mousedown', this.onTriggerClick, this);
16410 this.inputEl().removeClass('x-combo-noedit');
16416 onBeforeLoad : function(combo,opts){
16417 if(!this.hasFocus){
16421 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16423 this.restrictHeight();
16424 this.selectedIndex = -1;
16428 onLoad : function(){
16430 this.hasQuery = false;
16432 if(!this.hasFocus){
16436 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16437 this.loading.hide();
16440 if(this.store.getCount() > 0){
16443 this.restrictHeight();
16444 if(this.lastQuery == this.allQuery){
16445 if(this.editable && !this.tickable){
16446 this.inputEl().dom.select();
16450 !this.selectByValue(this.value, true) &&
16453 !this.store.lastOptions ||
16454 typeof(this.store.lastOptions.add) == 'undefined' ||
16455 this.store.lastOptions.add != true
16458 this.select(0, true);
16461 if(this.autoFocus){
16464 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16465 this.taTask.delay(this.typeAheadDelay);
16469 this.onEmptyResults();
16475 onLoadException : function()
16477 this.hasQuery = false;
16479 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16480 this.loading.hide();
16483 if(this.tickable && this.editable){
16488 // only causes errors at present
16489 //Roo.log(this.store.reader.jsonData);
16490 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16492 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16498 onTypeAhead : function(){
16499 if(this.store.getCount() > 0){
16500 var r = this.store.getAt(0);
16501 var newValue = r.data[this.displayField];
16502 var len = newValue.length;
16503 var selStart = this.getRawValue().length;
16505 if(selStart != len){
16506 this.setRawValue(newValue);
16507 this.selectText(selStart, newValue.length);
16513 onSelect : function(record, index){
16515 if(this.fireEvent('beforeselect', this, record, index) !== false){
16517 this.setFromData(index > -1 ? record.data : false);
16520 this.fireEvent('select', this, record, index);
16525 * Returns the currently selected field value or empty string if no value is set.
16526 * @return {String} value The selected value
16528 getValue : function()
16530 if(Roo.isIOS && this.useNativeIOS){
16531 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16535 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16538 if(this.valueField){
16539 return typeof this.value != 'undefined' ? this.value : '';
16541 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16545 getRawValue : function()
16547 if(Roo.isIOS && this.useNativeIOS){
16548 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16551 var v = this.inputEl().getValue();
16557 * Clears any text/value currently set in the field
16559 clearValue : function(){
16561 if(this.hiddenField){
16562 this.hiddenField.dom.value = '';
16565 this.setRawValue('');
16566 this.lastSelectionText = '';
16567 this.lastData = false;
16569 var close = this.closeTriggerEl();
16580 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16581 * will be displayed in the field. If the value does not match the data value of an existing item,
16582 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16583 * Otherwise the field will be blank (although the value will still be set).
16584 * @param {String} value The value to match
16586 setValue : function(v)
16588 if(Roo.isIOS && this.useNativeIOS){
16589 this.setIOSValue(v);
16599 if(this.valueField){
16600 var r = this.findRecord(this.valueField, v);
16602 text = r.data[this.displayField];
16603 }else if(this.valueNotFoundText !== undefined){
16604 text = this.valueNotFoundText;
16607 this.lastSelectionText = text;
16608 if(this.hiddenField){
16609 this.hiddenField.dom.value = v;
16611 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16614 var close = this.closeTriggerEl();
16617 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16623 * @property {Object} the last set data for the element
16628 * Sets the value of the field based on a object which is related to the record format for the store.
16629 * @param {Object} value the value to set as. or false on reset?
16631 setFromData : function(o){
16638 var dv = ''; // display value
16639 var vv = ''; // value value..
16641 if (this.displayField) {
16642 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16644 // this is an error condition!!!
16645 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16648 if(this.valueField){
16649 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16652 var close = this.closeTriggerEl();
16655 if(dv.length || vv * 1 > 0){
16657 this.blockFocus=true;
16663 if(this.hiddenField){
16664 this.hiddenField.dom.value = vv;
16666 this.lastSelectionText = dv;
16667 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16671 // no hidden field.. - we store the value in 'value', but still display
16672 // display field!!!!
16673 this.lastSelectionText = dv;
16674 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16681 reset : function(){
16682 // overridden so that last data is reset..
16689 this.setValue(this.originalValue);
16690 //this.clearInvalid();
16691 this.lastData = false;
16693 this.view.clearSelections();
16699 findRecord : function(prop, value){
16701 if(this.store.getCount() > 0){
16702 this.store.each(function(r){
16703 if(r.data[prop] == value){
16713 getName: function()
16715 // returns hidden if it's set..
16716 if (!this.rendered) {return ''};
16717 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16721 onViewMove : function(e, t){
16722 this.inKeyMode = false;
16726 onViewOver : function(e, t){
16727 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16730 var item = this.view.findItemFromChild(t);
16733 var index = this.view.indexOf(item);
16734 this.select(index, false);
16739 onViewClick : function(view, doFocus, el, e)
16741 var index = this.view.getSelectedIndexes()[0];
16743 var r = this.store.getAt(index);
16747 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16754 Roo.each(this.tickItems, function(v,k){
16756 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16758 _this.tickItems.splice(k, 1);
16760 if(typeof(e) == 'undefined' && view == false){
16761 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16773 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16774 this.tickItems.push(r.data);
16777 if(typeof(e) == 'undefined' && view == false){
16778 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16785 this.onSelect(r, index);
16787 if(doFocus !== false && !this.blockFocus){
16788 this.inputEl().focus();
16793 restrictHeight : function(){
16794 //this.innerList.dom.style.height = '';
16795 //var inner = this.innerList.dom;
16796 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16797 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16798 //this.list.beginUpdate();
16799 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16800 this.list.alignTo(this.inputEl(), this.listAlign);
16801 this.list.alignTo(this.inputEl(), this.listAlign);
16802 //this.list.endUpdate();
16806 onEmptyResults : function(){
16808 if(this.tickable && this.editable){
16809 this.hasFocus = false;
16810 this.restrictHeight();
16818 * Returns true if the dropdown list is expanded, else false.
16820 isExpanded : function(){
16821 return this.list.isVisible();
16825 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16826 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16827 * @param {String} value The data value of the item to select
16828 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16829 * selected item if it is not currently in view (defaults to true)
16830 * @return {Boolean} True if the value matched an item in the list, else false
16832 selectByValue : function(v, scrollIntoView){
16833 if(v !== undefined && v !== null){
16834 var r = this.findRecord(this.valueField || this.displayField, v);
16836 this.select(this.store.indexOf(r), scrollIntoView);
16844 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16845 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16846 * @param {Number} index The zero-based index of the list item to select
16847 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16848 * selected item if it is not currently in view (defaults to true)
16850 select : function(index, scrollIntoView){
16851 this.selectedIndex = index;
16852 this.view.select(index);
16853 if(scrollIntoView !== false){
16854 var el = this.view.getNode(index);
16856 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16859 this.list.scrollChildIntoView(el, false);
16865 selectNext : function(){
16866 var ct = this.store.getCount();
16868 if(this.selectedIndex == -1){
16870 }else if(this.selectedIndex < ct-1){
16871 this.select(this.selectedIndex+1);
16877 selectPrev : function(){
16878 var ct = this.store.getCount();
16880 if(this.selectedIndex == -1){
16882 }else if(this.selectedIndex != 0){
16883 this.select(this.selectedIndex-1);
16889 onKeyUp : function(e){
16890 if(this.editable !== false && !e.isSpecialKey()){
16891 this.lastKey = e.getKey();
16892 this.dqTask.delay(this.queryDelay);
16897 validateBlur : function(){
16898 return !this.list || !this.list.isVisible();
16902 initQuery : function(){
16904 var v = this.getRawValue();
16906 if(this.tickable && this.editable){
16907 v = this.tickableInputEl().getValue();
16914 doForce : function(){
16915 if(this.inputEl().dom.value.length > 0){
16916 this.inputEl().dom.value =
16917 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16923 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16924 * query allowing the query action to be canceled if needed.
16925 * @param {String} query The SQL query to execute
16926 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16927 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16928 * saved in the current store (defaults to false)
16930 doQuery : function(q, forceAll){
16932 if(q === undefined || q === null){
16937 forceAll: forceAll,
16941 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16946 forceAll = qe.forceAll;
16947 if(forceAll === true || (q.length >= this.minChars)){
16949 this.hasQuery = true;
16951 if(this.lastQuery != q || this.alwaysQuery){
16952 this.lastQuery = q;
16953 if(this.mode == 'local'){
16954 this.selectedIndex = -1;
16956 this.store.clearFilter();
16959 if(this.specialFilter){
16960 this.fireEvent('specialfilter', this);
16965 this.store.filter(this.displayField, q);
16968 this.store.fireEvent("datachanged", this.store);
16975 this.store.baseParams[this.queryParam] = q;
16977 var options = {params : this.getParams(q)};
16980 options.add = true;
16981 options.params.start = this.page * this.pageSize;
16984 this.store.load(options);
16987 * this code will make the page width larger, at the beginning, the list not align correctly,
16988 * we should expand the list on onLoad
16989 * so command out it
16994 this.selectedIndex = -1;
16999 this.loadNext = false;
17003 getParams : function(q){
17005 //p[this.queryParam] = q;
17009 p.limit = this.pageSize;
17015 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17017 collapse : function(){
17018 if(!this.isExpanded()){
17024 this.hasFocus = false;
17028 this.cancelBtn.hide();
17029 this.trigger.show();
17032 this.tickableInputEl().dom.value = '';
17033 this.tickableInputEl().blur();
17038 Roo.get(document).un('mousedown', this.collapseIf, this);
17039 Roo.get(document).un('mousewheel', this.collapseIf, this);
17040 if (!this.editable) {
17041 Roo.get(document).un('keydown', this.listKeyPress, this);
17043 this.fireEvent('collapse', this);
17049 collapseIf : function(e){
17050 var in_combo = e.within(this.el);
17051 var in_list = e.within(this.list);
17052 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17054 if (in_combo || in_list || is_list) {
17055 //e.stopPropagation();
17060 this.onTickableFooterButtonClick(e, false, false);
17068 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17070 expand : function(){
17072 if(this.isExpanded() || !this.hasFocus){
17076 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17077 this.list.setWidth(lw);
17083 this.restrictHeight();
17087 this.tickItems = Roo.apply([], this.item);
17090 this.cancelBtn.show();
17091 this.trigger.hide();
17094 this.tickableInputEl().focus();
17099 Roo.get(document).on('mousedown', this.collapseIf, this);
17100 Roo.get(document).on('mousewheel', this.collapseIf, this);
17101 if (!this.editable) {
17102 Roo.get(document).on('keydown', this.listKeyPress, this);
17105 this.fireEvent('expand', this);
17109 // Implements the default empty TriggerField.onTriggerClick function
17110 onTriggerClick : function(e)
17112 Roo.log('trigger click');
17114 if(this.disabled || !this.triggerList){
17119 this.loadNext = false;
17121 if(this.isExpanded()){
17123 if (!this.blockFocus) {
17124 this.inputEl().focus();
17128 this.hasFocus = true;
17129 if(this.triggerAction == 'all') {
17130 this.doQuery(this.allQuery, true);
17132 this.doQuery(this.getRawValue());
17134 if (!this.blockFocus) {
17135 this.inputEl().focus();
17140 onTickableTriggerClick : function(e)
17147 this.loadNext = false;
17148 this.hasFocus = true;
17150 if(this.triggerAction == 'all') {
17151 this.doQuery(this.allQuery, true);
17153 this.doQuery(this.getRawValue());
17157 onSearchFieldClick : function(e)
17159 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17160 this.onTickableFooterButtonClick(e, false, false);
17164 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17169 this.loadNext = false;
17170 this.hasFocus = true;
17172 if(this.triggerAction == 'all') {
17173 this.doQuery(this.allQuery, true);
17175 this.doQuery(this.getRawValue());
17179 listKeyPress : function(e)
17181 //Roo.log('listkeypress');
17182 // scroll to first matching element based on key pres..
17183 if (e.isSpecialKey()) {
17186 var k = String.fromCharCode(e.getKey()).toUpperCase();
17189 var csel = this.view.getSelectedNodes();
17190 var cselitem = false;
17192 var ix = this.view.indexOf(csel[0]);
17193 cselitem = this.store.getAt(ix);
17194 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17200 this.store.each(function(v) {
17202 // start at existing selection.
17203 if (cselitem.id == v.id) {
17209 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17210 match = this.store.indexOf(v);
17216 if (match === false) {
17217 return true; // no more action?
17220 this.view.select(match);
17221 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17222 sn.scrollIntoView(sn.dom.parentNode, false);
17225 onViewScroll : function(e, t){
17227 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){
17231 this.hasQuery = true;
17233 this.loading = this.list.select('.loading', true).first();
17235 if(this.loading === null){
17236 this.list.createChild({
17238 cls: 'loading roo-select2-more-results roo-select2-active',
17239 html: 'Loading more results...'
17242 this.loading = this.list.select('.loading', true).first();
17244 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17246 this.loading.hide();
17249 this.loading.show();
17254 this.loadNext = true;
17256 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17261 addItem : function(o)
17263 var dv = ''; // display value
17265 if (this.displayField) {
17266 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17268 // this is an error condition!!!
17269 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17276 var choice = this.choices.createChild({
17278 cls: 'roo-select2-search-choice',
17287 cls: 'roo-select2-search-choice-close fa fa-times',
17292 }, this.searchField);
17294 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17296 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17304 this.inputEl().dom.value = '';
17309 onRemoveItem : function(e, _self, o)
17311 e.preventDefault();
17313 this.lastItem = Roo.apply([], this.item);
17315 var index = this.item.indexOf(o.data) * 1;
17318 Roo.log('not this item?!');
17322 this.item.splice(index, 1);
17327 this.fireEvent('remove', this, e);
17333 syncValue : function()
17335 if(!this.item.length){
17342 Roo.each(this.item, function(i){
17343 if(_this.valueField){
17344 value.push(i[_this.valueField]);
17351 this.value = value.join(',');
17353 if(this.hiddenField){
17354 this.hiddenField.dom.value = this.value;
17357 this.store.fireEvent("datachanged", this.store);
17362 clearItem : function()
17364 if(!this.multiple){
17370 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17378 if(this.tickable && !Roo.isTouch){
17379 this.view.refresh();
17383 inputEl: function ()
17385 if(Roo.isIOS && this.useNativeIOS){
17386 return this.el.select('select.roo-ios-select', true).first();
17389 if(Roo.isTouch && this.mobileTouchView){
17390 return this.el.select('input.form-control',true).first();
17394 return this.searchField;
17397 return this.el.select('input.form-control',true).first();
17400 onTickableFooterButtonClick : function(e, btn, el)
17402 e.preventDefault();
17404 this.lastItem = Roo.apply([], this.item);
17406 if(btn && btn.name == 'cancel'){
17407 this.tickItems = Roo.apply([], this.item);
17416 Roo.each(this.tickItems, function(o){
17424 validate : function()
17426 if(this.getVisibilityEl().hasClass('hidden')){
17430 var v = this.getRawValue();
17433 v = this.getValue();
17436 if(this.disabled || this.allowBlank || v.length){
17441 this.markInvalid();
17445 tickableInputEl : function()
17447 if(!this.tickable || !this.editable){
17448 return this.inputEl();
17451 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17455 getAutoCreateTouchView : function()
17460 cls: 'form-group' //input-group
17466 type : this.inputType,
17467 cls : 'form-control x-combo-noedit',
17468 autocomplete: 'new-password',
17469 placeholder : this.placeholder || '',
17474 input.name = this.name;
17478 input.cls += ' input-' + this.size;
17481 if (this.disabled) {
17482 input.disabled = true;
17486 cls : 'roo-combobox-wrap',
17493 inputblock.cls += ' input-group';
17495 inputblock.cn.unshift({
17497 cls : 'input-group-addon input-group-prepend input-group-text',
17502 if(this.removable && !this.multiple){
17503 inputblock.cls += ' roo-removable';
17505 inputblock.cn.push({
17508 cls : 'roo-combo-removable-btn close'
17512 if(this.hasFeedback && !this.allowBlank){
17514 inputblock.cls += ' has-feedback';
17516 inputblock.cn.push({
17518 cls: 'glyphicon form-control-feedback'
17525 inputblock.cls += (this.before) ? '' : ' input-group';
17527 inputblock.cn.push({
17529 cls : 'input-group-addon input-group-append input-group-text',
17535 var ibwrap = inputblock;
17540 cls: 'roo-select2-choices',
17544 cls: 'roo-select2-search-field',
17557 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17562 cls: 'form-hidden-field'
17568 if(!this.multiple && this.showToggleBtn){
17574 if (this.caret != false) {
17577 cls: 'fa fa-' + this.caret
17584 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17586 Roo.bootstrap.version == 3 ? caret : '',
17589 cls: 'combobox-clear',
17603 combobox.cls += ' roo-select2-container-multi';
17606 var required = this.allowBlank ? {
17608 style: 'display: none'
17611 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17612 tooltip : 'This field is required'
17615 var align = this.labelAlign || this.parentLabelAlign();
17617 if (align ==='left' && this.fieldLabel.length) {
17623 cls : 'control-label col-form-label',
17624 html : this.fieldLabel
17628 cls : 'roo-combobox-wrap ',
17635 var labelCfg = cfg.cn[1];
17636 var contentCfg = cfg.cn[2];
17639 if(this.indicatorpos == 'right'){
17644 cls : 'control-label col-form-label',
17648 html : this.fieldLabel
17654 cls : "roo-combobox-wrap ",
17662 labelCfg = cfg.cn[0];
17663 contentCfg = cfg.cn[1];
17668 if(this.labelWidth > 12){
17669 labelCfg.style = "width: " + this.labelWidth + 'px';
17672 if(this.labelWidth < 13 && this.labelmd == 0){
17673 this.labelmd = this.labelWidth;
17676 if(this.labellg > 0){
17677 labelCfg.cls += ' col-lg-' + this.labellg;
17678 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17681 if(this.labelmd > 0){
17682 labelCfg.cls += ' col-md-' + this.labelmd;
17683 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17686 if(this.labelsm > 0){
17687 labelCfg.cls += ' col-sm-' + this.labelsm;
17688 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17691 if(this.labelxs > 0){
17692 labelCfg.cls += ' col-xs-' + this.labelxs;
17693 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17697 } else if ( this.fieldLabel.length) {
17702 cls : 'control-label',
17703 html : this.fieldLabel
17714 if(this.indicatorpos == 'right'){
17718 cls : 'control-label',
17719 html : this.fieldLabel,
17737 var settings = this;
17739 ['xs','sm','md','lg'].map(function(size){
17740 if (settings[size]) {
17741 cfg.cls += ' col-' + size + '-' + settings[size];
17748 initTouchView : function()
17750 this.renderTouchView();
17752 this.touchViewEl.on('scroll', function(){
17753 this.el.dom.scrollTop = 0;
17756 this.originalValue = this.getValue();
17758 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17760 this.inputEl().on("click", this.showTouchView, this);
17761 if (this.triggerEl) {
17762 this.triggerEl.on("click", this.showTouchView, this);
17766 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17767 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17769 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17771 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17772 this.store.on('load', this.onTouchViewLoad, this);
17773 this.store.on('loadexception', this.onTouchViewLoadException, this);
17775 if(this.hiddenName){
17777 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17779 this.hiddenField.dom.value =
17780 this.hiddenValue !== undefined ? this.hiddenValue :
17781 this.value !== undefined ? this.value : '';
17783 this.el.dom.removeAttribute('name');
17784 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17788 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17789 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17792 if(this.removable && !this.multiple){
17793 var close = this.closeTriggerEl();
17795 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17796 close.on('click', this.removeBtnClick, this, close);
17800 * fix the bug in Safari iOS8
17802 this.inputEl().on("focus", function(e){
17803 document.activeElement.blur();
17806 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17813 renderTouchView : function()
17815 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17816 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17818 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17819 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17821 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17822 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17823 this.touchViewBodyEl.setStyle('overflow', 'auto');
17825 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17826 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17828 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17829 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17833 showTouchView : function()
17839 this.touchViewHeaderEl.hide();
17841 if(this.modalTitle.length){
17842 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17843 this.touchViewHeaderEl.show();
17846 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17847 this.touchViewEl.show();
17849 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17851 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17852 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17854 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17856 if(this.modalTitle.length){
17857 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17860 this.touchViewBodyEl.setHeight(bodyHeight);
17864 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17866 this.touchViewEl.addClass(['in','show']);
17869 if(this._touchViewMask){
17870 Roo.get(document.body).addClass("x-body-masked");
17871 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17872 this._touchViewMask.setStyle('z-index', 10000);
17873 this._touchViewMask.addClass('show');
17876 this.doTouchViewQuery();
17880 hideTouchView : function()
17882 this.touchViewEl.removeClass(['in','show']);
17886 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17888 this.touchViewEl.setStyle('display', 'none');
17891 if(this._touchViewMask){
17892 this._touchViewMask.removeClass('show');
17893 Roo.get(document.body).removeClass("x-body-masked");
17897 setTouchViewValue : function()
17904 Roo.each(this.tickItems, function(o){
17909 this.hideTouchView();
17912 doTouchViewQuery : function()
17921 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17925 if(!this.alwaysQuery || this.mode == 'local'){
17926 this.onTouchViewLoad();
17933 onTouchViewBeforeLoad : function(combo,opts)
17939 onTouchViewLoad : function()
17941 if(this.store.getCount() < 1){
17942 this.onTouchViewEmptyResults();
17946 this.clearTouchView();
17948 var rawValue = this.getRawValue();
17950 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17952 this.tickItems = [];
17954 this.store.data.each(function(d, rowIndex){
17955 var row = this.touchViewListGroup.createChild(template);
17957 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17958 row.addClass(d.data.cls);
17961 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17964 html : d.data[this.displayField]
17967 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17968 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17971 row.removeClass('selected');
17972 if(!this.multiple && this.valueField &&
17973 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17976 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17977 row.addClass('selected');
17980 if(this.multiple && this.valueField &&
17981 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17985 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17986 this.tickItems.push(d.data);
17989 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17993 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17995 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17997 if(this.modalTitle.length){
17998 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18001 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18003 if(this.mobile_restrict_height && listHeight < bodyHeight){
18004 this.touchViewBodyEl.setHeight(listHeight);
18009 if(firstChecked && listHeight > bodyHeight){
18010 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18015 onTouchViewLoadException : function()
18017 this.hideTouchView();
18020 onTouchViewEmptyResults : function()
18022 this.clearTouchView();
18024 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18026 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18030 clearTouchView : function()
18032 this.touchViewListGroup.dom.innerHTML = '';
18035 onTouchViewClick : function(e, el, o)
18037 e.preventDefault();
18040 var rowIndex = o.rowIndex;
18042 var r = this.store.getAt(rowIndex);
18044 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18046 if(!this.multiple){
18047 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18048 c.dom.removeAttribute('checked');
18051 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18053 this.setFromData(r.data);
18055 var close = this.closeTriggerEl();
18061 this.hideTouchView();
18063 this.fireEvent('select', this, r, rowIndex);
18068 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18069 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18070 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18074 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18075 this.addItem(r.data);
18076 this.tickItems.push(r.data);
18080 getAutoCreateNativeIOS : function()
18083 cls: 'form-group' //input-group,
18088 cls : 'roo-ios-select'
18092 combobox.name = this.name;
18095 if (this.disabled) {
18096 combobox.disabled = true;
18099 var settings = this;
18101 ['xs','sm','md','lg'].map(function(size){
18102 if (settings[size]) {
18103 cfg.cls += ' col-' + size + '-' + settings[size];
18113 initIOSView : function()
18115 this.store.on('load', this.onIOSViewLoad, this);
18120 onIOSViewLoad : function()
18122 if(this.store.getCount() < 1){
18126 this.clearIOSView();
18128 if(this.allowBlank) {
18130 var default_text = '-- SELECT --';
18132 if(this.placeholder.length){
18133 default_text = this.placeholder;
18136 if(this.emptyTitle.length){
18137 default_text += ' - ' + this.emptyTitle + ' -';
18140 var opt = this.inputEl().createChild({
18143 html : default_text
18147 o[this.valueField] = 0;
18148 o[this.displayField] = default_text;
18150 this.ios_options.push({
18157 this.store.data.each(function(d, rowIndex){
18161 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18162 html = d.data[this.displayField];
18167 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18168 value = d.data[this.valueField];
18177 if(this.value == d.data[this.valueField]){
18178 option['selected'] = true;
18181 var opt = this.inputEl().createChild(option);
18183 this.ios_options.push({
18190 this.inputEl().on('change', function(){
18191 this.fireEvent('select', this);
18196 clearIOSView: function()
18198 this.inputEl().dom.innerHTML = '';
18200 this.ios_options = [];
18203 setIOSValue: function(v)
18207 if(!this.ios_options){
18211 Roo.each(this.ios_options, function(opts){
18213 opts.el.dom.removeAttribute('selected');
18215 if(opts.data[this.valueField] != v){
18219 opts.el.dom.setAttribute('selected', true);
18225 * @cfg {Boolean} grow
18229 * @cfg {Number} growMin
18233 * @cfg {Number} growMax
18242 Roo.apply(Roo.bootstrap.ComboBox, {
18246 cls: 'modal-header',
18268 cls: 'list-group-item',
18272 cls: 'roo-combobox-list-group-item-value'
18276 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18290 listItemCheckbox : {
18292 cls: 'list-group-item',
18296 cls: 'roo-combobox-list-group-item-value'
18300 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18316 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18321 cls: 'modal-footer',
18329 cls: 'col-xs-6 text-left',
18332 cls: 'btn btn-danger roo-touch-view-cancel',
18338 cls: 'col-xs-6 text-right',
18341 cls: 'btn btn-success roo-touch-view-ok',
18352 Roo.apply(Roo.bootstrap.ComboBox, {
18354 touchViewTemplate : {
18356 cls: 'modal fade roo-combobox-touch-view',
18360 cls: 'modal-dialog',
18361 style : 'position:fixed', // we have to fix position....
18365 cls: 'modal-content',
18367 Roo.bootstrap.ComboBox.header,
18368 Roo.bootstrap.ComboBox.body,
18369 Roo.bootstrap.ComboBox.footer
18378 * Ext JS Library 1.1.1
18379 * Copyright(c) 2006-2007, Ext JS, LLC.
18381 * Originally Released Under LGPL - original licence link has changed is not relivant.
18384 * <script type="text/javascript">
18389 * @extends Roo.util.Observable
18390 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18391 * This class also supports single and multi selection modes. <br>
18392 * Create a data model bound view:
18394 var store = new Roo.data.Store(...);
18396 var view = new Roo.View({
18398 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18400 singleSelect: true,
18401 selectedClass: "ydataview-selected",
18405 // listen for node click?
18406 view.on("click", function(vw, index, node, e){
18407 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18411 dataModel.load("foobar.xml");
18413 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18415 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18416 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18418 * Note: old style constructor is still suported (container, template, config)
18421 * Create a new View
18422 * @param {Object} config The config object
18425 Roo.View = function(config, depreciated_tpl, depreciated_config){
18427 this.parent = false;
18429 if (typeof(depreciated_tpl) == 'undefined') {
18430 // new way.. - universal constructor.
18431 Roo.apply(this, config);
18432 this.el = Roo.get(this.el);
18435 this.el = Roo.get(config);
18436 this.tpl = depreciated_tpl;
18437 Roo.apply(this, depreciated_config);
18439 this.wrapEl = this.el.wrap().wrap();
18440 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18443 if(typeof(this.tpl) == "string"){
18444 this.tpl = new Roo.Template(this.tpl);
18446 // support xtype ctors..
18447 this.tpl = new Roo.factory(this.tpl, Roo);
18451 this.tpl.compile();
18456 * @event beforeclick
18457 * Fires before a click is processed. Returns false to cancel the default action.
18458 * @param {Roo.View} this
18459 * @param {Number} index The index of the target node
18460 * @param {HTMLElement} node The target node
18461 * @param {Roo.EventObject} e The raw event object
18463 "beforeclick" : true,
18466 * Fires when a template node is clicked.
18467 * @param {Roo.View} this
18468 * @param {Number} index The index of the target node
18469 * @param {HTMLElement} node The target node
18470 * @param {Roo.EventObject} e The raw event object
18475 * Fires when a template node is double clicked.
18476 * @param {Roo.View} this
18477 * @param {Number} index The index of the target node
18478 * @param {HTMLElement} node The target node
18479 * @param {Roo.EventObject} e The raw event object
18483 * @event contextmenu
18484 * Fires when a template node is right clicked.
18485 * @param {Roo.View} this
18486 * @param {Number} index The index of the target node
18487 * @param {HTMLElement} node The target node
18488 * @param {Roo.EventObject} e The raw event object
18490 "contextmenu" : true,
18492 * @event selectionchange
18493 * Fires when the selected nodes change.
18494 * @param {Roo.View} this
18495 * @param {Array} selections Array of the selected nodes
18497 "selectionchange" : true,
18500 * @event beforeselect
18501 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18502 * @param {Roo.View} this
18503 * @param {HTMLElement} node The node to be selected
18504 * @param {Array} selections Array of currently selected nodes
18506 "beforeselect" : true,
18508 * @event preparedata
18509 * Fires on every row to render, to allow you to change the data.
18510 * @param {Roo.View} this
18511 * @param {Object} data to be rendered (change this)
18513 "preparedata" : true
18521 "click": this.onClick,
18522 "dblclick": this.onDblClick,
18523 "contextmenu": this.onContextMenu,
18527 this.selections = [];
18529 this.cmp = new Roo.CompositeElementLite([]);
18531 this.store = Roo.factory(this.store, Roo.data);
18532 this.setStore(this.store, true);
18535 if ( this.footer && this.footer.xtype) {
18537 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18539 this.footer.dataSource = this.store;
18540 this.footer.container = fctr;
18541 this.footer = Roo.factory(this.footer, Roo);
18542 fctr.insertFirst(this.el);
18544 // this is a bit insane - as the paging toolbar seems to detach the el..
18545 // dom.parentNode.parentNode.parentNode
18546 // they get detached?
18550 Roo.View.superclass.constructor.call(this);
18555 Roo.extend(Roo.View, Roo.util.Observable, {
18558 * @cfg {Roo.data.Store} store Data store to load data from.
18563 * @cfg {String|Roo.Element} el The container element.
18568 * @cfg {String|Roo.Template} tpl The template used by this View
18572 * @cfg {String} dataName the named area of the template to use as the data area
18573 * Works with domtemplates roo-name="name"
18577 * @cfg {String} selectedClass The css class to add to selected nodes
18579 selectedClass : "x-view-selected",
18581 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18586 * @cfg {String} text to display on mask (default Loading)
18590 * @cfg {Boolean} multiSelect Allow multiple selection
18592 multiSelect : false,
18594 * @cfg {Boolean} singleSelect Allow single selection
18596 singleSelect: false,
18599 * @cfg {Boolean} toggleSelect - selecting
18601 toggleSelect : false,
18604 * @cfg {Boolean} tickable - selecting
18609 * Returns the element this view is bound to.
18610 * @return {Roo.Element}
18612 getEl : function(){
18613 return this.wrapEl;
18619 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18621 refresh : function(){
18622 //Roo.log('refresh');
18625 // if we are using something like 'domtemplate', then
18626 // the what gets used is:
18627 // t.applySubtemplate(NAME, data, wrapping data..)
18628 // the outer template then get' applied with
18629 // the store 'extra data'
18630 // and the body get's added to the
18631 // roo-name="data" node?
18632 // <span class='roo-tpl-{name}'></span> ?????
18636 this.clearSelections();
18637 this.el.update("");
18639 var records = this.store.getRange();
18640 if(records.length < 1) {
18642 // is this valid?? = should it render a template??
18644 this.el.update(this.emptyText);
18648 if (this.dataName) {
18649 this.el.update(t.apply(this.store.meta)); //????
18650 el = this.el.child('.roo-tpl-' + this.dataName);
18653 for(var i = 0, len = records.length; i < len; i++){
18654 var data = this.prepareData(records[i].data, i, records[i]);
18655 this.fireEvent("preparedata", this, data, i, records[i]);
18657 var d = Roo.apply({}, data);
18660 Roo.apply(d, {'roo-id' : Roo.id()});
18664 Roo.each(this.parent.item, function(item){
18665 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18668 Roo.apply(d, {'roo-data-checked' : 'checked'});
18672 html[html.length] = Roo.util.Format.trim(
18674 t.applySubtemplate(this.dataName, d, this.store.meta) :
18681 el.update(html.join(""));
18682 this.nodes = el.dom.childNodes;
18683 this.updateIndexes(0);
18688 * Function to override to reformat the data that is sent to
18689 * the template for each node.
18690 * DEPRICATED - use the preparedata event handler.
18691 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18692 * a JSON object for an UpdateManager bound view).
18694 prepareData : function(data, index, record)
18696 this.fireEvent("preparedata", this, data, index, record);
18700 onUpdate : function(ds, record){
18701 // Roo.log('on update');
18702 this.clearSelections();
18703 var index = this.store.indexOf(record);
18704 var n = this.nodes[index];
18705 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18706 n.parentNode.removeChild(n);
18707 this.updateIndexes(index, index);
18713 onAdd : function(ds, records, index)
18715 //Roo.log(['on Add', ds, records, index] );
18716 this.clearSelections();
18717 if(this.nodes.length == 0){
18721 var n = this.nodes[index];
18722 for(var i = 0, len = records.length; i < len; i++){
18723 var d = this.prepareData(records[i].data, i, records[i]);
18725 this.tpl.insertBefore(n, d);
18728 this.tpl.append(this.el, d);
18731 this.updateIndexes(index);
18734 onRemove : function(ds, record, index){
18735 // Roo.log('onRemove');
18736 this.clearSelections();
18737 var el = this.dataName ?
18738 this.el.child('.roo-tpl-' + this.dataName) :
18741 el.dom.removeChild(this.nodes[index]);
18742 this.updateIndexes(index);
18746 * Refresh an individual node.
18747 * @param {Number} index
18749 refreshNode : function(index){
18750 this.onUpdate(this.store, this.store.getAt(index));
18753 updateIndexes : function(startIndex, endIndex){
18754 var ns = this.nodes;
18755 startIndex = startIndex || 0;
18756 endIndex = endIndex || ns.length - 1;
18757 for(var i = startIndex; i <= endIndex; i++){
18758 ns[i].nodeIndex = i;
18763 * Changes the data store this view uses and refresh the view.
18764 * @param {Store} store
18766 setStore : function(store, initial){
18767 if(!initial && this.store){
18768 this.store.un("datachanged", this.refresh);
18769 this.store.un("add", this.onAdd);
18770 this.store.un("remove", this.onRemove);
18771 this.store.un("update", this.onUpdate);
18772 this.store.un("clear", this.refresh);
18773 this.store.un("beforeload", this.onBeforeLoad);
18774 this.store.un("load", this.onLoad);
18775 this.store.un("loadexception", this.onLoad);
18779 store.on("datachanged", this.refresh, this);
18780 store.on("add", this.onAdd, this);
18781 store.on("remove", this.onRemove, this);
18782 store.on("update", this.onUpdate, this);
18783 store.on("clear", this.refresh, this);
18784 store.on("beforeload", this.onBeforeLoad, this);
18785 store.on("load", this.onLoad, this);
18786 store.on("loadexception", this.onLoad, this);
18794 * onbeforeLoad - masks the loading area.
18797 onBeforeLoad : function(store,opts)
18799 //Roo.log('onBeforeLoad');
18801 this.el.update("");
18803 this.el.mask(this.mask ? this.mask : "Loading" );
18805 onLoad : function ()
18812 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18813 * @param {HTMLElement} node
18814 * @return {HTMLElement} The template node
18816 findItemFromChild : function(node){
18817 var el = this.dataName ?
18818 this.el.child('.roo-tpl-' + this.dataName,true) :
18821 if(!node || node.parentNode == el){
18824 var p = node.parentNode;
18825 while(p && p != el){
18826 if(p.parentNode == el){
18835 onClick : function(e){
18836 var item = this.findItemFromChild(e.getTarget());
18838 var index = this.indexOf(item);
18839 if(this.onItemClick(item, index, e) !== false){
18840 this.fireEvent("click", this, index, item, e);
18843 this.clearSelections();
18848 onContextMenu : function(e){
18849 var item = this.findItemFromChild(e.getTarget());
18851 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18856 onDblClick : function(e){
18857 var item = this.findItemFromChild(e.getTarget());
18859 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18863 onItemClick : function(item, index, e)
18865 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18868 if (this.toggleSelect) {
18869 var m = this.isSelected(item) ? 'unselect' : 'select';
18872 _t[m](item, true, false);
18875 if(this.multiSelect || this.singleSelect){
18876 if(this.multiSelect && e.shiftKey && this.lastSelection){
18877 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18879 this.select(item, this.multiSelect && e.ctrlKey);
18880 this.lastSelection = item;
18883 if(!this.tickable){
18884 e.preventDefault();
18892 * Get the number of selected nodes.
18895 getSelectionCount : function(){
18896 return this.selections.length;
18900 * Get the currently selected nodes.
18901 * @return {Array} An array of HTMLElements
18903 getSelectedNodes : function(){
18904 return this.selections;
18908 * Get the indexes of the selected nodes.
18911 getSelectedIndexes : function(){
18912 var indexes = [], s = this.selections;
18913 for(var i = 0, len = s.length; i < len; i++){
18914 indexes.push(s[i].nodeIndex);
18920 * Clear all selections
18921 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18923 clearSelections : function(suppressEvent){
18924 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18925 this.cmp.elements = this.selections;
18926 this.cmp.removeClass(this.selectedClass);
18927 this.selections = [];
18928 if(!suppressEvent){
18929 this.fireEvent("selectionchange", this, this.selections);
18935 * Returns true if the passed node is selected
18936 * @param {HTMLElement/Number} node The node or node index
18937 * @return {Boolean}
18939 isSelected : function(node){
18940 var s = this.selections;
18944 node = this.getNode(node);
18945 return s.indexOf(node) !== -1;
18950 * @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
18951 * @param {Boolean} keepExisting (optional) true to keep existing selections
18952 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18954 select : function(nodeInfo, keepExisting, suppressEvent){
18955 if(nodeInfo instanceof Array){
18957 this.clearSelections(true);
18959 for(var i = 0, len = nodeInfo.length; i < len; i++){
18960 this.select(nodeInfo[i], true, true);
18964 var node = this.getNode(nodeInfo);
18965 if(!node || this.isSelected(node)){
18966 return; // already selected.
18969 this.clearSelections(true);
18972 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18973 Roo.fly(node).addClass(this.selectedClass);
18974 this.selections.push(node);
18975 if(!suppressEvent){
18976 this.fireEvent("selectionchange", this, this.selections);
18984 * @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
18985 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18986 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18988 unselect : function(nodeInfo, keepExisting, suppressEvent)
18990 if(nodeInfo instanceof Array){
18991 Roo.each(this.selections, function(s) {
18992 this.unselect(s, nodeInfo);
18996 var node = this.getNode(nodeInfo);
18997 if(!node || !this.isSelected(node)){
18998 //Roo.log("not selected");
18999 return; // not selected.
19003 Roo.each(this.selections, function(s) {
19005 Roo.fly(node).removeClass(this.selectedClass);
19012 this.selections= ns;
19013 this.fireEvent("selectionchange", this, this.selections);
19017 * Gets a template node.
19018 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19019 * @return {HTMLElement} The node or null if it wasn't found
19021 getNode : function(nodeInfo){
19022 if(typeof nodeInfo == "string"){
19023 return document.getElementById(nodeInfo);
19024 }else if(typeof nodeInfo == "number"){
19025 return this.nodes[nodeInfo];
19031 * Gets a range template nodes.
19032 * @param {Number} startIndex
19033 * @param {Number} endIndex
19034 * @return {Array} An array of nodes
19036 getNodes : function(start, end){
19037 var ns = this.nodes;
19038 start = start || 0;
19039 end = typeof end == "undefined" ? ns.length - 1 : end;
19042 for(var i = start; i <= end; i++){
19046 for(var i = start; i >= end; i--){
19054 * Finds the index of the passed node
19055 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19056 * @return {Number} The index of the node or -1
19058 indexOf : function(node){
19059 node = this.getNode(node);
19060 if(typeof node.nodeIndex == "number"){
19061 return node.nodeIndex;
19063 var ns = this.nodes;
19064 for(var i = 0, len = ns.length; i < len; i++){
19075 * based on jquery fullcalendar
19079 Roo.bootstrap = Roo.bootstrap || {};
19081 * @class Roo.bootstrap.Calendar
19082 * @extends Roo.bootstrap.Component
19083 * Bootstrap Calendar class
19084 * @cfg {Boolean} loadMask (true|false) default false
19085 * @cfg {Object} header generate the user specific header of the calendar, default false
19088 * Create a new Container
19089 * @param {Object} config The config object
19094 Roo.bootstrap.Calendar = function(config){
19095 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19099 * Fires when a date is selected
19100 * @param {DatePicker} this
19101 * @param {Date} date The selected date
19105 * @event monthchange
19106 * Fires when the displayed month changes
19107 * @param {DatePicker} this
19108 * @param {Date} date The selected month
19110 'monthchange': true,
19112 * @event evententer
19113 * Fires when mouse over an event
19114 * @param {Calendar} this
19115 * @param {event} Event
19117 'evententer': true,
19119 * @event eventleave
19120 * Fires when the mouse leaves an
19121 * @param {Calendar} this
19124 'eventleave': true,
19126 * @event eventclick
19127 * Fires when the mouse click an
19128 * @param {Calendar} this
19137 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19140 * @cfg {Number} startDay
19141 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19149 getAutoCreate : function(){
19152 var fc_button = function(name, corner, style, content ) {
19153 return Roo.apply({},{
19155 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19157 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19160 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19171 style : 'width:100%',
19178 cls : 'fc-header-left',
19180 fc_button('prev', 'left', 'arrow', '‹' ),
19181 fc_button('next', 'right', 'arrow', '›' ),
19182 { tag: 'span', cls: 'fc-header-space' },
19183 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19191 cls : 'fc-header-center',
19195 cls: 'fc-header-title',
19198 html : 'month / year'
19206 cls : 'fc-header-right',
19208 /* fc_button('month', 'left', '', 'month' ),
19209 fc_button('week', '', '', 'week' ),
19210 fc_button('day', 'right', '', 'day' )
19222 header = this.header;
19225 var cal_heads = function() {
19227 // fixme - handle this.
19229 for (var i =0; i < Date.dayNames.length; i++) {
19230 var d = Date.dayNames[i];
19233 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19234 html : d.substring(0,3)
19238 ret[0].cls += ' fc-first';
19239 ret[6].cls += ' fc-last';
19242 var cal_cell = function(n) {
19245 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19250 cls: 'fc-day-number',
19254 cls: 'fc-day-content',
19258 style: 'position: relative;' // height: 17px;
19270 var cal_rows = function() {
19273 for (var r = 0; r < 6; r++) {
19280 for (var i =0; i < Date.dayNames.length; i++) {
19281 var d = Date.dayNames[i];
19282 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19285 row.cn[0].cls+=' fc-first';
19286 row.cn[0].cn[0].style = 'min-height:90px';
19287 row.cn[6].cls+=' fc-last';
19291 ret[0].cls += ' fc-first';
19292 ret[4].cls += ' fc-prev-last';
19293 ret[5].cls += ' fc-last';
19300 cls: 'fc-border-separate',
19301 style : 'width:100%',
19309 cls : 'fc-first fc-last',
19327 cls : 'fc-content',
19328 style : "position: relative;",
19331 cls : 'fc-view fc-view-month fc-grid',
19332 style : 'position: relative',
19333 unselectable : 'on',
19336 cls : 'fc-event-container',
19337 style : 'position:absolute;z-index:8;top:0;left:0;'
19355 initEvents : function()
19358 throw "can not find store for calendar";
19364 style: "text-align:center",
19368 style: "background-color:white;width:50%;margin:250 auto",
19372 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19383 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19385 var size = this.el.select('.fc-content', true).first().getSize();
19386 this.maskEl.setSize(size.width, size.height);
19387 this.maskEl.enableDisplayMode("block");
19388 if(!this.loadMask){
19389 this.maskEl.hide();
19392 this.store = Roo.factory(this.store, Roo.data);
19393 this.store.on('load', this.onLoad, this);
19394 this.store.on('beforeload', this.onBeforeLoad, this);
19398 this.cells = this.el.select('.fc-day',true);
19399 //Roo.log(this.cells);
19400 this.textNodes = this.el.query('.fc-day-number');
19401 this.cells.addClassOnOver('fc-state-hover');
19403 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19404 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19405 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19406 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19408 this.on('monthchange', this.onMonthChange, this);
19410 this.update(new Date().clearTime());
19413 resize : function() {
19414 var sz = this.el.getSize();
19416 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19417 this.el.select('.fc-day-content div',true).setHeight(34);
19422 showPrevMonth : function(e){
19423 this.update(this.activeDate.add("mo", -1));
19425 showToday : function(e){
19426 this.update(new Date().clearTime());
19429 showNextMonth : function(e){
19430 this.update(this.activeDate.add("mo", 1));
19434 showPrevYear : function(){
19435 this.update(this.activeDate.add("y", -1));
19439 showNextYear : function(){
19440 this.update(this.activeDate.add("y", 1));
19445 update : function(date)
19447 var vd = this.activeDate;
19448 this.activeDate = date;
19449 // if(vd && this.el){
19450 // var t = date.getTime();
19451 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19452 // Roo.log('using add remove');
19454 // this.fireEvent('monthchange', this, date);
19456 // this.cells.removeClass("fc-state-highlight");
19457 // this.cells.each(function(c){
19458 // if(c.dateValue == t){
19459 // c.addClass("fc-state-highlight");
19460 // setTimeout(function(){
19461 // try{c.dom.firstChild.focus();}catch(e){}
19471 var days = date.getDaysInMonth();
19473 var firstOfMonth = date.getFirstDateOfMonth();
19474 var startingPos = firstOfMonth.getDay()-this.startDay;
19476 if(startingPos < this.startDay){
19480 var pm = date.add(Date.MONTH, -1);
19481 var prevStart = pm.getDaysInMonth()-startingPos;
19483 this.cells = this.el.select('.fc-day',true);
19484 this.textNodes = this.el.query('.fc-day-number');
19485 this.cells.addClassOnOver('fc-state-hover');
19487 var cells = this.cells.elements;
19488 var textEls = this.textNodes;
19490 Roo.each(cells, function(cell){
19491 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19494 days += startingPos;
19496 // convert everything to numbers so it's fast
19497 var day = 86400000;
19498 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19501 //Roo.log(prevStart);
19503 var today = new Date().clearTime().getTime();
19504 var sel = date.clearTime().getTime();
19505 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19506 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19507 var ddMatch = this.disabledDatesRE;
19508 var ddText = this.disabledDatesText;
19509 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19510 var ddaysText = this.disabledDaysText;
19511 var format = this.format;
19513 var setCellClass = function(cal, cell){
19517 //Roo.log('set Cell Class');
19519 var t = d.getTime();
19523 cell.dateValue = t;
19525 cell.className += " fc-today";
19526 cell.className += " fc-state-highlight";
19527 cell.title = cal.todayText;
19530 // disable highlight in other month..
19531 //cell.className += " fc-state-highlight";
19536 cell.className = " fc-state-disabled";
19537 cell.title = cal.minText;
19541 cell.className = " fc-state-disabled";
19542 cell.title = cal.maxText;
19546 if(ddays.indexOf(d.getDay()) != -1){
19547 cell.title = ddaysText;
19548 cell.className = " fc-state-disabled";
19551 if(ddMatch && format){
19552 var fvalue = d.dateFormat(format);
19553 if(ddMatch.test(fvalue)){
19554 cell.title = ddText.replace("%0", fvalue);
19555 cell.className = " fc-state-disabled";
19559 if (!cell.initialClassName) {
19560 cell.initialClassName = cell.dom.className;
19563 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19568 for(; i < startingPos; i++) {
19569 textEls[i].innerHTML = (++prevStart);
19570 d.setDate(d.getDate()+1);
19572 cells[i].className = "fc-past fc-other-month";
19573 setCellClass(this, cells[i]);
19578 for(; i < days; i++){
19579 intDay = i - startingPos + 1;
19580 textEls[i].innerHTML = (intDay);
19581 d.setDate(d.getDate()+1);
19583 cells[i].className = ''; // "x-date-active";
19584 setCellClass(this, cells[i]);
19588 for(; i < 42; i++) {
19589 textEls[i].innerHTML = (++extraDays);
19590 d.setDate(d.getDate()+1);
19592 cells[i].className = "fc-future fc-other-month";
19593 setCellClass(this, cells[i]);
19596 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19598 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19600 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19601 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19603 if(totalRows != 6){
19604 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19605 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19608 this.fireEvent('monthchange', this, date);
19612 if(!this.internalRender){
19613 var main = this.el.dom.firstChild;
19614 var w = main.offsetWidth;
19615 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19616 Roo.fly(main).setWidth(w);
19617 this.internalRender = true;
19618 // opera does not respect the auto grow header center column
19619 // then, after it gets a width opera refuses to recalculate
19620 // without a second pass
19621 if(Roo.isOpera && !this.secondPass){
19622 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19623 this.secondPass = true;
19624 this.update.defer(10, this, [date]);
19631 findCell : function(dt) {
19632 dt = dt.clearTime().getTime();
19634 this.cells.each(function(c){
19635 //Roo.log("check " +c.dateValue + '?=' + dt);
19636 if(c.dateValue == dt){
19646 findCells : function(ev) {
19647 var s = ev.start.clone().clearTime().getTime();
19649 var e= ev.end.clone().clearTime().getTime();
19652 this.cells.each(function(c){
19653 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19655 if(c.dateValue > e){
19658 if(c.dateValue < s){
19667 // findBestRow: function(cells)
19671 // for (var i =0 ; i < cells.length;i++) {
19672 // ret = Math.max(cells[i].rows || 0,ret);
19679 addItem : function(ev)
19681 // look for vertical location slot in
19682 var cells = this.findCells(ev);
19684 // ev.row = this.findBestRow(cells);
19686 // work out the location.
19690 for(var i =0; i < cells.length; i++) {
19692 cells[i].row = cells[0].row;
19695 cells[i].row = cells[i].row + 1;
19705 if (crow.start.getY() == cells[i].getY()) {
19707 crow.end = cells[i];
19724 cells[0].events.push(ev);
19726 this.calevents.push(ev);
19729 clearEvents: function() {
19731 if(!this.calevents){
19735 Roo.each(this.cells.elements, function(c){
19741 Roo.each(this.calevents, function(e) {
19742 Roo.each(e.els, function(el) {
19743 el.un('mouseenter' ,this.onEventEnter, this);
19744 el.un('mouseleave' ,this.onEventLeave, this);
19749 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19755 renderEvents: function()
19759 this.cells.each(function(c) {
19768 if(c.row != c.events.length){
19769 r = 4 - (4 - (c.row - c.events.length));
19772 c.events = ev.slice(0, r);
19773 c.more = ev.slice(r);
19775 if(c.more.length && c.more.length == 1){
19776 c.events.push(c.more.pop());
19779 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19783 this.cells.each(function(c) {
19785 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19788 for (var e = 0; e < c.events.length; e++){
19789 var ev = c.events[e];
19790 var rows = ev.rows;
19792 for(var i = 0; i < rows.length; i++) {
19794 // how many rows should it span..
19797 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19798 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19800 unselectable : "on",
19803 cls: 'fc-event-inner',
19807 // cls: 'fc-event-time',
19808 // html : cells.length > 1 ? '' : ev.time
19812 cls: 'fc-event-title',
19813 html : String.format('{0}', ev.title)
19820 cls: 'ui-resizable-handle ui-resizable-e',
19821 html : '  '
19828 cfg.cls += ' fc-event-start';
19830 if ((i+1) == rows.length) {
19831 cfg.cls += ' fc-event-end';
19834 var ctr = _this.el.select('.fc-event-container',true).first();
19835 var cg = ctr.createChild(cfg);
19837 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19838 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19840 var r = (c.more.length) ? 1 : 0;
19841 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19842 cg.setWidth(ebox.right - sbox.x -2);
19844 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19845 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19846 cg.on('click', _this.onEventClick, _this, ev);
19857 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19858 style : 'position: absolute',
19859 unselectable : "on",
19862 cls: 'fc-event-inner',
19866 cls: 'fc-event-title',
19874 cls: 'ui-resizable-handle ui-resizable-e',
19875 html : '  '
19881 var ctr = _this.el.select('.fc-event-container',true).first();
19882 var cg = ctr.createChild(cfg);
19884 var sbox = c.select('.fc-day-content',true).first().getBox();
19885 var ebox = c.select('.fc-day-content',true).first().getBox();
19887 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19888 cg.setWidth(ebox.right - sbox.x -2);
19890 cg.on('click', _this.onMoreEventClick, _this, c.more);
19900 onEventEnter: function (e, el,event,d) {
19901 this.fireEvent('evententer', this, el, event);
19904 onEventLeave: function (e, el,event,d) {
19905 this.fireEvent('eventleave', this, el, event);
19908 onEventClick: function (e, el,event,d) {
19909 this.fireEvent('eventclick', this, el, event);
19912 onMonthChange: function () {
19916 onMoreEventClick: function(e, el, more)
19920 this.calpopover.placement = 'right';
19921 this.calpopover.setTitle('More');
19923 this.calpopover.setContent('');
19925 var ctr = this.calpopover.el.select('.popover-content', true).first();
19927 Roo.each(more, function(m){
19929 cls : 'fc-event-hori fc-event-draggable',
19932 var cg = ctr.createChild(cfg);
19934 cg.on('click', _this.onEventClick, _this, m);
19937 this.calpopover.show(el);
19942 onLoad: function ()
19944 this.calevents = [];
19947 if(this.store.getCount() > 0){
19948 this.store.data.each(function(d){
19951 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19952 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19953 time : d.data.start_time,
19954 title : d.data.title,
19955 description : d.data.description,
19956 venue : d.data.venue
19961 this.renderEvents();
19963 if(this.calevents.length && this.loadMask){
19964 this.maskEl.hide();
19968 onBeforeLoad: function()
19970 this.clearEvents();
19972 this.maskEl.show();
19986 * @class Roo.bootstrap.Popover
19987 * @extends Roo.bootstrap.Component
19988 * Bootstrap Popover class
19989 * @cfg {String} html contents of the popover (or false to use children..)
19990 * @cfg {String} title of popover (or false to hide)
19991 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19992 * @cfg {String} trigger click || hover (or false to trigger manually)
19993 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19994 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19995 * - if false and it has a 'parent' then it will be automatically added to that element
19996 * - if string - Roo.get will be called
19997 * @cfg {Number} delay - delay before showing
20000 * Create a new Popover
20001 * @param {Object} config The config object
20004 Roo.bootstrap.Popover = function(config){
20005 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20011 * After the popover show
20013 * @param {Roo.bootstrap.Popover} this
20018 * After the popover hide
20020 * @param {Roo.bootstrap.Popover} this
20026 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20031 placement : 'right',
20032 trigger : 'hover', // hover
20038 can_build_overlaid : false,
20040 maskEl : false, // the mask element
20043 alignEl : false, // when show is called with an element - this get's stored.
20045 getChildContainer : function()
20047 return this.contentEl;
20050 getPopoverHeader : function()
20052 this.title = true; // flag not to hide it..
20053 this.headerEl.addClass('p-0');
20054 return this.headerEl
20058 getAutoCreate : function(){
20061 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20062 style: 'display:block',
20068 cls : 'popover-inner ',
20072 cls: 'popover-title popover-header',
20073 html : this.title === false ? '' : this.title
20076 cls : 'popover-content popover-body ' + (this.cls || ''),
20077 html : this.html || ''
20088 * @param {string} the title
20090 setTitle: function(str)
20094 this.headerEl.dom.innerHTML = str;
20099 * @param {string} the body content
20101 setContent: function(str)
20104 if (this.contentEl) {
20105 this.contentEl.dom.innerHTML = str;
20109 // as it get's added to the bottom of the page.
20110 onRender : function(ct, position)
20112 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20117 var cfg = Roo.apply({}, this.getAutoCreate());
20121 cfg.cls += ' ' + this.cls;
20124 cfg.style = this.style;
20126 //Roo.log("adding to ");
20127 this.el = Roo.get(document.body).createChild(cfg, position);
20128 // Roo.log(this.el);
20131 this.contentEl = this.el.select('.popover-content',true).first();
20132 this.headerEl = this.el.select('.popover-title',true).first();
20135 if(typeof(this.items) != 'undefined'){
20136 var items = this.items;
20139 for(var i =0;i < items.length;i++) {
20140 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20144 this.items = nitems;
20146 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20147 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20154 resizeMask : function()
20156 this.maskEl.setSize(
20157 Roo.lib.Dom.getViewWidth(true),
20158 Roo.lib.Dom.getViewHeight(true)
20162 initEvents : function()
20166 Roo.bootstrap.Popover.register(this);
20169 this.arrowEl = this.el.select('.arrow',true).first();
20170 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20171 this.el.enableDisplayMode('block');
20175 if (this.over === false && !this.parent()) {
20178 if (this.triggers === false) {
20183 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20184 var triggers = this.trigger ? this.trigger.split(' ') : [];
20185 Roo.each(triggers, function(trigger) {
20187 if (trigger == 'click') {
20188 on_el.on('click', this.toggle, this);
20189 } else if (trigger != 'manual') {
20190 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20191 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20193 on_el.on(eventIn ,this.enter, this);
20194 on_el.on(eventOut, this.leave, this);
20204 toggle : function () {
20205 this.hoverState == 'in' ? this.leave() : this.enter();
20208 enter : function () {
20210 clearTimeout(this.timeout);
20212 this.hoverState = 'in';
20214 if (!this.delay || !this.delay.show) {
20219 this.timeout = setTimeout(function () {
20220 if (_t.hoverState == 'in') {
20223 }, this.delay.show)
20226 leave : function() {
20227 clearTimeout(this.timeout);
20229 this.hoverState = 'out';
20231 if (!this.delay || !this.delay.hide) {
20236 this.timeout = setTimeout(function () {
20237 if (_t.hoverState == 'out') {
20240 }, this.delay.hide)
20244 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20245 * @param {string} (left|right|top|bottom) position
20247 show : function (on_el, placement)
20249 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20250 on_el = on_el || false; // default to false
20253 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20254 on_el = this.parent().el;
20255 } else if (this.over) {
20256 on_el = Roo.get(this.over);
20261 this.alignEl = Roo.get( on_el );
20264 this.render(document.body);
20270 if (this.title === false) {
20271 this.headerEl.hide();
20276 this.el.dom.style.display = 'block';
20279 if (this.alignEl) {
20280 this.updatePosition(this.placement, true);
20283 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20284 var es = this.el.getSize();
20285 var x = Roo.lib.Dom.getViewWidth()/2;
20286 var y = Roo.lib.Dom.getViewHeight()/2;
20287 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20292 //var arrow = this.el.select('.arrow',true).first();
20293 //arrow.set(align[2],
20295 this.el.addClass('in');
20299 this.hoverState = 'in';
20302 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20303 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20304 this.maskEl.dom.style.display = 'block';
20305 this.maskEl.addClass('show');
20307 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20309 this.fireEvent('show', this);
20313 * fire this manually after loading a grid in the table for example
20314 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20315 * @param {Boolean} try and move it if we cant get right position.
20317 updatePosition : function(placement, try_move)
20319 // allow for calling with no parameters
20320 placement = placement ? placement : this.placement;
20321 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20323 this.el.removeClass([
20324 'fade','top','bottom', 'left', 'right','in',
20325 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20327 this.el.addClass(placement + ' bs-popover-' + placement);
20329 if (!this.alignEl ) {
20333 switch (placement) {
20335 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20336 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20337 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20338 //normal display... or moved up/down.
20339 this.el.setXY(offset);
20340 var xy = this.alignEl.getAnchorXY('tr', false);
20342 this.arrowEl.setXY(xy);
20345 // continue through...
20346 return this.updatePosition('left', false);
20350 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20351 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20352 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20353 //normal display... or moved up/down.
20354 this.el.setXY(offset);
20355 var xy = this.alignEl.getAnchorXY('tl', false);
20356 xy[0]-=10;xy[1]+=5; // << fix me
20357 this.arrowEl.setXY(xy);
20361 return this.updatePosition('right', false);
20364 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20365 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20366 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20367 //normal display... or moved up/down.
20368 this.el.setXY(offset);
20369 var xy = this.alignEl.getAnchorXY('t', false);
20370 xy[1]-=10; // << fix me
20371 this.arrowEl.setXY(xy);
20375 return this.updatePosition('bottom', false);
20378 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20379 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20380 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20381 //normal display... or moved up/down.
20382 this.el.setXY(offset);
20383 var xy = this.alignEl.getAnchorXY('b', false);
20384 xy[1]+=2; // << fix me
20385 this.arrowEl.setXY(xy);
20389 return this.updatePosition('top', false);
20400 this.el.setXY([0,0]);
20401 this.el.removeClass('in');
20403 this.hoverState = null;
20404 this.maskEl.hide(); // always..
20405 this.fireEvent('hide', this);
20411 Roo.apply(Roo.bootstrap.Popover, {
20414 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20415 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20416 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20417 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20422 clickHander : false,
20426 onMouseDown : function(e)
20428 if (this.popups.length && !e.getTarget(".roo-popover")) {
20429 /// what is nothing is showing..
20438 register : function(popup)
20440 if (!Roo.bootstrap.Popover.clickHandler) {
20441 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20443 // hide other popups.
20444 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
20445 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
20446 this.hideAll(); //<< why?
20447 //this.popups.push(popup);
20449 hideAll : function()
20451 this.popups.forEach(function(p) {
20455 onShow : function() {
20456 Roo.bootstrap.Popover.popups.push(this);
20458 onHide : function() {
20459 Roo.bootstrap.Popover.popups.remove(this);
20465 * Card header - holder for the card header elements.
20470 * @class Roo.bootstrap.PopoverNav
20471 * @extends Roo.bootstrap.NavGroup
20472 * Bootstrap Popover header navigation class
20474 * Create a new Popover Header Navigation
20475 * @param {Object} config The config object
20478 Roo.bootstrap.PopoverNav = function(config){
20479 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20482 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20485 container_method : 'getPopoverHeader'
20503 * @class Roo.bootstrap.Progress
20504 * @extends Roo.bootstrap.Component
20505 * Bootstrap Progress class
20506 * @cfg {Boolean} striped striped of the progress bar
20507 * @cfg {Boolean} active animated of the progress bar
20511 * Create a new Progress
20512 * @param {Object} config The config object
20515 Roo.bootstrap.Progress = function(config){
20516 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20519 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20524 getAutoCreate : function(){
20532 cfg.cls += ' progress-striped';
20536 cfg.cls += ' active';
20555 * @class Roo.bootstrap.ProgressBar
20556 * @extends Roo.bootstrap.Component
20557 * Bootstrap ProgressBar class
20558 * @cfg {Number} aria_valuenow aria-value now
20559 * @cfg {Number} aria_valuemin aria-value min
20560 * @cfg {Number} aria_valuemax aria-value max
20561 * @cfg {String} label label for the progress bar
20562 * @cfg {String} panel (success | info | warning | danger )
20563 * @cfg {String} role role of the progress bar
20564 * @cfg {String} sr_only text
20568 * Create a new ProgressBar
20569 * @param {Object} config The config object
20572 Roo.bootstrap.ProgressBar = function(config){
20573 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20576 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20580 aria_valuemax : 100,
20586 getAutoCreate : function()
20591 cls: 'progress-bar',
20592 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20604 cfg.role = this.role;
20607 if(this.aria_valuenow){
20608 cfg['aria-valuenow'] = this.aria_valuenow;
20611 if(this.aria_valuemin){
20612 cfg['aria-valuemin'] = this.aria_valuemin;
20615 if(this.aria_valuemax){
20616 cfg['aria-valuemax'] = this.aria_valuemax;
20619 if(this.label && !this.sr_only){
20620 cfg.html = this.label;
20624 cfg.cls += ' progress-bar-' + this.panel;
20630 update : function(aria_valuenow)
20632 this.aria_valuenow = aria_valuenow;
20634 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20649 * @class Roo.bootstrap.TabGroup
20650 * @extends Roo.bootstrap.Column
20651 * Bootstrap Column class
20652 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20653 * @cfg {Boolean} carousel true to make the group behave like a carousel
20654 * @cfg {Boolean} bullets show bullets for the panels
20655 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20656 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20657 * @cfg {Boolean} showarrow (true|false) show arrow default true
20660 * Create a new TabGroup
20661 * @param {Object} config The config object
20664 Roo.bootstrap.TabGroup = function(config){
20665 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20667 this.navId = Roo.id();
20670 Roo.bootstrap.TabGroup.register(this);
20674 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20677 transition : false,
20682 slideOnTouch : false,
20685 getAutoCreate : function()
20687 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20689 cfg.cls += ' tab-content';
20691 if (this.carousel) {
20692 cfg.cls += ' carousel slide';
20695 cls : 'carousel-inner',
20699 if(this.bullets && !Roo.isTouch){
20702 cls : 'carousel-bullets',
20706 if(this.bullets_cls){
20707 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20714 cfg.cn[0].cn.push(bullets);
20717 if(this.showarrow){
20718 cfg.cn[0].cn.push({
20720 class : 'carousel-arrow',
20724 class : 'carousel-prev',
20728 class : 'fa fa-chevron-left'
20734 class : 'carousel-next',
20738 class : 'fa fa-chevron-right'
20751 initEvents: function()
20753 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20754 // this.el.on("touchstart", this.onTouchStart, this);
20757 if(this.autoslide){
20760 this.slideFn = window.setInterval(function() {
20761 _this.showPanelNext();
20765 if(this.showarrow){
20766 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20767 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20773 // onTouchStart : function(e, el, o)
20775 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20779 // this.showPanelNext();
20783 getChildContainer : function()
20785 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20789 * register a Navigation item
20790 * @param {Roo.bootstrap.NavItem} the navitem to add
20792 register : function(item)
20794 this.tabs.push( item);
20795 item.navId = this.navId; // not really needed..
20800 getActivePanel : function()
20803 Roo.each(this.tabs, function(t) {
20813 getPanelByName : function(n)
20816 Roo.each(this.tabs, function(t) {
20817 if (t.tabId == n) {
20825 indexOfPanel : function(p)
20828 Roo.each(this.tabs, function(t,i) {
20829 if (t.tabId == p.tabId) {
20838 * show a specific panel
20839 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20840 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20842 showPanel : function (pan)
20844 if(this.transition || typeof(pan) == 'undefined'){
20845 Roo.log("waiting for the transitionend");
20849 if (typeof(pan) == 'number') {
20850 pan = this.tabs[pan];
20853 if (typeof(pan) == 'string') {
20854 pan = this.getPanelByName(pan);
20857 var cur = this.getActivePanel();
20860 Roo.log('pan or acitve pan is undefined');
20864 if (pan.tabId == this.getActivePanel().tabId) {
20868 if (false === cur.fireEvent('beforedeactivate')) {
20872 if(this.bullets > 0 && !Roo.isTouch){
20873 this.setActiveBullet(this.indexOfPanel(pan));
20876 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20878 //class="carousel-item carousel-item-next carousel-item-left"
20880 this.transition = true;
20881 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20882 var lr = dir == 'next' ? 'left' : 'right';
20883 pan.el.addClass(dir); // or prev
20884 pan.el.addClass('carousel-item-' + dir); // or prev
20885 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20886 cur.el.addClass(lr); // or right
20887 pan.el.addClass(lr);
20888 cur.el.addClass('carousel-item-' +lr); // or right
20889 pan.el.addClass('carousel-item-' +lr);
20893 cur.el.on('transitionend', function() {
20894 Roo.log("trans end?");
20896 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20897 pan.setActive(true);
20899 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20900 cur.setActive(false);
20902 _this.transition = false;
20904 }, this, { single: true } );
20909 cur.setActive(false);
20910 pan.setActive(true);
20915 showPanelNext : function()
20917 var i = this.indexOfPanel(this.getActivePanel());
20919 if (i >= this.tabs.length - 1 && !this.autoslide) {
20923 if (i >= this.tabs.length - 1 && this.autoslide) {
20927 this.showPanel(this.tabs[i+1]);
20930 showPanelPrev : function()
20932 var i = this.indexOfPanel(this.getActivePanel());
20934 if (i < 1 && !this.autoslide) {
20938 if (i < 1 && this.autoslide) {
20939 i = this.tabs.length;
20942 this.showPanel(this.tabs[i-1]);
20946 addBullet: function()
20948 if(!this.bullets || Roo.isTouch){
20951 var ctr = this.el.select('.carousel-bullets',true).first();
20952 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20953 var bullet = ctr.createChild({
20954 cls : 'bullet bullet-' + i
20955 },ctr.dom.lastChild);
20960 bullet.on('click', (function(e, el, o, ii, t){
20962 e.preventDefault();
20964 this.showPanel(ii);
20966 if(this.autoslide && this.slideFn){
20967 clearInterval(this.slideFn);
20968 this.slideFn = window.setInterval(function() {
20969 _this.showPanelNext();
20973 }).createDelegate(this, [i, bullet], true));
20978 setActiveBullet : function(i)
20984 Roo.each(this.el.select('.bullet', true).elements, function(el){
20985 el.removeClass('selected');
20988 var bullet = this.el.select('.bullet-' + i, true).first();
20994 bullet.addClass('selected');
21005 Roo.apply(Roo.bootstrap.TabGroup, {
21009 * register a Navigation Group
21010 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21012 register : function(navgrp)
21014 this.groups[navgrp.navId] = navgrp;
21018 * fetch a Navigation Group based on the navigation ID
21019 * if one does not exist , it will get created.
21020 * @param {string} the navgroup to add
21021 * @returns {Roo.bootstrap.NavGroup} the navgroup
21023 get: function(navId) {
21024 if (typeof(this.groups[navId]) == 'undefined') {
21025 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21027 return this.groups[navId] ;
21042 * @class Roo.bootstrap.TabPanel
21043 * @extends Roo.bootstrap.Component
21044 * Bootstrap TabPanel class
21045 * @cfg {Boolean} active panel active
21046 * @cfg {String} html panel content
21047 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21048 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21049 * @cfg {String} href click to link..
21050 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21054 * Create a new TabPanel
21055 * @param {Object} config The config object
21058 Roo.bootstrap.TabPanel = function(config){
21059 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21063 * Fires when the active status changes
21064 * @param {Roo.bootstrap.TabPanel} this
21065 * @param {Boolean} state the new state
21070 * @event beforedeactivate
21071 * Fires before a tab is de-activated - can be used to do validation on a form.
21072 * @param {Roo.bootstrap.TabPanel} this
21073 * @return {Boolean} false if there is an error
21076 'beforedeactivate': true
21079 this.tabId = this.tabId || Roo.id();
21083 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21090 touchSlide : false,
21091 getAutoCreate : function(){
21096 // item is needed for carousel - not sure if it has any effect otherwise
21097 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21098 html: this.html || ''
21102 cfg.cls += ' active';
21106 cfg.tabId = this.tabId;
21114 initEvents: function()
21116 var p = this.parent();
21118 this.navId = this.navId || p.navId;
21120 if (typeof(this.navId) != 'undefined') {
21121 // not really needed.. but just in case.. parent should be a NavGroup.
21122 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21126 var i = tg.tabs.length - 1;
21128 if(this.active && tg.bullets > 0 && i < tg.bullets){
21129 tg.setActiveBullet(i);
21133 this.el.on('click', this.onClick, this);
21135 if(Roo.isTouch && this.touchSlide){
21136 this.el.on("touchstart", this.onTouchStart, this);
21137 this.el.on("touchmove", this.onTouchMove, this);
21138 this.el.on("touchend", this.onTouchEnd, this);
21143 onRender : function(ct, position)
21145 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21148 setActive : function(state)
21150 Roo.log("panel - set active " + this.tabId + "=" + state);
21152 this.active = state;
21154 this.el.removeClass('active');
21156 } else if (!this.el.hasClass('active')) {
21157 this.el.addClass('active');
21160 this.fireEvent('changed', this, state);
21163 onClick : function(e)
21165 e.preventDefault();
21167 if(!this.href.length){
21171 window.location.href = this.href;
21180 onTouchStart : function(e)
21182 this.swiping = false;
21184 this.startX = e.browserEvent.touches[0].clientX;
21185 this.startY = e.browserEvent.touches[0].clientY;
21188 onTouchMove : function(e)
21190 this.swiping = true;
21192 this.endX = e.browserEvent.touches[0].clientX;
21193 this.endY = e.browserEvent.touches[0].clientY;
21196 onTouchEnd : function(e)
21203 var tabGroup = this.parent();
21205 if(this.endX > this.startX){ // swiping right
21206 tabGroup.showPanelPrev();
21210 if(this.startX > this.endX){ // swiping left
21211 tabGroup.showPanelNext();
21230 * @class Roo.bootstrap.DateField
21231 * @extends Roo.bootstrap.Input
21232 * Bootstrap DateField class
21233 * @cfg {Number} weekStart default 0
21234 * @cfg {String} viewMode default empty, (months|years)
21235 * @cfg {String} minViewMode default empty, (months|years)
21236 * @cfg {Number} startDate default -Infinity
21237 * @cfg {Number} endDate default Infinity
21238 * @cfg {Boolean} todayHighlight default false
21239 * @cfg {Boolean} todayBtn default false
21240 * @cfg {Boolean} calendarWeeks default false
21241 * @cfg {Object} daysOfWeekDisabled default empty
21242 * @cfg {Boolean} singleMode default false (true | false)
21244 * @cfg {Boolean} keyboardNavigation default true
21245 * @cfg {String} language default en
21248 * Create a new DateField
21249 * @param {Object} config The config object
21252 Roo.bootstrap.DateField = function(config){
21253 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21257 * Fires when this field show.
21258 * @param {Roo.bootstrap.DateField} this
21259 * @param {Mixed} date The date value
21264 * Fires when this field hide.
21265 * @param {Roo.bootstrap.DateField} this
21266 * @param {Mixed} date The date value
21271 * Fires when select a date.
21272 * @param {Roo.bootstrap.DateField} this
21273 * @param {Mixed} date The date value
21277 * @event beforeselect
21278 * Fires when before select a date.
21279 * @param {Roo.bootstrap.DateField} this
21280 * @param {Mixed} date The date value
21282 beforeselect : true
21286 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21289 * @cfg {String} format
21290 * The default date format string which can be overriden for localization support. The format must be
21291 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21295 * @cfg {String} altFormats
21296 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21297 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21299 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21307 todayHighlight : false,
21313 keyboardNavigation: true,
21315 calendarWeeks: false,
21317 startDate: -Infinity,
21321 daysOfWeekDisabled: [],
21325 singleMode : false,
21327 UTCDate: function()
21329 return new Date(Date.UTC.apply(Date, arguments));
21332 UTCToday: function()
21334 var today = new Date();
21335 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21338 getDate: function() {
21339 var d = this.getUTCDate();
21340 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21343 getUTCDate: function() {
21347 setDate: function(d) {
21348 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21351 setUTCDate: function(d) {
21353 this.setValue(this.formatDate(this.date));
21356 onRender: function(ct, position)
21359 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21361 this.language = this.language || 'en';
21362 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21363 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21365 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21366 this.format = this.format || 'm/d/y';
21367 this.isInline = false;
21368 this.isInput = true;
21369 this.component = this.el.select('.add-on', true).first() || false;
21370 this.component = (this.component && this.component.length === 0) ? false : this.component;
21371 this.hasInput = this.component && this.inputEl().length;
21373 if (typeof(this.minViewMode === 'string')) {
21374 switch (this.minViewMode) {
21376 this.minViewMode = 1;
21379 this.minViewMode = 2;
21382 this.minViewMode = 0;
21387 if (typeof(this.viewMode === 'string')) {
21388 switch (this.viewMode) {
21401 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21403 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21405 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21407 this.picker().on('mousedown', this.onMousedown, this);
21408 this.picker().on('click', this.onClick, this);
21410 this.picker().addClass('datepicker-dropdown');
21412 this.startViewMode = this.viewMode;
21414 if(this.singleMode){
21415 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21416 v.setVisibilityMode(Roo.Element.DISPLAY);
21420 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21421 v.setStyle('width', '189px');
21425 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21426 if(!this.calendarWeeks){
21431 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21432 v.attr('colspan', function(i, val){
21433 return parseInt(val) + 1;
21438 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21440 this.setStartDate(this.startDate);
21441 this.setEndDate(this.endDate);
21443 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21450 if(this.isInline) {
21455 picker : function()
21457 return this.pickerEl;
21458 // return this.el.select('.datepicker', true).first();
21461 fillDow: function()
21463 var dowCnt = this.weekStart;
21472 if(this.calendarWeeks){
21480 while (dowCnt < this.weekStart + 7) {
21484 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21488 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21491 fillMonths: function()
21494 var months = this.picker().select('>.datepicker-months td', true).first();
21496 months.dom.innerHTML = '';
21502 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21505 months.createChild(month);
21512 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;
21514 if (this.date < this.startDate) {
21515 this.viewDate = new Date(this.startDate);
21516 } else if (this.date > this.endDate) {
21517 this.viewDate = new Date(this.endDate);
21519 this.viewDate = new Date(this.date);
21527 var d = new Date(this.viewDate),
21528 year = d.getUTCFullYear(),
21529 month = d.getUTCMonth(),
21530 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21531 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21532 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21533 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21534 currentDate = this.date && this.date.valueOf(),
21535 today = this.UTCToday();
21537 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21539 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21541 // this.picker.select('>tfoot th.today').
21542 // .text(dates[this.language].today)
21543 // .toggle(this.todayBtn !== false);
21545 this.updateNavArrows();
21548 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21550 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21552 prevMonth.setUTCDate(day);
21554 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21556 var nextMonth = new Date(prevMonth);
21558 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21560 nextMonth = nextMonth.valueOf();
21562 var fillMonths = false;
21564 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21566 while(prevMonth.valueOf() <= nextMonth) {
21569 if (prevMonth.getUTCDay() === this.weekStart) {
21571 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21579 if(this.calendarWeeks){
21580 // ISO 8601: First week contains first thursday.
21581 // ISO also states week starts on Monday, but we can be more abstract here.
21583 // Start of current week: based on weekstart/current date
21584 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21585 // Thursday of this week
21586 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21587 // First Thursday of year, year from thursday
21588 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21589 // Calendar week: ms between thursdays, div ms per day, div 7 days
21590 calWeek = (th - yth) / 864e5 / 7 + 1;
21592 fillMonths.cn.push({
21600 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21602 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21605 if (this.todayHighlight &&
21606 prevMonth.getUTCFullYear() == today.getFullYear() &&
21607 prevMonth.getUTCMonth() == today.getMonth() &&
21608 prevMonth.getUTCDate() == today.getDate()) {
21609 clsName += ' today';
21612 if (currentDate && prevMonth.valueOf() === currentDate) {
21613 clsName += ' active';
21616 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21617 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21618 clsName += ' disabled';
21621 fillMonths.cn.push({
21623 cls: 'day ' + clsName,
21624 html: prevMonth.getDate()
21627 prevMonth.setDate(prevMonth.getDate()+1);
21630 var currentYear = this.date && this.date.getUTCFullYear();
21631 var currentMonth = this.date && this.date.getUTCMonth();
21633 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21635 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21636 v.removeClass('active');
21638 if(currentYear === year && k === currentMonth){
21639 v.addClass('active');
21642 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21643 v.addClass('disabled');
21649 year = parseInt(year/10, 10) * 10;
21651 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21653 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21656 for (var i = -1; i < 11; i++) {
21657 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21659 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21667 showMode: function(dir)
21670 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21673 Roo.each(this.picker().select('>div',true).elements, function(v){
21674 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21677 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21682 if(this.isInline) {
21686 this.picker().removeClass(['bottom', 'top']);
21688 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21690 * place to the top of element!
21694 this.picker().addClass('top');
21695 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21700 this.picker().addClass('bottom');
21702 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21705 parseDate : function(value)
21707 if(!value || value instanceof Date){
21710 var v = Date.parseDate(value, this.format);
21711 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21712 v = Date.parseDate(value, 'Y-m-d');
21714 if(!v && this.altFormats){
21715 if(!this.altFormatsArray){
21716 this.altFormatsArray = this.altFormats.split("|");
21718 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21719 v = Date.parseDate(value, this.altFormatsArray[i]);
21725 formatDate : function(date, fmt)
21727 return (!date || !(date instanceof Date)) ?
21728 date : date.dateFormat(fmt || this.format);
21731 onFocus : function()
21733 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21737 onBlur : function()
21739 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21741 var d = this.inputEl().getValue();
21748 showPopup : function()
21750 this.picker().show();
21754 this.fireEvent('showpopup', this, this.date);
21757 hidePopup : function()
21759 if(this.isInline) {
21762 this.picker().hide();
21763 this.viewMode = this.startViewMode;
21766 this.fireEvent('hidepopup', this, this.date);
21770 onMousedown: function(e)
21772 e.stopPropagation();
21773 e.preventDefault();
21778 Roo.bootstrap.DateField.superclass.keyup.call(this);
21782 setValue: function(v)
21784 if(this.fireEvent('beforeselect', this, v) !== false){
21785 var d = new Date(this.parseDate(v) ).clearTime();
21787 if(isNaN(d.getTime())){
21788 this.date = this.viewDate = '';
21789 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21793 v = this.formatDate(d);
21795 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21797 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21801 this.fireEvent('select', this, this.date);
21805 getValue: function()
21807 return this.formatDate(this.date);
21810 fireKey: function(e)
21812 if (!this.picker().isVisible()){
21813 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21819 var dateChanged = false,
21821 newDate, newViewDate;
21826 e.preventDefault();
21830 if (!this.keyboardNavigation) {
21833 dir = e.keyCode == 37 ? -1 : 1;
21836 newDate = this.moveYear(this.date, dir);
21837 newViewDate = this.moveYear(this.viewDate, dir);
21838 } else if (e.shiftKey){
21839 newDate = this.moveMonth(this.date, dir);
21840 newViewDate = this.moveMonth(this.viewDate, dir);
21842 newDate = new Date(this.date);
21843 newDate.setUTCDate(this.date.getUTCDate() + dir);
21844 newViewDate = new Date(this.viewDate);
21845 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21847 if (this.dateWithinRange(newDate)){
21848 this.date = newDate;
21849 this.viewDate = newViewDate;
21850 this.setValue(this.formatDate(this.date));
21852 e.preventDefault();
21853 dateChanged = true;
21858 if (!this.keyboardNavigation) {
21861 dir = e.keyCode == 38 ? -1 : 1;
21863 newDate = this.moveYear(this.date, dir);
21864 newViewDate = this.moveYear(this.viewDate, dir);
21865 } else if (e.shiftKey){
21866 newDate = this.moveMonth(this.date, dir);
21867 newViewDate = this.moveMonth(this.viewDate, dir);
21869 newDate = new Date(this.date);
21870 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21871 newViewDate = new Date(this.viewDate);
21872 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21874 if (this.dateWithinRange(newDate)){
21875 this.date = newDate;
21876 this.viewDate = newViewDate;
21877 this.setValue(this.formatDate(this.date));
21879 e.preventDefault();
21880 dateChanged = true;
21884 this.setValue(this.formatDate(this.date));
21886 e.preventDefault();
21889 this.setValue(this.formatDate(this.date));
21903 onClick: function(e)
21905 e.stopPropagation();
21906 e.preventDefault();
21908 var target = e.getTarget();
21910 if(target.nodeName.toLowerCase() === 'i'){
21911 target = Roo.get(target).dom.parentNode;
21914 var nodeName = target.nodeName;
21915 var className = target.className;
21916 var html = target.innerHTML;
21917 //Roo.log(nodeName);
21919 switch(nodeName.toLowerCase()) {
21921 switch(className) {
21927 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21928 switch(this.viewMode){
21930 this.viewDate = this.moveMonth(this.viewDate, dir);
21934 this.viewDate = this.moveYear(this.viewDate, dir);
21940 var date = new Date();
21941 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21943 this.setValue(this.formatDate(this.date));
21950 if (className.indexOf('disabled') < 0) {
21951 this.viewDate.setUTCDate(1);
21952 if (className.indexOf('month') > -1) {
21953 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21955 var year = parseInt(html, 10) || 0;
21956 this.viewDate.setUTCFullYear(year);
21960 if(this.singleMode){
21961 this.setValue(this.formatDate(this.viewDate));
21972 //Roo.log(className);
21973 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21974 var day = parseInt(html, 10) || 1;
21975 var year = (this.viewDate || new Date()).getUTCFullYear(),
21976 month = (this.viewDate || new Date()).getUTCMonth();
21978 if (className.indexOf('old') > -1) {
21985 } else if (className.indexOf('new') > -1) {
21993 //Roo.log([year,month,day]);
21994 this.date = this.UTCDate(year, month, day,0,0,0,0);
21995 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21997 //Roo.log(this.formatDate(this.date));
21998 this.setValue(this.formatDate(this.date));
22005 setStartDate: function(startDate)
22007 this.startDate = startDate || -Infinity;
22008 if (this.startDate !== -Infinity) {
22009 this.startDate = this.parseDate(this.startDate);
22012 this.updateNavArrows();
22015 setEndDate: function(endDate)
22017 this.endDate = endDate || Infinity;
22018 if (this.endDate !== Infinity) {
22019 this.endDate = this.parseDate(this.endDate);
22022 this.updateNavArrows();
22025 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22027 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22028 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22029 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22031 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22032 return parseInt(d, 10);
22035 this.updateNavArrows();
22038 updateNavArrows: function()
22040 if(this.singleMode){
22044 var d = new Date(this.viewDate),
22045 year = d.getUTCFullYear(),
22046 month = d.getUTCMonth();
22048 Roo.each(this.picker().select('.prev', true).elements, function(v){
22050 switch (this.viewMode) {
22053 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22059 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22066 Roo.each(this.picker().select('.next', true).elements, function(v){
22068 switch (this.viewMode) {
22071 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22077 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22085 moveMonth: function(date, dir)
22090 var new_date = new Date(date.valueOf()),
22091 day = new_date.getUTCDate(),
22092 month = new_date.getUTCMonth(),
22093 mag = Math.abs(dir),
22095 dir = dir > 0 ? 1 : -1;
22098 // If going back one month, make sure month is not current month
22099 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22101 return new_date.getUTCMonth() == month;
22103 // If going forward one month, make sure month is as expected
22104 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22106 return new_date.getUTCMonth() != new_month;
22108 new_month = month + dir;
22109 new_date.setUTCMonth(new_month);
22110 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22111 if (new_month < 0 || new_month > 11) {
22112 new_month = (new_month + 12) % 12;
22115 // For magnitudes >1, move one month at a time...
22116 for (var i=0; i<mag; i++) {
22117 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22118 new_date = this.moveMonth(new_date, dir);
22120 // ...then reset the day, keeping it in the new month
22121 new_month = new_date.getUTCMonth();
22122 new_date.setUTCDate(day);
22124 return new_month != new_date.getUTCMonth();
22127 // Common date-resetting loop -- if date is beyond end of month, make it
22130 new_date.setUTCDate(--day);
22131 new_date.setUTCMonth(new_month);
22136 moveYear: function(date, dir)
22138 return this.moveMonth(date, dir*12);
22141 dateWithinRange: function(date)
22143 return date >= this.startDate && date <= this.endDate;
22149 this.picker().remove();
22152 validateValue : function(value)
22154 if(this.getVisibilityEl().hasClass('hidden')){
22158 if(value.length < 1) {
22159 if(this.allowBlank){
22165 if(value.length < this.minLength){
22168 if(value.length > this.maxLength){
22172 var vt = Roo.form.VTypes;
22173 if(!vt[this.vtype](value, this)){
22177 if(typeof this.validator == "function"){
22178 var msg = this.validator(value);
22184 if(this.regex && !this.regex.test(value)){
22188 if(typeof(this.parseDate(value)) == 'undefined'){
22192 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22196 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22206 this.date = this.viewDate = '';
22208 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22213 Roo.apply(Roo.bootstrap.DateField, {
22224 html: '<i class="fa fa-arrow-left"/>'
22234 html: '<i class="fa fa-arrow-right"/>'
22276 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22277 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22278 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22279 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22280 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22293 navFnc: 'FullYear',
22298 navFnc: 'FullYear',
22303 Roo.apply(Roo.bootstrap.DateField, {
22307 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22311 cls: 'datepicker-days',
22315 cls: 'table-condensed',
22317 Roo.bootstrap.DateField.head,
22321 Roo.bootstrap.DateField.footer
22328 cls: 'datepicker-months',
22332 cls: 'table-condensed',
22334 Roo.bootstrap.DateField.head,
22335 Roo.bootstrap.DateField.content,
22336 Roo.bootstrap.DateField.footer
22343 cls: 'datepicker-years',
22347 cls: 'table-condensed',
22349 Roo.bootstrap.DateField.head,
22350 Roo.bootstrap.DateField.content,
22351 Roo.bootstrap.DateField.footer
22370 * @class Roo.bootstrap.TimeField
22371 * @extends Roo.bootstrap.Input
22372 * Bootstrap DateField class
22376 * Create a new TimeField
22377 * @param {Object} config The config object
22380 Roo.bootstrap.TimeField = function(config){
22381 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22385 * Fires when this field show.
22386 * @param {Roo.bootstrap.DateField} thisthis
22387 * @param {Mixed} date The date value
22392 * Fires when this field hide.
22393 * @param {Roo.bootstrap.DateField} this
22394 * @param {Mixed} date The date value
22399 * Fires when select a date.
22400 * @param {Roo.bootstrap.DateField} this
22401 * @param {Mixed} date The date value
22407 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22410 * @cfg {String} format
22411 * The default time format string which can be overriden for localization support. The format must be
22412 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22416 getAutoCreate : function()
22418 this.after = '<i class="fa far fa-clock"></i>';
22419 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22423 onRender: function(ct, position)
22426 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22428 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22430 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22432 this.pop = this.picker().select('>.datepicker-time',true).first();
22433 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22435 this.picker().on('mousedown', this.onMousedown, this);
22436 this.picker().on('click', this.onClick, this);
22438 this.picker().addClass('datepicker-dropdown');
22443 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22444 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22445 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22446 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22447 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22448 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22452 fireKey: function(e){
22453 if (!this.picker().isVisible()){
22454 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22460 e.preventDefault();
22468 this.onTogglePeriod();
22471 this.onIncrementMinutes();
22474 this.onDecrementMinutes();
22483 onClick: function(e) {
22484 e.stopPropagation();
22485 e.preventDefault();
22488 picker : function()
22490 return this.pickerEl;
22493 fillTime: function()
22495 var time = this.pop.select('tbody', true).first();
22497 time.dom.innerHTML = '';
22512 cls: 'hours-up fa fas fa-chevron-up'
22532 cls: 'minutes-up fa fas fa-chevron-up'
22553 cls: 'timepicker-hour',
22568 cls: 'timepicker-minute',
22583 cls: 'btn btn-primary period',
22605 cls: 'hours-down fa fas fa-chevron-down'
22625 cls: 'minutes-down fa fas fa-chevron-down'
22643 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22650 var hours = this.time.getHours();
22651 var minutes = this.time.getMinutes();
22664 hours = hours - 12;
22668 hours = '0' + hours;
22672 minutes = '0' + minutes;
22675 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22676 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22677 this.pop.select('button', true).first().dom.innerHTML = period;
22683 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22685 var cls = ['bottom'];
22687 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22694 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22698 //this.picker().setXY(20000,20000);
22699 this.picker().addClass(cls.join('-'));
22703 Roo.each(cls, function(c){
22708 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22709 //_this.picker().setTop(_this.inputEl().getHeight());
22713 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22715 //_this.picker().setTop(0 - _this.picker().getHeight());
22720 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22724 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22732 onFocus : function()
22734 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22738 onBlur : function()
22740 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22746 this.picker().show();
22751 this.fireEvent('show', this, this.date);
22756 this.picker().hide();
22759 this.fireEvent('hide', this, this.date);
22762 setTime : function()
22765 this.setValue(this.time.format(this.format));
22767 this.fireEvent('select', this, this.date);
22772 onMousedown: function(e){
22773 e.stopPropagation();
22774 e.preventDefault();
22777 onIncrementHours: function()
22779 Roo.log('onIncrementHours');
22780 this.time = this.time.add(Date.HOUR, 1);
22785 onDecrementHours: function()
22787 Roo.log('onDecrementHours');
22788 this.time = this.time.add(Date.HOUR, -1);
22792 onIncrementMinutes: function()
22794 Roo.log('onIncrementMinutes');
22795 this.time = this.time.add(Date.MINUTE, 1);
22799 onDecrementMinutes: function()
22801 Roo.log('onDecrementMinutes');
22802 this.time = this.time.add(Date.MINUTE, -1);
22806 onTogglePeriod: function()
22808 Roo.log('onTogglePeriod');
22809 this.time = this.time.add(Date.HOUR, 12);
22817 Roo.apply(Roo.bootstrap.TimeField, {
22821 cls: 'datepicker dropdown-menu',
22825 cls: 'datepicker-time',
22829 cls: 'table-condensed',
22858 cls: 'btn btn-info ok',
22886 * @class Roo.bootstrap.MonthField
22887 * @extends Roo.bootstrap.Input
22888 * Bootstrap MonthField class
22890 * @cfg {String} language default en
22893 * Create a new MonthField
22894 * @param {Object} config The config object
22897 Roo.bootstrap.MonthField = function(config){
22898 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22903 * Fires when this field show.
22904 * @param {Roo.bootstrap.MonthField} this
22905 * @param {Mixed} date The date value
22910 * Fires when this field hide.
22911 * @param {Roo.bootstrap.MonthField} this
22912 * @param {Mixed} date The date value
22917 * Fires when select a date.
22918 * @param {Roo.bootstrap.MonthField} this
22919 * @param {String} oldvalue The old value
22920 * @param {String} newvalue The new value
22926 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22928 onRender: function(ct, position)
22931 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22933 this.language = this.language || 'en';
22934 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22935 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22937 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22938 this.isInline = false;
22939 this.isInput = true;
22940 this.component = this.el.select('.add-on', true).first() || false;
22941 this.component = (this.component && this.component.length === 0) ? false : this.component;
22942 this.hasInput = this.component && this.inputEL().length;
22944 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22946 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22948 this.picker().on('mousedown', this.onMousedown, this);
22949 this.picker().on('click', this.onClick, this);
22951 this.picker().addClass('datepicker-dropdown');
22953 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22954 v.setStyle('width', '189px');
22961 if(this.isInline) {
22967 setValue: function(v, suppressEvent)
22969 var o = this.getValue();
22971 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22975 if(suppressEvent !== true){
22976 this.fireEvent('select', this, o, v);
22981 getValue: function()
22986 onClick: function(e)
22988 e.stopPropagation();
22989 e.preventDefault();
22991 var target = e.getTarget();
22993 if(target.nodeName.toLowerCase() === 'i'){
22994 target = Roo.get(target).dom.parentNode;
22997 var nodeName = target.nodeName;
22998 var className = target.className;
22999 var html = target.innerHTML;
23001 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23005 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23007 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23013 picker : function()
23015 return this.pickerEl;
23018 fillMonths: function()
23021 var months = this.picker().select('>.datepicker-months td', true).first();
23023 months.dom.innerHTML = '';
23029 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23032 months.createChild(month);
23041 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23042 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23045 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23046 e.removeClass('active');
23048 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23049 e.addClass('active');
23056 if(this.isInline) {
23060 this.picker().removeClass(['bottom', 'top']);
23062 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23064 * place to the top of element!
23068 this.picker().addClass('top');
23069 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23074 this.picker().addClass('bottom');
23076 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23079 onFocus : function()
23081 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23085 onBlur : function()
23087 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23089 var d = this.inputEl().getValue();
23098 this.picker().show();
23099 this.picker().select('>.datepicker-months', true).first().show();
23103 this.fireEvent('show', this, this.date);
23108 if(this.isInline) {
23111 this.picker().hide();
23112 this.fireEvent('hide', this, this.date);
23116 onMousedown: function(e)
23118 e.stopPropagation();
23119 e.preventDefault();
23124 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23128 fireKey: function(e)
23130 if (!this.picker().isVisible()){
23131 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23142 e.preventDefault();
23146 dir = e.keyCode == 37 ? -1 : 1;
23148 this.vIndex = this.vIndex + dir;
23150 if(this.vIndex < 0){
23154 if(this.vIndex > 11){
23158 if(isNaN(this.vIndex)){
23162 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23168 dir = e.keyCode == 38 ? -1 : 1;
23170 this.vIndex = this.vIndex + dir * 4;
23172 if(this.vIndex < 0){
23176 if(this.vIndex > 11){
23180 if(isNaN(this.vIndex)){
23184 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23189 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23190 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23194 e.preventDefault();
23197 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23198 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23214 this.picker().remove();
23219 Roo.apply(Roo.bootstrap.MonthField, {
23238 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23239 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23244 Roo.apply(Roo.bootstrap.MonthField, {
23248 cls: 'datepicker dropdown-menu roo-dynamic',
23252 cls: 'datepicker-months',
23256 cls: 'table-condensed',
23258 Roo.bootstrap.DateField.content
23278 * @class Roo.bootstrap.CheckBox
23279 * @extends Roo.bootstrap.Input
23280 * Bootstrap CheckBox class
23282 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23283 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23284 * @cfg {String} boxLabel The text that appears beside the checkbox
23285 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23286 * @cfg {Boolean} checked initnal the element
23287 * @cfg {Boolean} inline inline the element (default false)
23288 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23289 * @cfg {String} tooltip label tooltip
23292 * Create a new CheckBox
23293 * @param {Object} config The config object
23296 Roo.bootstrap.CheckBox = function(config){
23297 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23302 * Fires when the element is checked or unchecked.
23303 * @param {Roo.bootstrap.CheckBox} this This input
23304 * @param {Boolean} checked The new checked value
23309 * Fires when the element is click.
23310 * @param {Roo.bootstrap.CheckBox} this This input
23317 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23319 inputType: 'checkbox',
23328 // checkbox success does not make any sense really..
23333 getAutoCreate : function()
23335 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23341 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23344 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23350 type : this.inputType,
23351 value : this.inputValue,
23352 cls : 'roo-' + this.inputType, //'form-box',
23353 placeholder : this.placeholder || ''
23357 if(this.inputType != 'radio'){
23361 cls : 'roo-hidden-value',
23362 value : this.checked ? this.inputValue : this.valueOff
23367 if (this.weight) { // Validity check?
23368 cfg.cls += " " + this.inputType + "-" + this.weight;
23371 if (this.disabled) {
23372 input.disabled=true;
23376 input.checked = this.checked;
23381 input.name = this.name;
23383 if(this.inputType != 'radio'){
23384 hidden.name = this.name;
23385 input.name = '_hidden_' + this.name;
23390 input.cls += ' input-' + this.size;
23395 ['xs','sm','md','lg'].map(function(size){
23396 if (settings[size]) {
23397 cfg.cls += ' col-' + size + '-' + settings[size];
23401 var inputblock = input;
23403 if (this.before || this.after) {
23406 cls : 'input-group',
23411 inputblock.cn.push({
23413 cls : 'input-group-addon',
23418 inputblock.cn.push(input);
23420 if(this.inputType != 'radio'){
23421 inputblock.cn.push(hidden);
23425 inputblock.cn.push({
23427 cls : 'input-group-addon',
23433 var boxLabelCfg = false;
23439 //'for': id, // box label is handled by onclick - so no for...
23441 html: this.boxLabel
23444 boxLabelCfg.tooltip = this.tooltip;
23450 if (align ==='left' && this.fieldLabel.length) {
23451 // Roo.log("left and has label");
23456 cls : 'control-label',
23457 html : this.fieldLabel
23468 cfg.cn[1].cn.push(boxLabelCfg);
23471 if(this.labelWidth > 12){
23472 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23475 if(this.labelWidth < 13 && this.labelmd == 0){
23476 this.labelmd = this.labelWidth;
23479 if(this.labellg > 0){
23480 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23481 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23484 if(this.labelmd > 0){
23485 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23486 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23489 if(this.labelsm > 0){
23490 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23491 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23494 if(this.labelxs > 0){
23495 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23496 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23499 } else if ( this.fieldLabel.length) {
23500 // Roo.log(" label");
23504 tag: this.boxLabel ? 'span' : 'label',
23506 cls: 'control-label box-input-label',
23507 //cls : 'input-group-addon',
23508 html : this.fieldLabel
23515 cfg.cn.push(boxLabelCfg);
23520 // Roo.log(" no label && no align");
23521 cfg.cn = [ inputblock ] ;
23523 cfg.cn.push(boxLabelCfg);
23531 if(this.inputType != 'radio'){
23532 cfg.cn.push(hidden);
23540 * return the real input element.
23542 inputEl: function ()
23544 return this.el.select('input.roo-' + this.inputType,true).first();
23546 hiddenEl: function ()
23548 return this.el.select('input.roo-hidden-value',true).first();
23551 labelEl: function()
23553 return this.el.select('label.control-label',true).first();
23555 /* depricated... */
23559 return this.labelEl();
23562 boxLabelEl: function()
23564 return this.el.select('label.box-label',true).first();
23567 initEvents : function()
23569 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23571 this.inputEl().on('click', this.onClick, this);
23573 if (this.boxLabel) {
23574 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23577 this.startValue = this.getValue();
23580 Roo.bootstrap.CheckBox.register(this);
23584 onClick : function(e)
23586 if(this.fireEvent('click', this, e) !== false){
23587 this.setChecked(!this.checked);
23592 setChecked : function(state,suppressEvent)
23594 this.startValue = this.getValue();
23596 if(this.inputType == 'radio'){
23598 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23599 e.dom.checked = false;
23602 this.inputEl().dom.checked = true;
23604 this.inputEl().dom.value = this.inputValue;
23606 if(suppressEvent !== true){
23607 this.fireEvent('check', this, true);
23615 this.checked = state;
23617 this.inputEl().dom.checked = state;
23620 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23622 if(suppressEvent !== true){
23623 this.fireEvent('check', this, state);
23629 getValue : function()
23631 if(this.inputType == 'radio'){
23632 return this.getGroupValue();
23635 return this.hiddenEl().dom.value;
23639 getGroupValue : function()
23641 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23645 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23648 setValue : function(v,suppressEvent)
23650 if(this.inputType == 'radio'){
23651 this.setGroupValue(v, suppressEvent);
23655 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23660 setGroupValue : function(v, suppressEvent)
23662 this.startValue = this.getValue();
23664 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23665 e.dom.checked = false;
23667 if(e.dom.value == v){
23668 e.dom.checked = true;
23672 if(suppressEvent !== true){
23673 this.fireEvent('check', this, true);
23681 validate : function()
23683 if(this.getVisibilityEl().hasClass('hidden')){
23689 (this.inputType == 'radio' && this.validateRadio()) ||
23690 (this.inputType == 'checkbox' && this.validateCheckbox())
23696 this.markInvalid();
23700 validateRadio : function()
23702 if(this.getVisibilityEl().hasClass('hidden')){
23706 if(this.allowBlank){
23712 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23713 if(!e.dom.checked){
23725 validateCheckbox : function()
23728 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23729 //return (this.getValue() == this.inputValue) ? true : false;
23732 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23740 for(var i in group){
23741 if(group[i].el.isVisible(true)){
23749 for(var i in group){
23754 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23761 * Mark this field as valid
23763 markValid : function()
23767 this.fireEvent('valid', this);
23769 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23772 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23779 if(this.inputType == 'radio'){
23780 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23781 var fg = e.findParent('.form-group', false, true);
23782 if (Roo.bootstrap.version == 3) {
23783 fg.removeClass([_this.invalidClass, _this.validClass]);
23784 fg.addClass(_this.validClass);
23786 fg.removeClass(['is-valid', 'is-invalid']);
23787 fg.addClass('is-valid');
23795 var fg = this.el.findParent('.form-group', false, true);
23796 if (Roo.bootstrap.version == 3) {
23797 fg.removeClass([this.invalidClass, this.validClass]);
23798 fg.addClass(this.validClass);
23800 fg.removeClass(['is-valid', 'is-invalid']);
23801 fg.addClass('is-valid');
23806 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23812 for(var i in group){
23813 var fg = group[i].el.findParent('.form-group', false, true);
23814 if (Roo.bootstrap.version == 3) {
23815 fg.removeClass([this.invalidClass, this.validClass]);
23816 fg.addClass(this.validClass);
23818 fg.removeClass(['is-valid', 'is-invalid']);
23819 fg.addClass('is-valid');
23825 * Mark this field as invalid
23826 * @param {String} msg The validation message
23828 markInvalid : function(msg)
23830 if(this.allowBlank){
23836 this.fireEvent('invalid', this, msg);
23838 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23841 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23845 label.markInvalid();
23848 if(this.inputType == 'radio'){
23850 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23851 var fg = e.findParent('.form-group', false, true);
23852 if (Roo.bootstrap.version == 3) {
23853 fg.removeClass([_this.invalidClass, _this.validClass]);
23854 fg.addClass(_this.invalidClass);
23856 fg.removeClass(['is-invalid', 'is-valid']);
23857 fg.addClass('is-invalid');
23865 var fg = this.el.findParent('.form-group', false, true);
23866 if (Roo.bootstrap.version == 3) {
23867 fg.removeClass([_this.invalidClass, _this.validClass]);
23868 fg.addClass(_this.invalidClass);
23870 fg.removeClass(['is-invalid', 'is-valid']);
23871 fg.addClass('is-invalid');
23876 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23882 for(var i in group){
23883 var fg = group[i].el.findParent('.form-group', false, true);
23884 if (Roo.bootstrap.version == 3) {
23885 fg.removeClass([_this.invalidClass, _this.validClass]);
23886 fg.addClass(_this.invalidClass);
23888 fg.removeClass(['is-invalid', 'is-valid']);
23889 fg.addClass('is-invalid');
23895 clearInvalid : function()
23897 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23899 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23901 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23903 if (label && label.iconEl) {
23904 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23905 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23909 disable : function()
23911 if(this.inputType != 'radio'){
23912 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23919 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23920 _this.getActionEl().addClass(this.disabledClass);
23921 e.dom.disabled = true;
23925 this.disabled = true;
23926 this.fireEvent("disable", this);
23930 enable : function()
23932 if(this.inputType != 'radio'){
23933 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23940 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23941 _this.getActionEl().removeClass(this.disabledClass);
23942 e.dom.disabled = false;
23946 this.disabled = false;
23947 this.fireEvent("enable", this);
23951 setBoxLabel : function(v)
23956 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23962 Roo.apply(Roo.bootstrap.CheckBox, {
23967 * register a CheckBox Group
23968 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23970 register : function(checkbox)
23972 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23973 this.groups[checkbox.groupId] = {};
23976 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23980 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23984 * fetch a CheckBox Group based on the group ID
23985 * @param {string} the group ID
23986 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23988 get: function(groupId) {
23989 if (typeof(this.groups[groupId]) == 'undefined') {
23993 return this.groups[groupId] ;
24006 * @class Roo.bootstrap.Radio
24007 * @extends Roo.bootstrap.Component
24008 * Bootstrap Radio class
24009 * @cfg {String} boxLabel - the label associated
24010 * @cfg {String} value - the value of radio
24013 * Create a new Radio
24014 * @param {Object} config The config object
24016 Roo.bootstrap.Radio = function(config){
24017 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24021 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24027 getAutoCreate : function()
24031 cls : 'form-group radio',
24036 html : this.boxLabel
24044 initEvents : function()
24046 this.parent().register(this);
24048 this.el.on('click', this.onClick, this);
24052 onClick : function(e)
24054 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24055 this.setChecked(true);
24059 setChecked : function(state, suppressEvent)
24061 this.parent().setValue(this.value, suppressEvent);
24065 setBoxLabel : function(v)
24070 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24085 * @class Roo.bootstrap.SecurePass
24086 * @extends Roo.bootstrap.Input
24087 * Bootstrap SecurePass class
24091 * Create a new SecurePass
24092 * @param {Object} config The config object
24095 Roo.bootstrap.SecurePass = function (config) {
24096 // these go here, so the translation tool can replace them..
24098 PwdEmpty: "Please type a password, and then retype it to confirm.",
24099 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24100 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24101 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24102 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24103 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24104 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24105 TooWeak: "Your password is Too Weak."
24107 this.meterLabel = "Password strength:";
24108 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24109 this.meterClass = [
24110 "roo-password-meter-tooweak",
24111 "roo-password-meter-weak",
24112 "roo-password-meter-medium",
24113 "roo-password-meter-strong",
24114 "roo-password-meter-grey"
24119 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24122 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24124 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24126 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24127 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24128 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24129 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24130 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24131 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24132 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24142 * @cfg {String/Object} Label for the strength meter (defaults to
24143 * 'Password strength:')
24148 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24149 * ['Weak', 'Medium', 'Strong'])
24152 pwdStrengths: false,
24165 initEvents: function ()
24167 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24169 if (this.el.is('input[type=password]') && Roo.isSafari) {
24170 this.el.on('keydown', this.SafariOnKeyDown, this);
24173 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24176 onRender: function (ct, position)
24178 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24179 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24180 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24182 this.trigger.createChild({
24187 cls: 'roo-password-meter-grey col-xs-12',
24190 //width: this.meterWidth + 'px'
24194 cls: 'roo-password-meter-text'
24200 if (this.hideTrigger) {
24201 this.trigger.setDisplayed(false);
24203 this.setSize(this.width || '', this.height || '');
24206 onDestroy: function ()
24208 if (this.trigger) {
24209 this.trigger.removeAllListeners();
24210 this.trigger.remove();
24213 this.wrap.remove();
24215 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24218 checkStrength: function ()
24220 var pwd = this.inputEl().getValue();
24221 if (pwd == this._lastPwd) {
24226 if (this.ClientSideStrongPassword(pwd)) {
24228 } else if (this.ClientSideMediumPassword(pwd)) {
24230 } else if (this.ClientSideWeakPassword(pwd)) {
24236 Roo.log('strength1: ' + strength);
24238 //var pm = this.trigger.child('div/div/div').dom;
24239 var pm = this.trigger.child('div/div');
24240 pm.removeClass(this.meterClass);
24241 pm.addClass(this.meterClass[strength]);
24244 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24246 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24248 this._lastPwd = pwd;
24252 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24254 this._lastPwd = '';
24256 var pm = this.trigger.child('div/div');
24257 pm.removeClass(this.meterClass);
24258 pm.addClass('roo-password-meter-grey');
24261 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24264 this.inputEl().dom.type='password';
24267 validateValue: function (value)
24269 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24272 if (value.length == 0) {
24273 if (this.allowBlank) {
24274 this.clearInvalid();
24278 this.markInvalid(this.errors.PwdEmpty);
24279 this.errorMsg = this.errors.PwdEmpty;
24287 if (!value.match(/[\x21-\x7e]+/)) {
24288 this.markInvalid(this.errors.PwdBadChar);
24289 this.errorMsg = this.errors.PwdBadChar;
24292 if (value.length < 6) {
24293 this.markInvalid(this.errors.PwdShort);
24294 this.errorMsg = this.errors.PwdShort;
24297 if (value.length > 16) {
24298 this.markInvalid(this.errors.PwdLong);
24299 this.errorMsg = this.errors.PwdLong;
24303 if (this.ClientSideStrongPassword(value)) {
24305 } else if (this.ClientSideMediumPassword(value)) {
24307 } else if (this.ClientSideWeakPassword(value)) {
24314 if (strength < 2) {
24315 //this.markInvalid(this.errors.TooWeak);
24316 this.errorMsg = this.errors.TooWeak;
24321 console.log('strength2: ' + strength);
24323 //var pm = this.trigger.child('div/div/div').dom;
24325 var pm = this.trigger.child('div/div');
24326 pm.removeClass(this.meterClass);
24327 pm.addClass(this.meterClass[strength]);
24329 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24331 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24333 this.errorMsg = '';
24337 CharacterSetChecks: function (type)
24340 this.fResult = false;
24343 isctype: function (character, type)
24346 case this.kCapitalLetter:
24347 if (character >= 'A' && character <= 'Z') {
24352 case this.kSmallLetter:
24353 if (character >= 'a' && character <= 'z') {
24359 if (character >= '0' && character <= '9') {
24364 case this.kPunctuation:
24365 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24376 IsLongEnough: function (pwd, size)
24378 return !(pwd == null || isNaN(size) || pwd.length < size);
24381 SpansEnoughCharacterSets: function (word, nb)
24383 if (!this.IsLongEnough(word, nb))
24388 var characterSetChecks = new Array(
24389 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24390 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24393 for (var index = 0; index < word.length; ++index) {
24394 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24395 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24396 characterSetChecks[nCharSet].fResult = true;
24403 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24404 if (characterSetChecks[nCharSet].fResult) {
24409 if (nCharSets < nb) {
24415 ClientSideStrongPassword: function (pwd)
24417 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24420 ClientSideMediumPassword: function (pwd)
24422 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24425 ClientSideWeakPassword: function (pwd)
24427 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24430 })//<script type="text/javascript">
24433 * Based Ext JS Library 1.1.1
24434 * Copyright(c) 2006-2007, Ext JS, LLC.
24440 * @class Roo.HtmlEditorCore
24441 * @extends Roo.Component
24442 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24444 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24447 Roo.HtmlEditorCore = function(config){
24450 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24455 * @event initialize
24456 * Fires when the editor is fully initialized (including the iframe)
24457 * @param {Roo.HtmlEditorCore} this
24462 * Fires when the editor is first receives the focus. Any insertion must wait
24463 * until after this event.
24464 * @param {Roo.HtmlEditorCore} this
24468 * @event beforesync
24469 * Fires before the textarea is updated with content from the editor iframe. Return false
24470 * to cancel the sync.
24471 * @param {Roo.HtmlEditorCore} this
24472 * @param {String} html
24476 * @event beforepush
24477 * Fires before the iframe editor is updated with content from the textarea. Return false
24478 * to cancel the push.
24479 * @param {Roo.HtmlEditorCore} this
24480 * @param {String} html
24485 * Fires when the textarea is updated with content from the editor iframe.
24486 * @param {Roo.HtmlEditorCore} this
24487 * @param {String} html
24492 * Fires when the iframe editor is updated with content from the textarea.
24493 * @param {Roo.HtmlEditorCore} this
24494 * @param {String} html
24499 * @event editorevent
24500 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24501 * @param {Roo.HtmlEditorCore} this
24507 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24509 // defaults : white / black...
24510 this.applyBlacklists();
24517 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24521 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24527 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24532 * @cfg {Number} height (in pixels)
24536 * @cfg {Number} width (in pixels)
24541 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24544 stylesheets: false,
24549 // private properties
24550 validationEvent : false,
24552 initialized : false,
24554 sourceEditMode : false,
24555 onFocus : Roo.emptyFn,
24557 hideMode:'offsets',
24561 // blacklist + whitelisted elements..
24568 * Protected method that will not generally be called directly. It
24569 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24570 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24572 getDocMarkup : function(){
24576 // inherit styels from page...??
24577 if (this.stylesheets === false) {
24579 Roo.get(document.head).select('style').each(function(node) {
24580 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24583 Roo.get(document.head).select('link').each(function(node) {
24584 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24587 } else if (!this.stylesheets.length) {
24589 st = '<style type="text/css">' +
24590 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24593 for (var i in this.stylesheets) {
24594 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24599 st += '<style type="text/css">' +
24600 'IMG { cursor: pointer } ' +
24603 var cls = 'roo-htmleditor-body';
24605 if(this.bodyCls.length){
24606 cls += ' ' + this.bodyCls;
24609 return '<html><head>' + st +
24610 //<style type="text/css">' +
24611 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24613 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24617 onRender : function(ct, position)
24620 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24621 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24624 this.el.dom.style.border = '0 none';
24625 this.el.dom.setAttribute('tabIndex', -1);
24626 this.el.addClass('x-hidden hide');
24630 if(Roo.isIE){ // fix IE 1px bogus margin
24631 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24635 this.frameId = Roo.id();
24639 var iframe = this.owner.wrap.createChild({
24641 cls: 'form-control', // bootstrap..
24643 name: this.frameId,
24644 frameBorder : 'no',
24645 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24650 this.iframe = iframe.dom;
24652 this.assignDocWin();
24654 this.doc.designMode = 'on';
24657 this.doc.write(this.getDocMarkup());
24661 var task = { // must defer to wait for browser to be ready
24663 //console.log("run task?" + this.doc.readyState);
24664 this.assignDocWin();
24665 if(this.doc.body || this.doc.readyState == 'complete'){
24667 this.doc.designMode="on";
24671 Roo.TaskMgr.stop(task);
24672 this.initEditor.defer(10, this);
24679 Roo.TaskMgr.start(task);
24684 onResize : function(w, h)
24686 Roo.log('resize: ' +w + ',' + h );
24687 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24691 if(typeof w == 'number'){
24693 this.iframe.style.width = w + 'px';
24695 if(typeof h == 'number'){
24697 this.iframe.style.height = h + 'px';
24699 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24706 * Toggles the editor between standard and source edit mode.
24707 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24709 toggleSourceEdit : function(sourceEditMode){
24711 this.sourceEditMode = sourceEditMode === true;
24713 if(this.sourceEditMode){
24715 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24718 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24719 //this.iframe.className = '';
24722 //this.setSize(this.owner.wrap.getSize());
24723 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24730 * Protected method that will not generally be called directly. If you need/want
24731 * custom HTML cleanup, this is the method you should override.
24732 * @param {String} html The HTML to be cleaned
24733 * return {String} The cleaned HTML
24735 cleanHtml : function(html){
24736 html = String(html);
24737 if(html.length > 5){
24738 if(Roo.isSafari){ // strip safari nonsense
24739 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24742 if(html == ' '){
24749 * HTML Editor -> Textarea
24750 * Protected method that will not generally be called directly. Syncs the contents
24751 * of the editor iframe with the textarea.
24753 syncValue : function(){
24754 if(this.initialized){
24755 var bd = (this.doc.body || this.doc.documentElement);
24756 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24757 var html = bd.innerHTML;
24759 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24760 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24762 html = '<div style="'+m[0]+'">' + html + '</div>';
24765 html = this.cleanHtml(html);
24766 // fix up the special chars.. normaly like back quotes in word...
24767 // however we do not want to do this with chinese..
24768 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24770 var cc = match.charCodeAt();
24772 // Get the character value, handling surrogate pairs
24773 if (match.length == 2) {
24774 // It's a surrogate pair, calculate the Unicode code point
24775 var high = match.charCodeAt(0) - 0xD800;
24776 var low = match.charCodeAt(1) - 0xDC00;
24777 cc = (high * 0x400) + low + 0x10000;
24779 (cc >= 0x4E00 && cc < 0xA000 ) ||
24780 (cc >= 0x3400 && cc < 0x4E00 ) ||
24781 (cc >= 0xf900 && cc < 0xfb00 )
24786 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24787 return "&#" + cc + ";";
24794 if(this.owner.fireEvent('beforesync', this, html) !== false){
24795 this.el.dom.value = html;
24796 this.owner.fireEvent('sync', this, html);
24802 * Protected method that will not generally be called directly. Pushes the value of the textarea
24803 * into the iframe editor.
24805 pushValue : function(){
24806 if(this.initialized){
24807 var v = this.el.dom.value.trim();
24809 // if(v.length < 1){
24813 if(this.owner.fireEvent('beforepush', this, v) !== false){
24814 var d = (this.doc.body || this.doc.documentElement);
24816 this.cleanUpPaste();
24817 this.el.dom.value = d.innerHTML;
24818 this.owner.fireEvent('push', this, v);
24824 deferFocus : function(){
24825 this.focus.defer(10, this);
24829 focus : function(){
24830 if(this.win && !this.sourceEditMode){
24837 assignDocWin: function()
24839 var iframe = this.iframe;
24842 this.doc = iframe.contentWindow.document;
24843 this.win = iframe.contentWindow;
24845 // if (!Roo.get(this.frameId)) {
24848 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24849 // this.win = Roo.get(this.frameId).dom.contentWindow;
24851 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24855 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24856 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24861 initEditor : function(){
24862 //console.log("INIT EDITOR");
24863 this.assignDocWin();
24867 this.doc.designMode="on";
24869 this.doc.write(this.getDocMarkup());
24872 var dbody = (this.doc.body || this.doc.documentElement);
24873 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24874 // this copies styles from the containing element into thsi one..
24875 // not sure why we need all of this..
24876 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24878 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24879 //ss['background-attachment'] = 'fixed'; // w3c
24880 dbody.bgProperties = 'fixed'; // ie
24881 //Roo.DomHelper.applyStyles(dbody, ss);
24882 Roo.EventManager.on(this.doc, {
24883 //'mousedown': this.onEditorEvent,
24884 'mouseup': this.onEditorEvent,
24885 'dblclick': this.onEditorEvent,
24886 'click': this.onEditorEvent,
24887 'keyup': this.onEditorEvent,
24892 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24894 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24895 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24897 this.initialized = true;
24899 this.owner.fireEvent('initialize', this);
24904 onDestroy : function(){
24910 //for (var i =0; i < this.toolbars.length;i++) {
24911 // // fixme - ask toolbars for heights?
24912 // this.toolbars[i].onDestroy();
24915 //this.wrap.dom.innerHTML = '';
24916 //this.wrap.remove();
24921 onFirstFocus : function(){
24923 this.assignDocWin();
24926 this.activated = true;
24929 if(Roo.isGecko){ // prevent silly gecko errors
24931 var s = this.win.getSelection();
24932 if(!s.focusNode || s.focusNode.nodeType != 3){
24933 var r = s.getRangeAt(0);
24934 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24939 this.execCmd('useCSS', true);
24940 this.execCmd('styleWithCSS', false);
24943 this.owner.fireEvent('activate', this);
24947 adjustFont: function(btn){
24948 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24949 //if(Roo.isSafari){ // safari
24952 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24953 if(Roo.isSafari){ // safari
24954 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24955 v = (v < 10) ? 10 : v;
24956 v = (v > 48) ? 48 : v;
24957 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24962 v = Math.max(1, v+adjust);
24964 this.execCmd('FontSize', v );
24967 onEditorEvent : function(e)
24969 this.owner.fireEvent('editorevent', this, e);
24970 // this.updateToolbar();
24971 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24974 insertTag : function(tg)
24976 // could be a bit smarter... -> wrap the current selected tRoo..
24977 if (tg.toLowerCase() == 'span' ||
24978 tg.toLowerCase() == 'code' ||
24979 tg.toLowerCase() == 'sup' ||
24980 tg.toLowerCase() == 'sub'
24983 range = this.createRange(this.getSelection());
24984 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24985 wrappingNode.appendChild(range.extractContents());
24986 range.insertNode(wrappingNode);
24993 this.execCmd("formatblock", tg);
24997 insertText : function(txt)
25001 var range = this.createRange();
25002 range.deleteContents();
25003 //alert(Sender.getAttribute('label'));
25005 range.insertNode(this.doc.createTextNode(txt));
25011 * Executes a Midas editor command on the editor document and performs necessary focus and
25012 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25013 * @param {String} cmd The Midas command
25014 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25016 relayCmd : function(cmd, value){
25018 this.execCmd(cmd, value);
25019 this.owner.fireEvent('editorevent', this);
25020 //this.updateToolbar();
25021 this.owner.deferFocus();
25025 * Executes a Midas editor command directly on the editor document.
25026 * For visual commands, you should use {@link #relayCmd} instead.
25027 * <b>This should only be called after the editor is initialized.</b>
25028 * @param {String} cmd The Midas command
25029 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25031 execCmd : function(cmd, value){
25032 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25039 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25041 * @param {String} text | dom node..
25043 insertAtCursor : function(text)
25046 if(!this.activated){
25052 var r = this.doc.selection.createRange();
25063 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25067 // from jquery ui (MIT licenced)
25069 var win = this.win;
25071 if (win.getSelection && win.getSelection().getRangeAt) {
25072 range = win.getSelection().getRangeAt(0);
25073 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25074 range.insertNode(node);
25075 } else if (win.document.selection && win.document.selection.createRange) {
25076 // no firefox support
25077 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25078 win.document.selection.createRange().pasteHTML(txt);
25080 // no firefox support
25081 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25082 this.execCmd('InsertHTML', txt);
25091 mozKeyPress : function(e){
25093 var c = e.getCharCode(), cmd;
25096 c = String.fromCharCode(c).toLowerCase();
25110 this.cleanUpPaste.defer(100, this);
25118 e.preventDefault();
25126 fixKeys : function(){ // load time branching for fastest keydown performance
25128 return function(e){
25129 var k = e.getKey(), r;
25132 r = this.doc.selection.createRange();
25135 r.pasteHTML('    ');
25142 r = this.doc.selection.createRange();
25144 var target = r.parentElement();
25145 if(!target || target.tagName.toLowerCase() != 'li'){
25147 r.pasteHTML('<br />');
25153 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25154 this.cleanUpPaste.defer(100, this);
25160 }else if(Roo.isOpera){
25161 return function(e){
25162 var k = e.getKey();
25166 this.execCmd('InsertHTML','    ');
25169 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25170 this.cleanUpPaste.defer(100, this);
25175 }else if(Roo.isSafari){
25176 return function(e){
25177 var k = e.getKey();
25181 this.execCmd('InsertText','\t');
25185 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25186 this.cleanUpPaste.defer(100, this);
25194 getAllAncestors: function()
25196 var p = this.getSelectedNode();
25199 a.push(p); // push blank onto stack..
25200 p = this.getParentElement();
25204 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25208 a.push(this.doc.body);
25212 lastSelNode : false,
25215 getSelection : function()
25217 this.assignDocWin();
25218 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25221 getSelectedNode: function()
25223 // this may only work on Gecko!!!
25225 // should we cache this!!!!
25230 var range = this.createRange(this.getSelection()).cloneRange();
25233 var parent = range.parentElement();
25235 var testRange = range.duplicate();
25236 testRange.moveToElementText(parent);
25237 if (testRange.inRange(range)) {
25240 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25243 parent = parent.parentElement;
25248 // is ancestor a text element.
25249 var ac = range.commonAncestorContainer;
25250 if (ac.nodeType == 3) {
25251 ac = ac.parentNode;
25254 var ar = ac.childNodes;
25257 var other_nodes = [];
25258 var has_other_nodes = false;
25259 for (var i=0;i<ar.length;i++) {
25260 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25263 // fullly contained node.
25265 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25270 // probably selected..
25271 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25272 other_nodes.push(ar[i]);
25276 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25281 has_other_nodes = true;
25283 if (!nodes.length && other_nodes.length) {
25284 nodes= other_nodes;
25286 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25292 createRange: function(sel)
25294 // this has strange effects when using with
25295 // top toolbar - not sure if it's a great idea.
25296 //this.editor.contentWindow.focus();
25297 if (typeof sel != "undefined") {
25299 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25301 return this.doc.createRange();
25304 return this.doc.createRange();
25307 getParentElement: function()
25310 this.assignDocWin();
25311 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25313 var range = this.createRange(sel);
25316 var p = range.commonAncestorContainer;
25317 while (p.nodeType == 3) { // text node
25328 * Range intersection.. the hard stuff...
25332 * [ -- selected range --- ]
25336 * if end is before start or hits it. fail.
25337 * if start is after end or hits it fail.
25339 * if either hits (but other is outside. - then it's not
25345 // @see http://www.thismuchiknow.co.uk/?p=64.
25346 rangeIntersectsNode : function(range, node)
25348 var nodeRange = node.ownerDocument.createRange();
25350 nodeRange.selectNode(node);
25352 nodeRange.selectNodeContents(node);
25355 var rangeStartRange = range.cloneRange();
25356 rangeStartRange.collapse(true);
25358 var rangeEndRange = range.cloneRange();
25359 rangeEndRange.collapse(false);
25361 var nodeStartRange = nodeRange.cloneRange();
25362 nodeStartRange.collapse(true);
25364 var nodeEndRange = nodeRange.cloneRange();
25365 nodeEndRange.collapse(false);
25367 return rangeStartRange.compareBoundaryPoints(
25368 Range.START_TO_START, nodeEndRange) == -1 &&
25369 rangeEndRange.compareBoundaryPoints(
25370 Range.START_TO_START, nodeStartRange) == 1;
25374 rangeCompareNode : function(range, node)
25376 var nodeRange = node.ownerDocument.createRange();
25378 nodeRange.selectNode(node);
25380 nodeRange.selectNodeContents(node);
25384 range.collapse(true);
25386 nodeRange.collapse(true);
25388 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25389 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25391 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25393 var nodeIsBefore = ss == 1;
25394 var nodeIsAfter = ee == -1;
25396 if (nodeIsBefore && nodeIsAfter) {
25399 if (!nodeIsBefore && nodeIsAfter) {
25400 return 1; //right trailed.
25403 if (nodeIsBefore && !nodeIsAfter) {
25404 return 2; // left trailed.
25410 // private? - in a new class?
25411 cleanUpPaste : function()
25413 // cleans up the whole document..
25414 Roo.log('cleanuppaste');
25416 this.cleanUpChildren(this.doc.body);
25417 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25418 if (clean != this.doc.body.innerHTML) {
25419 this.doc.body.innerHTML = clean;
25424 cleanWordChars : function(input) {// change the chars to hex code
25425 var he = Roo.HtmlEditorCore;
25427 var output = input;
25428 Roo.each(he.swapCodes, function(sw) {
25429 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25431 output = output.replace(swapper, sw[1]);
25438 cleanUpChildren : function (n)
25440 if (!n.childNodes.length) {
25443 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25444 this.cleanUpChild(n.childNodes[i]);
25451 cleanUpChild : function (node)
25454 //console.log(node);
25455 if (node.nodeName == "#text") {
25456 // clean up silly Windows -- stuff?
25459 if (node.nodeName == "#comment") {
25460 node.parentNode.removeChild(node);
25461 // clean up silly Windows -- stuff?
25464 var lcname = node.tagName.toLowerCase();
25465 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25466 // whitelist of tags..
25468 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25470 node.parentNode.removeChild(node);
25475 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25477 // spans with no attributes - just remove them..
25478 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25479 remove_keep_children = true;
25482 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25483 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25485 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25486 // remove_keep_children = true;
25489 if (remove_keep_children) {
25490 this.cleanUpChildren(node);
25491 // inserts everything just before this node...
25492 while (node.childNodes.length) {
25493 var cn = node.childNodes[0];
25494 node.removeChild(cn);
25495 node.parentNode.insertBefore(cn, node);
25497 node.parentNode.removeChild(node);
25501 if (!node.attributes || !node.attributes.length) {
25506 this.cleanUpChildren(node);
25510 function cleanAttr(n,v)
25513 if (v.match(/^\./) || v.match(/^\//)) {
25516 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25519 if (v.match(/^#/)) {
25522 if (v.match(/^\{/)) { // allow template editing.
25525 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25526 node.removeAttribute(n);
25530 var cwhite = this.cwhite;
25531 var cblack = this.cblack;
25533 function cleanStyle(n,v)
25535 if (v.match(/expression/)) { //XSS?? should we even bother..
25536 node.removeAttribute(n);
25540 var parts = v.split(/;/);
25543 Roo.each(parts, function(p) {
25544 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25548 var l = p.split(':').shift().replace(/\s+/g,'');
25549 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25551 if ( cwhite.length && cblack.indexOf(l) > -1) {
25552 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25553 //node.removeAttribute(n);
25557 // only allow 'c whitelisted system attributes'
25558 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25559 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25560 //node.removeAttribute(n);
25570 if (clean.length) {
25571 node.setAttribute(n, clean.join(';'));
25573 node.removeAttribute(n);
25579 for (var i = node.attributes.length-1; i > -1 ; i--) {
25580 var a = node.attributes[i];
25583 if (a.name.toLowerCase().substr(0,2)=='on') {
25584 node.removeAttribute(a.name);
25587 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25588 node.removeAttribute(a.name);
25591 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25592 cleanAttr(a.name,a.value); // fixme..
25595 if (a.name == 'style') {
25596 cleanStyle(a.name,a.value);
25599 /// clean up MS crap..
25600 // tecnically this should be a list of valid class'es..
25603 if (a.name == 'class') {
25604 if (a.value.match(/^Mso/)) {
25605 node.removeAttribute('class');
25608 if (a.value.match(/^body$/)) {
25609 node.removeAttribute('class');
25620 this.cleanUpChildren(node);
25626 * Clean up MS wordisms...
25628 cleanWord : function(node)
25631 this.cleanWord(this.doc.body);
25636 node.nodeName == 'SPAN' &&
25637 !node.hasAttributes() &&
25638 node.childNodes.length == 1 &&
25639 node.firstChild.nodeName == "#text"
25641 var textNode = node.firstChild;
25642 node.removeChild(textNode);
25643 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25644 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25646 node.parentNode.insertBefore(textNode, node);
25647 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25648 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25650 node.parentNode.removeChild(node);
25653 if (node.nodeName == "#text") {
25654 // clean up silly Windows -- stuff?
25657 if (node.nodeName == "#comment") {
25658 node.parentNode.removeChild(node);
25659 // clean up silly Windows -- stuff?
25663 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25664 node.parentNode.removeChild(node);
25667 //Roo.log(node.tagName);
25668 // remove - but keep children..
25669 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25670 //Roo.log('-- removed');
25671 while (node.childNodes.length) {
25672 var cn = node.childNodes[0];
25673 node.removeChild(cn);
25674 node.parentNode.insertBefore(cn, node);
25675 // move node to parent - and clean it..
25676 this.cleanWord(cn);
25678 node.parentNode.removeChild(node);
25679 /// no need to iterate chidlren = it's got none..
25680 //this.iterateChildren(node, this.cleanWord);
25684 if (node.className.length) {
25686 var cn = node.className.split(/\W+/);
25688 Roo.each(cn, function(cls) {
25689 if (cls.match(/Mso[a-zA-Z]+/)) {
25694 node.className = cna.length ? cna.join(' ') : '';
25696 node.removeAttribute("class");
25700 if (node.hasAttribute("lang")) {
25701 node.removeAttribute("lang");
25704 if (node.hasAttribute("style")) {
25706 var styles = node.getAttribute("style").split(";");
25708 Roo.each(styles, function(s) {
25709 if (!s.match(/:/)) {
25712 var kv = s.split(":");
25713 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25716 // what ever is left... we allow.
25719 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25720 if (!nstyle.length) {
25721 node.removeAttribute('style');
25724 this.iterateChildren(node, this.cleanWord);
25730 * iterateChildren of a Node, calling fn each time, using this as the scole..
25731 * @param {DomNode} node node to iterate children of.
25732 * @param {Function} fn method of this class to call on each item.
25734 iterateChildren : function(node, fn)
25736 if (!node.childNodes.length) {
25739 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25740 fn.call(this, node.childNodes[i])
25746 * cleanTableWidths.
25748 * Quite often pasting from word etc.. results in tables with column and widths.
25749 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25752 cleanTableWidths : function(node)
25757 this.cleanTableWidths(this.doc.body);
25762 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25765 Roo.log(node.tagName);
25766 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25767 this.iterateChildren(node, this.cleanTableWidths);
25770 if (node.hasAttribute('width')) {
25771 node.removeAttribute('width');
25775 if (node.hasAttribute("style")) {
25778 var styles = node.getAttribute("style").split(";");
25780 Roo.each(styles, function(s) {
25781 if (!s.match(/:/)) {
25784 var kv = s.split(":");
25785 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25788 // what ever is left... we allow.
25791 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25792 if (!nstyle.length) {
25793 node.removeAttribute('style');
25797 this.iterateChildren(node, this.cleanTableWidths);
25805 domToHTML : function(currentElement, depth, nopadtext) {
25807 depth = depth || 0;
25808 nopadtext = nopadtext || false;
25810 if (!currentElement) {
25811 return this.domToHTML(this.doc.body);
25814 //Roo.log(currentElement);
25816 var allText = false;
25817 var nodeName = currentElement.nodeName;
25818 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25820 if (nodeName == '#text') {
25822 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25827 if (nodeName != 'BODY') {
25830 // Prints the node tagName, such as <A>, <IMG>, etc
25833 for(i = 0; i < currentElement.attributes.length;i++) {
25835 var aname = currentElement.attributes.item(i).name;
25836 if (!currentElement.attributes.item(i).value.length) {
25839 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25842 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25851 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25854 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25859 // Traverse the tree
25861 var currentElementChild = currentElement.childNodes.item(i);
25862 var allText = true;
25863 var innerHTML = '';
25865 while (currentElementChild) {
25866 // Formatting code (indent the tree so it looks nice on the screen)
25867 var nopad = nopadtext;
25868 if (lastnode == 'SPAN') {
25872 if (currentElementChild.nodeName == '#text') {
25873 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25874 toadd = nopadtext ? toadd : toadd.trim();
25875 if (!nopad && toadd.length > 80) {
25876 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25878 innerHTML += toadd;
25881 currentElementChild = currentElement.childNodes.item(i);
25887 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25889 // Recursively traverse the tree structure of the child node
25890 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25891 lastnode = currentElementChild.nodeName;
25893 currentElementChild=currentElement.childNodes.item(i);
25899 // The remaining code is mostly for formatting the tree
25900 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25905 ret+= "</"+tagName+">";
25911 applyBlacklists : function()
25913 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25914 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25918 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25919 if (b.indexOf(tag) > -1) {
25922 this.white.push(tag);
25926 Roo.each(w, function(tag) {
25927 if (b.indexOf(tag) > -1) {
25930 if (this.white.indexOf(tag) > -1) {
25933 this.white.push(tag);
25938 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25939 if (w.indexOf(tag) > -1) {
25942 this.black.push(tag);
25946 Roo.each(b, function(tag) {
25947 if (w.indexOf(tag) > -1) {
25950 if (this.black.indexOf(tag) > -1) {
25953 this.black.push(tag);
25958 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25959 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25963 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25964 if (b.indexOf(tag) > -1) {
25967 this.cwhite.push(tag);
25971 Roo.each(w, function(tag) {
25972 if (b.indexOf(tag) > -1) {
25975 if (this.cwhite.indexOf(tag) > -1) {
25978 this.cwhite.push(tag);
25983 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25984 if (w.indexOf(tag) > -1) {
25987 this.cblack.push(tag);
25991 Roo.each(b, function(tag) {
25992 if (w.indexOf(tag) > -1) {
25995 if (this.cblack.indexOf(tag) > -1) {
25998 this.cblack.push(tag);
26003 setStylesheets : function(stylesheets)
26005 if(typeof(stylesheets) == 'string'){
26006 Roo.get(this.iframe.contentDocument.head).createChild({
26008 rel : 'stylesheet',
26017 Roo.each(stylesheets, function(s) {
26022 Roo.get(_this.iframe.contentDocument.head).createChild({
26024 rel : 'stylesheet',
26033 removeStylesheets : function()
26037 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26042 setStyle : function(style)
26044 Roo.get(this.iframe.contentDocument.head).createChild({
26053 // hide stuff that is not compatible
26067 * @event specialkey
26071 * @cfg {String} fieldClass @hide
26074 * @cfg {String} focusClass @hide
26077 * @cfg {String} autoCreate @hide
26080 * @cfg {String} inputType @hide
26083 * @cfg {String} invalidClass @hide
26086 * @cfg {String} invalidText @hide
26089 * @cfg {String} msgFx @hide
26092 * @cfg {String} validateOnBlur @hide
26096 Roo.HtmlEditorCore.white = [
26097 'area', 'br', 'img', 'input', 'hr', 'wbr',
26099 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26100 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26101 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26102 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26103 'table', 'ul', 'xmp',
26105 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26108 'dir', 'menu', 'ol', 'ul', 'dl',
26114 Roo.HtmlEditorCore.black = [
26115 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26117 'base', 'basefont', 'bgsound', 'blink', 'body',
26118 'frame', 'frameset', 'head', 'html', 'ilayer',
26119 'iframe', 'layer', 'link', 'meta', 'object',
26120 'script', 'style' ,'title', 'xml' // clean later..
26122 Roo.HtmlEditorCore.clean = [
26123 'script', 'style', 'title', 'xml'
26125 Roo.HtmlEditorCore.remove = [
26130 Roo.HtmlEditorCore.ablack = [
26134 Roo.HtmlEditorCore.aclean = [
26135 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26139 Roo.HtmlEditorCore.pwhite= [
26140 'http', 'https', 'mailto'
26143 // white listed style attributes.
26144 Roo.HtmlEditorCore.cwhite= [
26145 // 'text-align', /// default is to allow most things..
26151 // black listed style attributes.
26152 Roo.HtmlEditorCore.cblack= [
26153 // 'font-size' -- this can be set by the project
26157 Roo.HtmlEditorCore.swapCodes =[
26158 [ 8211, "–" ],
26159 [ 8212, "—" ],
26176 * @class Roo.bootstrap.HtmlEditor
26177 * @extends Roo.bootstrap.TextArea
26178 * Bootstrap HtmlEditor class
26181 * Create a new HtmlEditor
26182 * @param {Object} config The config object
26185 Roo.bootstrap.HtmlEditor = function(config){
26186 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26187 if (!this.toolbars) {
26188 this.toolbars = [];
26191 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26194 * @event initialize
26195 * Fires when the editor is fully initialized (including the iframe)
26196 * @param {HtmlEditor} this
26201 * Fires when the editor is first receives the focus. Any insertion must wait
26202 * until after this event.
26203 * @param {HtmlEditor} this
26207 * @event beforesync
26208 * Fires before the textarea is updated with content from the editor iframe. Return false
26209 * to cancel the sync.
26210 * @param {HtmlEditor} this
26211 * @param {String} html
26215 * @event beforepush
26216 * Fires before the iframe editor is updated with content from the textarea. Return false
26217 * to cancel the push.
26218 * @param {HtmlEditor} this
26219 * @param {String} html
26224 * Fires when the textarea is updated with content from the editor iframe.
26225 * @param {HtmlEditor} this
26226 * @param {String} html
26231 * Fires when the iframe editor is updated with content from the textarea.
26232 * @param {HtmlEditor} this
26233 * @param {String} html
26237 * @event editmodechange
26238 * Fires when the editor switches edit modes
26239 * @param {HtmlEditor} this
26240 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26242 editmodechange: true,
26244 * @event editorevent
26245 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26246 * @param {HtmlEditor} this
26250 * @event firstfocus
26251 * Fires when on first focus - needed by toolbars..
26252 * @param {HtmlEditor} this
26257 * Auto save the htmlEditor value as a file into Events
26258 * @param {HtmlEditor} this
26262 * @event savedpreview
26263 * preview the saved version of htmlEditor
26264 * @param {HtmlEditor} this
26271 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26275 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26280 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26285 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26290 * @cfg {Number} height (in pixels)
26294 * @cfg {Number} width (in pixels)
26299 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26302 stylesheets: false,
26307 // private properties
26308 validationEvent : false,
26310 initialized : false,
26313 onFocus : Roo.emptyFn,
26315 hideMode:'offsets',
26317 tbContainer : false,
26321 toolbarContainer :function() {
26322 return this.wrap.select('.x-html-editor-tb',true).first();
26326 * Protected method that will not generally be called directly. It
26327 * is called when the editor creates its toolbar. Override this method if you need to
26328 * add custom toolbar buttons.
26329 * @param {HtmlEditor} editor
26331 createToolbar : function(){
26332 Roo.log('renewing');
26333 Roo.log("create toolbars");
26335 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26336 this.toolbars[0].render(this.toolbarContainer());
26340 // if (!editor.toolbars || !editor.toolbars.length) {
26341 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26344 // for (var i =0 ; i < editor.toolbars.length;i++) {
26345 // editor.toolbars[i] = Roo.factory(
26346 // typeof(editor.toolbars[i]) == 'string' ?
26347 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26348 // Roo.bootstrap.HtmlEditor);
26349 // editor.toolbars[i].init(editor);
26355 onRender : function(ct, position)
26357 // Roo.log("Call onRender: " + this.xtype);
26359 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26361 this.wrap = this.inputEl().wrap({
26362 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26365 this.editorcore.onRender(ct, position);
26367 if (this.resizable) {
26368 this.resizeEl = new Roo.Resizable(this.wrap, {
26372 minHeight : this.height,
26373 height: this.height,
26374 handles : this.resizable,
26377 resize : function(r, w, h) {
26378 _t.onResize(w,h); // -something
26384 this.createToolbar(this);
26387 if(!this.width && this.resizable){
26388 this.setSize(this.wrap.getSize());
26390 if (this.resizeEl) {
26391 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26392 // should trigger onReize..
26398 onResize : function(w, h)
26400 Roo.log('resize: ' +w + ',' + h );
26401 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26405 if(this.inputEl() ){
26406 if(typeof w == 'number'){
26407 var aw = w - this.wrap.getFrameWidth('lr');
26408 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26411 if(typeof h == 'number'){
26412 var tbh = -11; // fixme it needs to tool bar size!
26413 for (var i =0; i < this.toolbars.length;i++) {
26414 // fixme - ask toolbars for heights?
26415 tbh += this.toolbars[i].el.getHeight();
26416 //if (this.toolbars[i].footer) {
26417 // tbh += this.toolbars[i].footer.el.getHeight();
26425 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26426 ah -= 5; // knock a few pixes off for look..
26427 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26431 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26432 this.editorcore.onResize(ew,eh);
26437 * Toggles the editor between standard and source edit mode.
26438 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26440 toggleSourceEdit : function(sourceEditMode)
26442 this.editorcore.toggleSourceEdit(sourceEditMode);
26444 if(this.editorcore.sourceEditMode){
26445 Roo.log('editor - showing textarea');
26448 // Roo.log(this.syncValue());
26450 this.inputEl().removeClass(['hide', 'x-hidden']);
26451 this.inputEl().dom.removeAttribute('tabIndex');
26452 this.inputEl().focus();
26454 Roo.log('editor - hiding textarea');
26456 // Roo.log(this.pushValue());
26459 this.inputEl().addClass(['hide', 'x-hidden']);
26460 this.inputEl().dom.setAttribute('tabIndex', -1);
26461 //this.deferFocus();
26464 if(this.resizable){
26465 this.setSize(this.wrap.getSize());
26468 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26471 // private (for BoxComponent)
26472 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26474 // private (for BoxComponent)
26475 getResizeEl : function(){
26479 // private (for BoxComponent)
26480 getPositionEl : function(){
26485 initEvents : function(){
26486 this.originalValue = this.getValue();
26490 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26493 // markInvalid : Roo.emptyFn,
26495 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26498 // clearInvalid : Roo.emptyFn,
26500 setValue : function(v){
26501 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26502 this.editorcore.pushValue();
26507 deferFocus : function(){
26508 this.focus.defer(10, this);
26512 focus : function(){
26513 this.editorcore.focus();
26519 onDestroy : function(){
26525 for (var i =0; i < this.toolbars.length;i++) {
26526 // fixme - ask toolbars for heights?
26527 this.toolbars[i].onDestroy();
26530 this.wrap.dom.innerHTML = '';
26531 this.wrap.remove();
26536 onFirstFocus : function(){
26537 //Roo.log("onFirstFocus");
26538 this.editorcore.onFirstFocus();
26539 for (var i =0; i < this.toolbars.length;i++) {
26540 this.toolbars[i].onFirstFocus();
26546 syncValue : function()
26548 this.editorcore.syncValue();
26551 pushValue : function()
26553 this.editorcore.pushValue();
26557 // hide stuff that is not compatible
26571 * @event specialkey
26575 * @cfg {String} fieldClass @hide
26578 * @cfg {String} focusClass @hide
26581 * @cfg {String} autoCreate @hide
26584 * @cfg {String} inputType @hide
26588 * @cfg {String} invalidText @hide
26591 * @cfg {String} msgFx @hide
26594 * @cfg {String} validateOnBlur @hide
26603 Roo.namespace('Roo.bootstrap.htmleditor');
26605 * @class Roo.bootstrap.HtmlEditorToolbar1
26611 new Roo.bootstrap.HtmlEditor({
26614 new Roo.bootstrap.HtmlEditorToolbar1({
26615 disable : { fonts: 1 , format: 1, ..., ... , ...],
26621 * @cfg {Object} disable List of elements to disable..
26622 * @cfg {Array} btns List of additional buttons.
26626 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26629 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26632 Roo.apply(this, config);
26634 // default disabled, based on 'good practice'..
26635 this.disable = this.disable || {};
26636 Roo.applyIf(this.disable, {
26639 specialElements : true
26641 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26643 this.editor = config.editor;
26644 this.editorcore = config.editor.editorcore;
26646 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26648 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26649 // dont call parent... till later.
26651 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26656 editorcore : false,
26661 "h1","h2","h3","h4","h5","h6",
26663 "abbr", "acronym", "address", "cite", "samp", "var",
26667 onRender : function(ct, position)
26669 // Roo.log("Call onRender: " + this.xtype);
26671 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26673 this.el.dom.style.marginBottom = '0';
26675 var editorcore = this.editorcore;
26676 var editor= this.editor;
26679 var btn = function(id,cmd , toggle, handler, html){
26681 var event = toggle ? 'toggle' : 'click';
26686 xns: Roo.bootstrap,
26690 enableToggle:toggle !== false,
26692 pressed : toggle ? false : null,
26695 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26696 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26702 // var cb_box = function...
26707 xns: Roo.bootstrap,
26712 xns: Roo.bootstrap,
26716 Roo.each(this.formats, function(f) {
26717 style.menu.items.push({
26719 xns: Roo.bootstrap,
26720 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26725 editorcore.insertTag(this.tagname);
26732 children.push(style);
26734 btn('bold',false,true);
26735 btn('italic',false,true);
26736 btn('align-left', 'justifyleft',true);
26737 btn('align-center', 'justifycenter',true);
26738 btn('align-right' , 'justifyright',true);
26739 btn('link', false, false, function(btn) {
26740 //Roo.log("create link?");
26741 var url = prompt(this.createLinkText, this.defaultLinkValue);
26742 if(url && url != 'http:/'+'/'){
26743 this.editorcore.relayCmd('createlink', url);
26746 btn('list','insertunorderedlist',true);
26747 btn('pencil', false,true, function(btn){
26749 this.toggleSourceEdit(btn.pressed);
26752 if (this.editor.btns.length > 0) {
26753 for (var i = 0; i<this.editor.btns.length; i++) {
26754 children.push(this.editor.btns[i]);
26762 xns: Roo.bootstrap,
26767 xns: Roo.bootstrap,
26772 cog.menu.items.push({
26774 xns: Roo.bootstrap,
26775 html : Clean styles,
26780 editorcore.insertTag(this.tagname);
26789 this.xtype = 'NavSimplebar';
26791 for(var i=0;i< children.length;i++) {
26793 this.buttons.add(this.addxtypeChild(children[i]));
26797 editor.on('editorevent', this.updateToolbar, this);
26799 onBtnClick : function(id)
26801 this.editorcore.relayCmd(id);
26802 this.editorcore.focus();
26806 * Protected method that will not generally be called directly. It triggers
26807 * a toolbar update by reading the markup state of the current selection in the editor.
26809 updateToolbar: function(){
26811 if(!this.editorcore.activated){
26812 this.editor.onFirstFocus(); // is this neeed?
26816 var btns = this.buttons;
26817 var doc = this.editorcore.doc;
26818 btns.get('bold').setActive(doc.queryCommandState('bold'));
26819 btns.get('italic').setActive(doc.queryCommandState('italic'));
26820 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26822 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26823 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26824 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26826 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26827 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26830 var ans = this.editorcore.getAllAncestors();
26831 if (this.formatCombo) {
26834 var store = this.formatCombo.store;
26835 this.formatCombo.setValue("");
26836 for (var i =0; i < ans.length;i++) {
26837 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26839 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26847 // hides menus... - so this cant be on a menu...
26848 Roo.bootstrap.MenuMgr.hideAll();
26850 Roo.bootstrap.MenuMgr.hideAll();
26851 //this.editorsyncValue();
26853 onFirstFocus: function() {
26854 this.buttons.each(function(item){
26858 toggleSourceEdit : function(sourceEditMode){
26861 if(sourceEditMode){
26862 Roo.log("disabling buttons");
26863 this.buttons.each( function(item){
26864 if(item.cmd != 'pencil'){
26870 Roo.log("enabling buttons");
26871 if(this.editorcore.initialized){
26872 this.buttons.each( function(item){
26878 Roo.log("calling toggole on editor");
26879 // tell the editor that it's been pressed..
26880 this.editor.toggleSourceEdit(sourceEditMode);
26894 * @class Roo.bootstrap.Markdown
26895 * @extends Roo.bootstrap.TextArea
26896 * Bootstrap Showdown editable area
26897 * @cfg {string} content
26900 * Create a new Showdown
26903 Roo.bootstrap.Markdown = function(config){
26904 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26908 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26912 initEvents : function()
26915 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26916 this.markdownEl = this.el.createChild({
26917 cls : 'roo-markdown-area'
26919 this.inputEl().addClass('d-none');
26920 if (this.getValue() == '') {
26921 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26924 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26926 this.markdownEl.on('click', this.toggleTextEdit, this);
26927 this.on('blur', this.toggleTextEdit, this);
26928 this.on('specialkey', this.resizeTextArea, this);
26931 toggleTextEdit : function()
26933 var sh = this.markdownEl.getHeight();
26934 this.inputEl().addClass('d-none');
26935 this.markdownEl.addClass('d-none');
26936 if (!this.editing) {
26938 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26939 this.inputEl().removeClass('d-none');
26940 this.inputEl().focus();
26941 this.editing = true;
26944 // show showdown...
26945 this.updateMarkdown();
26946 this.markdownEl.removeClass('d-none');
26947 this.editing = false;
26950 updateMarkdown : function()
26952 if (this.getValue() == '') {
26953 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26957 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26960 resizeTextArea: function () {
26963 Roo.log([sh, this.getValue().split("\n").length * 30]);
26964 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26966 setValue : function(val)
26968 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26969 if (!this.editing) {
26970 this.updateMarkdown();
26976 if (!this.editing) {
26977 this.toggleTextEdit();
26985 * @class Roo.bootstrap.Table.AbstractSelectionModel
26986 * @extends Roo.util.Observable
26987 * Abstract base class for grid SelectionModels. It provides the interface that should be
26988 * implemented by descendant classes. This class should not be directly instantiated.
26991 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26992 this.locked = false;
26993 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26997 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26998 /** @ignore Called by the grid automatically. Do not call directly. */
26999 init : function(grid){
27005 * Locks the selections.
27008 this.locked = true;
27012 * Unlocks the selections.
27014 unlock : function(){
27015 this.locked = false;
27019 * Returns true if the selections are locked.
27020 * @return {Boolean}
27022 isLocked : function(){
27023 return this.locked;
27027 initEvents : function ()
27033 * @extends Roo.bootstrap.Table.AbstractSelectionModel
27034 * @class Roo.bootstrap.Table.RowSelectionModel
27035 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27036 * It supports multiple selections and keyboard selection/navigation.
27038 * @param {Object} config
27041 Roo.bootstrap.Table.RowSelectionModel = function(config){
27042 Roo.apply(this, config);
27043 this.selections = new Roo.util.MixedCollection(false, function(o){
27048 this.lastActive = false;
27052 * @event selectionchange
27053 * Fires when the selection changes
27054 * @param {SelectionModel} this
27056 "selectionchange" : true,
27058 * @event afterselectionchange
27059 * Fires after the selection changes (eg. by key press or clicking)
27060 * @param {SelectionModel} this
27062 "afterselectionchange" : true,
27064 * @event beforerowselect
27065 * Fires when a row is selected being selected, return false to cancel.
27066 * @param {SelectionModel} this
27067 * @param {Number} rowIndex The selected index
27068 * @param {Boolean} keepExisting False if other selections will be cleared
27070 "beforerowselect" : true,
27073 * Fires when a row is selected.
27074 * @param {SelectionModel} this
27075 * @param {Number} rowIndex The selected index
27076 * @param {Roo.data.Record} r The record
27078 "rowselect" : true,
27080 * @event rowdeselect
27081 * Fires when a row is deselected.
27082 * @param {SelectionModel} this
27083 * @param {Number} rowIndex The selected index
27085 "rowdeselect" : true
27087 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27088 this.locked = false;
27091 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27093 * @cfg {Boolean} singleSelect
27094 * True to allow selection of only one row at a time (defaults to false)
27096 singleSelect : false,
27099 initEvents : function()
27102 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27103 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27104 //}else{ // allow click to work like normal
27105 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27107 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27108 this.grid.on("rowclick", this.handleMouseDown, this);
27110 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27111 "up" : function(e){
27113 this.selectPrevious(e.shiftKey);
27114 }else if(this.last !== false && this.lastActive !== false){
27115 var last = this.last;
27116 this.selectRange(this.last, this.lastActive-1);
27117 this.grid.getView().focusRow(this.lastActive);
27118 if(last !== false){
27122 this.selectFirstRow();
27124 this.fireEvent("afterselectionchange", this);
27126 "down" : function(e){
27128 this.selectNext(e.shiftKey);
27129 }else if(this.last !== false && this.lastActive !== false){
27130 var last = this.last;
27131 this.selectRange(this.last, this.lastActive+1);
27132 this.grid.getView().focusRow(this.lastActive);
27133 if(last !== false){
27137 this.selectFirstRow();
27139 this.fireEvent("afterselectionchange", this);
27143 this.grid.store.on('load', function(){
27144 this.selections.clear();
27147 var view = this.grid.view;
27148 view.on("refresh", this.onRefresh, this);
27149 view.on("rowupdated", this.onRowUpdated, this);
27150 view.on("rowremoved", this.onRemove, this);
27155 onRefresh : function()
27157 var ds = this.grid.store, i, v = this.grid.view;
27158 var s = this.selections;
27159 s.each(function(r){
27160 if((i = ds.indexOfId(r.id)) != -1){
27169 onRemove : function(v, index, r){
27170 this.selections.remove(r);
27174 onRowUpdated : function(v, index, r){
27175 if(this.isSelected(r)){
27176 v.onRowSelect(index);
27182 * @param {Array} records The records to select
27183 * @param {Boolean} keepExisting (optional) True to keep existing selections
27185 selectRecords : function(records, keepExisting)
27188 this.clearSelections();
27190 var ds = this.grid.store;
27191 for(var i = 0, len = records.length; i < len; i++){
27192 this.selectRow(ds.indexOf(records[i]), true);
27197 * Gets the number of selected rows.
27200 getCount : function(){
27201 return this.selections.length;
27205 * Selects the first row in the grid.
27207 selectFirstRow : function(){
27212 * Select the last row.
27213 * @param {Boolean} keepExisting (optional) True to keep existing selections
27215 selectLastRow : function(keepExisting){
27216 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27217 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27221 * Selects the row immediately following the last selected row.
27222 * @param {Boolean} keepExisting (optional) True to keep existing selections
27224 selectNext : function(keepExisting)
27226 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27227 this.selectRow(this.last+1, keepExisting);
27228 this.grid.getView().focusRow(this.last);
27233 * Selects the row that precedes the last selected row.
27234 * @param {Boolean} keepExisting (optional) True to keep existing selections
27236 selectPrevious : function(keepExisting){
27238 this.selectRow(this.last-1, keepExisting);
27239 this.grid.getView().focusRow(this.last);
27244 * Returns the selected records
27245 * @return {Array} Array of selected records
27247 getSelections : function(){
27248 return [].concat(this.selections.items);
27252 * Returns the first selected record.
27255 getSelected : function(){
27256 return this.selections.itemAt(0);
27261 * Clears all selections.
27263 clearSelections : function(fast)
27269 var ds = this.grid.store;
27270 var s = this.selections;
27271 s.each(function(r){
27272 this.deselectRow(ds.indexOfId(r.id));
27276 this.selections.clear();
27283 * Selects all rows.
27285 selectAll : function(){
27289 this.selections.clear();
27290 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27291 this.selectRow(i, true);
27296 * Returns True if there is a selection.
27297 * @return {Boolean}
27299 hasSelection : function(){
27300 return this.selections.length > 0;
27304 * Returns True if the specified row is selected.
27305 * @param {Number/Record} record The record or index of the record to check
27306 * @return {Boolean}
27308 isSelected : function(index){
27309 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27310 return (r && this.selections.key(r.id) ? true : false);
27314 * Returns True if the specified record id is selected.
27315 * @param {String} id The id of record to check
27316 * @return {Boolean}
27318 isIdSelected : function(id){
27319 return (this.selections.key(id) ? true : false);
27324 handleMouseDBClick : function(e, t){
27328 handleMouseDown : function(e, t)
27330 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27331 if(this.isLocked() || rowIndex < 0 ){
27334 if(e.shiftKey && this.last !== false){
27335 var last = this.last;
27336 this.selectRange(last, rowIndex, e.ctrlKey);
27337 this.last = last; // reset the last
27341 var isSelected = this.isSelected(rowIndex);
27342 //Roo.log("select row:" + rowIndex);
27344 this.deselectRow(rowIndex);
27346 this.selectRow(rowIndex, true);
27350 if(e.button !== 0 && isSelected){
27351 alert('rowIndex 2: ' + rowIndex);
27352 view.focusRow(rowIndex);
27353 }else if(e.ctrlKey && isSelected){
27354 this.deselectRow(rowIndex);
27355 }else if(!isSelected){
27356 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27357 view.focusRow(rowIndex);
27361 this.fireEvent("afterselectionchange", this);
27364 handleDragableRowClick : function(grid, rowIndex, e)
27366 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27367 this.selectRow(rowIndex, false);
27368 grid.view.focusRow(rowIndex);
27369 this.fireEvent("afterselectionchange", this);
27374 * Selects multiple rows.
27375 * @param {Array} rows Array of the indexes of the row to select
27376 * @param {Boolean} keepExisting (optional) True to keep existing selections
27378 selectRows : function(rows, keepExisting){
27380 this.clearSelections();
27382 for(var i = 0, len = rows.length; i < len; i++){
27383 this.selectRow(rows[i], true);
27388 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27389 * @param {Number} startRow The index of the first row in the range
27390 * @param {Number} endRow The index of the last row in the range
27391 * @param {Boolean} keepExisting (optional) True to retain existing selections
27393 selectRange : function(startRow, endRow, keepExisting){
27398 this.clearSelections();
27400 if(startRow <= endRow){
27401 for(var i = startRow; i <= endRow; i++){
27402 this.selectRow(i, true);
27405 for(var i = startRow; i >= endRow; i--){
27406 this.selectRow(i, true);
27412 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27413 * @param {Number} startRow The index of the first row in the range
27414 * @param {Number} endRow The index of the last row in the range
27416 deselectRange : function(startRow, endRow, preventViewNotify){
27420 for(var i = startRow; i <= endRow; i++){
27421 this.deselectRow(i, preventViewNotify);
27427 * @param {Number} row The index of the row to select
27428 * @param {Boolean} keepExisting (optional) True to keep existing selections
27430 selectRow : function(index, keepExisting, preventViewNotify)
27432 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27435 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27436 if(!keepExisting || this.singleSelect){
27437 this.clearSelections();
27440 var r = this.grid.store.getAt(index);
27441 //console.log('selectRow - record id :' + r.id);
27443 this.selections.add(r);
27444 this.last = this.lastActive = index;
27445 if(!preventViewNotify){
27446 var proxy = new Roo.Element(
27447 this.grid.getRowDom(index)
27449 proxy.addClass('bg-info info');
27451 this.fireEvent("rowselect", this, index, r);
27452 this.fireEvent("selectionchange", this);
27458 * @param {Number} row The index of the row to deselect
27460 deselectRow : function(index, preventViewNotify)
27465 if(this.last == index){
27468 if(this.lastActive == index){
27469 this.lastActive = false;
27472 var r = this.grid.store.getAt(index);
27477 this.selections.remove(r);
27478 //.console.log('deselectRow - record id :' + r.id);
27479 if(!preventViewNotify){
27481 var proxy = new Roo.Element(
27482 this.grid.getRowDom(index)
27484 proxy.removeClass('bg-info info');
27486 this.fireEvent("rowdeselect", this, index);
27487 this.fireEvent("selectionchange", this);
27491 restoreLast : function(){
27493 this.last = this._last;
27498 acceptsNav : function(row, col, cm){
27499 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27503 onEditorKey : function(field, e){
27504 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27509 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27511 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27513 }else if(k == e.ENTER && !e.ctrlKey){
27517 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27519 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27521 }else if(k == e.ESC){
27525 g.startEditing(newCell[0], newCell[1]);
27531 * Ext JS Library 1.1.1
27532 * Copyright(c) 2006-2007, Ext JS, LLC.
27534 * Originally Released Under LGPL - original licence link has changed is not relivant.
27537 * <script type="text/javascript">
27541 * @class Roo.bootstrap.PagingToolbar
27542 * @extends Roo.bootstrap.NavSimplebar
27543 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27545 * Create a new PagingToolbar
27546 * @param {Object} config The config object
27547 * @param {Roo.data.Store} store
27549 Roo.bootstrap.PagingToolbar = function(config)
27551 // old args format still supported... - xtype is prefered..
27552 // created from xtype...
27554 this.ds = config.dataSource;
27556 if (config.store && !this.ds) {
27557 this.store= Roo.factory(config.store, Roo.data);
27558 this.ds = this.store;
27559 this.ds.xmodule = this.xmodule || false;
27562 this.toolbarItems = [];
27563 if (config.items) {
27564 this.toolbarItems = config.items;
27567 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27572 this.bind(this.ds);
27575 if (Roo.bootstrap.version == 4) {
27576 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27578 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27583 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27585 * @cfg {Roo.data.Store} dataSource
27586 * The underlying data store providing the paged data
27589 * @cfg {String/HTMLElement/Element} container
27590 * container The id or element that will contain the toolbar
27593 * @cfg {Boolean} displayInfo
27594 * True to display the displayMsg (defaults to false)
27597 * @cfg {Number} pageSize
27598 * The number of records to display per page (defaults to 20)
27602 * @cfg {String} displayMsg
27603 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27605 displayMsg : 'Displaying {0} - {1} of {2}',
27607 * @cfg {String} emptyMsg
27608 * The message to display when no records are found (defaults to "No data to display")
27610 emptyMsg : 'No data to display',
27612 * Customizable piece of the default paging text (defaults to "Page")
27615 beforePageText : "Page",
27617 * Customizable piece of the default paging text (defaults to "of %0")
27620 afterPageText : "of {0}",
27622 * Customizable piece of the default paging text (defaults to "First Page")
27625 firstText : "First Page",
27627 * Customizable piece of the default paging text (defaults to "Previous Page")
27630 prevText : "Previous Page",
27632 * Customizable piece of the default paging text (defaults to "Next Page")
27635 nextText : "Next Page",
27637 * Customizable piece of the default paging text (defaults to "Last Page")
27640 lastText : "Last Page",
27642 * Customizable piece of the default paging text (defaults to "Refresh")
27645 refreshText : "Refresh",
27649 onRender : function(ct, position)
27651 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27652 this.navgroup.parentId = this.id;
27653 this.navgroup.onRender(this.el, null);
27654 // add the buttons to the navgroup
27656 if(this.displayInfo){
27657 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27658 this.displayEl = this.el.select('.x-paging-info', true).first();
27659 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27660 // this.displayEl = navel.el.select('span',true).first();
27666 Roo.each(_this.buttons, function(e){ // this might need to use render????
27667 Roo.factory(e).render(_this.el);
27671 Roo.each(_this.toolbarItems, function(e) {
27672 _this.navgroup.addItem(e);
27676 this.first = this.navgroup.addItem({
27677 tooltip: this.firstText,
27678 cls: "prev btn-outline-secondary",
27679 html : ' <i class="fa fa-step-backward"></i>',
27681 preventDefault: true,
27682 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27685 this.prev = this.navgroup.addItem({
27686 tooltip: this.prevText,
27687 cls: "prev btn-outline-secondary",
27688 html : ' <i class="fa fa-backward"></i>',
27690 preventDefault: true,
27691 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27693 //this.addSeparator();
27696 var field = this.navgroup.addItem( {
27698 cls : 'x-paging-position btn-outline-secondary',
27700 html : this.beforePageText +
27701 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27702 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27705 this.field = field.el.select('input', true).first();
27706 this.field.on("keydown", this.onPagingKeydown, this);
27707 this.field.on("focus", function(){this.dom.select();});
27710 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27711 //this.field.setHeight(18);
27712 //this.addSeparator();
27713 this.next = this.navgroup.addItem({
27714 tooltip: this.nextText,
27715 cls: "next btn-outline-secondary",
27716 html : ' <i class="fa fa-forward"></i>',
27718 preventDefault: true,
27719 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27721 this.last = this.navgroup.addItem({
27722 tooltip: this.lastText,
27723 html : ' <i class="fa fa-step-forward"></i>',
27724 cls: "next btn-outline-secondary",
27726 preventDefault: true,
27727 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27729 //this.addSeparator();
27730 this.loading = this.navgroup.addItem({
27731 tooltip: this.refreshText,
27732 cls: "btn-outline-secondary",
27733 html : ' <i class="fa fa-refresh"></i>',
27734 preventDefault: true,
27735 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27741 updateInfo : function(){
27742 if(this.displayEl){
27743 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27744 var msg = count == 0 ?
27748 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27750 this.displayEl.update(msg);
27755 onLoad : function(ds, r, o)
27757 this.cursor = o.params && o.params.start ? o.params.start : 0;
27759 var d = this.getPageData(),
27764 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27765 this.field.dom.value = ap;
27766 this.first.setDisabled(ap == 1);
27767 this.prev.setDisabled(ap == 1);
27768 this.next.setDisabled(ap == ps);
27769 this.last.setDisabled(ap == ps);
27770 this.loading.enable();
27775 getPageData : function(){
27776 var total = this.ds.getTotalCount();
27779 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27780 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27785 onLoadError : function(){
27786 this.loading.enable();
27790 onPagingKeydown : function(e){
27791 var k = e.getKey();
27792 var d = this.getPageData();
27794 var v = this.field.dom.value, pageNum;
27795 if(!v || isNaN(pageNum = parseInt(v, 10))){
27796 this.field.dom.value = d.activePage;
27799 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27800 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27803 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))
27805 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27806 this.field.dom.value = pageNum;
27807 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27810 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27812 var v = this.field.dom.value, pageNum;
27813 var increment = (e.shiftKey) ? 10 : 1;
27814 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27817 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27818 this.field.dom.value = d.activePage;
27821 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27823 this.field.dom.value = parseInt(v, 10) + increment;
27824 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27825 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27832 beforeLoad : function(){
27834 this.loading.disable();
27839 onClick : function(which){
27848 ds.load({params:{start: 0, limit: this.pageSize}});
27851 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27854 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27857 var total = ds.getTotalCount();
27858 var extra = total % this.pageSize;
27859 var lastStart = extra ? (total - extra) : total-this.pageSize;
27860 ds.load({params:{start: lastStart, limit: this.pageSize}});
27863 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27869 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27870 * @param {Roo.data.Store} store The data store to unbind
27872 unbind : function(ds){
27873 ds.un("beforeload", this.beforeLoad, this);
27874 ds.un("load", this.onLoad, this);
27875 ds.un("loadexception", this.onLoadError, this);
27876 ds.un("remove", this.updateInfo, this);
27877 ds.un("add", this.updateInfo, this);
27878 this.ds = undefined;
27882 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27883 * @param {Roo.data.Store} store The data store to bind
27885 bind : function(ds){
27886 ds.on("beforeload", this.beforeLoad, this);
27887 ds.on("load", this.onLoad, this);
27888 ds.on("loadexception", this.onLoadError, this);
27889 ds.on("remove", this.updateInfo, this);
27890 ds.on("add", this.updateInfo, this);
27901 * @class Roo.bootstrap.MessageBar
27902 * @extends Roo.bootstrap.Component
27903 * Bootstrap MessageBar class
27904 * @cfg {String} html contents of the MessageBar
27905 * @cfg {String} weight (info | success | warning | danger) default info
27906 * @cfg {String} beforeClass insert the bar before the given class
27907 * @cfg {Boolean} closable (true | false) default false
27908 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27911 * Create a new Element
27912 * @param {Object} config The config object
27915 Roo.bootstrap.MessageBar = function(config){
27916 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27919 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27925 beforeClass: 'bootstrap-sticky-wrap',
27927 getAutoCreate : function(){
27931 cls: 'alert alert-dismissable alert-' + this.weight,
27936 html: this.html || ''
27942 cfg.cls += ' alert-messages-fixed';
27956 onRender : function(ct, position)
27958 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27961 var cfg = Roo.apply({}, this.getAutoCreate());
27965 cfg.cls += ' ' + this.cls;
27968 cfg.style = this.style;
27970 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27972 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27975 this.el.select('>button.close').on('click', this.hide, this);
27981 if (!this.rendered) {
27987 this.fireEvent('show', this);
27993 if (!this.rendered) {
27999 this.fireEvent('hide', this);
28002 update : function()
28004 // var e = this.el.dom.firstChild;
28006 // if(this.closable){
28007 // e = e.nextSibling;
28010 // e.data = this.html || '';
28012 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28028 * @class Roo.bootstrap.Graph
28029 * @extends Roo.bootstrap.Component
28030 * Bootstrap Graph class
28034 @cfg {String} graphtype bar | vbar | pie
28035 @cfg {number} g_x coodinator | centre x (pie)
28036 @cfg {number} g_y coodinator | centre y (pie)
28037 @cfg {number} g_r radius (pie)
28038 @cfg {number} g_height height of the chart (respected by all elements in the set)
28039 @cfg {number} g_width width of the chart (respected by all elements in the set)
28040 @cfg {Object} title The title of the chart
28043 -opts (object) options for the chart
28045 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28046 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28048 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.
28049 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28051 o stretch (boolean)
28053 -opts (object) options for the pie
28056 o startAngle (number)
28057 o endAngle (number)
28061 * Create a new Input
28062 * @param {Object} config The config object
28065 Roo.bootstrap.Graph = function(config){
28066 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28072 * The img click event for the img.
28073 * @param {Roo.EventObject} e
28079 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28090 //g_colors: this.colors,
28097 getAutoCreate : function(){
28108 onRender : function(ct,position){
28111 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28113 if (typeof(Raphael) == 'undefined') {
28114 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28118 this.raphael = Raphael(this.el.dom);
28120 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28121 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28122 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28123 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28125 r.text(160, 10, "Single Series Chart").attr(txtattr);
28126 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28127 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28128 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28130 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28131 r.barchart(330, 10, 300, 220, data1);
28132 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28133 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28136 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28137 // r.barchart(30, 30, 560, 250, xdata, {
28138 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28139 // axis : "0 0 1 1",
28140 // axisxlabels : xdata
28141 // //yvalues : cols,
28144 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28146 // this.load(null,xdata,{
28147 // axis : "0 0 1 1",
28148 // axisxlabels : xdata
28153 load : function(graphtype,xdata,opts)
28155 this.raphael.clear();
28157 graphtype = this.graphtype;
28162 var r = this.raphael,
28163 fin = function () {
28164 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28166 fout = function () {
28167 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28169 pfin = function() {
28170 this.sector.stop();
28171 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28174 this.label[0].stop();
28175 this.label[0].attr({ r: 7.5 });
28176 this.label[1].attr({ "font-weight": 800 });
28179 pfout = function() {
28180 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28183 this.label[0].animate({ r: 5 }, 500, "bounce");
28184 this.label[1].attr({ "font-weight": 400 });
28190 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28193 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28196 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28197 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28199 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28206 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28211 setTitle: function(o)
28216 initEvents: function() {
28219 this.el.on('click', this.onClick, this);
28223 onClick : function(e)
28225 Roo.log('img onclick');
28226 this.fireEvent('click', this, e);
28238 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28241 * @class Roo.bootstrap.dash.NumberBox
28242 * @extends Roo.bootstrap.Component
28243 * Bootstrap NumberBox class
28244 * @cfg {String} headline Box headline
28245 * @cfg {String} content Box content
28246 * @cfg {String} icon Box icon
28247 * @cfg {String} footer Footer text
28248 * @cfg {String} fhref Footer href
28251 * Create a new NumberBox
28252 * @param {Object} config The config object
28256 Roo.bootstrap.dash.NumberBox = function(config){
28257 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28261 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28270 getAutoCreate : function(){
28274 cls : 'small-box ',
28282 cls : 'roo-headline',
28283 html : this.headline
28287 cls : 'roo-content',
28288 html : this.content
28302 cls : 'ion ' + this.icon
28311 cls : 'small-box-footer',
28312 href : this.fhref || '#',
28316 cfg.cn.push(footer);
28323 onRender : function(ct,position){
28324 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28331 setHeadline: function (value)
28333 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28336 setFooter: function (value, href)
28338 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28341 this.el.select('a.small-box-footer',true).first().attr('href', href);
28346 setContent: function (value)
28348 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28351 initEvents: function()
28365 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28368 * @class Roo.bootstrap.dash.TabBox
28369 * @extends Roo.bootstrap.Component
28370 * Bootstrap TabBox class
28371 * @cfg {String} title Title of the TabBox
28372 * @cfg {String} icon Icon of the TabBox
28373 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28374 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28377 * Create a new TabBox
28378 * @param {Object} config The config object
28382 Roo.bootstrap.dash.TabBox = function(config){
28383 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28388 * When a pane is added
28389 * @param {Roo.bootstrap.dash.TabPane} pane
28393 * @event activatepane
28394 * When a pane is activated
28395 * @param {Roo.bootstrap.dash.TabPane} pane
28397 "activatepane" : true
28405 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28410 tabScrollable : false,
28412 getChildContainer : function()
28414 return this.el.select('.tab-content', true).first();
28417 getAutoCreate : function(){
28421 cls: 'pull-left header',
28429 cls: 'fa ' + this.icon
28435 cls: 'nav nav-tabs pull-right',
28441 if(this.tabScrollable){
28448 cls: 'nav nav-tabs pull-right',
28459 cls: 'nav-tabs-custom',
28464 cls: 'tab-content no-padding',
28472 initEvents : function()
28474 //Roo.log('add add pane handler');
28475 this.on('addpane', this.onAddPane, this);
28478 * Updates the box title
28479 * @param {String} html to set the title to.
28481 setTitle : function(value)
28483 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28485 onAddPane : function(pane)
28487 this.panes.push(pane);
28488 //Roo.log('addpane');
28490 // tabs are rendere left to right..
28491 if(!this.showtabs){
28495 var ctr = this.el.select('.nav-tabs', true).first();
28498 var existing = ctr.select('.nav-tab',true);
28499 var qty = existing.getCount();;
28502 var tab = ctr.createChild({
28504 cls : 'nav-tab' + (qty ? '' : ' active'),
28512 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28515 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28517 pane.el.addClass('active');
28522 onTabClick : function(ev,un,ob,pane)
28524 //Roo.log('tab - prev default');
28525 ev.preventDefault();
28528 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28529 pane.tab.addClass('active');
28530 //Roo.log(pane.title);
28531 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28532 // technically we should have a deactivate event.. but maybe add later.
28533 // and it should not de-activate the selected tab...
28534 this.fireEvent('activatepane', pane);
28535 pane.el.addClass('active');
28536 pane.fireEvent('activate');
28541 getActivePane : function()
28544 Roo.each(this.panes, function(p) {
28545 if(p.el.hasClass('active')){
28566 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28568 * @class Roo.bootstrap.TabPane
28569 * @extends Roo.bootstrap.Component
28570 * Bootstrap TabPane class
28571 * @cfg {Boolean} active (false | true) Default false
28572 * @cfg {String} title title of panel
28576 * Create a new TabPane
28577 * @param {Object} config The config object
28580 Roo.bootstrap.dash.TabPane = function(config){
28581 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28587 * When a pane is activated
28588 * @param {Roo.bootstrap.dash.TabPane} pane
28595 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28600 // the tabBox that this is attached to.
28603 getAutoCreate : function()
28611 cfg.cls += ' active';
28616 initEvents : function()
28618 //Roo.log('trigger add pane handler');
28619 this.parent().fireEvent('addpane', this)
28623 * Updates the tab title
28624 * @param {String} html to set the title to.
28626 setTitle: function(str)
28632 this.tab.select('a', true).first().dom.innerHTML = str;
28649 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28652 * @class Roo.bootstrap.menu.Menu
28653 * @extends Roo.bootstrap.Component
28654 * Bootstrap Menu class - container for Menu
28655 * @cfg {String} html Text of the menu
28656 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28657 * @cfg {String} icon Font awesome icon
28658 * @cfg {String} pos Menu align to (top | bottom) default bottom
28662 * Create a new Menu
28663 * @param {Object} config The config object
28667 Roo.bootstrap.menu.Menu = function(config){
28668 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28672 * @event beforeshow
28673 * Fires before this menu is displayed
28674 * @param {Roo.bootstrap.menu.Menu} this
28678 * @event beforehide
28679 * Fires before this menu is hidden
28680 * @param {Roo.bootstrap.menu.Menu} this
28685 * Fires after this menu is displayed
28686 * @param {Roo.bootstrap.menu.Menu} this
28691 * Fires after this menu is hidden
28692 * @param {Roo.bootstrap.menu.Menu} this
28697 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28698 * @param {Roo.bootstrap.menu.Menu} this
28699 * @param {Roo.EventObject} e
28706 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28710 weight : 'default',
28715 getChildContainer : function() {
28716 if(this.isSubMenu){
28720 return this.el.select('ul.dropdown-menu', true).first();
28723 getAutoCreate : function()
28728 cls : 'roo-menu-text',
28736 cls : 'fa ' + this.icon
28747 cls : 'dropdown-button btn btn-' + this.weight,
28752 cls : 'dropdown-toggle btn btn-' + this.weight,
28762 cls : 'dropdown-menu'
28768 if(this.pos == 'top'){
28769 cfg.cls += ' dropup';
28772 if(this.isSubMenu){
28775 cls : 'dropdown-menu'
28782 onRender : function(ct, position)
28784 this.isSubMenu = ct.hasClass('dropdown-submenu');
28786 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28789 initEvents : function()
28791 if(this.isSubMenu){
28795 this.hidden = true;
28797 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28798 this.triggerEl.on('click', this.onTriggerPress, this);
28800 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28801 this.buttonEl.on('click', this.onClick, this);
28807 if(this.isSubMenu){
28811 return this.el.select('ul.dropdown-menu', true).first();
28814 onClick : function(e)
28816 this.fireEvent("click", this, e);
28819 onTriggerPress : function(e)
28821 if (this.isVisible()) {
28828 isVisible : function(){
28829 return !this.hidden;
28834 this.fireEvent("beforeshow", this);
28836 this.hidden = false;
28837 this.el.addClass('open');
28839 Roo.get(document).on("mouseup", this.onMouseUp, this);
28841 this.fireEvent("show", this);
28848 this.fireEvent("beforehide", this);
28850 this.hidden = true;
28851 this.el.removeClass('open');
28853 Roo.get(document).un("mouseup", this.onMouseUp);
28855 this.fireEvent("hide", this);
28858 onMouseUp : function()
28872 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28875 * @class Roo.bootstrap.menu.Item
28876 * @extends Roo.bootstrap.Component
28877 * Bootstrap MenuItem class
28878 * @cfg {Boolean} submenu (true | false) default false
28879 * @cfg {String} html text of the item
28880 * @cfg {String} href the link
28881 * @cfg {Boolean} disable (true | false) default false
28882 * @cfg {Boolean} preventDefault (true | false) default true
28883 * @cfg {String} icon Font awesome icon
28884 * @cfg {String} pos Submenu align to (left | right) default right
28888 * Create a new Item
28889 * @param {Object} config The config object
28893 Roo.bootstrap.menu.Item = function(config){
28894 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28898 * Fires when the mouse is hovering over this menu
28899 * @param {Roo.bootstrap.menu.Item} this
28900 * @param {Roo.EventObject} e
28905 * Fires when the mouse exits this menu
28906 * @param {Roo.bootstrap.menu.Item} this
28907 * @param {Roo.EventObject} e
28913 * The raw click event for the entire grid.
28914 * @param {Roo.EventObject} e
28920 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28925 preventDefault: true,
28930 getAutoCreate : function()
28935 cls : 'roo-menu-item-text',
28943 cls : 'fa ' + this.icon
28952 href : this.href || '#',
28959 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28963 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28965 if(this.pos == 'left'){
28966 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28973 initEvents : function()
28975 this.el.on('mouseover', this.onMouseOver, this);
28976 this.el.on('mouseout', this.onMouseOut, this);
28978 this.el.select('a', true).first().on('click', this.onClick, this);
28982 onClick : function(e)
28984 if(this.preventDefault){
28985 e.preventDefault();
28988 this.fireEvent("click", this, e);
28991 onMouseOver : function(e)
28993 if(this.submenu && this.pos == 'left'){
28994 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28997 this.fireEvent("mouseover", this, e);
29000 onMouseOut : function(e)
29002 this.fireEvent("mouseout", this, e);
29014 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29017 * @class Roo.bootstrap.menu.Separator
29018 * @extends Roo.bootstrap.Component
29019 * Bootstrap Separator class
29022 * Create a new Separator
29023 * @param {Object} config The config object
29027 Roo.bootstrap.menu.Separator = function(config){
29028 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29031 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29033 getAutoCreate : function(){
29036 cls: 'dropdown-divider divider'
29054 * @class Roo.bootstrap.Tooltip
29055 * Bootstrap Tooltip class
29056 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29057 * to determine which dom element triggers the tooltip.
29059 * It needs to add support for additional attributes like tooltip-position
29062 * Create a new Toolti
29063 * @param {Object} config The config object
29066 Roo.bootstrap.Tooltip = function(config){
29067 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29069 this.alignment = Roo.bootstrap.Tooltip.alignment;
29071 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29072 this.alignment = config.alignment;
29077 Roo.apply(Roo.bootstrap.Tooltip, {
29079 * @function init initialize tooltip monitoring.
29083 currentTip : false,
29084 currentRegion : false,
29090 Roo.get(document).on('mouseover', this.enter ,this);
29091 Roo.get(document).on('mouseout', this.leave, this);
29094 this.currentTip = new Roo.bootstrap.Tooltip();
29097 enter : function(ev)
29099 var dom = ev.getTarget();
29101 //Roo.log(['enter',dom]);
29102 var el = Roo.fly(dom);
29103 if (this.currentEl) {
29105 //Roo.log(this.currentEl);
29106 //Roo.log(this.currentEl.contains(dom));
29107 if (this.currentEl == el) {
29110 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29116 if (this.currentTip.el) {
29117 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29121 if(!el || el.dom == document){
29127 if (!el.attr('tooltip')) {
29128 pel = el.findParent("[tooltip]");
29130 bindEl = Roo.get(pel);
29136 // you can not look for children, as if el is the body.. then everythign is the child..
29137 if (!pel && !el.attr('tooltip')) { //
29138 if (!el.select("[tooltip]").elements.length) {
29141 // is the mouse over this child...?
29142 bindEl = el.select("[tooltip]").first();
29143 var xy = ev.getXY();
29144 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29145 //Roo.log("not in region.");
29148 //Roo.log("child element over..");
29151 this.currentEl = el;
29152 this.currentTip.bind(bindEl);
29153 this.currentRegion = Roo.lib.Region.getRegion(dom);
29154 this.currentTip.enter();
29157 leave : function(ev)
29159 var dom = ev.getTarget();
29160 //Roo.log(['leave',dom]);
29161 if (!this.currentEl) {
29166 if (dom != this.currentEl.dom) {
29169 var xy = ev.getXY();
29170 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29173 // only activate leave if mouse cursor is outside... bounding box..
29178 if (this.currentTip) {
29179 this.currentTip.leave();
29181 //Roo.log('clear currentEl');
29182 this.currentEl = false;
29187 'left' : ['r-l', [-2,0], 'right'],
29188 'right' : ['l-r', [2,0], 'left'],
29189 'bottom' : ['t-b', [0,2], 'top'],
29190 'top' : [ 'b-t', [0,-2], 'bottom']
29196 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29201 delay : null, // can be { show : 300 , hide: 500}
29205 hoverState : null, //???
29207 placement : 'bottom',
29211 getAutoCreate : function(){
29218 cls : 'tooltip-arrow arrow'
29221 cls : 'tooltip-inner'
29228 bind : function(el)
29233 initEvents : function()
29235 this.arrowEl = this.el.select('.arrow', true).first();
29236 this.innerEl = this.el.select('.tooltip-inner', true).first();
29239 enter : function () {
29241 if (this.timeout != null) {
29242 clearTimeout(this.timeout);
29245 this.hoverState = 'in';
29246 //Roo.log("enter - show");
29247 if (!this.delay || !this.delay.show) {
29252 this.timeout = setTimeout(function () {
29253 if (_t.hoverState == 'in') {
29256 }, this.delay.show);
29260 clearTimeout(this.timeout);
29262 this.hoverState = 'out';
29263 if (!this.delay || !this.delay.hide) {
29269 this.timeout = setTimeout(function () {
29270 //Roo.log("leave - timeout");
29272 if (_t.hoverState == 'out') {
29274 Roo.bootstrap.Tooltip.currentEl = false;
29279 show : function (msg)
29282 this.render(document.body);
29285 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29287 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29289 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29291 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29292 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29294 var placement = typeof this.placement == 'function' ?
29295 this.placement.call(this, this.el, on_el) :
29298 var autoToken = /\s?auto?\s?/i;
29299 var autoPlace = autoToken.test(placement);
29301 placement = placement.replace(autoToken, '') || 'top';
29305 //this.el.setXY([0,0]);
29307 //this.el.dom.style.display='block';
29309 //this.el.appendTo(on_el);
29311 var p = this.getPosition();
29312 var box = this.el.getBox();
29318 var align = this.alignment[placement];
29320 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29322 if(placement == 'top' || placement == 'bottom'){
29324 placement = 'right';
29327 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29328 placement = 'left';
29331 var scroll = Roo.select('body', true).first().getScroll();
29333 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29337 align = this.alignment[placement];
29339 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29343 var elems = document.getElementsByTagName('div');
29344 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29345 for (var i = 0; i < elems.length; i++) {
29346 var zindex = Number.parseInt(
29347 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29350 if (zindex > highest) {
29357 this.el.dom.style.zIndex = highest;
29359 this.el.alignTo(this.bindEl, align[0],align[1]);
29360 //var arrow = this.el.select('.arrow',true).first();
29361 //arrow.set(align[2],
29363 this.el.addClass(placement);
29364 this.el.addClass("bs-tooltip-"+ placement);
29366 this.el.addClass('in fade show');
29368 this.hoverState = null;
29370 if (this.el.hasClass('fade')) {
29385 //this.el.setXY([0,0]);
29386 this.el.removeClass(['show', 'in']);
29402 * @class Roo.bootstrap.LocationPicker
29403 * @extends Roo.bootstrap.Component
29404 * Bootstrap LocationPicker class
29405 * @cfg {Number} latitude Position when init default 0
29406 * @cfg {Number} longitude Position when init default 0
29407 * @cfg {Number} zoom default 15
29408 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29409 * @cfg {Boolean} mapTypeControl default false
29410 * @cfg {Boolean} disableDoubleClickZoom default false
29411 * @cfg {Boolean} scrollwheel default true
29412 * @cfg {Boolean} streetViewControl default false
29413 * @cfg {Number} radius default 0
29414 * @cfg {String} locationName
29415 * @cfg {Boolean} draggable default true
29416 * @cfg {Boolean} enableAutocomplete default false
29417 * @cfg {Boolean} enableReverseGeocode default true
29418 * @cfg {String} markerTitle
29421 * Create a new LocationPicker
29422 * @param {Object} config The config object
29426 Roo.bootstrap.LocationPicker = function(config){
29428 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29433 * Fires when the picker initialized.
29434 * @param {Roo.bootstrap.LocationPicker} this
29435 * @param {Google Location} location
29439 * @event positionchanged
29440 * Fires when the picker position changed.
29441 * @param {Roo.bootstrap.LocationPicker} this
29442 * @param {Google Location} location
29444 positionchanged : true,
29447 * Fires when the map resize.
29448 * @param {Roo.bootstrap.LocationPicker} this
29453 * Fires when the map show.
29454 * @param {Roo.bootstrap.LocationPicker} this
29459 * Fires when the map hide.
29460 * @param {Roo.bootstrap.LocationPicker} this
29465 * Fires when click the map.
29466 * @param {Roo.bootstrap.LocationPicker} this
29467 * @param {Map event} e
29471 * @event mapRightClick
29472 * Fires when right click the map.
29473 * @param {Roo.bootstrap.LocationPicker} this
29474 * @param {Map event} e
29476 mapRightClick : true,
29478 * @event markerClick
29479 * Fires when click the marker.
29480 * @param {Roo.bootstrap.LocationPicker} this
29481 * @param {Map event} e
29483 markerClick : true,
29485 * @event markerRightClick
29486 * Fires when right click the marker.
29487 * @param {Roo.bootstrap.LocationPicker} this
29488 * @param {Map event} e
29490 markerRightClick : true,
29492 * @event OverlayViewDraw
29493 * Fires when OverlayView Draw
29494 * @param {Roo.bootstrap.LocationPicker} this
29496 OverlayViewDraw : true,
29498 * @event OverlayViewOnAdd
29499 * Fires when OverlayView Draw
29500 * @param {Roo.bootstrap.LocationPicker} this
29502 OverlayViewOnAdd : true,
29504 * @event OverlayViewOnRemove
29505 * Fires when OverlayView Draw
29506 * @param {Roo.bootstrap.LocationPicker} this
29508 OverlayViewOnRemove : true,
29510 * @event OverlayViewShow
29511 * Fires when OverlayView Draw
29512 * @param {Roo.bootstrap.LocationPicker} this
29513 * @param {Pixel} cpx
29515 OverlayViewShow : true,
29517 * @event OverlayViewHide
29518 * Fires when OverlayView Draw
29519 * @param {Roo.bootstrap.LocationPicker} this
29521 OverlayViewHide : true,
29523 * @event loadexception
29524 * Fires when load google lib failed.
29525 * @param {Roo.bootstrap.LocationPicker} this
29527 loadexception : true
29532 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29534 gMapContext: false,
29540 mapTypeControl: false,
29541 disableDoubleClickZoom: false,
29543 streetViewControl: false,
29547 enableAutocomplete: false,
29548 enableReverseGeocode: true,
29551 getAutoCreate: function()
29556 cls: 'roo-location-picker'
29562 initEvents: function(ct, position)
29564 if(!this.el.getWidth() || this.isApplied()){
29568 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29573 initial: function()
29575 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29576 this.fireEvent('loadexception', this);
29580 if(!this.mapTypeId){
29581 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29584 this.gMapContext = this.GMapContext();
29586 this.initOverlayView();
29588 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29592 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29593 _this.setPosition(_this.gMapContext.marker.position);
29596 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29597 _this.fireEvent('mapClick', this, event);
29601 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29602 _this.fireEvent('mapRightClick', this, event);
29606 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29607 _this.fireEvent('markerClick', this, event);
29611 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29612 _this.fireEvent('markerRightClick', this, event);
29616 this.setPosition(this.gMapContext.location);
29618 this.fireEvent('initial', this, this.gMapContext.location);
29621 initOverlayView: function()
29625 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29629 _this.fireEvent('OverlayViewDraw', _this);
29634 _this.fireEvent('OverlayViewOnAdd', _this);
29637 onRemove: function()
29639 _this.fireEvent('OverlayViewOnRemove', _this);
29642 show: function(cpx)
29644 _this.fireEvent('OverlayViewShow', _this, cpx);
29649 _this.fireEvent('OverlayViewHide', _this);
29655 fromLatLngToContainerPixel: function(event)
29657 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29660 isApplied: function()
29662 return this.getGmapContext() == false ? false : true;
29665 getGmapContext: function()
29667 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29670 GMapContext: function()
29672 var position = new google.maps.LatLng(this.latitude, this.longitude);
29674 var _map = new google.maps.Map(this.el.dom, {
29677 mapTypeId: this.mapTypeId,
29678 mapTypeControl: this.mapTypeControl,
29679 disableDoubleClickZoom: this.disableDoubleClickZoom,
29680 scrollwheel: this.scrollwheel,
29681 streetViewControl: this.streetViewControl,
29682 locationName: this.locationName,
29683 draggable: this.draggable,
29684 enableAutocomplete: this.enableAutocomplete,
29685 enableReverseGeocode: this.enableReverseGeocode
29688 var _marker = new google.maps.Marker({
29689 position: position,
29691 title: this.markerTitle,
29692 draggable: this.draggable
29699 location: position,
29700 radius: this.radius,
29701 locationName: this.locationName,
29702 addressComponents: {
29703 formatted_address: null,
29704 addressLine1: null,
29705 addressLine2: null,
29707 streetNumber: null,
29711 stateOrProvince: null
29714 domContainer: this.el.dom,
29715 geodecoder: new google.maps.Geocoder()
29719 drawCircle: function(center, radius, options)
29721 if (this.gMapContext.circle != null) {
29722 this.gMapContext.circle.setMap(null);
29726 options = Roo.apply({}, options, {
29727 strokeColor: "#0000FF",
29728 strokeOpacity: .35,
29730 fillColor: "#0000FF",
29734 options.map = this.gMapContext.map;
29735 options.radius = radius;
29736 options.center = center;
29737 this.gMapContext.circle = new google.maps.Circle(options);
29738 return this.gMapContext.circle;
29744 setPosition: function(location)
29746 this.gMapContext.location = location;
29747 this.gMapContext.marker.setPosition(location);
29748 this.gMapContext.map.panTo(location);
29749 this.drawCircle(location, this.gMapContext.radius, {});
29753 if (this.gMapContext.settings.enableReverseGeocode) {
29754 this.gMapContext.geodecoder.geocode({
29755 latLng: this.gMapContext.location
29756 }, function(results, status) {
29758 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29759 _this.gMapContext.locationName = results[0].formatted_address;
29760 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29762 _this.fireEvent('positionchanged', this, location);
29769 this.fireEvent('positionchanged', this, location);
29774 google.maps.event.trigger(this.gMapContext.map, "resize");
29776 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29778 this.fireEvent('resize', this);
29781 setPositionByLatLng: function(latitude, longitude)
29783 this.setPosition(new google.maps.LatLng(latitude, longitude));
29786 getCurrentPosition: function()
29789 latitude: this.gMapContext.location.lat(),
29790 longitude: this.gMapContext.location.lng()
29794 getAddressName: function()
29796 return this.gMapContext.locationName;
29799 getAddressComponents: function()
29801 return this.gMapContext.addressComponents;
29804 address_component_from_google_geocode: function(address_components)
29808 for (var i = 0; i < address_components.length; i++) {
29809 var component = address_components[i];
29810 if (component.types.indexOf("postal_code") >= 0) {
29811 result.postalCode = component.short_name;
29812 } else if (component.types.indexOf("street_number") >= 0) {
29813 result.streetNumber = component.short_name;
29814 } else if (component.types.indexOf("route") >= 0) {
29815 result.streetName = component.short_name;
29816 } else if (component.types.indexOf("neighborhood") >= 0) {
29817 result.city = component.short_name;
29818 } else if (component.types.indexOf("locality") >= 0) {
29819 result.city = component.short_name;
29820 } else if (component.types.indexOf("sublocality") >= 0) {
29821 result.district = component.short_name;
29822 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29823 result.stateOrProvince = component.short_name;
29824 } else if (component.types.indexOf("country") >= 0) {
29825 result.country = component.short_name;
29829 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29830 result.addressLine2 = "";
29834 setZoomLevel: function(zoom)
29836 this.gMapContext.map.setZoom(zoom);
29849 this.fireEvent('show', this);
29860 this.fireEvent('hide', this);
29865 Roo.apply(Roo.bootstrap.LocationPicker, {
29867 OverlayView : function(map, options)
29869 options = options || {};
29876 * @class Roo.bootstrap.Alert
29877 * @extends Roo.bootstrap.Component
29878 * Bootstrap Alert class - shows an alert area box
29880 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29881 Enter a valid email address
29884 * @cfg {String} title The title of alert
29885 * @cfg {String} html The content of alert
29886 * @cfg {String} weight ( success | info | warning | danger )
29887 * @cfg {String} fa font-awesomeicon
29888 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29889 * @cfg {Boolean} close true to show a x closer
29893 * Create a new alert
29894 * @param {Object} config The config object
29898 Roo.bootstrap.Alert = function(config){
29899 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29903 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29909 faicon: false, // BC
29913 getAutoCreate : function()
29925 style : this.close ? '' : 'display:none'
29929 cls : 'roo-alert-icon'
29934 cls : 'roo-alert-title',
29939 cls : 'roo-alert-text',
29946 cfg.cn[0].cls += ' fa ' + this.faicon;
29949 cfg.cn[0].cls += ' fa ' + this.fa;
29953 cfg.cls += ' alert-' + this.weight;
29959 initEvents: function()
29961 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29962 this.titleEl = this.el.select('.roo-alert-title',true).first();
29963 this.iconEl = this.el.select('.roo-alert-icon',true).first();
29964 if (this.seconds > 0) {
29965 this.hide.defer(this.seconds, this);
29969 setTitle : function(str)
29971 this.titleEl.dom.innerHTML = str;
29974 setText : function(str)
29976 this.titleEl.dom.innerHTML = str;
29979 setWeight : function(weight)
29982 this.el.removeClass('alert-' + this.weight);
29985 this.weight = weight;
29987 this.el.addClass('alert-' + this.weight);
29990 setIcon : function(icon)
29993 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29996 this.faicon = icon;
29998 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30019 * @class Roo.bootstrap.UploadCropbox
30020 * @extends Roo.bootstrap.Component
30021 * Bootstrap UploadCropbox class
30022 * @cfg {String} emptyText show when image has been loaded
30023 * @cfg {String} rotateNotify show when image too small to rotate
30024 * @cfg {Number} errorTimeout default 3000
30025 * @cfg {Number} minWidth default 300
30026 * @cfg {Number} minHeight default 300
30027 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30028 * @cfg {Boolean} isDocument (true|false) default false
30029 * @cfg {String} url action url
30030 * @cfg {String} paramName default 'imageUpload'
30031 * @cfg {String} method default POST
30032 * @cfg {Boolean} loadMask (true|false) default true
30033 * @cfg {Boolean} loadingText default 'Loading...'
30036 * Create a new UploadCropbox
30037 * @param {Object} config The config object
30040 Roo.bootstrap.UploadCropbox = function(config){
30041 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30045 * @event beforeselectfile
30046 * Fire before select file
30047 * @param {Roo.bootstrap.UploadCropbox} this
30049 "beforeselectfile" : true,
30052 * Fire after initEvent
30053 * @param {Roo.bootstrap.UploadCropbox} this
30058 * Fire after initEvent
30059 * @param {Roo.bootstrap.UploadCropbox} this
30060 * @param {String} data
30065 * Fire when preparing the file data
30066 * @param {Roo.bootstrap.UploadCropbox} this
30067 * @param {Object} file
30072 * Fire when get exception
30073 * @param {Roo.bootstrap.UploadCropbox} this
30074 * @param {XMLHttpRequest} xhr
30076 "exception" : true,
30078 * @event beforeloadcanvas
30079 * Fire before load the canvas
30080 * @param {Roo.bootstrap.UploadCropbox} this
30081 * @param {String} src
30083 "beforeloadcanvas" : true,
30086 * Fire when trash image
30087 * @param {Roo.bootstrap.UploadCropbox} this
30092 * Fire when download the image
30093 * @param {Roo.bootstrap.UploadCropbox} this
30097 * @event footerbuttonclick
30098 * Fire when footerbuttonclick
30099 * @param {Roo.bootstrap.UploadCropbox} this
30100 * @param {String} type
30102 "footerbuttonclick" : true,
30106 * @param {Roo.bootstrap.UploadCropbox} this
30111 * Fire when rotate the image
30112 * @param {Roo.bootstrap.UploadCropbox} this
30113 * @param {String} pos
30118 * Fire when inspect the file
30119 * @param {Roo.bootstrap.UploadCropbox} this
30120 * @param {Object} file
30125 * Fire when xhr upload the file
30126 * @param {Roo.bootstrap.UploadCropbox} this
30127 * @param {Object} data
30132 * Fire when arrange the file data
30133 * @param {Roo.bootstrap.UploadCropbox} this
30134 * @param {Object} formData
30139 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30142 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30144 emptyText : 'Click to upload image',
30145 rotateNotify : 'Image is too small to rotate',
30146 errorTimeout : 3000,
30160 cropType : 'image/jpeg',
30162 canvasLoaded : false,
30163 isDocument : false,
30165 paramName : 'imageUpload',
30167 loadingText : 'Loading...',
30170 getAutoCreate : function()
30174 cls : 'roo-upload-cropbox',
30178 cls : 'roo-upload-cropbox-selector',
30183 cls : 'roo-upload-cropbox-body',
30184 style : 'cursor:pointer',
30188 cls : 'roo-upload-cropbox-preview'
30192 cls : 'roo-upload-cropbox-thumb'
30196 cls : 'roo-upload-cropbox-empty-notify',
30197 html : this.emptyText
30201 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30202 html : this.rotateNotify
30208 cls : 'roo-upload-cropbox-footer',
30211 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30221 onRender : function(ct, position)
30223 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30225 if (this.buttons.length) {
30227 Roo.each(this.buttons, function(bb) {
30229 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30231 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30237 this.maskEl = this.el;
30241 initEvents : function()
30243 this.urlAPI = (window.createObjectURL && window) ||
30244 (window.URL && URL.revokeObjectURL && URL) ||
30245 (window.webkitURL && webkitURL);
30247 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30248 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30250 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30251 this.selectorEl.hide();
30253 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30254 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30256 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30257 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30258 this.thumbEl.hide();
30260 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30261 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30263 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30264 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30265 this.errorEl.hide();
30267 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30268 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30269 this.footerEl.hide();
30271 this.setThumbBoxSize();
30277 this.fireEvent('initial', this);
30284 window.addEventListener("resize", function() { _this.resize(); } );
30286 this.bodyEl.on('click', this.beforeSelectFile, this);
30289 this.bodyEl.on('touchstart', this.onTouchStart, this);
30290 this.bodyEl.on('touchmove', this.onTouchMove, this);
30291 this.bodyEl.on('touchend', this.onTouchEnd, this);
30295 this.bodyEl.on('mousedown', this.onMouseDown, this);
30296 this.bodyEl.on('mousemove', this.onMouseMove, this);
30297 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30298 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30299 Roo.get(document).on('mouseup', this.onMouseUp, this);
30302 this.selectorEl.on('change', this.onFileSelected, this);
30308 this.baseScale = 1;
30310 this.baseRotate = 1;
30311 this.dragable = false;
30312 this.pinching = false;
30315 this.cropData = false;
30316 this.notifyEl.dom.innerHTML = this.emptyText;
30318 this.selectorEl.dom.value = '';
30322 resize : function()
30324 if(this.fireEvent('resize', this) != false){
30325 this.setThumbBoxPosition();
30326 this.setCanvasPosition();
30330 onFooterButtonClick : function(e, el, o, type)
30333 case 'rotate-left' :
30334 this.onRotateLeft(e);
30336 case 'rotate-right' :
30337 this.onRotateRight(e);
30340 this.beforeSelectFile(e);
30355 this.fireEvent('footerbuttonclick', this, type);
30358 beforeSelectFile : function(e)
30360 e.preventDefault();
30362 if(this.fireEvent('beforeselectfile', this) != false){
30363 this.selectorEl.dom.click();
30367 onFileSelected : function(e)
30369 e.preventDefault();
30371 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30375 var file = this.selectorEl.dom.files[0];
30377 if(this.fireEvent('inspect', this, file) != false){
30378 this.prepare(file);
30383 trash : function(e)
30385 this.fireEvent('trash', this);
30388 download : function(e)
30390 this.fireEvent('download', this);
30393 loadCanvas : function(src)
30395 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30399 this.imageEl = document.createElement('img');
30403 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30405 this.imageEl.src = src;
30409 onLoadCanvas : function()
30411 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30412 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30414 this.bodyEl.un('click', this.beforeSelectFile, this);
30416 this.notifyEl.hide();
30417 this.thumbEl.show();
30418 this.footerEl.show();
30420 this.baseRotateLevel();
30422 if(this.isDocument){
30423 this.setThumbBoxSize();
30426 this.setThumbBoxPosition();
30428 this.baseScaleLevel();
30434 this.canvasLoaded = true;
30437 this.maskEl.unmask();
30442 setCanvasPosition : function()
30444 if(!this.canvasEl){
30448 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30449 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30451 this.previewEl.setLeft(pw);
30452 this.previewEl.setTop(ph);
30456 onMouseDown : function(e)
30460 this.dragable = true;
30461 this.pinching = false;
30463 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30464 this.dragable = false;
30468 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30469 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30473 onMouseMove : function(e)
30477 if(!this.canvasLoaded){
30481 if (!this.dragable){
30485 var minX = Math.ceil(this.thumbEl.getLeft(true));
30486 var minY = Math.ceil(this.thumbEl.getTop(true));
30488 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30489 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30491 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30492 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30494 x = x - this.mouseX;
30495 y = y - this.mouseY;
30497 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30498 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30500 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30501 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30503 this.previewEl.setLeft(bgX);
30504 this.previewEl.setTop(bgY);
30506 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30507 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30510 onMouseUp : function(e)
30514 this.dragable = false;
30517 onMouseWheel : function(e)
30521 this.startScale = this.scale;
30523 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30525 if(!this.zoomable()){
30526 this.scale = this.startScale;
30535 zoomable : function()
30537 var minScale = this.thumbEl.getWidth() / this.minWidth;
30539 if(this.minWidth < this.minHeight){
30540 minScale = this.thumbEl.getHeight() / this.minHeight;
30543 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30544 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30548 (this.rotate == 0 || this.rotate == 180) &&
30550 width > this.imageEl.OriginWidth ||
30551 height > this.imageEl.OriginHeight ||
30552 (width < this.minWidth && height < this.minHeight)
30560 (this.rotate == 90 || this.rotate == 270) &&
30562 width > this.imageEl.OriginWidth ||
30563 height > this.imageEl.OriginHeight ||
30564 (width < this.minHeight && height < this.minWidth)
30571 !this.isDocument &&
30572 (this.rotate == 0 || this.rotate == 180) &&
30574 width < this.minWidth ||
30575 width > this.imageEl.OriginWidth ||
30576 height < this.minHeight ||
30577 height > this.imageEl.OriginHeight
30584 !this.isDocument &&
30585 (this.rotate == 90 || this.rotate == 270) &&
30587 width < this.minHeight ||
30588 width > this.imageEl.OriginWidth ||
30589 height < this.minWidth ||
30590 height > this.imageEl.OriginHeight
30600 onRotateLeft : function(e)
30602 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30604 var minScale = this.thumbEl.getWidth() / this.minWidth;
30606 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30607 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30609 this.startScale = this.scale;
30611 while (this.getScaleLevel() < minScale){
30613 this.scale = this.scale + 1;
30615 if(!this.zoomable()){
30620 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30621 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30626 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30633 this.scale = this.startScale;
30635 this.onRotateFail();
30640 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30642 if(this.isDocument){
30643 this.setThumbBoxSize();
30644 this.setThumbBoxPosition();
30645 this.setCanvasPosition();
30650 this.fireEvent('rotate', this, 'left');
30654 onRotateRight : function(e)
30656 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30658 var minScale = this.thumbEl.getWidth() / this.minWidth;
30660 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30661 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30663 this.startScale = this.scale;
30665 while (this.getScaleLevel() < minScale){
30667 this.scale = this.scale + 1;
30669 if(!this.zoomable()){
30674 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30675 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30680 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30687 this.scale = this.startScale;
30689 this.onRotateFail();
30694 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30696 if(this.isDocument){
30697 this.setThumbBoxSize();
30698 this.setThumbBoxPosition();
30699 this.setCanvasPosition();
30704 this.fireEvent('rotate', this, 'right');
30707 onRotateFail : function()
30709 this.errorEl.show(true);
30713 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30718 this.previewEl.dom.innerHTML = '';
30720 var canvasEl = document.createElement("canvas");
30722 var contextEl = canvasEl.getContext("2d");
30724 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30725 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30726 var center = this.imageEl.OriginWidth / 2;
30728 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30729 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30730 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30731 center = this.imageEl.OriginHeight / 2;
30734 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30736 contextEl.translate(center, center);
30737 contextEl.rotate(this.rotate * Math.PI / 180);
30739 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30741 this.canvasEl = document.createElement("canvas");
30743 this.contextEl = this.canvasEl.getContext("2d");
30745 switch (this.rotate) {
30748 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30749 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30751 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30756 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30757 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30759 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30760 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);
30764 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30769 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30770 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30772 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30773 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);
30777 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);
30782 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30783 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30785 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30786 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30790 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);
30797 this.previewEl.appendChild(this.canvasEl);
30799 this.setCanvasPosition();
30804 if(!this.canvasLoaded){
30808 var imageCanvas = document.createElement("canvas");
30810 var imageContext = imageCanvas.getContext("2d");
30812 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30813 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30815 var center = imageCanvas.width / 2;
30817 imageContext.translate(center, center);
30819 imageContext.rotate(this.rotate * Math.PI / 180);
30821 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30823 var canvas = document.createElement("canvas");
30825 var context = canvas.getContext("2d");
30827 canvas.width = this.minWidth;
30828 canvas.height = this.minHeight;
30830 switch (this.rotate) {
30833 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30834 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30836 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30837 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30839 var targetWidth = this.minWidth - 2 * x;
30840 var targetHeight = this.minHeight - 2 * y;
30844 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30845 scale = targetWidth / width;
30848 if(x > 0 && y == 0){
30849 scale = targetHeight / height;
30852 if(x > 0 && y > 0){
30853 scale = targetWidth / width;
30855 if(width < height){
30856 scale = targetHeight / height;
30860 context.scale(scale, scale);
30862 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30863 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30865 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30866 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30868 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30873 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30874 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30876 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30877 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30879 var targetWidth = this.minWidth - 2 * x;
30880 var targetHeight = this.minHeight - 2 * y;
30884 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30885 scale = targetWidth / width;
30888 if(x > 0 && y == 0){
30889 scale = targetHeight / height;
30892 if(x > 0 && y > 0){
30893 scale = targetWidth / width;
30895 if(width < height){
30896 scale = targetHeight / height;
30900 context.scale(scale, scale);
30902 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30903 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30905 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30906 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30908 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30910 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30915 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30916 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30918 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30919 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30921 var targetWidth = this.minWidth - 2 * x;
30922 var targetHeight = this.minHeight - 2 * y;
30926 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30927 scale = targetWidth / width;
30930 if(x > 0 && y == 0){
30931 scale = targetHeight / height;
30934 if(x > 0 && y > 0){
30935 scale = targetWidth / width;
30937 if(width < height){
30938 scale = targetHeight / height;
30942 context.scale(scale, scale);
30944 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30945 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30947 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30948 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30950 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30951 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30953 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30958 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30959 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30961 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30962 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30964 var targetWidth = this.minWidth - 2 * x;
30965 var targetHeight = this.minHeight - 2 * y;
30969 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30970 scale = targetWidth / width;
30973 if(x > 0 && y == 0){
30974 scale = targetHeight / height;
30977 if(x > 0 && y > 0){
30978 scale = targetWidth / width;
30980 if(width < height){
30981 scale = targetHeight / height;
30985 context.scale(scale, scale);
30987 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30988 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30990 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30991 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30993 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30995 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31002 this.cropData = canvas.toDataURL(this.cropType);
31004 if(this.fireEvent('crop', this, this.cropData) !== false){
31005 this.process(this.file, this.cropData);
31012 setThumbBoxSize : function()
31016 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31017 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31018 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31020 this.minWidth = width;
31021 this.minHeight = height;
31023 if(this.rotate == 90 || this.rotate == 270){
31024 this.minWidth = height;
31025 this.minHeight = width;
31030 width = Math.ceil(this.minWidth * height / this.minHeight);
31032 if(this.minWidth > this.minHeight){
31034 height = Math.ceil(this.minHeight * width / this.minWidth);
31037 this.thumbEl.setStyle({
31038 width : width + 'px',
31039 height : height + 'px'
31046 setThumbBoxPosition : function()
31048 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31049 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31051 this.thumbEl.setLeft(x);
31052 this.thumbEl.setTop(y);
31056 baseRotateLevel : function()
31058 this.baseRotate = 1;
31061 typeof(this.exif) != 'undefined' &&
31062 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31063 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31065 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31068 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31072 baseScaleLevel : function()
31076 if(this.isDocument){
31078 if(this.baseRotate == 6 || this.baseRotate == 8){
31080 height = this.thumbEl.getHeight();
31081 this.baseScale = height / this.imageEl.OriginWidth;
31083 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31084 width = this.thumbEl.getWidth();
31085 this.baseScale = width / this.imageEl.OriginHeight;
31091 height = this.thumbEl.getHeight();
31092 this.baseScale = height / this.imageEl.OriginHeight;
31094 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31095 width = this.thumbEl.getWidth();
31096 this.baseScale = width / this.imageEl.OriginWidth;
31102 if(this.baseRotate == 6 || this.baseRotate == 8){
31104 width = this.thumbEl.getHeight();
31105 this.baseScale = width / this.imageEl.OriginHeight;
31107 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31108 height = this.thumbEl.getWidth();
31109 this.baseScale = height / this.imageEl.OriginHeight;
31112 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31113 height = this.thumbEl.getWidth();
31114 this.baseScale = height / this.imageEl.OriginHeight;
31116 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31117 width = this.thumbEl.getHeight();
31118 this.baseScale = width / this.imageEl.OriginWidth;
31125 width = this.thumbEl.getWidth();
31126 this.baseScale = width / this.imageEl.OriginWidth;
31128 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31129 height = this.thumbEl.getHeight();
31130 this.baseScale = height / this.imageEl.OriginHeight;
31133 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31135 height = this.thumbEl.getHeight();
31136 this.baseScale = height / this.imageEl.OriginHeight;
31138 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31139 width = this.thumbEl.getWidth();
31140 this.baseScale = width / this.imageEl.OriginWidth;
31148 getScaleLevel : function()
31150 return this.baseScale * Math.pow(1.1, this.scale);
31153 onTouchStart : function(e)
31155 if(!this.canvasLoaded){
31156 this.beforeSelectFile(e);
31160 var touches = e.browserEvent.touches;
31166 if(touches.length == 1){
31167 this.onMouseDown(e);
31171 if(touches.length != 2){
31177 for(var i = 0, finger; finger = touches[i]; i++){
31178 coords.push(finger.pageX, finger.pageY);
31181 var x = Math.pow(coords[0] - coords[2], 2);
31182 var y = Math.pow(coords[1] - coords[3], 2);
31184 this.startDistance = Math.sqrt(x + y);
31186 this.startScale = this.scale;
31188 this.pinching = true;
31189 this.dragable = false;
31193 onTouchMove : function(e)
31195 if(!this.pinching && !this.dragable){
31199 var touches = e.browserEvent.touches;
31206 this.onMouseMove(e);
31212 for(var i = 0, finger; finger = touches[i]; i++){
31213 coords.push(finger.pageX, finger.pageY);
31216 var x = Math.pow(coords[0] - coords[2], 2);
31217 var y = Math.pow(coords[1] - coords[3], 2);
31219 this.endDistance = Math.sqrt(x + y);
31221 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31223 if(!this.zoomable()){
31224 this.scale = this.startScale;
31232 onTouchEnd : function(e)
31234 this.pinching = false;
31235 this.dragable = false;
31239 process : function(file, crop)
31242 this.maskEl.mask(this.loadingText);
31245 this.xhr = new XMLHttpRequest();
31247 file.xhr = this.xhr;
31249 this.xhr.open(this.method, this.url, true);
31252 "Accept": "application/json",
31253 "Cache-Control": "no-cache",
31254 "X-Requested-With": "XMLHttpRequest"
31257 for (var headerName in headers) {
31258 var headerValue = headers[headerName];
31260 this.xhr.setRequestHeader(headerName, headerValue);
31266 this.xhr.onload = function()
31268 _this.xhrOnLoad(_this.xhr);
31271 this.xhr.onerror = function()
31273 _this.xhrOnError(_this.xhr);
31276 var formData = new FormData();
31278 formData.append('returnHTML', 'NO');
31281 formData.append('crop', crop);
31284 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31285 formData.append(this.paramName, file, file.name);
31288 if(typeof(file.filename) != 'undefined'){
31289 formData.append('filename', file.filename);
31292 if(typeof(file.mimetype) != 'undefined'){
31293 formData.append('mimetype', file.mimetype);
31296 if(this.fireEvent('arrange', this, formData) != false){
31297 this.xhr.send(formData);
31301 xhrOnLoad : function(xhr)
31304 this.maskEl.unmask();
31307 if (xhr.readyState !== 4) {
31308 this.fireEvent('exception', this, xhr);
31312 var response = Roo.decode(xhr.responseText);
31314 if(!response.success){
31315 this.fireEvent('exception', this, xhr);
31319 var response = Roo.decode(xhr.responseText);
31321 this.fireEvent('upload', this, response);
31325 xhrOnError : function()
31328 this.maskEl.unmask();
31331 Roo.log('xhr on error');
31333 var response = Roo.decode(xhr.responseText);
31339 prepare : function(file)
31342 this.maskEl.mask(this.loadingText);
31348 if(typeof(file) === 'string'){
31349 this.loadCanvas(file);
31353 if(!file || !this.urlAPI){
31358 this.cropType = file.type;
31362 if(this.fireEvent('prepare', this, this.file) != false){
31364 var reader = new FileReader();
31366 reader.onload = function (e) {
31367 if (e.target.error) {
31368 Roo.log(e.target.error);
31372 var buffer = e.target.result,
31373 dataView = new DataView(buffer),
31375 maxOffset = dataView.byteLength - 4,
31379 if (dataView.getUint16(0) === 0xffd8) {
31380 while (offset < maxOffset) {
31381 markerBytes = dataView.getUint16(offset);
31383 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31384 markerLength = dataView.getUint16(offset + 2) + 2;
31385 if (offset + markerLength > dataView.byteLength) {
31386 Roo.log('Invalid meta data: Invalid segment size.');
31390 if(markerBytes == 0xffe1){
31391 _this.parseExifData(
31398 offset += markerLength;
31408 var url = _this.urlAPI.createObjectURL(_this.file);
31410 _this.loadCanvas(url);
31415 reader.readAsArrayBuffer(this.file);
31421 parseExifData : function(dataView, offset, length)
31423 var tiffOffset = offset + 10,
31427 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31428 // No Exif data, might be XMP data instead
31432 // Check for the ASCII code for "Exif" (0x45786966):
31433 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31434 // No Exif data, might be XMP data instead
31437 if (tiffOffset + 8 > dataView.byteLength) {
31438 Roo.log('Invalid Exif data: Invalid segment size.');
31441 // Check for the two null bytes:
31442 if (dataView.getUint16(offset + 8) !== 0x0000) {
31443 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31446 // Check the byte alignment:
31447 switch (dataView.getUint16(tiffOffset)) {
31449 littleEndian = true;
31452 littleEndian = false;
31455 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31458 // Check for the TIFF tag marker (0x002A):
31459 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31460 Roo.log('Invalid Exif data: Missing TIFF marker.');
31463 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31464 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31466 this.parseExifTags(
31469 tiffOffset + dirOffset,
31474 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31479 if (dirOffset + 6 > dataView.byteLength) {
31480 Roo.log('Invalid Exif data: Invalid directory offset.');
31483 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31484 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31485 if (dirEndOffset + 4 > dataView.byteLength) {
31486 Roo.log('Invalid Exif data: Invalid directory size.');
31489 for (i = 0; i < tagsNumber; i += 1) {
31493 dirOffset + 2 + 12 * i, // tag offset
31497 // Return the offset to the next directory:
31498 return dataView.getUint32(dirEndOffset, littleEndian);
31501 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31503 var tag = dataView.getUint16(offset, littleEndian);
31505 this.exif[tag] = this.getExifValue(
31509 dataView.getUint16(offset + 2, littleEndian), // tag type
31510 dataView.getUint32(offset + 4, littleEndian), // tag length
31515 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31517 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31526 Roo.log('Invalid Exif data: Invalid tag type.');
31530 tagSize = tagType.size * length;
31531 // Determine if the value is contained in the dataOffset bytes,
31532 // or if the value at the dataOffset is a pointer to the actual data:
31533 dataOffset = tagSize > 4 ?
31534 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31535 if (dataOffset + tagSize > dataView.byteLength) {
31536 Roo.log('Invalid Exif data: Invalid data offset.');
31539 if (length === 1) {
31540 return tagType.getValue(dataView, dataOffset, littleEndian);
31543 for (i = 0; i < length; i += 1) {
31544 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31547 if (tagType.ascii) {
31549 // Concatenate the chars:
31550 for (i = 0; i < values.length; i += 1) {
31552 // Ignore the terminating NULL byte(s):
31553 if (c === '\u0000') {
31565 Roo.apply(Roo.bootstrap.UploadCropbox, {
31567 'Orientation': 0x0112
31571 1: 0, //'top-left',
31573 3: 180, //'bottom-right',
31574 // 4: 'bottom-left',
31576 6: 90, //'right-top',
31577 // 7: 'right-bottom',
31578 8: 270 //'left-bottom'
31582 // byte, 8-bit unsigned int:
31584 getValue: function (dataView, dataOffset) {
31585 return dataView.getUint8(dataOffset);
31589 // ascii, 8-bit byte:
31591 getValue: function (dataView, dataOffset) {
31592 return String.fromCharCode(dataView.getUint8(dataOffset));
31597 // short, 16 bit int:
31599 getValue: function (dataView, dataOffset, littleEndian) {
31600 return dataView.getUint16(dataOffset, littleEndian);
31604 // long, 32 bit int:
31606 getValue: function (dataView, dataOffset, littleEndian) {
31607 return dataView.getUint32(dataOffset, littleEndian);
31611 // rational = two long values, first is numerator, second is denominator:
31613 getValue: function (dataView, dataOffset, littleEndian) {
31614 return dataView.getUint32(dataOffset, littleEndian) /
31615 dataView.getUint32(dataOffset + 4, littleEndian);
31619 // slong, 32 bit signed int:
31621 getValue: function (dataView, dataOffset, littleEndian) {
31622 return dataView.getInt32(dataOffset, littleEndian);
31626 // srational, two slongs, first is numerator, second is denominator:
31628 getValue: function (dataView, dataOffset, littleEndian) {
31629 return dataView.getInt32(dataOffset, littleEndian) /
31630 dataView.getInt32(dataOffset + 4, littleEndian);
31640 cls : 'btn-group roo-upload-cropbox-rotate-left',
31641 action : 'rotate-left',
31645 cls : 'btn btn-default',
31646 html : '<i class="fa fa-undo"></i>'
31652 cls : 'btn-group roo-upload-cropbox-picture',
31653 action : 'picture',
31657 cls : 'btn btn-default',
31658 html : '<i class="fa fa-picture-o"></i>'
31664 cls : 'btn-group roo-upload-cropbox-rotate-right',
31665 action : 'rotate-right',
31669 cls : 'btn btn-default',
31670 html : '<i class="fa fa-repeat"></i>'
31678 cls : 'btn-group roo-upload-cropbox-rotate-left',
31679 action : 'rotate-left',
31683 cls : 'btn btn-default',
31684 html : '<i class="fa fa-undo"></i>'
31690 cls : 'btn-group roo-upload-cropbox-download',
31691 action : 'download',
31695 cls : 'btn btn-default',
31696 html : '<i class="fa fa-download"></i>'
31702 cls : 'btn-group roo-upload-cropbox-crop',
31707 cls : 'btn btn-default',
31708 html : '<i class="fa fa-crop"></i>'
31714 cls : 'btn-group roo-upload-cropbox-trash',
31719 cls : 'btn btn-default',
31720 html : '<i class="fa fa-trash"></i>'
31726 cls : 'btn-group roo-upload-cropbox-rotate-right',
31727 action : 'rotate-right',
31731 cls : 'btn btn-default',
31732 html : '<i class="fa fa-repeat"></i>'
31740 cls : 'btn-group roo-upload-cropbox-rotate-left',
31741 action : 'rotate-left',
31745 cls : 'btn btn-default',
31746 html : '<i class="fa fa-undo"></i>'
31752 cls : 'btn-group roo-upload-cropbox-rotate-right',
31753 action : 'rotate-right',
31757 cls : 'btn btn-default',
31758 html : '<i class="fa fa-repeat"></i>'
31771 * @class Roo.bootstrap.DocumentManager
31772 * @extends Roo.bootstrap.Component
31773 * Bootstrap DocumentManager class
31774 * @cfg {String} paramName default 'imageUpload'
31775 * @cfg {String} toolTipName default 'filename'
31776 * @cfg {String} method default POST
31777 * @cfg {String} url action url
31778 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31779 * @cfg {Boolean} multiple multiple upload default true
31780 * @cfg {Number} thumbSize default 300
31781 * @cfg {String} fieldLabel
31782 * @cfg {Number} labelWidth default 4
31783 * @cfg {String} labelAlign (left|top) default left
31784 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31785 * @cfg {Number} labellg set the width of label (1-12)
31786 * @cfg {Number} labelmd set the width of label (1-12)
31787 * @cfg {Number} labelsm set the width of label (1-12)
31788 * @cfg {Number} labelxs set the width of label (1-12)
31791 * Create a new DocumentManager
31792 * @param {Object} config The config object
31795 Roo.bootstrap.DocumentManager = function(config){
31796 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31799 this.delegates = [];
31804 * Fire when initial the DocumentManager
31805 * @param {Roo.bootstrap.DocumentManager} this
31810 * inspect selected file
31811 * @param {Roo.bootstrap.DocumentManager} this
31812 * @param {File} file
31817 * Fire when xhr load exception
31818 * @param {Roo.bootstrap.DocumentManager} this
31819 * @param {XMLHttpRequest} xhr
31821 "exception" : true,
31823 * @event afterupload
31824 * Fire when xhr load exception
31825 * @param {Roo.bootstrap.DocumentManager} this
31826 * @param {XMLHttpRequest} xhr
31828 "afterupload" : true,
31831 * prepare the form data
31832 * @param {Roo.bootstrap.DocumentManager} this
31833 * @param {Object} formData
31838 * Fire when remove the file
31839 * @param {Roo.bootstrap.DocumentManager} this
31840 * @param {Object} file
31845 * Fire after refresh the file
31846 * @param {Roo.bootstrap.DocumentManager} this
31851 * Fire after click the image
31852 * @param {Roo.bootstrap.DocumentManager} this
31853 * @param {Object} file
31858 * Fire when upload a image and editable set to true
31859 * @param {Roo.bootstrap.DocumentManager} this
31860 * @param {Object} file
31864 * @event beforeselectfile
31865 * Fire before select file
31866 * @param {Roo.bootstrap.DocumentManager} this
31868 "beforeselectfile" : true,
31871 * Fire before process file
31872 * @param {Roo.bootstrap.DocumentManager} this
31873 * @param {Object} file
31877 * @event previewrendered
31878 * Fire when preview rendered
31879 * @param {Roo.bootstrap.DocumentManager} this
31880 * @param {Object} file
31882 "previewrendered" : true,
31885 "previewResize" : true
31890 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31899 paramName : 'imageUpload',
31900 toolTipName : 'filename',
31903 labelAlign : 'left',
31913 getAutoCreate : function()
31915 var managerWidget = {
31917 cls : 'roo-document-manager',
31921 cls : 'roo-document-manager-selector',
31926 cls : 'roo-document-manager-uploader',
31930 cls : 'roo-document-manager-upload-btn',
31931 html : '<i class="fa fa-plus"></i>'
31942 cls : 'column col-md-12',
31947 if(this.fieldLabel.length){
31952 cls : 'column col-md-12',
31953 html : this.fieldLabel
31957 cls : 'column col-md-12',
31962 if(this.labelAlign == 'left'){
31967 html : this.fieldLabel
31976 if(this.labelWidth > 12){
31977 content[0].style = "width: " + this.labelWidth + 'px';
31980 if(this.labelWidth < 13 && this.labelmd == 0){
31981 this.labelmd = this.labelWidth;
31984 if(this.labellg > 0){
31985 content[0].cls += ' col-lg-' + this.labellg;
31986 content[1].cls += ' col-lg-' + (12 - this.labellg);
31989 if(this.labelmd > 0){
31990 content[0].cls += ' col-md-' + this.labelmd;
31991 content[1].cls += ' col-md-' + (12 - this.labelmd);
31994 if(this.labelsm > 0){
31995 content[0].cls += ' col-sm-' + this.labelsm;
31996 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31999 if(this.labelxs > 0){
32000 content[0].cls += ' col-xs-' + this.labelxs;
32001 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32009 cls : 'row clearfix',
32017 initEvents : function()
32019 this.managerEl = this.el.select('.roo-document-manager', true).first();
32020 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32022 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32023 this.selectorEl.hide();
32026 this.selectorEl.attr('multiple', 'multiple');
32029 this.selectorEl.on('change', this.onFileSelected, this);
32031 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32032 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32034 this.uploader.on('click', this.onUploaderClick, this);
32036 this.renderProgressDialog();
32040 window.addEventListener("resize", function() { _this.refresh(); } );
32042 this.fireEvent('initial', this);
32045 renderProgressDialog : function()
32049 this.progressDialog = new Roo.bootstrap.Modal({
32050 cls : 'roo-document-manager-progress-dialog',
32051 allow_close : false,
32062 btnclick : function() {
32063 _this.uploadCancel();
32069 this.progressDialog.render(Roo.get(document.body));
32071 this.progress = new Roo.bootstrap.Progress({
32072 cls : 'roo-document-manager-progress',
32077 this.progress.render(this.progressDialog.getChildContainer());
32079 this.progressBar = new Roo.bootstrap.ProgressBar({
32080 cls : 'roo-document-manager-progress-bar',
32083 aria_valuemax : 12,
32087 this.progressBar.render(this.progress.getChildContainer());
32090 onUploaderClick : function(e)
32092 e.preventDefault();
32094 if(this.fireEvent('beforeselectfile', this) != false){
32095 this.selectorEl.dom.click();
32100 onFileSelected : function(e)
32102 e.preventDefault();
32104 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32108 Roo.each(this.selectorEl.dom.files, function(file){
32109 if(this.fireEvent('inspect', this, file) != false){
32110 this.files.push(file);
32120 this.selectorEl.dom.value = '';
32122 if(!this.files || !this.files.length){
32126 if(this.boxes > 0 && this.files.length > this.boxes){
32127 this.files = this.files.slice(0, this.boxes);
32130 this.uploader.show();
32132 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32133 this.uploader.hide();
32142 Roo.each(this.files, function(file){
32144 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32145 var f = this.renderPreview(file);
32150 if(file.type.indexOf('image') != -1){
32151 this.delegates.push(
32153 _this.process(file);
32154 }).createDelegate(this)
32162 _this.process(file);
32163 }).createDelegate(this)
32168 this.files = files;
32170 this.delegates = this.delegates.concat(docs);
32172 if(!this.delegates.length){
32177 this.progressBar.aria_valuemax = this.delegates.length;
32184 arrange : function()
32186 if(!this.delegates.length){
32187 this.progressDialog.hide();
32192 var delegate = this.delegates.shift();
32194 this.progressDialog.show();
32196 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32198 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32203 refresh : function()
32205 this.uploader.show();
32207 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32208 this.uploader.hide();
32211 Roo.isTouch ? this.closable(false) : this.closable(true);
32213 this.fireEvent('refresh', this);
32216 onRemove : function(e, el, o)
32218 e.preventDefault();
32220 this.fireEvent('remove', this, o);
32224 remove : function(o)
32228 Roo.each(this.files, function(file){
32229 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32238 this.files = files;
32245 Roo.each(this.files, function(file){
32250 file.target.remove();
32259 onClick : function(e, el, o)
32261 e.preventDefault();
32263 this.fireEvent('click', this, o);
32267 closable : function(closable)
32269 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32271 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32283 xhrOnLoad : function(xhr)
32285 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32289 if (xhr.readyState !== 4) {
32291 this.fireEvent('exception', this, xhr);
32295 var response = Roo.decode(xhr.responseText);
32297 if(!response.success){
32299 this.fireEvent('exception', this, xhr);
32303 var file = this.renderPreview(response.data);
32305 this.files.push(file);
32309 this.fireEvent('afterupload', this, xhr);
32313 xhrOnError : function(xhr)
32315 Roo.log('xhr on error');
32317 var response = Roo.decode(xhr.responseText);
32324 process : function(file)
32326 if(this.fireEvent('process', this, file) !== false){
32327 if(this.editable && file.type.indexOf('image') != -1){
32328 this.fireEvent('edit', this, file);
32332 this.uploadStart(file, false);
32339 uploadStart : function(file, crop)
32341 this.xhr = new XMLHttpRequest();
32343 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32348 file.xhr = this.xhr;
32350 this.managerEl.createChild({
32352 cls : 'roo-document-manager-loading',
32356 tooltip : file.name,
32357 cls : 'roo-document-manager-thumb',
32358 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32364 this.xhr.open(this.method, this.url, true);
32367 "Accept": "application/json",
32368 "Cache-Control": "no-cache",
32369 "X-Requested-With": "XMLHttpRequest"
32372 for (var headerName in headers) {
32373 var headerValue = headers[headerName];
32375 this.xhr.setRequestHeader(headerName, headerValue);
32381 this.xhr.onload = function()
32383 _this.xhrOnLoad(_this.xhr);
32386 this.xhr.onerror = function()
32388 _this.xhrOnError(_this.xhr);
32391 var formData = new FormData();
32393 formData.append('returnHTML', 'NO');
32396 formData.append('crop', crop);
32399 formData.append(this.paramName, file, file.name);
32406 if(this.fireEvent('prepare', this, formData, options) != false){
32408 if(options.manually){
32412 this.xhr.send(formData);
32416 this.uploadCancel();
32419 uploadCancel : function()
32425 this.delegates = [];
32427 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32434 renderPreview : function(file)
32436 if(typeof(file.target) != 'undefined' && file.target){
32440 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32442 var previewEl = this.managerEl.createChild({
32444 cls : 'roo-document-manager-preview',
32448 tooltip : file[this.toolTipName],
32449 cls : 'roo-document-manager-thumb',
32450 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32455 html : '<i class="fa fa-times-circle"></i>'
32460 var close = previewEl.select('button.close', true).first();
32462 close.on('click', this.onRemove, this, file);
32464 file.target = previewEl;
32466 var image = previewEl.select('img', true).first();
32470 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32472 image.on('click', this.onClick, this, file);
32474 this.fireEvent('previewrendered', this, file);
32480 onPreviewLoad : function(file, image)
32482 if(typeof(file.target) == 'undefined' || !file.target){
32486 var width = image.dom.naturalWidth || image.dom.width;
32487 var height = image.dom.naturalHeight || image.dom.height;
32489 if(!this.previewResize) {
32493 if(width > height){
32494 file.target.addClass('wide');
32498 file.target.addClass('tall');
32503 uploadFromSource : function(file, crop)
32505 this.xhr = new XMLHttpRequest();
32507 this.managerEl.createChild({
32509 cls : 'roo-document-manager-loading',
32513 tooltip : file.name,
32514 cls : 'roo-document-manager-thumb',
32515 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32521 this.xhr.open(this.method, this.url, true);
32524 "Accept": "application/json",
32525 "Cache-Control": "no-cache",
32526 "X-Requested-With": "XMLHttpRequest"
32529 for (var headerName in headers) {
32530 var headerValue = headers[headerName];
32532 this.xhr.setRequestHeader(headerName, headerValue);
32538 this.xhr.onload = function()
32540 _this.xhrOnLoad(_this.xhr);
32543 this.xhr.onerror = function()
32545 _this.xhrOnError(_this.xhr);
32548 var formData = new FormData();
32550 formData.append('returnHTML', 'NO');
32552 formData.append('crop', crop);
32554 if(typeof(file.filename) != 'undefined'){
32555 formData.append('filename', file.filename);
32558 if(typeof(file.mimetype) != 'undefined'){
32559 formData.append('mimetype', file.mimetype);
32564 if(this.fireEvent('prepare', this, formData) != false){
32565 this.xhr.send(formData);
32575 * @class Roo.bootstrap.DocumentViewer
32576 * @extends Roo.bootstrap.Component
32577 * Bootstrap DocumentViewer class
32578 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32579 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32582 * Create a new DocumentViewer
32583 * @param {Object} config The config object
32586 Roo.bootstrap.DocumentViewer = function(config){
32587 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32592 * Fire after initEvent
32593 * @param {Roo.bootstrap.DocumentViewer} this
32599 * @param {Roo.bootstrap.DocumentViewer} this
32604 * Fire after download button
32605 * @param {Roo.bootstrap.DocumentViewer} this
32610 * Fire after trash button
32611 * @param {Roo.bootstrap.DocumentViewer} this
32618 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32620 showDownload : true,
32624 getAutoCreate : function()
32628 cls : 'roo-document-viewer',
32632 cls : 'roo-document-viewer-body',
32636 cls : 'roo-document-viewer-thumb',
32640 cls : 'roo-document-viewer-image'
32648 cls : 'roo-document-viewer-footer',
32651 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32655 cls : 'btn-group roo-document-viewer-download',
32659 cls : 'btn btn-default',
32660 html : '<i class="fa fa-download"></i>'
32666 cls : 'btn-group roo-document-viewer-trash',
32670 cls : 'btn btn-default',
32671 html : '<i class="fa fa-trash"></i>'
32684 initEvents : function()
32686 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32687 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32689 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32690 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32692 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32693 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32695 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32696 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32698 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32699 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32701 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32702 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32704 this.bodyEl.on('click', this.onClick, this);
32705 this.downloadBtn.on('click', this.onDownload, this);
32706 this.trashBtn.on('click', this.onTrash, this);
32708 this.downloadBtn.hide();
32709 this.trashBtn.hide();
32711 if(this.showDownload){
32712 this.downloadBtn.show();
32715 if(this.showTrash){
32716 this.trashBtn.show();
32719 if(!this.showDownload && !this.showTrash) {
32720 this.footerEl.hide();
32725 initial : function()
32727 this.fireEvent('initial', this);
32731 onClick : function(e)
32733 e.preventDefault();
32735 this.fireEvent('click', this);
32738 onDownload : function(e)
32740 e.preventDefault();
32742 this.fireEvent('download', this);
32745 onTrash : function(e)
32747 e.preventDefault();
32749 this.fireEvent('trash', this);
32761 * @class Roo.bootstrap.NavProgressBar
32762 * @extends Roo.bootstrap.Component
32763 * Bootstrap NavProgressBar class
32766 * Create a new nav progress bar
32767 * @param {Object} config The config object
32770 Roo.bootstrap.NavProgressBar = function(config){
32771 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32773 this.bullets = this.bullets || [];
32775 // Roo.bootstrap.NavProgressBar.register(this);
32779 * Fires when the active item changes
32780 * @param {Roo.bootstrap.NavProgressBar} this
32781 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32782 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32789 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32794 getAutoCreate : function()
32796 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32800 cls : 'roo-navigation-bar-group',
32804 cls : 'roo-navigation-top-bar'
32808 cls : 'roo-navigation-bullets-bar',
32812 cls : 'roo-navigation-bar'
32819 cls : 'roo-navigation-bottom-bar'
32829 initEvents: function()
32834 onRender : function(ct, position)
32836 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32838 if(this.bullets.length){
32839 Roo.each(this.bullets, function(b){
32848 addItem : function(cfg)
32850 var item = new Roo.bootstrap.NavProgressItem(cfg);
32852 item.parentId = this.id;
32853 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32856 var top = new Roo.bootstrap.Element({
32858 cls : 'roo-navigation-bar-text'
32861 var bottom = new Roo.bootstrap.Element({
32863 cls : 'roo-navigation-bar-text'
32866 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32867 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32869 var topText = new Roo.bootstrap.Element({
32871 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32874 var bottomText = new Roo.bootstrap.Element({
32876 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32879 topText.onRender(top.el, null);
32880 bottomText.onRender(bottom.el, null);
32883 item.bottomEl = bottom;
32886 this.barItems.push(item);
32891 getActive : function()
32893 var active = false;
32895 Roo.each(this.barItems, function(v){
32897 if (!v.isActive()) {
32909 setActiveItem : function(item)
32913 Roo.each(this.barItems, function(v){
32914 if (v.rid == item.rid) {
32918 if (v.isActive()) {
32919 v.setActive(false);
32924 item.setActive(true);
32926 this.fireEvent('changed', this, item, prev);
32929 getBarItem: function(rid)
32933 Roo.each(this.barItems, function(e) {
32934 if (e.rid != rid) {
32945 indexOfItem : function(item)
32949 Roo.each(this.barItems, function(v, i){
32951 if (v.rid != item.rid) {
32962 setActiveNext : function()
32964 var i = this.indexOfItem(this.getActive());
32966 if (i > this.barItems.length) {
32970 this.setActiveItem(this.barItems[i+1]);
32973 setActivePrev : function()
32975 var i = this.indexOfItem(this.getActive());
32981 this.setActiveItem(this.barItems[i-1]);
32984 format : function()
32986 if(!this.barItems.length){
32990 var width = 100 / this.barItems.length;
32992 Roo.each(this.barItems, function(i){
32993 i.el.setStyle('width', width + '%');
32994 i.topEl.el.setStyle('width', width + '%');
32995 i.bottomEl.el.setStyle('width', width + '%');
33004 * Nav Progress Item
33009 * @class Roo.bootstrap.NavProgressItem
33010 * @extends Roo.bootstrap.Component
33011 * Bootstrap NavProgressItem class
33012 * @cfg {String} rid the reference id
33013 * @cfg {Boolean} active (true|false) Is item active default false
33014 * @cfg {Boolean} disabled (true|false) Is item active default false
33015 * @cfg {String} html
33016 * @cfg {String} position (top|bottom) text position default bottom
33017 * @cfg {String} icon show icon instead of number
33020 * Create a new NavProgressItem
33021 * @param {Object} config The config object
33023 Roo.bootstrap.NavProgressItem = function(config){
33024 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33029 * The raw click event for the entire grid.
33030 * @param {Roo.bootstrap.NavProgressItem} this
33031 * @param {Roo.EventObject} e
33038 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33044 position : 'bottom',
33047 getAutoCreate : function()
33049 var iconCls = 'roo-navigation-bar-item-icon';
33051 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33055 cls: 'roo-navigation-bar-item',
33065 cfg.cls += ' active';
33068 cfg.cls += ' disabled';
33074 disable : function()
33076 this.setDisabled(true);
33079 enable : function()
33081 this.setDisabled(false);
33084 initEvents: function()
33086 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33088 this.iconEl.on('click', this.onClick, this);
33091 onClick : function(e)
33093 e.preventDefault();
33099 if(this.fireEvent('click', this, e) === false){
33103 this.parent().setActiveItem(this);
33106 isActive: function ()
33108 return this.active;
33111 setActive : function(state)
33113 if(this.active == state){
33117 this.active = state;
33120 this.el.addClass('active');
33124 this.el.removeClass('active');
33129 setDisabled : function(state)
33131 if(this.disabled == state){
33135 this.disabled = state;
33138 this.el.addClass('disabled');
33142 this.el.removeClass('disabled');
33145 tooltipEl : function()
33147 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33160 * @class Roo.bootstrap.FieldLabel
33161 * @extends Roo.bootstrap.Component
33162 * Bootstrap FieldLabel class
33163 * @cfg {String} html contents of the element
33164 * @cfg {String} tag tag of the element default label
33165 * @cfg {String} cls class of the element
33166 * @cfg {String} target label target
33167 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33168 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33169 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33170 * @cfg {String} iconTooltip default "This field is required"
33171 * @cfg {String} indicatorpos (left|right) default left
33174 * Create a new FieldLabel
33175 * @param {Object} config The config object
33178 Roo.bootstrap.FieldLabel = function(config){
33179 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33184 * Fires after the field has been marked as invalid.
33185 * @param {Roo.form.FieldLabel} this
33186 * @param {String} msg The validation message
33191 * Fires after the field has been validated with no errors.
33192 * @param {Roo.form.FieldLabel} this
33198 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33205 invalidClass : 'has-warning',
33206 validClass : 'has-success',
33207 iconTooltip : 'This field is required',
33208 indicatorpos : 'left',
33210 getAutoCreate : function(){
33213 if (!this.allowBlank) {
33219 cls : 'roo-bootstrap-field-label ' + this.cls,
33224 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33225 tooltip : this.iconTooltip
33234 if(this.indicatorpos == 'right'){
33237 cls : 'roo-bootstrap-field-label ' + this.cls,
33246 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33247 tooltip : this.iconTooltip
33256 initEvents: function()
33258 Roo.bootstrap.Element.superclass.initEvents.call(this);
33260 this.indicator = this.indicatorEl();
33262 if(this.indicator){
33263 this.indicator.removeClass('visible');
33264 this.indicator.addClass('invisible');
33267 Roo.bootstrap.FieldLabel.register(this);
33270 indicatorEl : function()
33272 var indicator = this.el.select('i.roo-required-indicator',true).first();
33283 * Mark this field as valid
33285 markValid : function()
33287 if(this.indicator){
33288 this.indicator.removeClass('visible');
33289 this.indicator.addClass('invisible');
33291 if (Roo.bootstrap.version == 3) {
33292 this.el.removeClass(this.invalidClass);
33293 this.el.addClass(this.validClass);
33295 this.el.removeClass('is-invalid');
33296 this.el.addClass('is-valid');
33300 this.fireEvent('valid', this);
33304 * Mark this field as invalid
33305 * @param {String} msg The validation message
33307 markInvalid : function(msg)
33309 if(this.indicator){
33310 this.indicator.removeClass('invisible');
33311 this.indicator.addClass('visible');
33313 if (Roo.bootstrap.version == 3) {
33314 this.el.removeClass(this.validClass);
33315 this.el.addClass(this.invalidClass);
33317 this.el.removeClass('is-valid');
33318 this.el.addClass('is-invalid');
33322 this.fireEvent('invalid', this, msg);
33328 Roo.apply(Roo.bootstrap.FieldLabel, {
33333 * register a FieldLabel Group
33334 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33336 register : function(label)
33338 if(this.groups.hasOwnProperty(label.target)){
33342 this.groups[label.target] = label;
33346 * fetch a FieldLabel Group based on the target
33347 * @param {string} target
33348 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33350 get: function(target) {
33351 if (typeof(this.groups[target]) == 'undefined') {
33355 return this.groups[target] ;
33364 * page DateSplitField.
33370 * @class Roo.bootstrap.DateSplitField
33371 * @extends Roo.bootstrap.Component
33372 * Bootstrap DateSplitField class
33373 * @cfg {string} fieldLabel - the label associated
33374 * @cfg {Number} labelWidth set the width of label (0-12)
33375 * @cfg {String} labelAlign (top|left)
33376 * @cfg {Boolean} dayAllowBlank (true|false) default false
33377 * @cfg {Boolean} monthAllowBlank (true|false) default false
33378 * @cfg {Boolean} yearAllowBlank (true|false) default false
33379 * @cfg {string} dayPlaceholder
33380 * @cfg {string} monthPlaceholder
33381 * @cfg {string} yearPlaceholder
33382 * @cfg {string} dayFormat default 'd'
33383 * @cfg {string} monthFormat default 'm'
33384 * @cfg {string} yearFormat default 'Y'
33385 * @cfg {Number} labellg set the width of label (1-12)
33386 * @cfg {Number} labelmd set the width of label (1-12)
33387 * @cfg {Number} labelsm set the width of label (1-12)
33388 * @cfg {Number} labelxs set the width of label (1-12)
33392 * Create a new DateSplitField
33393 * @param {Object} config The config object
33396 Roo.bootstrap.DateSplitField = function(config){
33397 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33403 * getting the data of years
33404 * @param {Roo.bootstrap.DateSplitField} this
33405 * @param {Object} years
33410 * getting the data of days
33411 * @param {Roo.bootstrap.DateSplitField} this
33412 * @param {Object} days
33417 * Fires after the field has been marked as invalid.
33418 * @param {Roo.form.Field} this
33419 * @param {String} msg The validation message
33424 * Fires after the field has been validated with no errors.
33425 * @param {Roo.form.Field} this
33431 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33434 labelAlign : 'top',
33436 dayAllowBlank : false,
33437 monthAllowBlank : false,
33438 yearAllowBlank : false,
33439 dayPlaceholder : '',
33440 monthPlaceholder : '',
33441 yearPlaceholder : '',
33445 isFormField : true,
33451 getAutoCreate : function()
33455 cls : 'row roo-date-split-field-group',
33460 cls : 'form-hidden-field roo-date-split-field-group-value',
33466 var labelCls = 'col-md-12';
33467 var contentCls = 'col-md-4';
33469 if(this.fieldLabel){
33473 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33477 html : this.fieldLabel
33482 if(this.labelAlign == 'left'){
33484 if(this.labelWidth > 12){
33485 label.style = "width: " + this.labelWidth + 'px';
33488 if(this.labelWidth < 13 && this.labelmd == 0){
33489 this.labelmd = this.labelWidth;
33492 if(this.labellg > 0){
33493 labelCls = ' col-lg-' + this.labellg;
33494 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33497 if(this.labelmd > 0){
33498 labelCls = ' col-md-' + this.labelmd;
33499 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33502 if(this.labelsm > 0){
33503 labelCls = ' col-sm-' + this.labelsm;
33504 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33507 if(this.labelxs > 0){
33508 labelCls = ' col-xs-' + this.labelxs;
33509 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33513 label.cls += ' ' + labelCls;
33515 cfg.cn.push(label);
33518 Roo.each(['day', 'month', 'year'], function(t){
33521 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33528 inputEl: function ()
33530 return this.el.select('.roo-date-split-field-group-value', true).first();
33533 onRender : function(ct, position)
33537 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33539 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33541 this.dayField = new Roo.bootstrap.ComboBox({
33542 allowBlank : this.dayAllowBlank,
33543 alwaysQuery : true,
33544 displayField : 'value',
33547 forceSelection : true,
33549 placeholder : this.dayPlaceholder,
33550 selectOnFocus : true,
33551 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33552 triggerAction : 'all',
33554 valueField : 'value',
33555 store : new Roo.data.SimpleStore({
33556 data : (function() {
33558 _this.fireEvent('days', _this, days);
33561 fields : [ 'value' ]
33564 select : function (_self, record, index)
33566 _this.setValue(_this.getValue());
33571 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33573 this.monthField = new Roo.bootstrap.MonthField({
33574 after : '<i class=\"fa fa-calendar\"></i>',
33575 allowBlank : this.monthAllowBlank,
33576 placeholder : this.monthPlaceholder,
33579 render : function (_self)
33581 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33582 e.preventDefault();
33586 select : function (_self, oldvalue, newvalue)
33588 _this.setValue(_this.getValue());
33593 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33595 this.yearField = new Roo.bootstrap.ComboBox({
33596 allowBlank : this.yearAllowBlank,
33597 alwaysQuery : true,
33598 displayField : 'value',
33601 forceSelection : true,
33603 placeholder : this.yearPlaceholder,
33604 selectOnFocus : true,
33605 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33606 triggerAction : 'all',
33608 valueField : 'value',
33609 store : new Roo.data.SimpleStore({
33610 data : (function() {
33612 _this.fireEvent('years', _this, years);
33615 fields : [ 'value' ]
33618 select : function (_self, record, index)
33620 _this.setValue(_this.getValue());
33625 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33628 setValue : function(v, format)
33630 this.inputEl.dom.value = v;
33632 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33634 var d = Date.parseDate(v, f);
33641 this.setDay(d.format(this.dayFormat));
33642 this.setMonth(d.format(this.monthFormat));
33643 this.setYear(d.format(this.yearFormat));
33650 setDay : function(v)
33652 this.dayField.setValue(v);
33653 this.inputEl.dom.value = this.getValue();
33658 setMonth : function(v)
33660 this.monthField.setValue(v, true);
33661 this.inputEl.dom.value = this.getValue();
33666 setYear : function(v)
33668 this.yearField.setValue(v);
33669 this.inputEl.dom.value = this.getValue();
33674 getDay : function()
33676 return this.dayField.getValue();
33679 getMonth : function()
33681 return this.monthField.getValue();
33684 getYear : function()
33686 return this.yearField.getValue();
33689 getValue : function()
33691 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33693 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33703 this.inputEl.dom.value = '';
33708 validate : function()
33710 var d = this.dayField.validate();
33711 var m = this.monthField.validate();
33712 var y = this.yearField.validate();
33717 (!this.dayAllowBlank && !d) ||
33718 (!this.monthAllowBlank && !m) ||
33719 (!this.yearAllowBlank && !y)
33724 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33733 this.markInvalid();
33738 markValid : function()
33741 var label = this.el.select('label', true).first();
33742 var icon = this.el.select('i.fa-star', true).first();
33748 this.fireEvent('valid', this);
33752 * Mark this field as invalid
33753 * @param {String} msg The validation message
33755 markInvalid : function(msg)
33758 var label = this.el.select('label', true).first();
33759 var icon = this.el.select('i.fa-star', true).first();
33761 if(label && !icon){
33762 this.el.select('.roo-date-split-field-label', true).createChild({
33764 cls : 'text-danger fa fa-lg fa-star',
33765 tooltip : 'This field is required',
33766 style : 'margin-right:5px;'
33770 this.fireEvent('invalid', this, msg);
33773 clearInvalid : function()
33775 var label = this.el.select('label', true).first();
33776 var icon = this.el.select('i.fa-star', true).first();
33782 this.fireEvent('valid', this);
33785 getName: function()
33795 * http://masonry.desandro.com
33797 * The idea is to render all the bricks based on vertical width...
33799 * The original code extends 'outlayer' - we might need to use that....
33805 * @class Roo.bootstrap.LayoutMasonry
33806 * @extends Roo.bootstrap.Component
33807 * Bootstrap Layout Masonry class
33810 * Create a new Element
33811 * @param {Object} config The config object
33814 Roo.bootstrap.LayoutMasonry = function(config){
33816 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33820 Roo.bootstrap.LayoutMasonry.register(this);
33826 * Fire after layout the items
33827 * @param {Roo.bootstrap.LayoutMasonry} this
33828 * @param {Roo.EventObject} e
33835 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33838 * @cfg {Boolean} isLayoutInstant = no animation?
33840 isLayoutInstant : false, // needed?
33843 * @cfg {Number} boxWidth width of the columns
33848 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33853 * @cfg {Number} padWidth padding below box..
33858 * @cfg {Number} gutter gutter width..
33863 * @cfg {Number} maxCols maximum number of columns
33869 * @cfg {Boolean} isAutoInitial defalut true
33871 isAutoInitial : true,
33876 * @cfg {Boolean} isHorizontal defalut false
33878 isHorizontal : false,
33880 currentSize : null,
33886 bricks: null, //CompositeElement
33890 _isLayoutInited : false,
33892 // isAlternative : false, // only use for vertical layout...
33895 * @cfg {Number} alternativePadWidth padding below box..
33897 alternativePadWidth : 50,
33899 selectedBrick : [],
33901 getAutoCreate : function(){
33903 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33907 cls: 'blog-masonary-wrapper ' + this.cls,
33909 cls : 'mas-boxes masonary'
33916 getChildContainer: function( )
33918 if (this.boxesEl) {
33919 return this.boxesEl;
33922 this.boxesEl = this.el.select('.mas-boxes').first();
33924 return this.boxesEl;
33928 initEvents : function()
33932 if(this.isAutoInitial){
33933 Roo.log('hook children rendered');
33934 this.on('childrenrendered', function() {
33935 Roo.log('children rendered');
33941 initial : function()
33943 this.selectedBrick = [];
33945 this.currentSize = this.el.getBox(true);
33947 Roo.EventManager.onWindowResize(this.resize, this);
33949 if(!this.isAutoInitial){
33957 //this.layout.defer(500,this);
33961 resize : function()
33963 var cs = this.el.getBox(true);
33966 this.currentSize.width == cs.width &&
33967 this.currentSize.x == cs.x &&
33968 this.currentSize.height == cs.height &&
33969 this.currentSize.y == cs.y
33971 Roo.log("no change in with or X or Y");
33975 this.currentSize = cs;
33981 layout : function()
33983 this._resetLayout();
33985 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33987 this.layoutItems( isInstant );
33989 this._isLayoutInited = true;
33991 this.fireEvent('layout', this);
33995 _resetLayout : function()
33997 if(this.isHorizontal){
33998 this.horizontalMeasureColumns();
34002 this.verticalMeasureColumns();
34006 verticalMeasureColumns : function()
34008 this.getContainerWidth();
34010 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34011 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34015 var boxWidth = this.boxWidth + this.padWidth;
34017 if(this.containerWidth < this.boxWidth){
34018 boxWidth = this.containerWidth
34021 var containerWidth = this.containerWidth;
34023 var cols = Math.floor(containerWidth / boxWidth);
34025 this.cols = Math.max( cols, 1 );
34027 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34029 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34031 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34033 this.colWidth = boxWidth + avail - this.padWidth;
34035 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34036 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34039 horizontalMeasureColumns : function()
34041 this.getContainerWidth();
34043 var boxWidth = this.boxWidth;
34045 if(this.containerWidth < boxWidth){
34046 boxWidth = this.containerWidth;
34049 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34051 this.el.setHeight(boxWidth);
34055 getContainerWidth : function()
34057 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34060 layoutItems : function( isInstant )
34062 Roo.log(this.bricks);
34064 var items = Roo.apply([], this.bricks);
34066 if(this.isHorizontal){
34067 this._horizontalLayoutItems( items , isInstant );
34071 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34072 // this._verticalAlternativeLayoutItems( items , isInstant );
34076 this._verticalLayoutItems( items , isInstant );
34080 _verticalLayoutItems : function ( items , isInstant)
34082 if ( !items || !items.length ) {
34087 ['xs', 'xs', 'xs', 'tall'],
34088 ['xs', 'xs', 'tall'],
34089 ['xs', 'xs', 'sm'],
34090 ['xs', 'xs', 'xs'],
34096 ['sm', 'xs', 'xs'],
34100 ['tall', 'xs', 'xs', 'xs'],
34101 ['tall', 'xs', 'xs'],
34113 Roo.each(items, function(item, k){
34115 switch (item.size) {
34116 // these layouts take up a full box,
34127 boxes.push([item]);
34150 var filterPattern = function(box, length)
34158 var pattern = box.slice(0, length);
34162 Roo.each(pattern, function(i){
34163 format.push(i.size);
34166 Roo.each(standard, function(s){
34168 if(String(s) != String(format)){
34177 if(!match && length == 1){
34182 filterPattern(box, length - 1);
34186 queue.push(pattern);
34188 box = box.slice(length, box.length);
34190 filterPattern(box, 4);
34196 Roo.each(boxes, function(box, k){
34202 if(box.length == 1){
34207 filterPattern(box, 4);
34211 this._processVerticalLayoutQueue( queue, isInstant );
34215 // _verticalAlternativeLayoutItems : function( items , isInstant )
34217 // if ( !items || !items.length ) {
34221 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34225 _horizontalLayoutItems : function ( items , isInstant)
34227 if ( !items || !items.length || items.length < 3) {
34233 var eItems = items.slice(0, 3);
34235 items = items.slice(3, items.length);
34238 ['xs', 'xs', 'xs', 'wide'],
34239 ['xs', 'xs', 'wide'],
34240 ['xs', 'xs', 'sm'],
34241 ['xs', 'xs', 'xs'],
34247 ['sm', 'xs', 'xs'],
34251 ['wide', 'xs', 'xs', 'xs'],
34252 ['wide', 'xs', 'xs'],
34265 Roo.each(items, function(item, k){
34267 switch (item.size) {
34278 boxes.push([item]);
34302 var filterPattern = function(box, length)
34310 var pattern = box.slice(0, length);
34314 Roo.each(pattern, function(i){
34315 format.push(i.size);
34318 Roo.each(standard, function(s){
34320 if(String(s) != String(format)){
34329 if(!match && length == 1){
34334 filterPattern(box, length - 1);
34338 queue.push(pattern);
34340 box = box.slice(length, box.length);
34342 filterPattern(box, 4);
34348 Roo.each(boxes, function(box, k){
34354 if(box.length == 1){
34359 filterPattern(box, 4);
34366 var pos = this.el.getBox(true);
34370 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34372 var hit_end = false;
34374 Roo.each(queue, function(box){
34378 Roo.each(box, function(b){
34380 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34390 Roo.each(box, function(b){
34392 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34395 mx = Math.max(mx, b.x);
34399 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34403 Roo.each(box, function(b){
34405 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34419 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34422 /** Sets position of item in DOM
34423 * @param {Element} item
34424 * @param {Number} x - horizontal position
34425 * @param {Number} y - vertical position
34426 * @param {Boolean} isInstant - disables transitions
34428 _processVerticalLayoutQueue : function( queue, isInstant )
34430 var pos = this.el.getBox(true);
34435 for (var i = 0; i < this.cols; i++){
34439 Roo.each(queue, function(box, k){
34441 var col = k % this.cols;
34443 Roo.each(box, function(b,kk){
34445 b.el.position('absolute');
34447 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34448 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34450 if(b.size == 'md-left' || b.size == 'md-right'){
34451 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34452 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34455 b.el.setWidth(width);
34456 b.el.setHeight(height);
34458 b.el.select('iframe',true).setSize(width,height);
34462 for (var i = 0; i < this.cols; i++){
34464 if(maxY[i] < maxY[col]){
34469 col = Math.min(col, i);
34473 x = pos.x + col * (this.colWidth + this.padWidth);
34477 var positions = [];
34479 switch (box.length){
34481 positions = this.getVerticalOneBoxColPositions(x, y, box);
34484 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34487 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34490 positions = this.getVerticalFourBoxColPositions(x, y, box);
34496 Roo.each(box, function(b,kk){
34498 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34500 var sz = b.el.getSize();
34502 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34510 for (var i = 0; i < this.cols; i++){
34511 mY = Math.max(mY, maxY[i]);
34514 this.el.setHeight(mY - pos.y);
34518 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34520 // var pos = this.el.getBox(true);
34523 // var maxX = pos.right;
34525 // var maxHeight = 0;
34527 // Roo.each(items, function(item, k){
34531 // item.el.position('absolute');
34533 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34535 // item.el.setWidth(width);
34537 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34539 // item.el.setHeight(height);
34542 // item.el.setXY([x, y], isInstant ? false : true);
34544 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34547 // y = y + height + this.alternativePadWidth;
34549 // maxHeight = maxHeight + height + this.alternativePadWidth;
34553 // this.el.setHeight(maxHeight);
34557 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34559 var pos = this.el.getBox(true);
34564 var maxX = pos.right;
34566 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34568 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34570 Roo.each(queue, function(box, k){
34572 Roo.each(box, function(b, kk){
34574 b.el.position('absolute');
34576 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34577 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34579 if(b.size == 'md-left' || b.size == 'md-right'){
34580 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34581 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34584 b.el.setWidth(width);
34585 b.el.setHeight(height);
34593 var positions = [];
34595 switch (box.length){
34597 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34600 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34603 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34606 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34612 Roo.each(box, function(b,kk){
34614 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34616 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34624 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34626 Roo.each(eItems, function(b,k){
34628 b.size = (k == 0) ? 'sm' : 'xs';
34629 b.x = (k == 0) ? 2 : 1;
34630 b.y = (k == 0) ? 2 : 1;
34632 b.el.position('absolute');
34634 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34636 b.el.setWidth(width);
34638 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34640 b.el.setHeight(height);
34644 var positions = [];
34647 x : maxX - this.unitWidth * 2 - this.gutter,
34652 x : maxX - this.unitWidth,
34653 y : minY + (this.unitWidth + this.gutter) * 2
34657 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34661 Roo.each(eItems, function(b,k){
34663 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34669 getVerticalOneBoxColPositions : function(x, y, box)
34673 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34675 if(box[0].size == 'md-left'){
34679 if(box[0].size == 'md-right'){
34684 x : x + (this.unitWidth + this.gutter) * rand,
34691 getVerticalTwoBoxColPositions : function(x, y, box)
34695 if(box[0].size == 'xs'){
34699 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34703 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34717 x : x + (this.unitWidth + this.gutter) * 2,
34718 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34725 getVerticalThreeBoxColPositions : function(x, y, box)
34729 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34737 x : x + (this.unitWidth + this.gutter) * 1,
34742 x : x + (this.unitWidth + this.gutter) * 2,
34750 if(box[0].size == 'xs' && box[1].size == 'xs'){
34759 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34763 x : x + (this.unitWidth + this.gutter) * 1,
34777 x : x + (this.unitWidth + this.gutter) * 2,
34782 x : x + (this.unitWidth + this.gutter) * 2,
34783 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34790 getVerticalFourBoxColPositions : function(x, y, box)
34794 if(box[0].size == 'xs'){
34803 y : y + (this.unitHeight + this.gutter) * 1
34808 y : y + (this.unitHeight + this.gutter) * 2
34812 x : x + (this.unitWidth + this.gutter) * 1,
34826 x : x + (this.unitWidth + this.gutter) * 2,
34831 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34832 y : y + (this.unitHeight + this.gutter) * 1
34836 x : x + (this.unitWidth + this.gutter) * 2,
34837 y : y + (this.unitWidth + this.gutter) * 2
34844 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34848 if(box[0].size == 'md-left'){
34850 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34857 if(box[0].size == 'md-right'){
34859 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34860 y : minY + (this.unitWidth + this.gutter) * 1
34866 var rand = Math.floor(Math.random() * (4 - box[0].y));
34869 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34870 y : minY + (this.unitWidth + this.gutter) * rand
34877 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34881 if(box[0].size == 'xs'){
34884 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34889 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34890 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34898 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34903 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34904 y : minY + (this.unitWidth + this.gutter) * 2
34911 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34915 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34918 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34923 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34924 y : minY + (this.unitWidth + this.gutter) * 1
34928 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34929 y : minY + (this.unitWidth + this.gutter) * 2
34936 if(box[0].size == 'xs' && box[1].size == 'xs'){
34939 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34944 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34949 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34950 y : minY + (this.unitWidth + this.gutter) * 1
34958 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34963 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34964 y : minY + (this.unitWidth + this.gutter) * 2
34968 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34969 y : minY + (this.unitWidth + this.gutter) * 2
34976 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34980 if(box[0].size == 'xs'){
34983 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34988 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34993 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),
34998 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34999 y : minY + (this.unitWidth + this.gutter) * 1
35007 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35012 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35013 y : minY + (this.unitWidth + this.gutter) * 2
35017 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35018 y : minY + (this.unitWidth + this.gutter) * 2
35022 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),
35023 y : minY + (this.unitWidth + this.gutter) * 2
35031 * remove a Masonry Brick
35032 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35034 removeBrick : function(brick_id)
35040 for (var i = 0; i<this.bricks.length; i++) {
35041 if (this.bricks[i].id == brick_id) {
35042 this.bricks.splice(i,1);
35043 this.el.dom.removeChild(Roo.get(brick_id).dom);
35050 * adds a Masonry Brick
35051 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35053 addBrick : function(cfg)
35055 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35056 //this.register(cn);
35057 cn.parentId = this.id;
35058 cn.render(this.el);
35063 * register a Masonry Brick
35064 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35067 register : function(brick)
35069 this.bricks.push(brick);
35070 brick.masonryId = this.id;
35074 * clear all the Masonry Brick
35076 clearAll : function()
35079 //this.getChildContainer().dom.innerHTML = "";
35080 this.el.dom.innerHTML = '';
35083 getSelected : function()
35085 if (!this.selectedBrick) {
35089 return this.selectedBrick;
35093 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35097 * register a Masonry Layout
35098 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35101 register : function(layout)
35103 this.groups[layout.id] = layout;
35106 * fetch a Masonry Layout based on the masonry layout ID
35107 * @param {string} the masonry layout to add
35108 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35111 get: function(layout_id) {
35112 if (typeof(this.groups[layout_id]) == 'undefined') {
35115 return this.groups[layout_id] ;
35127 * http://masonry.desandro.com
35129 * The idea is to render all the bricks based on vertical width...
35131 * The original code extends 'outlayer' - we might need to use that....
35137 * @class Roo.bootstrap.LayoutMasonryAuto
35138 * @extends Roo.bootstrap.Component
35139 * Bootstrap Layout Masonry class
35142 * Create a new Element
35143 * @param {Object} config The config object
35146 Roo.bootstrap.LayoutMasonryAuto = function(config){
35147 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35150 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35153 * @cfg {Boolean} isFitWidth - resize the width..
35155 isFitWidth : false, // options..
35157 * @cfg {Boolean} isOriginLeft = left align?
35159 isOriginLeft : true,
35161 * @cfg {Boolean} isOriginTop = top align?
35163 isOriginTop : false,
35165 * @cfg {Boolean} isLayoutInstant = no animation?
35167 isLayoutInstant : false, // needed?
35169 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35171 isResizingContainer : true,
35173 * @cfg {Number} columnWidth width of the columns
35179 * @cfg {Number} maxCols maximum number of columns
35184 * @cfg {Number} padHeight padding below box..
35190 * @cfg {Boolean} isAutoInitial defalut true
35193 isAutoInitial : true,
35199 initialColumnWidth : 0,
35200 currentSize : null,
35202 colYs : null, // array.
35209 bricks: null, //CompositeElement
35210 cols : 0, // array?
35211 // element : null, // wrapped now this.el
35212 _isLayoutInited : null,
35215 getAutoCreate : function(){
35219 cls: 'blog-masonary-wrapper ' + this.cls,
35221 cls : 'mas-boxes masonary'
35228 getChildContainer: function( )
35230 if (this.boxesEl) {
35231 return this.boxesEl;
35234 this.boxesEl = this.el.select('.mas-boxes').first();
35236 return this.boxesEl;
35240 initEvents : function()
35244 if(this.isAutoInitial){
35245 Roo.log('hook children rendered');
35246 this.on('childrenrendered', function() {
35247 Roo.log('children rendered');
35254 initial : function()
35256 this.reloadItems();
35258 this.currentSize = this.el.getBox(true);
35260 /// was window resize... - let's see if this works..
35261 Roo.EventManager.onWindowResize(this.resize, this);
35263 if(!this.isAutoInitial){
35268 this.layout.defer(500,this);
35271 reloadItems: function()
35273 this.bricks = this.el.select('.masonry-brick', true);
35275 this.bricks.each(function(b) {
35276 //Roo.log(b.getSize());
35277 if (!b.attr('originalwidth')) {
35278 b.attr('originalwidth', b.getSize().width);
35283 Roo.log(this.bricks.elements.length);
35286 resize : function()
35289 var cs = this.el.getBox(true);
35291 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35292 Roo.log("no change in with or X");
35295 this.currentSize = cs;
35299 layout : function()
35302 this._resetLayout();
35303 //this._manageStamps();
35305 // don't animate first layout
35306 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35307 this.layoutItems( isInstant );
35309 // flag for initalized
35310 this._isLayoutInited = true;
35313 layoutItems : function( isInstant )
35315 //var items = this._getItemsForLayout( this.items );
35316 // original code supports filtering layout items.. we just ignore it..
35318 this._layoutItems( this.bricks , isInstant );
35320 this._postLayout();
35322 _layoutItems : function ( items , isInstant)
35324 //this.fireEvent( 'layout', this, items );
35327 if ( !items || !items.elements.length ) {
35328 // no items, emit event with empty array
35333 items.each(function(item) {
35334 Roo.log("layout item");
35336 // get x/y object from method
35337 var position = this._getItemLayoutPosition( item );
35339 position.item = item;
35340 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35341 queue.push( position );
35344 this._processLayoutQueue( queue );
35346 /** Sets position of item in DOM
35347 * @param {Element} item
35348 * @param {Number} x - horizontal position
35349 * @param {Number} y - vertical position
35350 * @param {Boolean} isInstant - disables transitions
35352 _processLayoutQueue : function( queue )
35354 for ( var i=0, len = queue.length; i < len; i++ ) {
35355 var obj = queue[i];
35356 obj.item.position('absolute');
35357 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35363 * Any logic you want to do after each layout,
35364 * i.e. size the container
35366 _postLayout : function()
35368 this.resizeContainer();
35371 resizeContainer : function()
35373 if ( !this.isResizingContainer ) {
35376 var size = this._getContainerSize();
35378 this.el.setSize(size.width,size.height);
35379 this.boxesEl.setSize(size.width,size.height);
35385 _resetLayout : function()
35387 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35388 this.colWidth = this.el.getWidth();
35389 //this.gutter = this.el.getWidth();
35391 this.measureColumns();
35397 this.colYs.push( 0 );
35403 measureColumns : function()
35405 this.getContainerWidth();
35406 // if columnWidth is 0, default to outerWidth of first item
35407 if ( !this.columnWidth ) {
35408 var firstItem = this.bricks.first();
35409 Roo.log(firstItem);
35410 this.columnWidth = this.containerWidth;
35411 if (firstItem && firstItem.attr('originalwidth') ) {
35412 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35414 // columnWidth fall back to item of first element
35415 Roo.log("set column width?");
35416 this.initialColumnWidth = this.columnWidth ;
35418 // if first elem has no width, default to size of container
35423 if (this.initialColumnWidth) {
35424 this.columnWidth = this.initialColumnWidth;
35429 // column width is fixed at the top - however if container width get's smaller we should
35432 // this bit calcs how man columns..
35434 var columnWidth = this.columnWidth += this.gutter;
35436 // calculate columns
35437 var containerWidth = this.containerWidth + this.gutter;
35439 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35440 // fix rounding errors, typically with gutters
35441 var excess = columnWidth - containerWidth % columnWidth;
35444 // if overshoot is less than a pixel, round up, otherwise floor it
35445 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35446 cols = Math[ mathMethod ]( cols );
35447 this.cols = Math.max( cols, 1 );
35448 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35450 // padding positioning..
35451 var totalColWidth = this.cols * this.columnWidth;
35452 var padavail = this.containerWidth - totalColWidth;
35453 // so for 2 columns - we need 3 'pads'
35455 var padNeeded = (1+this.cols) * this.padWidth;
35457 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35459 this.columnWidth += padExtra
35460 //this.padWidth = Math.floor(padavail / ( this.cols));
35462 // adjust colum width so that padding is fixed??
35464 // we have 3 columns ... total = width * 3
35465 // we have X left over... that should be used by
35467 //if (this.expandC) {
35475 getContainerWidth : function()
35477 /* // container is parent if fit width
35478 var container = this.isFitWidth ? this.element.parentNode : this.element;
35479 // check that this.size and size are there
35480 // IE8 triggers resize on body size change, so they might not be
35482 var size = getSize( container ); //FIXME
35483 this.containerWidth = size && size.innerWidth; //FIXME
35486 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35490 _getItemLayoutPosition : function( item ) // what is item?
35492 // we resize the item to our columnWidth..
35494 item.setWidth(this.columnWidth);
35495 item.autoBoxAdjust = false;
35497 var sz = item.getSize();
35499 // how many columns does this brick span
35500 var remainder = this.containerWidth % this.columnWidth;
35502 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35503 // round if off by 1 pixel, otherwise use ceil
35504 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35505 colSpan = Math.min( colSpan, this.cols );
35507 // normally this should be '1' as we dont' currently allow multi width columns..
35509 var colGroup = this._getColGroup( colSpan );
35510 // get the minimum Y value from the columns
35511 var minimumY = Math.min.apply( Math, colGroup );
35512 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35514 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35516 // position the brick
35518 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35519 y: this.currentSize.y + minimumY + this.padHeight
35523 // apply setHeight to necessary columns
35524 var setHeight = minimumY + sz.height + this.padHeight;
35525 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35527 var setSpan = this.cols + 1 - colGroup.length;
35528 for ( var i = 0; i < setSpan; i++ ) {
35529 this.colYs[ shortColIndex + i ] = setHeight ;
35536 * @param {Number} colSpan - number of columns the element spans
35537 * @returns {Array} colGroup
35539 _getColGroup : function( colSpan )
35541 if ( colSpan < 2 ) {
35542 // if brick spans only one column, use all the column Ys
35547 // how many different places could this brick fit horizontally
35548 var groupCount = this.cols + 1 - colSpan;
35549 // for each group potential horizontal position
35550 for ( var i = 0; i < groupCount; i++ ) {
35551 // make an array of colY values for that one group
35552 var groupColYs = this.colYs.slice( i, i + colSpan );
35553 // and get the max value of the array
35554 colGroup[i] = Math.max.apply( Math, groupColYs );
35559 _manageStamp : function( stamp )
35561 var stampSize = stamp.getSize();
35562 var offset = stamp.getBox();
35563 // get the columns that this stamp affects
35564 var firstX = this.isOriginLeft ? offset.x : offset.right;
35565 var lastX = firstX + stampSize.width;
35566 var firstCol = Math.floor( firstX / this.columnWidth );
35567 firstCol = Math.max( 0, firstCol );
35569 var lastCol = Math.floor( lastX / this.columnWidth );
35570 // lastCol should not go over if multiple of columnWidth #425
35571 lastCol -= lastX % this.columnWidth ? 0 : 1;
35572 lastCol = Math.min( this.cols - 1, lastCol );
35574 // set colYs to bottom of the stamp
35575 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35578 for ( var i = firstCol; i <= lastCol; i++ ) {
35579 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35584 _getContainerSize : function()
35586 this.maxY = Math.max.apply( Math, this.colYs );
35591 if ( this.isFitWidth ) {
35592 size.width = this._getContainerFitWidth();
35598 _getContainerFitWidth : function()
35600 var unusedCols = 0;
35601 // count unused columns
35604 if ( this.colYs[i] !== 0 ) {
35609 // fit container to columns that have been used
35610 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35613 needsResizeLayout : function()
35615 var previousWidth = this.containerWidth;
35616 this.getContainerWidth();
35617 return previousWidth !== this.containerWidth;
35632 * @class Roo.bootstrap.MasonryBrick
35633 * @extends Roo.bootstrap.Component
35634 * Bootstrap MasonryBrick class
35637 * Create a new MasonryBrick
35638 * @param {Object} config The config object
35641 Roo.bootstrap.MasonryBrick = function(config){
35643 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35645 Roo.bootstrap.MasonryBrick.register(this);
35651 * When a MasonryBrick is clcik
35652 * @param {Roo.bootstrap.MasonryBrick} this
35653 * @param {Roo.EventObject} e
35659 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35662 * @cfg {String} title
35666 * @cfg {String} html
35670 * @cfg {String} bgimage
35674 * @cfg {String} videourl
35678 * @cfg {String} cls
35682 * @cfg {String} href
35686 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35691 * @cfg {String} placetitle (center|bottom)
35696 * @cfg {Boolean} isFitContainer defalut true
35698 isFitContainer : true,
35701 * @cfg {Boolean} preventDefault defalut false
35703 preventDefault : false,
35706 * @cfg {Boolean} inverse defalut false
35708 maskInverse : false,
35710 getAutoCreate : function()
35712 if(!this.isFitContainer){
35713 return this.getSplitAutoCreate();
35716 var cls = 'masonry-brick masonry-brick-full';
35718 if(this.href.length){
35719 cls += ' masonry-brick-link';
35722 if(this.bgimage.length){
35723 cls += ' masonry-brick-image';
35726 if(this.maskInverse){
35727 cls += ' mask-inverse';
35730 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35731 cls += ' enable-mask';
35735 cls += ' masonry-' + this.size + '-brick';
35738 if(this.placetitle.length){
35740 switch (this.placetitle) {
35742 cls += ' masonry-center-title';
35745 cls += ' masonry-bottom-title';
35752 if(!this.html.length && !this.bgimage.length){
35753 cls += ' masonry-center-title';
35756 if(!this.html.length && this.bgimage.length){
35757 cls += ' masonry-bottom-title';
35762 cls += ' ' + this.cls;
35766 tag: (this.href.length) ? 'a' : 'div',
35771 cls: 'masonry-brick-mask'
35775 cls: 'masonry-brick-paragraph',
35781 if(this.href.length){
35782 cfg.href = this.href;
35785 var cn = cfg.cn[1].cn;
35787 if(this.title.length){
35790 cls: 'masonry-brick-title',
35795 if(this.html.length){
35798 cls: 'masonry-brick-text',
35803 if (!this.title.length && !this.html.length) {
35804 cfg.cn[1].cls += ' hide';
35807 if(this.bgimage.length){
35810 cls: 'masonry-brick-image-view',
35815 if(this.videourl.length){
35816 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35817 // youtube support only?
35820 cls: 'masonry-brick-image-view',
35823 allowfullscreen : true
35831 getSplitAutoCreate : function()
35833 var cls = 'masonry-brick masonry-brick-split';
35835 if(this.href.length){
35836 cls += ' masonry-brick-link';
35839 if(this.bgimage.length){
35840 cls += ' masonry-brick-image';
35844 cls += ' masonry-' + this.size + '-brick';
35847 switch (this.placetitle) {
35849 cls += ' masonry-center-title';
35852 cls += ' masonry-bottom-title';
35855 if(!this.bgimage.length){
35856 cls += ' masonry-center-title';
35859 if(this.bgimage.length){
35860 cls += ' masonry-bottom-title';
35866 cls += ' ' + this.cls;
35870 tag: (this.href.length) ? 'a' : 'div',
35875 cls: 'masonry-brick-split-head',
35879 cls: 'masonry-brick-paragraph',
35886 cls: 'masonry-brick-split-body',
35892 if(this.href.length){
35893 cfg.href = this.href;
35896 if(this.title.length){
35897 cfg.cn[0].cn[0].cn.push({
35899 cls: 'masonry-brick-title',
35904 if(this.html.length){
35905 cfg.cn[1].cn.push({
35907 cls: 'masonry-brick-text',
35912 if(this.bgimage.length){
35913 cfg.cn[0].cn.push({
35915 cls: 'masonry-brick-image-view',
35920 if(this.videourl.length){
35921 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35922 // youtube support only?
35923 cfg.cn[0].cn.cn.push({
35925 cls: 'masonry-brick-image-view',
35928 allowfullscreen : true
35935 initEvents: function()
35937 switch (this.size) {
35970 this.el.on('touchstart', this.onTouchStart, this);
35971 this.el.on('touchmove', this.onTouchMove, this);
35972 this.el.on('touchend', this.onTouchEnd, this);
35973 this.el.on('contextmenu', this.onContextMenu, this);
35975 this.el.on('mouseenter' ,this.enter, this);
35976 this.el.on('mouseleave', this.leave, this);
35977 this.el.on('click', this.onClick, this);
35980 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35981 this.parent().bricks.push(this);
35986 onClick: function(e, el)
35988 var time = this.endTimer - this.startTimer;
35989 // Roo.log(e.preventDefault());
35992 e.preventDefault();
35997 if(!this.preventDefault){
36001 e.preventDefault();
36003 if (this.activeClass != '') {
36004 this.selectBrick();
36007 this.fireEvent('click', this, e);
36010 enter: function(e, el)
36012 e.preventDefault();
36014 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36018 if(this.bgimage.length && this.html.length){
36019 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36023 leave: function(e, el)
36025 e.preventDefault();
36027 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36031 if(this.bgimage.length && this.html.length){
36032 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36036 onTouchStart: function(e, el)
36038 // e.preventDefault();
36040 this.touchmoved = false;
36042 if(!this.isFitContainer){
36046 if(!this.bgimage.length || !this.html.length){
36050 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36052 this.timer = new Date().getTime();
36056 onTouchMove: function(e, el)
36058 this.touchmoved = true;
36061 onContextMenu : function(e,el)
36063 e.preventDefault();
36064 e.stopPropagation();
36068 onTouchEnd: function(e, el)
36070 // e.preventDefault();
36072 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36079 if(!this.bgimage.length || !this.html.length){
36081 if(this.href.length){
36082 window.location.href = this.href;
36088 if(!this.isFitContainer){
36092 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36094 window.location.href = this.href;
36097 //selection on single brick only
36098 selectBrick : function() {
36100 if (!this.parentId) {
36104 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36105 var index = m.selectedBrick.indexOf(this.id);
36108 m.selectedBrick.splice(index,1);
36109 this.el.removeClass(this.activeClass);
36113 for(var i = 0; i < m.selectedBrick.length; i++) {
36114 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36115 b.el.removeClass(b.activeClass);
36118 m.selectedBrick = [];
36120 m.selectedBrick.push(this.id);
36121 this.el.addClass(this.activeClass);
36125 isSelected : function(){
36126 return this.el.hasClass(this.activeClass);
36131 Roo.apply(Roo.bootstrap.MasonryBrick, {
36134 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36136 * register a Masonry Brick
36137 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36140 register : function(brick)
36142 //this.groups[brick.id] = brick;
36143 this.groups.add(brick.id, brick);
36146 * fetch a masonry brick based on the masonry brick ID
36147 * @param {string} the masonry brick to add
36148 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36151 get: function(brick_id)
36153 // if (typeof(this.groups[brick_id]) == 'undefined') {
36156 // return this.groups[brick_id] ;
36158 if(this.groups.key(brick_id)) {
36159 return this.groups.key(brick_id);
36177 * @class Roo.bootstrap.Brick
36178 * @extends Roo.bootstrap.Component
36179 * Bootstrap Brick class
36182 * Create a new Brick
36183 * @param {Object} config The config object
36186 Roo.bootstrap.Brick = function(config){
36187 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36193 * When a Brick is click
36194 * @param {Roo.bootstrap.Brick} this
36195 * @param {Roo.EventObject} e
36201 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36204 * @cfg {String} title
36208 * @cfg {String} html
36212 * @cfg {String} bgimage
36216 * @cfg {String} cls
36220 * @cfg {String} href
36224 * @cfg {String} video
36228 * @cfg {Boolean} square
36232 getAutoCreate : function()
36234 var cls = 'roo-brick';
36236 if(this.href.length){
36237 cls += ' roo-brick-link';
36240 if(this.bgimage.length){
36241 cls += ' roo-brick-image';
36244 if(!this.html.length && !this.bgimage.length){
36245 cls += ' roo-brick-center-title';
36248 if(!this.html.length && this.bgimage.length){
36249 cls += ' roo-brick-bottom-title';
36253 cls += ' ' + this.cls;
36257 tag: (this.href.length) ? 'a' : 'div',
36262 cls: 'roo-brick-paragraph',
36268 if(this.href.length){
36269 cfg.href = this.href;
36272 var cn = cfg.cn[0].cn;
36274 if(this.title.length){
36277 cls: 'roo-brick-title',
36282 if(this.html.length){
36285 cls: 'roo-brick-text',
36292 if(this.bgimage.length){
36295 cls: 'roo-brick-image-view',
36303 initEvents: function()
36305 if(this.title.length || this.html.length){
36306 this.el.on('mouseenter' ,this.enter, this);
36307 this.el.on('mouseleave', this.leave, this);
36310 Roo.EventManager.onWindowResize(this.resize, this);
36312 if(this.bgimage.length){
36313 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36314 this.imageEl.on('load', this.onImageLoad, this);
36321 onImageLoad : function()
36326 resize : function()
36328 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36330 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36332 if(this.bgimage.length){
36333 var image = this.el.select('.roo-brick-image-view', true).first();
36335 image.setWidth(paragraph.getWidth());
36338 image.setHeight(paragraph.getWidth());
36341 this.el.setHeight(image.getHeight());
36342 paragraph.setHeight(image.getHeight());
36348 enter: function(e, el)
36350 e.preventDefault();
36352 if(this.bgimage.length){
36353 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36354 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36358 leave: function(e, el)
36360 e.preventDefault();
36362 if(this.bgimage.length){
36363 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36364 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36379 * @class Roo.bootstrap.NumberField
36380 * @extends Roo.bootstrap.Input
36381 * Bootstrap NumberField class
36387 * Create a new NumberField
36388 * @param {Object} config The config object
36391 Roo.bootstrap.NumberField = function(config){
36392 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36395 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36398 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36400 allowDecimals : true,
36402 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36404 decimalSeparator : ".",
36406 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36408 decimalPrecision : 2,
36410 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36412 allowNegative : true,
36415 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36419 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36421 minValue : Number.NEGATIVE_INFINITY,
36423 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36425 maxValue : Number.MAX_VALUE,
36427 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36429 minText : "The minimum value for this field is {0}",
36431 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36433 maxText : "The maximum value for this field is {0}",
36435 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36436 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36438 nanText : "{0} is not a valid number",
36440 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36442 thousandsDelimiter : false,
36444 * @cfg {String} valueAlign alignment of value
36446 valueAlign : "left",
36448 getAutoCreate : function()
36450 var hiddenInput = {
36454 cls: 'hidden-number-input'
36458 hiddenInput.name = this.name;
36463 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36465 this.name = hiddenInput.name;
36467 if(cfg.cn.length > 0) {
36468 cfg.cn.push(hiddenInput);
36475 initEvents : function()
36477 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36479 var allowed = "0123456789";
36481 if(this.allowDecimals){
36482 allowed += this.decimalSeparator;
36485 if(this.allowNegative){
36489 if(this.thousandsDelimiter) {
36493 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36495 var keyPress = function(e){
36497 var k = e.getKey();
36499 var c = e.getCharCode();
36502 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36503 allowed.indexOf(String.fromCharCode(c)) === -1
36509 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36513 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36518 this.el.on("keypress", keyPress, this);
36521 validateValue : function(value)
36524 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36528 var num = this.parseValue(value);
36531 this.markInvalid(String.format(this.nanText, value));
36535 if(num < this.minValue){
36536 this.markInvalid(String.format(this.minText, this.minValue));
36540 if(num > this.maxValue){
36541 this.markInvalid(String.format(this.maxText, this.maxValue));
36548 getValue : function()
36550 var v = this.hiddenEl().getValue();
36552 return this.fixPrecision(this.parseValue(v));
36555 parseValue : function(value)
36557 if(this.thousandsDelimiter) {
36559 r = new RegExp(",", "g");
36560 value = value.replace(r, "");
36563 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36564 return isNaN(value) ? '' : value;
36567 fixPrecision : function(value)
36569 if(this.thousandsDelimiter) {
36571 r = new RegExp(",", "g");
36572 value = value.replace(r, "");
36575 var nan = isNaN(value);
36577 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36578 return nan ? '' : value;
36580 return parseFloat(value).toFixed(this.decimalPrecision);
36583 setValue : function(v)
36585 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36591 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36593 this.inputEl().dom.value = (v == '') ? '' :
36594 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36596 if(!this.allowZero && v === '0') {
36597 this.hiddenEl().dom.value = '';
36598 this.inputEl().dom.value = '';
36605 decimalPrecisionFcn : function(v)
36607 return Math.floor(v);
36610 beforeBlur : function()
36612 var v = this.parseValue(this.getRawValue());
36614 if(v || v === 0 || v === ''){
36619 hiddenEl : function()
36621 return this.el.select('input.hidden-number-input',true).first();
36633 * @class Roo.bootstrap.DocumentSlider
36634 * @extends Roo.bootstrap.Component
36635 * Bootstrap DocumentSlider class
36638 * Create a new DocumentViewer
36639 * @param {Object} config The config object
36642 Roo.bootstrap.DocumentSlider = function(config){
36643 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36650 * Fire after initEvent
36651 * @param {Roo.bootstrap.DocumentSlider} this
36656 * Fire after update
36657 * @param {Roo.bootstrap.DocumentSlider} this
36663 * @param {Roo.bootstrap.DocumentSlider} this
36669 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36675 getAutoCreate : function()
36679 cls : 'roo-document-slider',
36683 cls : 'roo-document-slider-header',
36687 cls : 'roo-document-slider-header-title'
36693 cls : 'roo-document-slider-body',
36697 cls : 'roo-document-slider-prev',
36701 cls : 'fa fa-chevron-left'
36707 cls : 'roo-document-slider-thumb',
36711 cls : 'roo-document-slider-image'
36717 cls : 'roo-document-slider-next',
36721 cls : 'fa fa-chevron-right'
36733 initEvents : function()
36735 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36736 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36738 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36739 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36741 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36742 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36744 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36745 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36747 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36748 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36750 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36751 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36753 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36754 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36756 this.thumbEl.on('click', this.onClick, this);
36758 this.prevIndicator.on('click', this.prev, this);
36760 this.nextIndicator.on('click', this.next, this);
36764 initial : function()
36766 if(this.files.length){
36767 this.indicator = 1;
36771 this.fireEvent('initial', this);
36774 update : function()
36776 this.imageEl.attr('src', this.files[this.indicator - 1]);
36778 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36780 this.prevIndicator.show();
36782 if(this.indicator == 1){
36783 this.prevIndicator.hide();
36786 this.nextIndicator.show();
36788 if(this.indicator == this.files.length){
36789 this.nextIndicator.hide();
36792 this.thumbEl.scrollTo('top');
36794 this.fireEvent('update', this);
36797 onClick : function(e)
36799 e.preventDefault();
36801 this.fireEvent('click', this);
36806 e.preventDefault();
36808 this.indicator = Math.max(1, this.indicator - 1);
36815 e.preventDefault();
36817 this.indicator = Math.min(this.files.length, this.indicator + 1);
36831 * @class Roo.bootstrap.RadioSet
36832 * @extends Roo.bootstrap.Input
36833 * Bootstrap RadioSet class
36834 * @cfg {String} indicatorpos (left|right) default left
36835 * @cfg {Boolean} inline (true|false) inline the element (default true)
36836 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36838 * Create a new RadioSet
36839 * @param {Object} config The config object
36842 Roo.bootstrap.RadioSet = function(config){
36844 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36848 Roo.bootstrap.RadioSet.register(this);
36853 * Fires when the element is checked or unchecked.
36854 * @param {Roo.bootstrap.RadioSet} this This radio
36855 * @param {Roo.bootstrap.Radio} item The checked item
36860 * Fires when the element is click.
36861 * @param {Roo.bootstrap.RadioSet} this This radio set
36862 * @param {Roo.bootstrap.Radio} item The checked item
36863 * @param {Roo.EventObject} e The event object
36870 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36878 indicatorpos : 'left',
36880 getAutoCreate : function()
36884 cls : 'roo-radio-set-label',
36888 html : this.fieldLabel
36892 if (Roo.bootstrap.version == 3) {
36895 if(this.indicatorpos == 'left'){
36898 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36899 tooltip : 'This field is required'
36904 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36905 tooltip : 'This field is required'
36911 cls : 'roo-radio-set-items'
36914 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36916 if (align === 'left' && this.fieldLabel.length) {
36919 cls : "roo-radio-set-right",
36925 if(this.labelWidth > 12){
36926 label.style = "width: " + this.labelWidth + 'px';
36929 if(this.labelWidth < 13 && this.labelmd == 0){
36930 this.labelmd = this.labelWidth;
36933 if(this.labellg > 0){
36934 label.cls += ' col-lg-' + this.labellg;
36935 items.cls += ' col-lg-' + (12 - this.labellg);
36938 if(this.labelmd > 0){
36939 label.cls += ' col-md-' + this.labelmd;
36940 items.cls += ' col-md-' + (12 - this.labelmd);
36943 if(this.labelsm > 0){
36944 label.cls += ' col-sm-' + this.labelsm;
36945 items.cls += ' col-sm-' + (12 - this.labelsm);
36948 if(this.labelxs > 0){
36949 label.cls += ' col-xs-' + this.labelxs;
36950 items.cls += ' col-xs-' + (12 - this.labelxs);
36956 cls : 'roo-radio-set',
36960 cls : 'roo-radio-set-input',
36963 value : this.value ? this.value : ''
36970 if(this.weight.length){
36971 cfg.cls += ' roo-radio-' + this.weight;
36975 cfg.cls += ' roo-radio-set-inline';
36979 ['xs','sm','md','lg'].map(function(size){
36980 if (settings[size]) {
36981 cfg.cls += ' col-' + size + '-' + settings[size];
36989 initEvents : function()
36991 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36992 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36994 if(!this.fieldLabel.length){
36995 this.labelEl.hide();
36998 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36999 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37001 this.indicator = this.indicatorEl();
37003 if(this.indicator){
37004 this.indicator.addClass('invisible');
37007 this.originalValue = this.getValue();
37011 inputEl: function ()
37013 return this.el.select('.roo-radio-set-input', true).first();
37016 getChildContainer : function()
37018 return this.itemsEl;
37021 register : function(item)
37023 this.radioes.push(item);
37027 validate : function()
37029 if(this.getVisibilityEl().hasClass('hidden')){
37035 Roo.each(this.radioes, function(i){
37044 if(this.allowBlank) {
37048 if(this.disabled || valid){
37053 this.markInvalid();
37058 markValid : function()
37060 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37061 this.indicatorEl().removeClass('visible');
37062 this.indicatorEl().addClass('invisible');
37066 if (Roo.bootstrap.version == 3) {
37067 this.el.removeClass([this.invalidClass, this.validClass]);
37068 this.el.addClass(this.validClass);
37070 this.el.removeClass(['is-invalid','is-valid']);
37071 this.el.addClass(['is-valid']);
37073 this.fireEvent('valid', this);
37076 markInvalid : function(msg)
37078 if(this.allowBlank || this.disabled){
37082 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37083 this.indicatorEl().removeClass('invisible');
37084 this.indicatorEl().addClass('visible');
37086 if (Roo.bootstrap.version == 3) {
37087 this.el.removeClass([this.invalidClass, this.validClass]);
37088 this.el.addClass(this.invalidClass);
37090 this.el.removeClass(['is-invalid','is-valid']);
37091 this.el.addClass(['is-invalid']);
37094 this.fireEvent('invalid', this, msg);
37098 setValue : function(v, suppressEvent)
37100 if(this.value === v){
37107 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37110 Roo.each(this.radioes, function(i){
37112 i.el.removeClass('checked');
37115 Roo.each(this.radioes, function(i){
37117 if(i.value === v || i.value.toString() === v.toString()){
37119 i.el.addClass('checked');
37121 if(suppressEvent !== true){
37122 this.fireEvent('check', this, i);
37133 clearInvalid : function(){
37135 if(!this.el || this.preventMark){
37139 this.el.removeClass([this.invalidClass]);
37141 this.fireEvent('valid', this);
37146 Roo.apply(Roo.bootstrap.RadioSet, {
37150 register : function(set)
37152 this.groups[set.name] = set;
37155 get: function(name)
37157 if (typeof(this.groups[name]) == 'undefined') {
37161 return this.groups[name] ;
37167 * Ext JS Library 1.1.1
37168 * Copyright(c) 2006-2007, Ext JS, LLC.
37170 * Originally Released Under LGPL - original licence link has changed is not relivant.
37173 * <script type="text/javascript">
37178 * @class Roo.bootstrap.SplitBar
37179 * @extends Roo.util.Observable
37180 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37184 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37185 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37186 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37187 split.minSize = 100;
37188 split.maxSize = 600;
37189 split.animate = true;
37190 split.on('moved', splitterMoved);
37193 * Create a new SplitBar
37194 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37195 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37196 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37197 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37198 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37199 position of the SplitBar).
37201 Roo.bootstrap.SplitBar = function(cfg){
37206 // dragElement : elm
37207 // resizingElement: el,
37209 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37210 // placement : Roo.bootstrap.SplitBar.LEFT ,
37211 // existingProxy ???
37214 this.el = Roo.get(cfg.dragElement, true);
37215 this.el.dom.unselectable = "on";
37217 this.resizingEl = Roo.get(cfg.resizingElement, true);
37221 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37222 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37225 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37228 * The minimum size of the resizing element. (Defaults to 0)
37234 * The maximum size of the resizing element. (Defaults to 2000)
37237 this.maxSize = 2000;
37240 * Whether to animate the transition to the new size
37243 this.animate = false;
37246 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37249 this.useShim = false;
37254 if(!cfg.existingProxy){
37256 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37258 this.proxy = Roo.get(cfg.existingProxy).dom;
37261 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37264 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37267 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37270 this.dragSpecs = {};
37273 * @private The adapter to use to positon and resize elements
37275 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37276 this.adapter.init(this);
37278 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37280 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37281 this.el.addClass("roo-splitbar-h");
37284 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37285 this.el.addClass("roo-splitbar-v");
37291 * Fires when the splitter is moved (alias for {@link #event-moved})
37292 * @param {Roo.bootstrap.SplitBar} this
37293 * @param {Number} newSize the new width or height
37298 * Fires when the splitter is moved
37299 * @param {Roo.bootstrap.SplitBar} this
37300 * @param {Number} newSize the new width or height
37304 * @event beforeresize
37305 * Fires before the splitter is dragged
37306 * @param {Roo.bootstrap.SplitBar} this
37308 "beforeresize" : true,
37310 "beforeapply" : true
37313 Roo.util.Observable.call(this);
37316 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37317 onStartProxyDrag : function(x, y){
37318 this.fireEvent("beforeresize", this);
37320 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37322 o.enableDisplayMode("block");
37323 // all splitbars share the same overlay
37324 Roo.bootstrap.SplitBar.prototype.overlay = o;
37326 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37327 this.overlay.show();
37328 Roo.get(this.proxy).setDisplayed("block");
37329 var size = this.adapter.getElementSize(this);
37330 this.activeMinSize = this.getMinimumSize();;
37331 this.activeMaxSize = this.getMaximumSize();;
37332 var c1 = size - this.activeMinSize;
37333 var c2 = Math.max(this.activeMaxSize - size, 0);
37334 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37335 this.dd.resetConstraints();
37336 this.dd.setXConstraint(
37337 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37338 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37340 this.dd.setYConstraint(0, 0);
37342 this.dd.resetConstraints();
37343 this.dd.setXConstraint(0, 0);
37344 this.dd.setYConstraint(
37345 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37346 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37349 this.dragSpecs.startSize = size;
37350 this.dragSpecs.startPoint = [x, y];
37351 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37355 * @private Called after the drag operation by the DDProxy
37357 onEndProxyDrag : function(e){
37358 Roo.get(this.proxy).setDisplayed(false);
37359 var endPoint = Roo.lib.Event.getXY(e);
37361 this.overlay.hide();
37364 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37365 newSize = this.dragSpecs.startSize +
37366 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37367 endPoint[0] - this.dragSpecs.startPoint[0] :
37368 this.dragSpecs.startPoint[0] - endPoint[0]
37371 newSize = this.dragSpecs.startSize +
37372 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37373 endPoint[1] - this.dragSpecs.startPoint[1] :
37374 this.dragSpecs.startPoint[1] - endPoint[1]
37377 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37378 if(newSize != this.dragSpecs.startSize){
37379 if(this.fireEvent('beforeapply', this, newSize) !== false){
37380 this.adapter.setElementSize(this, newSize);
37381 this.fireEvent("moved", this, newSize);
37382 this.fireEvent("resize", this, newSize);
37388 * Get the adapter this SplitBar uses
37389 * @return The adapter object
37391 getAdapter : function(){
37392 return this.adapter;
37396 * Set the adapter this SplitBar uses
37397 * @param {Object} adapter A SplitBar adapter object
37399 setAdapter : function(adapter){
37400 this.adapter = adapter;
37401 this.adapter.init(this);
37405 * Gets the minimum size for the resizing element
37406 * @return {Number} The minimum size
37408 getMinimumSize : function(){
37409 return this.minSize;
37413 * Sets the minimum size for the resizing element
37414 * @param {Number} minSize The minimum size
37416 setMinimumSize : function(minSize){
37417 this.minSize = minSize;
37421 * Gets the maximum size for the resizing element
37422 * @return {Number} The maximum size
37424 getMaximumSize : function(){
37425 return this.maxSize;
37429 * Sets the maximum size for the resizing element
37430 * @param {Number} maxSize The maximum size
37432 setMaximumSize : function(maxSize){
37433 this.maxSize = maxSize;
37437 * Sets the initialize size for the resizing element
37438 * @param {Number} size The initial size
37440 setCurrentSize : function(size){
37441 var oldAnimate = this.animate;
37442 this.animate = false;
37443 this.adapter.setElementSize(this, size);
37444 this.animate = oldAnimate;
37448 * Destroy this splitbar.
37449 * @param {Boolean} removeEl True to remove the element
37451 destroy : function(removeEl){
37453 this.shim.remove();
37456 this.proxy.parentNode.removeChild(this.proxy);
37464 * @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.
37466 Roo.bootstrap.SplitBar.createProxy = function(dir){
37467 var proxy = new Roo.Element(document.createElement("div"));
37468 proxy.unselectable();
37469 var cls = 'roo-splitbar-proxy';
37470 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37471 document.body.appendChild(proxy.dom);
37476 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37477 * Default Adapter. It assumes the splitter and resizing element are not positioned
37478 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37480 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37483 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37484 // do nothing for now
37485 init : function(s){
37489 * Called before drag operations to get the current size of the resizing element.
37490 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37492 getElementSize : function(s){
37493 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37494 return s.resizingEl.getWidth();
37496 return s.resizingEl.getHeight();
37501 * Called after drag operations to set the size of the resizing element.
37502 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37503 * @param {Number} newSize The new size to set
37504 * @param {Function} onComplete A function to be invoked when resizing is complete
37506 setElementSize : function(s, newSize, onComplete){
37507 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37509 s.resizingEl.setWidth(newSize);
37511 onComplete(s, newSize);
37514 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37519 s.resizingEl.setHeight(newSize);
37521 onComplete(s, newSize);
37524 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37531 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37532 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37533 * Adapter that moves the splitter element to align with the resized sizing element.
37534 * Used with an absolute positioned SplitBar.
37535 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37536 * document.body, make sure you assign an id to the body element.
37538 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37539 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37540 this.container = Roo.get(container);
37543 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37544 init : function(s){
37545 this.basic.init(s);
37548 getElementSize : function(s){
37549 return this.basic.getElementSize(s);
37552 setElementSize : function(s, newSize, onComplete){
37553 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37556 moveSplitter : function(s){
37557 var yes = Roo.bootstrap.SplitBar;
37558 switch(s.placement){
37560 s.el.setX(s.resizingEl.getRight());
37563 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37566 s.el.setY(s.resizingEl.getBottom());
37569 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37576 * Orientation constant - Create a vertical SplitBar
37580 Roo.bootstrap.SplitBar.VERTICAL = 1;
37583 * Orientation constant - Create a horizontal SplitBar
37587 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37590 * Placement constant - The resizing element is to the left of the splitter element
37594 Roo.bootstrap.SplitBar.LEFT = 1;
37597 * Placement constant - The resizing element is to the right of the splitter element
37601 Roo.bootstrap.SplitBar.RIGHT = 2;
37604 * Placement constant - The resizing element is positioned above the splitter element
37608 Roo.bootstrap.SplitBar.TOP = 3;
37611 * Placement constant - The resizing element is positioned under splitter element
37615 Roo.bootstrap.SplitBar.BOTTOM = 4;
37616 Roo.namespace("Roo.bootstrap.layout");/*
37618 * Ext JS Library 1.1.1
37619 * Copyright(c) 2006-2007, Ext JS, LLC.
37621 * Originally Released Under LGPL - original licence link has changed is not relivant.
37624 * <script type="text/javascript">
37628 * @class Roo.bootstrap.layout.Manager
37629 * @extends Roo.bootstrap.Component
37630 * Base class for layout managers.
37632 Roo.bootstrap.layout.Manager = function(config)
37634 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37640 /** false to disable window resize monitoring @type Boolean */
37641 this.monitorWindowResize = true;
37646 * Fires when a layout is performed.
37647 * @param {Roo.LayoutManager} this
37651 * @event regionresized
37652 * Fires when the user resizes a region.
37653 * @param {Roo.LayoutRegion} region The resized region
37654 * @param {Number} newSize The new size (width for east/west, height for north/south)
37656 "regionresized" : true,
37658 * @event regioncollapsed
37659 * Fires when a region is collapsed.
37660 * @param {Roo.LayoutRegion} region The collapsed region
37662 "regioncollapsed" : true,
37664 * @event regionexpanded
37665 * Fires when a region is expanded.
37666 * @param {Roo.LayoutRegion} region The expanded region
37668 "regionexpanded" : true
37670 this.updating = false;
37673 this.el = Roo.get(config.el);
37679 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37684 monitorWindowResize : true,
37690 onRender : function(ct, position)
37693 this.el = Roo.get(ct);
37696 //this.fireEvent('render',this);
37700 initEvents: function()
37704 // ie scrollbar fix
37705 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37706 document.body.scroll = "no";
37707 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37708 this.el.position('relative');
37710 this.id = this.el.id;
37711 this.el.addClass("roo-layout-container");
37712 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37713 if(this.el.dom != document.body ) {
37714 this.el.on('resize', this.layout,this);
37715 this.el.on('show', this.layout,this);
37721 * Returns true if this layout is currently being updated
37722 * @return {Boolean}
37724 isUpdating : function(){
37725 return this.updating;
37729 * Suspend the LayoutManager from doing auto-layouts while
37730 * making multiple add or remove calls
37732 beginUpdate : function(){
37733 this.updating = true;
37737 * Restore auto-layouts and optionally disable the manager from performing a layout
37738 * @param {Boolean} noLayout true to disable a layout update
37740 endUpdate : function(noLayout){
37741 this.updating = false;
37747 layout: function(){
37751 onRegionResized : function(region, newSize){
37752 this.fireEvent("regionresized", region, newSize);
37756 onRegionCollapsed : function(region){
37757 this.fireEvent("regioncollapsed", region);
37760 onRegionExpanded : function(region){
37761 this.fireEvent("regionexpanded", region);
37765 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37766 * performs box-model adjustments.
37767 * @return {Object} The size as an object {width: (the width), height: (the height)}
37769 getViewSize : function()
37772 if(this.el.dom != document.body){
37773 size = this.el.getSize();
37775 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37777 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37778 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37783 * Returns the Element this layout is bound to.
37784 * @return {Roo.Element}
37786 getEl : function(){
37791 * Returns the specified region.
37792 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37793 * @return {Roo.LayoutRegion}
37795 getRegion : function(target){
37796 return this.regions[target.toLowerCase()];
37799 onWindowResize : function(){
37800 if(this.monitorWindowResize){
37807 * Ext JS Library 1.1.1
37808 * Copyright(c) 2006-2007, Ext JS, LLC.
37810 * Originally Released Under LGPL - original licence link has changed is not relivant.
37813 * <script type="text/javascript">
37816 * @class Roo.bootstrap.layout.Border
37817 * @extends Roo.bootstrap.layout.Manager
37818 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37819 * please see: examples/bootstrap/nested.html<br><br>
37821 <b>The container the layout is rendered into can be either the body element or any other element.
37822 If it is not the body element, the container needs to either be an absolute positioned element,
37823 or you will need to add "position:relative" to the css of the container. You will also need to specify
37824 the container size if it is not the body element.</b>
37827 * Create a new Border
37828 * @param {Object} config Configuration options
37830 Roo.bootstrap.layout.Border = function(config){
37831 config = config || {};
37832 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37836 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37837 if(config[region]){
37838 config[region].region = region;
37839 this.addRegion(config[region]);
37845 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37847 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37849 parent : false, // this might point to a 'nest' or a ???
37852 * Creates and adds a new region if it doesn't already exist.
37853 * @param {String} target The target region key (north, south, east, west or center).
37854 * @param {Object} config The regions config object
37855 * @return {BorderLayoutRegion} The new region
37857 addRegion : function(config)
37859 if(!this.regions[config.region]){
37860 var r = this.factory(config);
37861 this.bindRegion(r);
37863 return this.regions[config.region];
37867 bindRegion : function(r){
37868 this.regions[r.config.region] = r;
37870 r.on("visibilitychange", this.layout, this);
37871 r.on("paneladded", this.layout, this);
37872 r.on("panelremoved", this.layout, this);
37873 r.on("invalidated", this.layout, this);
37874 r.on("resized", this.onRegionResized, this);
37875 r.on("collapsed", this.onRegionCollapsed, this);
37876 r.on("expanded", this.onRegionExpanded, this);
37880 * Performs a layout update.
37882 layout : function()
37884 if(this.updating) {
37888 // render all the rebions if they have not been done alreayd?
37889 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37890 if(this.regions[region] && !this.regions[region].bodyEl){
37891 this.regions[region].onRender(this.el)
37895 var size = this.getViewSize();
37896 var w = size.width;
37897 var h = size.height;
37902 //var x = 0, y = 0;
37904 var rs = this.regions;
37905 var north = rs["north"];
37906 var south = rs["south"];
37907 var west = rs["west"];
37908 var east = rs["east"];
37909 var center = rs["center"];
37910 //if(this.hideOnLayout){ // not supported anymore
37911 //c.el.setStyle("display", "none");
37913 if(north && north.isVisible()){
37914 var b = north.getBox();
37915 var m = north.getMargins();
37916 b.width = w - (m.left+m.right);
37919 centerY = b.height + b.y + m.bottom;
37920 centerH -= centerY;
37921 north.updateBox(this.safeBox(b));
37923 if(south && south.isVisible()){
37924 var b = south.getBox();
37925 var m = south.getMargins();
37926 b.width = w - (m.left+m.right);
37928 var totalHeight = (b.height + m.top + m.bottom);
37929 b.y = h - totalHeight + m.top;
37930 centerH -= totalHeight;
37931 south.updateBox(this.safeBox(b));
37933 if(west && west.isVisible()){
37934 var b = west.getBox();
37935 var m = west.getMargins();
37936 b.height = centerH - (m.top+m.bottom);
37938 b.y = centerY + m.top;
37939 var totalWidth = (b.width + m.left + m.right);
37940 centerX += totalWidth;
37941 centerW -= totalWidth;
37942 west.updateBox(this.safeBox(b));
37944 if(east && east.isVisible()){
37945 var b = east.getBox();
37946 var m = east.getMargins();
37947 b.height = centerH - (m.top+m.bottom);
37948 var totalWidth = (b.width + m.left + m.right);
37949 b.x = w - totalWidth + m.left;
37950 b.y = centerY + m.top;
37951 centerW -= totalWidth;
37952 east.updateBox(this.safeBox(b));
37955 var m = center.getMargins();
37957 x: centerX + m.left,
37958 y: centerY + m.top,
37959 width: centerW - (m.left+m.right),
37960 height: centerH - (m.top+m.bottom)
37962 //if(this.hideOnLayout){
37963 //center.el.setStyle("display", "block");
37965 center.updateBox(this.safeBox(centerBox));
37968 this.fireEvent("layout", this);
37972 safeBox : function(box){
37973 box.width = Math.max(0, box.width);
37974 box.height = Math.max(0, box.height);
37979 * Adds a ContentPanel (or subclass) to this layout.
37980 * @param {String} target The target region key (north, south, east, west or center).
37981 * @param {Roo.ContentPanel} panel The panel to add
37982 * @return {Roo.ContentPanel} The added panel
37984 add : function(target, panel){
37986 target = target.toLowerCase();
37987 return this.regions[target].add(panel);
37991 * Remove a ContentPanel (or subclass) to this layout.
37992 * @param {String} target The target region key (north, south, east, west or center).
37993 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37994 * @return {Roo.ContentPanel} The removed panel
37996 remove : function(target, panel){
37997 target = target.toLowerCase();
37998 return this.regions[target].remove(panel);
38002 * Searches all regions for a panel with the specified id
38003 * @param {String} panelId
38004 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38006 findPanel : function(panelId){
38007 var rs = this.regions;
38008 for(var target in rs){
38009 if(typeof rs[target] != "function"){
38010 var p = rs[target].getPanel(panelId);
38020 * Searches all regions for a panel with the specified id and activates (shows) it.
38021 * @param {String/ContentPanel} panelId The panels id or the panel itself
38022 * @return {Roo.ContentPanel} The shown panel or null
38024 showPanel : function(panelId) {
38025 var rs = this.regions;
38026 for(var target in rs){
38027 var r = rs[target];
38028 if(typeof r != "function"){
38029 if(r.hasPanel(panelId)){
38030 return r.showPanel(panelId);
38038 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38039 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38042 restoreState : function(provider){
38044 provider = Roo.state.Manager;
38046 var sm = new Roo.LayoutStateManager();
38047 sm.init(this, provider);
38053 * Adds a xtype elements to the layout.
38057 xtype : 'ContentPanel',
38064 xtype : 'NestedLayoutPanel',
38070 items : [ ... list of content panels or nested layout panels.. ]
38074 * @param {Object} cfg Xtype definition of item to add.
38076 addxtype : function(cfg)
38078 // basically accepts a pannel...
38079 // can accept a layout region..!?!?
38080 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38083 // theory? children can only be panels??
38085 //if (!cfg.xtype.match(/Panel$/)) {
38090 if (typeof(cfg.region) == 'undefined') {
38091 Roo.log("Failed to add Panel, region was not set");
38095 var region = cfg.region;
38101 xitems = cfg.items;
38106 if ( region == 'center') {
38107 Roo.log("Center: " + cfg.title);
38113 case 'Content': // ContentPanel (el, cfg)
38114 case 'Scroll': // ContentPanel (el, cfg)
38116 cfg.autoCreate = cfg.autoCreate || true;
38117 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38119 // var el = this.el.createChild();
38120 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38123 this.add(region, ret);
38127 case 'TreePanel': // our new panel!
38128 cfg.el = this.el.createChild();
38129 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38130 this.add(region, ret);
38135 // create a new Layout (which is a Border Layout...
38137 var clayout = cfg.layout;
38138 clayout.el = this.el.createChild();
38139 clayout.items = clayout.items || [];
38143 // replace this exitems with the clayout ones..
38144 xitems = clayout.items;
38146 // force background off if it's in center...
38147 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38148 cfg.background = false;
38150 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38153 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38154 //console.log('adding nested layout panel ' + cfg.toSource());
38155 this.add(region, ret);
38156 nb = {}; /// find first...
38161 // needs grid and region
38163 //var el = this.getRegion(region).el.createChild();
38165 *var el = this.el.createChild();
38166 // create the grid first...
38167 cfg.grid.container = el;
38168 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38171 if (region == 'center' && this.active ) {
38172 cfg.background = false;
38175 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38177 this.add(region, ret);
38179 if (cfg.background) {
38180 // render grid on panel activation (if panel background)
38181 ret.on('activate', function(gp) {
38182 if (!gp.grid.rendered) {
38183 // gp.grid.render(el);
38187 // cfg.grid.render(el);
38193 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38194 // it was the old xcomponent building that caused this before.
38195 // espeically if border is the top element in the tree.
38205 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38207 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38208 this.add(region, ret);
38212 throw "Can not add '" + cfg.xtype + "' to Border";
38218 this.beginUpdate();
38222 Roo.each(xitems, function(i) {
38223 region = nb && i.region ? i.region : false;
38225 var add = ret.addxtype(i);
38228 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38229 if (!i.background) {
38230 abn[region] = nb[region] ;
38237 // make the last non-background panel active..
38238 //if (nb) { Roo.log(abn); }
38241 for(var r in abn) {
38242 region = this.getRegion(r);
38244 // tried using nb[r], but it does not work..
38246 region.showPanel(abn[r]);
38257 factory : function(cfg)
38260 var validRegions = Roo.bootstrap.layout.Border.regions;
38262 var target = cfg.region;
38265 var r = Roo.bootstrap.layout;
38269 return new r.North(cfg);
38271 return new r.South(cfg);
38273 return new r.East(cfg);
38275 return new r.West(cfg);
38277 return new r.Center(cfg);
38279 throw 'Layout region "'+target+'" not supported.';
38286 * Ext JS Library 1.1.1
38287 * Copyright(c) 2006-2007, Ext JS, LLC.
38289 * Originally Released Under LGPL - original licence link has changed is not relivant.
38292 * <script type="text/javascript">
38296 * @class Roo.bootstrap.layout.Basic
38297 * @extends Roo.util.Observable
38298 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38299 * and does not have a titlebar, tabs or any other features. All it does is size and position
38300 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38301 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38302 * @cfg {string} region the region that it inhabits..
38303 * @cfg {bool} skipConfig skip config?
38307 Roo.bootstrap.layout.Basic = function(config){
38309 this.mgr = config.mgr;
38311 this.position = config.region;
38313 var skipConfig = config.skipConfig;
38317 * @scope Roo.BasicLayoutRegion
38321 * @event beforeremove
38322 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38323 * @param {Roo.LayoutRegion} this
38324 * @param {Roo.ContentPanel} panel The panel
38325 * @param {Object} e The cancel event object
38327 "beforeremove" : true,
38329 * @event invalidated
38330 * Fires when the layout for this region is changed.
38331 * @param {Roo.LayoutRegion} this
38333 "invalidated" : true,
38335 * @event visibilitychange
38336 * Fires when this region is shown or hidden
38337 * @param {Roo.LayoutRegion} this
38338 * @param {Boolean} visibility true or false
38340 "visibilitychange" : true,
38342 * @event paneladded
38343 * Fires when a panel is added.
38344 * @param {Roo.LayoutRegion} this
38345 * @param {Roo.ContentPanel} panel The panel
38347 "paneladded" : true,
38349 * @event panelremoved
38350 * Fires when a panel is removed.
38351 * @param {Roo.LayoutRegion} this
38352 * @param {Roo.ContentPanel} panel The panel
38354 "panelremoved" : true,
38356 * @event beforecollapse
38357 * Fires when this region before collapse.
38358 * @param {Roo.LayoutRegion} this
38360 "beforecollapse" : true,
38363 * Fires when this region is collapsed.
38364 * @param {Roo.LayoutRegion} this
38366 "collapsed" : true,
38369 * Fires when this region is expanded.
38370 * @param {Roo.LayoutRegion} this
38375 * Fires when this region is slid into view.
38376 * @param {Roo.LayoutRegion} this
38378 "slideshow" : true,
38381 * Fires when this region slides out of view.
38382 * @param {Roo.LayoutRegion} this
38384 "slidehide" : true,
38386 * @event panelactivated
38387 * Fires when a panel is activated.
38388 * @param {Roo.LayoutRegion} this
38389 * @param {Roo.ContentPanel} panel The activated panel
38391 "panelactivated" : true,
38394 * Fires when the user resizes this region.
38395 * @param {Roo.LayoutRegion} this
38396 * @param {Number} newSize The new size (width for east/west, height for north/south)
38400 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38401 this.panels = new Roo.util.MixedCollection();
38402 this.panels.getKey = this.getPanelId.createDelegate(this);
38404 this.activePanel = null;
38405 // ensure listeners are added...
38407 if (config.listeners || config.events) {
38408 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38409 listeners : config.listeners || {},
38410 events : config.events || {}
38414 if(skipConfig !== true){
38415 this.applyConfig(config);
38419 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38421 getPanelId : function(p){
38425 applyConfig : function(config){
38426 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38427 this.config = config;
38432 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38433 * the width, for horizontal (north, south) the height.
38434 * @param {Number} newSize The new width or height
38436 resizeTo : function(newSize){
38437 var el = this.el ? this.el :
38438 (this.activePanel ? this.activePanel.getEl() : null);
38440 switch(this.position){
38443 el.setWidth(newSize);
38444 this.fireEvent("resized", this, newSize);
38448 el.setHeight(newSize);
38449 this.fireEvent("resized", this, newSize);
38455 getBox : function(){
38456 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38459 getMargins : function(){
38460 return this.margins;
38463 updateBox : function(box){
38465 var el = this.activePanel.getEl();
38466 el.dom.style.left = box.x + "px";
38467 el.dom.style.top = box.y + "px";
38468 this.activePanel.setSize(box.width, box.height);
38472 * Returns the container element for this region.
38473 * @return {Roo.Element}
38475 getEl : function(){
38476 return this.activePanel;
38480 * Returns true if this region is currently visible.
38481 * @return {Boolean}
38483 isVisible : function(){
38484 return this.activePanel ? true : false;
38487 setActivePanel : function(panel){
38488 panel = this.getPanel(panel);
38489 if(this.activePanel && this.activePanel != panel){
38490 this.activePanel.setActiveState(false);
38491 this.activePanel.getEl().setLeftTop(-10000,-10000);
38493 this.activePanel = panel;
38494 panel.setActiveState(true);
38496 panel.setSize(this.box.width, this.box.height);
38498 this.fireEvent("panelactivated", this, panel);
38499 this.fireEvent("invalidated");
38503 * Show the specified panel.
38504 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38505 * @return {Roo.ContentPanel} The shown panel or null
38507 showPanel : function(panel){
38508 panel = this.getPanel(panel);
38510 this.setActivePanel(panel);
38516 * Get the active panel for this region.
38517 * @return {Roo.ContentPanel} The active panel or null
38519 getActivePanel : function(){
38520 return this.activePanel;
38524 * Add the passed ContentPanel(s)
38525 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38526 * @return {Roo.ContentPanel} The panel added (if only one was added)
38528 add : function(panel){
38529 if(arguments.length > 1){
38530 for(var i = 0, len = arguments.length; i < len; i++) {
38531 this.add(arguments[i]);
38535 if(this.hasPanel(panel)){
38536 this.showPanel(panel);
38539 var el = panel.getEl();
38540 if(el.dom.parentNode != this.mgr.el.dom){
38541 this.mgr.el.dom.appendChild(el.dom);
38543 if(panel.setRegion){
38544 panel.setRegion(this);
38546 this.panels.add(panel);
38547 el.setStyle("position", "absolute");
38548 if(!panel.background){
38549 this.setActivePanel(panel);
38550 if(this.config.initialSize && this.panels.getCount()==1){
38551 this.resizeTo(this.config.initialSize);
38554 this.fireEvent("paneladded", this, panel);
38559 * Returns true if the panel is in this region.
38560 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38561 * @return {Boolean}
38563 hasPanel : function(panel){
38564 if(typeof panel == "object"){ // must be panel obj
38565 panel = panel.getId();
38567 return this.getPanel(panel) ? true : false;
38571 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38572 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38573 * @param {Boolean} preservePanel Overrides the config preservePanel option
38574 * @return {Roo.ContentPanel} The panel that was removed
38576 remove : function(panel, preservePanel){
38577 panel = this.getPanel(panel);
38582 this.fireEvent("beforeremove", this, panel, e);
38583 if(e.cancel === true){
38586 var panelId = panel.getId();
38587 this.panels.removeKey(panelId);
38592 * Returns the panel specified or null if it's not in this region.
38593 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38594 * @return {Roo.ContentPanel}
38596 getPanel : function(id){
38597 if(typeof id == "object"){ // must be panel obj
38600 return this.panels.get(id);
38604 * Returns this regions position (north/south/east/west/center).
38607 getPosition: function(){
38608 return this.position;
38612 * Ext JS Library 1.1.1
38613 * Copyright(c) 2006-2007, Ext JS, LLC.
38615 * Originally Released Under LGPL - original licence link has changed is not relivant.
38618 * <script type="text/javascript">
38622 * @class Roo.bootstrap.layout.Region
38623 * @extends Roo.bootstrap.layout.Basic
38624 * This class represents a region in a layout manager.
38626 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38627 * @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})
38628 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38629 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38630 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38631 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38632 * @cfg {String} title The title for the region (overrides panel titles)
38633 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38634 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38635 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38636 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38637 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38638 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38639 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38640 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38641 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38642 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38644 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38645 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38646 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38647 * @cfg {Number} width For East/West panels
38648 * @cfg {Number} height For North/South panels
38649 * @cfg {Boolean} split To show the splitter
38650 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38652 * @cfg {string} cls Extra CSS classes to add to region
38654 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38655 * @cfg {string} region the region that it inhabits..
38658 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38659 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38661 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38662 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38663 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38665 Roo.bootstrap.layout.Region = function(config)
38667 this.applyConfig(config);
38669 var mgr = config.mgr;
38670 var pos = config.region;
38671 config.skipConfig = true;
38672 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38675 this.onRender(mgr.el);
38678 this.visible = true;
38679 this.collapsed = false;
38680 this.unrendered_panels = [];
38683 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38685 position: '', // set by wrapper (eg. north/south etc..)
38686 unrendered_panels : null, // unrendered panels.
38688 tabPosition : false,
38690 mgr: false, // points to 'Border'
38693 createBody : function(){
38694 /** This region's body element
38695 * @type Roo.Element */
38696 this.bodyEl = this.el.createChild({
38698 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38702 onRender: function(ctr, pos)
38704 var dh = Roo.DomHelper;
38705 /** This region's container element
38706 * @type Roo.Element */
38707 this.el = dh.append(ctr.dom, {
38709 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38711 /** This region's title element
38712 * @type Roo.Element */
38714 this.titleEl = dh.append(this.el.dom, {
38716 unselectable: "on",
38717 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38719 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38720 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38724 this.titleEl.enableDisplayMode();
38725 /** This region's title text element
38726 * @type HTMLElement */
38727 this.titleTextEl = this.titleEl.dom.firstChild;
38728 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38730 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38731 this.closeBtn.enableDisplayMode();
38732 this.closeBtn.on("click", this.closeClicked, this);
38733 this.closeBtn.hide();
38735 this.createBody(this.config);
38736 if(this.config.hideWhenEmpty){
38738 this.on("paneladded", this.validateVisibility, this);
38739 this.on("panelremoved", this.validateVisibility, this);
38741 if(this.autoScroll){
38742 this.bodyEl.setStyle("overflow", "auto");
38744 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38746 //if(c.titlebar !== false){
38747 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38748 this.titleEl.hide();
38750 this.titleEl.show();
38751 if(this.config.title){
38752 this.titleTextEl.innerHTML = this.config.title;
38756 if(this.config.collapsed){
38757 this.collapse(true);
38759 if(this.config.hidden){
38763 if (this.unrendered_panels && this.unrendered_panels.length) {
38764 for (var i =0;i< this.unrendered_panels.length; i++) {
38765 this.add(this.unrendered_panels[i]);
38767 this.unrendered_panels = null;
38773 applyConfig : function(c)
38776 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38777 var dh = Roo.DomHelper;
38778 if(c.titlebar !== false){
38779 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38780 this.collapseBtn.on("click", this.collapse, this);
38781 this.collapseBtn.enableDisplayMode();
38783 if(c.showPin === true || this.showPin){
38784 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38785 this.stickBtn.enableDisplayMode();
38786 this.stickBtn.on("click", this.expand, this);
38787 this.stickBtn.hide();
38792 /** This region's collapsed element
38793 * @type Roo.Element */
38796 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38797 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38800 if(c.floatable !== false){
38801 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38802 this.collapsedEl.on("click", this.collapseClick, this);
38805 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38806 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38807 id: "message", unselectable: "on", style:{"float":"left"}});
38808 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38810 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38811 this.expandBtn.on("click", this.expand, this);
38815 if(this.collapseBtn){
38816 this.collapseBtn.setVisible(c.collapsible == true);
38819 this.cmargins = c.cmargins || this.cmargins ||
38820 (this.position == "west" || this.position == "east" ?
38821 {top: 0, left: 2, right:2, bottom: 0} :
38822 {top: 2, left: 0, right:0, bottom: 2});
38824 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38827 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38829 this.autoScroll = c.autoScroll || false;
38834 this.duration = c.duration || .30;
38835 this.slideDuration = c.slideDuration || .45;
38840 * Returns true if this region is currently visible.
38841 * @return {Boolean}
38843 isVisible : function(){
38844 return this.visible;
38848 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38849 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38851 //setCollapsedTitle : function(title){
38852 // title = title || " ";
38853 // if(this.collapsedTitleTextEl){
38854 // this.collapsedTitleTextEl.innerHTML = title;
38858 getBox : function(){
38860 // if(!this.collapsed){
38861 b = this.el.getBox(false, true);
38863 // b = this.collapsedEl.getBox(false, true);
38868 getMargins : function(){
38869 return this.margins;
38870 //return this.collapsed ? this.cmargins : this.margins;
38873 highlight : function(){
38874 this.el.addClass("x-layout-panel-dragover");
38877 unhighlight : function(){
38878 this.el.removeClass("x-layout-panel-dragover");
38881 updateBox : function(box)
38883 if (!this.bodyEl) {
38884 return; // not rendered yet..
38888 if(!this.collapsed){
38889 this.el.dom.style.left = box.x + "px";
38890 this.el.dom.style.top = box.y + "px";
38891 this.updateBody(box.width, box.height);
38893 this.collapsedEl.dom.style.left = box.x + "px";
38894 this.collapsedEl.dom.style.top = box.y + "px";
38895 this.collapsedEl.setSize(box.width, box.height);
38898 this.tabs.autoSizeTabs();
38902 updateBody : function(w, h)
38905 this.el.setWidth(w);
38906 w -= this.el.getBorderWidth("rl");
38907 if(this.config.adjustments){
38908 w += this.config.adjustments[0];
38911 if(h !== null && h > 0){
38912 this.el.setHeight(h);
38913 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38914 h -= this.el.getBorderWidth("tb");
38915 if(this.config.adjustments){
38916 h += this.config.adjustments[1];
38918 this.bodyEl.setHeight(h);
38920 h = this.tabs.syncHeight(h);
38923 if(this.panelSize){
38924 w = w !== null ? w : this.panelSize.width;
38925 h = h !== null ? h : this.panelSize.height;
38927 if(this.activePanel){
38928 var el = this.activePanel.getEl();
38929 w = w !== null ? w : el.getWidth();
38930 h = h !== null ? h : el.getHeight();
38931 this.panelSize = {width: w, height: h};
38932 this.activePanel.setSize(w, h);
38934 if(Roo.isIE && this.tabs){
38935 this.tabs.el.repaint();
38940 * Returns the container element for this region.
38941 * @return {Roo.Element}
38943 getEl : function(){
38948 * Hides this region.
38951 //if(!this.collapsed){
38952 this.el.dom.style.left = "-2000px";
38955 // this.collapsedEl.dom.style.left = "-2000px";
38956 // this.collapsedEl.hide();
38958 this.visible = false;
38959 this.fireEvent("visibilitychange", this, false);
38963 * Shows this region if it was previously hidden.
38966 //if(!this.collapsed){
38969 // this.collapsedEl.show();
38971 this.visible = true;
38972 this.fireEvent("visibilitychange", this, true);
38975 closeClicked : function(){
38976 if(this.activePanel){
38977 this.remove(this.activePanel);
38981 collapseClick : function(e){
38983 e.stopPropagation();
38986 e.stopPropagation();
38992 * Collapses this region.
38993 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38996 collapse : function(skipAnim, skipCheck = false){
38997 if(this.collapsed) {
39001 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39003 this.collapsed = true;
39005 this.split.el.hide();
39007 if(this.config.animate && skipAnim !== true){
39008 this.fireEvent("invalidated", this);
39009 this.animateCollapse();
39011 this.el.setLocation(-20000,-20000);
39013 this.collapsedEl.show();
39014 this.fireEvent("collapsed", this);
39015 this.fireEvent("invalidated", this);
39021 animateCollapse : function(){
39026 * Expands this region if it was previously collapsed.
39027 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39028 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39031 expand : function(e, skipAnim){
39033 e.stopPropagation();
39035 if(!this.collapsed || this.el.hasActiveFx()) {
39039 this.afterSlideIn();
39042 this.collapsed = false;
39043 if(this.config.animate && skipAnim !== true){
39044 this.animateExpand();
39048 this.split.el.show();
39050 this.collapsedEl.setLocation(-2000,-2000);
39051 this.collapsedEl.hide();
39052 this.fireEvent("invalidated", this);
39053 this.fireEvent("expanded", this);
39057 animateExpand : function(){
39061 initTabs : function()
39063 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39065 var ts = new Roo.bootstrap.panel.Tabs({
39066 el: this.bodyEl.dom,
39068 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39069 disableTooltips: this.config.disableTabTips,
39070 toolbar : this.config.toolbar
39073 if(this.config.hideTabs){
39074 ts.stripWrap.setDisplayed(false);
39077 ts.resizeTabs = this.config.resizeTabs === true;
39078 ts.minTabWidth = this.config.minTabWidth || 40;
39079 ts.maxTabWidth = this.config.maxTabWidth || 250;
39080 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39081 ts.monitorResize = false;
39082 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39083 ts.bodyEl.addClass('roo-layout-tabs-body');
39084 this.panels.each(this.initPanelAsTab, this);
39087 initPanelAsTab : function(panel){
39088 var ti = this.tabs.addTab(
39092 this.config.closeOnTab && panel.isClosable(),
39095 if(panel.tabTip !== undefined){
39096 ti.setTooltip(panel.tabTip);
39098 ti.on("activate", function(){
39099 this.setActivePanel(panel);
39102 if(this.config.closeOnTab){
39103 ti.on("beforeclose", function(t, e){
39105 this.remove(panel);
39109 panel.tabItem = ti;
39114 updatePanelTitle : function(panel, title)
39116 if(this.activePanel == panel){
39117 this.updateTitle(title);
39120 var ti = this.tabs.getTab(panel.getEl().id);
39122 if(panel.tabTip !== undefined){
39123 ti.setTooltip(panel.tabTip);
39128 updateTitle : function(title){
39129 if(this.titleTextEl && !this.config.title){
39130 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39134 setActivePanel : function(panel)
39136 panel = this.getPanel(panel);
39137 if(this.activePanel && this.activePanel != panel){
39138 if(this.activePanel.setActiveState(false) === false){
39142 this.activePanel = panel;
39143 panel.setActiveState(true);
39144 if(this.panelSize){
39145 panel.setSize(this.panelSize.width, this.panelSize.height);
39148 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39150 this.updateTitle(panel.getTitle());
39152 this.fireEvent("invalidated", this);
39154 this.fireEvent("panelactivated", this, panel);
39158 * Shows the specified panel.
39159 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39160 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39162 showPanel : function(panel)
39164 panel = this.getPanel(panel);
39167 var tab = this.tabs.getTab(panel.getEl().id);
39168 if(tab.isHidden()){
39169 this.tabs.unhideTab(tab.id);
39173 this.setActivePanel(panel);
39180 * Get the active panel for this region.
39181 * @return {Roo.ContentPanel} The active panel or null
39183 getActivePanel : function(){
39184 return this.activePanel;
39187 validateVisibility : function(){
39188 if(this.panels.getCount() < 1){
39189 this.updateTitle(" ");
39190 this.closeBtn.hide();
39193 if(!this.isVisible()){
39200 * Adds the passed ContentPanel(s) to this region.
39201 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39202 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39204 add : function(panel)
39206 if(arguments.length > 1){
39207 for(var i = 0, len = arguments.length; i < len; i++) {
39208 this.add(arguments[i]);
39213 // if we have not been rendered yet, then we can not really do much of this..
39214 if (!this.bodyEl) {
39215 this.unrendered_panels.push(panel);
39222 if(this.hasPanel(panel)){
39223 this.showPanel(panel);
39226 panel.setRegion(this);
39227 this.panels.add(panel);
39228 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39229 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39230 // and hide them... ???
39231 this.bodyEl.dom.appendChild(panel.getEl().dom);
39232 if(panel.background !== true){
39233 this.setActivePanel(panel);
39235 this.fireEvent("paneladded", this, panel);
39242 this.initPanelAsTab(panel);
39246 if(panel.background !== true){
39247 this.tabs.activate(panel.getEl().id);
39249 this.fireEvent("paneladded", this, panel);
39254 * Hides the tab for the specified panel.
39255 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39257 hidePanel : function(panel){
39258 if(this.tabs && (panel = this.getPanel(panel))){
39259 this.tabs.hideTab(panel.getEl().id);
39264 * Unhides the tab for a previously hidden panel.
39265 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39267 unhidePanel : function(panel){
39268 if(this.tabs && (panel = this.getPanel(panel))){
39269 this.tabs.unhideTab(panel.getEl().id);
39273 clearPanels : function(){
39274 while(this.panels.getCount() > 0){
39275 this.remove(this.panels.first());
39280 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39281 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39282 * @param {Boolean} preservePanel Overrides the config preservePanel option
39283 * @return {Roo.ContentPanel} The panel that was removed
39285 remove : function(panel, preservePanel)
39287 panel = this.getPanel(panel);
39292 this.fireEvent("beforeremove", this, panel, e);
39293 if(e.cancel === true){
39296 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39297 var panelId = panel.getId();
39298 this.panels.removeKey(panelId);
39300 document.body.appendChild(panel.getEl().dom);
39303 this.tabs.removeTab(panel.getEl().id);
39304 }else if (!preservePanel){
39305 this.bodyEl.dom.removeChild(panel.getEl().dom);
39307 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39308 var p = this.panels.first();
39309 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39310 tempEl.appendChild(p.getEl().dom);
39311 this.bodyEl.update("");
39312 this.bodyEl.dom.appendChild(p.getEl().dom);
39314 this.updateTitle(p.getTitle());
39316 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39317 this.setActivePanel(p);
39319 panel.setRegion(null);
39320 if(this.activePanel == panel){
39321 this.activePanel = null;
39323 if(this.config.autoDestroy !== false && preservePanel !== true){
39324 try{panel.destroy();}catch(e){}
39326 this.fireEvent("panelremoved", this, panel);
39331 * Returns the TabPanel component used by this region
39332 * @return {Roo.TabPanel}
39334 getTabs : function(){
39338 createTool : function(parentEl, className){
39339 var btn = Roo.DomHelper.append(parentEl, {
39341 cls: "x-layout-tools-button",
39344 cls: "roo-layout-tools-button-inner " + className,
39348 btn.addClassOnOver("roo-layout-tools-button-over");
39353 * Ext JS Library 1.1.1
39354 * Copyright(c) 2006-2007, Ext JS, LLC.
39356 * Originally Released Under LGPL - original licence link has changed is not relivant.
39359 * <script type="text/javascript">
39365 * @class Roo.SplitLayoutRegion
39366 * @extends Roo.LayoutRegion
39367 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39369 Roo.bootstrap.layout.Split = function(config){
39370 this.cursor = config.cursor;
39371 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39374 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39376 splitTip : "Drag to resize.",
39377 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39378 useSplitTips : false,
39380 applyConfig : function(config){
39381 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39384 onRender : function(ctr,pos) {
39386 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39387 if(!this.config.split){
39392 var splitEl = Roo.DomHelper.append(ctr.dom, {
39394 id: this.el.id + "-split",
39395 cls: "roo-layout-split roo-layout-split-"+this.position,
39398 /** The SplitBar for this region
39399 * @type Roo.SplitBar */
39400 // does not exist yet...
39401 Roo.log([this.position, this.orientation]);
39403 this.split = new Roo.bootstrap.SplitBar({
39404 dragElement : splitEl,
39405 resizingElement: this.el,
39406 orientation : this.orientation
39409 this.split.on("moved", this.onSplitMove, this);
39410 this.split.useShim = this.config.useShim === true;
39411 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39412 if(this.useSplitTips){
39413 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39415 //if(config.collapsible){
39416 // this.split.el.on("dblclick", this.collapse, this);
39419 if(typeof this.config.minSize != "undefined"){
39420 this.split.minSize = this.config.minSize;
39422 if(typeof this.config.maxSize != "undefined"){
39423 this.split.maxSize = this.config.maxSize;
39425 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39426 this.hideSplitter();
39431 getHMaxSize : function(){
39432 var cmax = this.config.maxSize || 10000;
39433 var center = this.mgr.getRegion("center");
39434 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39437 getVMaxSize : function(){
39438 var cmax = this.config.maxSize || 10000;
39439 var center = this.mgr.getRegion("center");
39440 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39443 onSplitMove : function(split, newSize){
39444 this.fireEvent("resized", this, newSize);
39448 * Returns the {@link Roo.SplitBar} for this region.
39449 * @return {Roo.SplitBar}
39451 getSplitBar : function(){
39456 this.hideSplitter();
39457 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39460 hideSplitter : function(){
39462 this.split.el.setLocation(-2000,-2000);
39463 this.split.el.hide();
39469 this.split.el.show();
39471 Roo.bootstrap.layout.Split.superclass.show.call(this);
39474 beforeSlide: function(){
39475 if(Roo.isGecko){// firefox overflow auto bug workaround
39476 this.bodyEl.clip();
39478 this.tabs.bodyEl.clip();
39480 if(this.activePanel){
39481 this.activePanel.getEl().clip();
39483 if(this.activePanel.beforeSlide){
39484 this.activePanel.beforeSlide();
39490 afterSlide : function(){
39491 if(Roo.isGecko){// firefox overflow auto bug workaround
39492 this.bodyEl.unclip();
39494 this.tabs.bodyEl.unclip();
39496 if(this.activePanel){
39497 this.activePanel.getEl().unclip();
39498 if(this.activePanel.afterSlide){
39499 this.activePanel.afterSlide();
39505 initAutoHide : function(){
39506 if(this.autoHide !== false){
39507 if(!this.autoHideHd){
39508 var st = new Roo.util.DelayedTask(this.slideIn, this);
39509 this.autoHideHd = {
39510 "mouseout": function(e){
39511 if(!e.within(this.el, true)){
39515 "mouseover" : function(e){
39521 this.el.on(this.autoHideHd);
39525 clearAutoHide : function(){
39526 if(this.autoHide !== false){
39527 this.el.un("mouseout", this.autoHideHd.mouseout);
39528 this.el.un("mouseover", this.autoHideHd.mouseover);
39532 clearMonitor : function(){
39533 Roo.get(document).un("click", this.slideInIf, this);
39536 // these names are backwards but not changed for compat
39537 slideOut : function(){
39538 if(this.isSlid || this.el.hasActiveFx()){
39541 this.isSlid = true;
39542 if(this.collapseBtn){
39543 this.collapseBtn.hide();
39545 this.closeBtnState = this.closeBtn.getStyle('display');
39546 this.closeBtn.hide();
39548 this.stickBtn.show();
39551 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39552 this.beforeSlide();
39553 this.el.setStyle("z-index", 10001);
39554 this.el.slideIn(this.getSlideAnchor(), {
39555 callback: function(){
39557 this.initAutoHide();
39558 Roo.get(document).on("click", this.slideInIf, this);
39559 this.fireEvent("slideshow", this);
39566 afterSlideIn : function(){
39567 this.clearAutoHide();
39568 this.isSlid = false;
39569 this.clearMonitor();
39570 this.el.setStyle("z-index", "");
39571 if(this.collapseBtn){
39572 this.collapseBtn.show();
39574 this.closeBtn.setStyle('display', this.closeBtnState);
39576 this.stickBtn.hide();
39578 this.fireEvent("slidehide", this);
39581 slideIn : function(cb){
39582 if(!this.isSlid || this.el.hasActiveFx()){
39586 this.isSlid = false;
39587 this.beforeSlide();
39588 this.el.slideOut(this.getSlideAnchor(), {
39589 callback: function(){
39590 this.el.setLeftTop(-10000, -10000);
39592 this.afterSlideIn();
39600 slideInIf : function(e){
39601 if(!e.within(this.el)){
39606 animateCollapse : function(){
39607 this.beforeSlide();
39608 this.el.setStyle("z-index", 20000);
39609 var anchor = this.getSlideAnchor();
39610 this.el.slideOut(anchor, {
39611 callback : function(){
39612 this.el.setStyle("z-index", "");
39613 this.collapsedEl.slideIn(anchor, {duration:.3});
39615 this.el.setLocation(-10000,-10000);
39617 this.fireEvent("collapsed", this);
39624 animateExpand : function(){
39625 this.beforeSlide();
39626 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39627 this.el.setStyle("z-index", 20000);
39628 this.collapsedEl.hide({
39631 this.el.slideIn(this.getSlideAnchor(), {
39632 callback : function(){
39633 this.el.setStyle("z-index", "");
39636 this.split.el.show();
39638 this.fireEvent("invalidated", this);
39639 this.fireEvent("expanded", this);
39667 getAnchor : function(){
39668 return this.anchors[this.position];
39671 getCollapseAnchor : function(){
39672 return this.canchors[this.position];
39675 getSlideAnchor : function(){
39676 return this.sanchors[this.position];
39679 getAlignAdj : function(){
39680 var cm = this.cmargins;
39681 switch(this.position){
39697 getExpandAdj : function(){
39698 var c = this.collapsedEl, cm = this.cmargins;
39699 switch(this.position){
39701 return [-(cm.right+c.getWidth()+cm.left), 0];
39704 return [cm.right+c.getWidth()+cm.left, 0];
39707 return [0, -(cm.top+cm.bottom+c.getHeight())];
39710 return [0, cm.top+cm.bottom+c.getHeight()];
39716 * Ext JS Library 1.1.1
39717 * Copyright(c) 2006-2007, Ext JS, LLC.
39719 * Originally Released Under LGPL - original licence link has changed is not relivant.
39722 * <script type="text/javascript">
39725 * These classes are private internal classes
39727 Roo.bootstrap.layout.Center = function(config){
39728 config.region = "center";
39729 Roo.bootstrap.layout.Region.call(this, config);
39730 this.visible = true;
39731 this.minWidth = config.minWidth || 20;
39732 this.minHeight = config.minHeight || 20;
39735 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39737 // center panel can't be hidden
39741 // center panel can't be hidden
39744 getMinWidth: function(){
39745 return this.minWidth;
39748 getMinHeight: function(){
39749 return this.minHeight;
39763 Roo.bootstrap.layout.North = function(config)
39765 config.region = 'north';
39766 config.cursor = 'n-resize';
39768 Roo.bootstrap.layout.Split.call(this, config);
39772 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39773 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39774 this.split.el.addClass("roo-layout-split-v");
39776 //var size = config.initialSize || config.height;
39777 //if(this.el && typeof size != "undefined"){
39778 // this.el.setHeight(size);
39781 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39783 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39786 onRender : function(ctr, pos)
39788 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39789 var size = this.config.initialSize || this.config.height;
39790 if(this.el && typeof size != "undefined"){
39791 this.el.setHeight(size);
39796 getBox : function(){
39797 if(this.collapsed){
39798 return this.collapsedEl.getBox();
39800 var box = this.el.getBox();
39802 box.height += this.split.el.getHeight();
39807 updateBox : function(box){
39808 if(this.split && !this.collapsed){
39809 box.height -= this.split.el.getHeight();
39810 this.split.el.setLeft(box.x);
39811 this.split.el.setTop(box.y+box.height);
39812 this.split.el.setWidth(box.width);
39814 if(this.collapsed){
39815 this.updateBody(box.width, null);
39817 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39825 Roo.bootstrap.layout.South = function(config){
39826 config.region = 'south';
39827 config.cursor = 's-resize';
39828 Roo.bootstrap.layout.Split.call(this, config);
39830 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39831 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39832 this.split.el.addClass("roo-layout-split-v");
39837 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39838 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39840 onRender : function(ctr, pos)
39842 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39843 var size = this.config.initialSize || this.config.height;
39844 if(this.el && typeof size != "undefined"){
39845 this.el.setHeight(size);
39850 getBox : function(){
39851 if(this.collapsed){
39852 return this.collapsedEl.getBox();
39854 var box = this.el.getBox();
39856 var sh = this.split.el.getHeight();
39863 updateBox : function(box){
39864 if(this.split && !this.collapsed){
39865 var sh = this.split.el.getHeight();
39868 this.split.el.setLeft(box.x);
39869 this.split.el.setTop(box.y-sh);
39870 this.split.el.setWidth(box.width);
39872 if(this.collapsed){
39873 this.updateBody(box.width, null);
39875 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39879 Roo.bootstrap.layout.East = function(config){
39880 config.region = "east";
39881 config.cursor = "e-resize";
39882 Roo.bootstrap.layout.Split.call(this, config);
39884 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39885 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39886 this.split.el.addClass("roo-layout-split-h");
39890 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39891 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39893 onRender : function(ctr, pos)
39895 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39896 var size = this.config.initialSize || this.config.width;
39897 if(this.el && typeof size != "undefined"){
39898 this.el.setWidth(size);
39903 getBox : function(){
39904 if(this.collapsed){
39905 return this.collapsedEl.getBox();
39907 var box = this.el.getBox();
39909 var sw = this.split.el.getWidth();
39916 updateBox : function(box){
39917 if(this.split && !this.collapsed){
39918 var sw = this.split.el.getWidth();
39920 this.split.el.setLeft(box.x);
39921 this.split.el.setTop(box.y);
39922 this.split.el.setHeight(box.height);
39925 if(this.collapsed){
39926 this.updateBody(null, box.height);
39928 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39932 Roo.bootstrap.layout.West = function(config){
39933 config.region = "west";
39934 config.cursor = "w-resize";
39936 Roo.bootstrap.layout.Split.call(this, config);
39938 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39939 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39940 this.split.el.addClass("roo-layout-split-h");
39944 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39945 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39947 onRender: function(ctr, pos)
39949 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39950 var size = this.config.initialSize || this.config.width;
39951 if(typeof size != "undefined"){
39952 this.el.setWidth(size);
39956 getBox : function(){
39957 if(this.collapsed){
39958 return this.collapsedEl.getBox();
39960 var box = this.el.getBox();
39961 if (box.width == 0) {
39962 box.width = this.config.width; // kludge?
39965 box.width += this.split.el.getWidth();
39970 updateBox : function(box){
39971 if(this.split && !this.collapsed){
39972 var sw = this.split.el.getWidth();
39974 this.split.el.setLeft(box.x+box.width);
39975 this.split.el.setTop(box.y);
39976 this.split.el.setHeight(box.height);
39978 if(this.collapsed){
39979 this.updateBody(null, box.height);
39981 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39983 });Roo.namespace("Roo.bootstrap.panel");/*
39985 * Ext JS Library 1.1.1
39986 * Copyright(c) 2006-2007, Ext JS, LLC.
39988 * Originally Released Under LGPL - original licence link has changed is not relivant.
39991 * <script type="text/javascript">
39994 * @class Roo.ContentPanel
39995 * @extends Roo.util.Observable
39996 * A basic ContentPanel element.
39997 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39998 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39999 * @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
40000 * @cfg {Boolean} closable True if the panel can be closed/removed
40001 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40002 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40003 * @cfg {Toolbar} toolbar A toolbar for this panel
40004 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40005 * @cfg {String} title The title for this panel
40006 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40007 * @cfg {String} url Calls {@link #setUrl} with this value
40008 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40009 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40010 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40011 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40012 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40013 * @cfg {Boolean} badges render the badges
40014 * @cfg {String} cls extra classes to use
40015 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40018 * Create a new ContentPanel.
40019 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40020 * @param {String/Object} config A string to set only the title or a config object
40021 * @param {String} content (optional) Set the HTML content for this panel
40022 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40024 Roo.bootstrap.panel.Content = function( config){
40026 this.tpl = config.tpl || false;
40028 var el = config.el;
40029 var content = config.content;
40031 if(config.autoCreate){ // xtype is available if this is called from factory
40034 this.el = Roo.get(el);
40035 if(!this.el && config && config.autoCreate){
40036 if(typeof config.autoCreate == "object"){
40037 if(!config.autoCreate.id){
40038 config.autoCreate.id = config.id||el;
40040 this.el = Roo.DomHelper.append(document.body,
40041 config.autoCreate, true);
40045 cls: (config.cls || '') +
40046 (config.background ? ' bg-' + config.background : '') +
40047 " roo-layout-inactive-content",
40050 if (config.iframe) {
40054 style : 'border: 0px',
40055 src : 'about:blank'
40061 elcfg.html = config.html;
40065 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40066 if (config.iframe) {
40067 this.iframeEl = this.el.select('iframe',true).first();
40072 this.closable = false;
40073 this.loaded = false;
40074 this.active = false;
40077 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40079 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40081 this.wrapEl = this.el; //this.el.wrap();
40083 if (config.toolbar.items) {
40084 ti = config.toolbar.items ;
40085 delete config.toolbar.items ;
40089 this.toolbar.render(this.wrapEl, 'before');
40090 for(var i =0;i < ti.length;i++) {
40091 // Roo.log(['add child', items[i]]);
40092 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40094 this.toolbar.items = nitems;
40095 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40096 delete config.toolbar;
40100 // xtype created footer. - not sure if will work as we normally have to render first..
40101 if (this.footer && !this.footer.el && this.footer.xtype) {
40102 if (!this.wrapEl) {
40103 this.wrapEl = this.el.wrap();
40106 this.footer.container = this.wrapEl.createChild();
40108 this.footer = Roo.factory(this.footer, Roo);
40113 if(typeof config == "string"){
40114 this.title = config;
40116 Roo.apply(this, config);
40120 this.resizeEl = Roo.get(this.resizeEl, true);
40122 this.resizeEl = this.el;
40124 // handle view.xtype
40132 * Fires when this panel is activated.
40133 * @param {Roo.ContentPanel} this
40137 * @event deactivate
40138 * Fires when this panel is activated.
40139 * @param {Roo.ContentPanel} this
40141 "deactivate" : true,
40145 * Fires when this panel is resized if fitToFrame is true.
40146 * @param {Roo.ContentPanel} this
40147 * @param {Number} width The width after any component adjustments
40148 * @param {Number} height The height after any component adjustments
40154 * Fires when this tab is created
40155 * @param {Roo.ContentPanel} this
40166 if(this.autoScroll && !this.iframe){
40167 this.resizeEl.setStyle("overflow", "auto");
40169 // fix randome scrolling
40170 //this.el.on('scroll', function() {
40171 // Roo.log('fix random scolling');
40172 // this.scrollTo('top',0);
40175 content = content || this.content;
40177 this.setContent(content);
40179 if(config && config.url){
40180 this.setUrl(this.url, this.params, this.loadOnce);
40185 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40187 if (this.view && typeof(this.view.xtype) != 'undefined') {
40188 this.view.el = this.el.appendChild(document.createElement("div"));
40189 this.view = Roo.factory(this.view);
40190 this.view.render && this.view.render(false, '');
40194 this.fireEvent('render', this);
40197 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40207 setRegion : function(region){
40208 this.region = region;
40209 this.setActiveClass(region && !this.background);
40213 setActiveClass: function(state)
40216 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40217 this.el.setStyle('position','relative');
40219 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40220 this.el.setStyle('position', 'absolute');
40225 * Returns the toolbar for this Panel if one was configured.
40226 * @return {Roo.Toolbar}
40228 getToolbar : function(){
40229 return this.toolbar;
40232 setActiveState : function(active)
40234 this.active = active;
40235 this.setActiveClass(active);
40237 if(this.fireEvent("deactivate", this) === false){
40242 this.fireEvent("activate", this);
40246 * Updates this panel's element (not for iframe)
40247 * @param {String} content The new content
40248 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40250 setContent : function(content, loadScripts){
40255 this.el.update(content, loadScripts);
40258 ignoreResize : function(w, h){
40259 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40262 this.lastSize = {width: w, height: h};
40267 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40268 * @return {Roo.UpdateManager} The UpdateManager
40270 getUpdateManager : function(){
40274 return this.el.getUpdateManager();
40277 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40278 * Does not work with IFRAME contents
40279 * @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:
40282 url: "your-url.php",
40283 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40284 callback: yourFunction,
40285 scope: yourObject, //(optional scope)
40288 text: "Loading...",
40294 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40295 * 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.
40296 * @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}
40297 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40298 * @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.
40299 * @return {Roo.ContentPanel} this
40307 var um = this.el.getUpdateManager();
40308 um.update.apply(um, arguments);
40314 * 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.
40315 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40316 * @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)
40317 * @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)
40318 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40320 setUrl : function(url, params, loadOnce){
40322 this.iframeEl.dom.src = url;
40326 if(this.refreshDelegate){
40327 this.removeListener("activate", this.refreshDelegate);
40329 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40330 this.on("activate", this.refreshDelegate);
40331 return this.el.getUpdateManager();
40334 _handleRefresh : function(url, params, loadOnce){
40335 if(!loadOnce || !this.loaded){
40336 var updater = this.el.getUpdateManager();
40337 updater.update(url, params, this._setLoaded.createDelegate(this));
40341 _setLoaded : function(){
40342 this.loaded = true;
40346 * Returns this panel's id
40349 getId : function(){
40354 * Returns this panel's element - used by regiosn to add.
40355 * @return {Roo.Element}
40357 getEl : function(){
40358 return this.wrapEl || this.el;
40363 adjustForComponents : function(width, height)
40365 //Roo.log('adjustForComponents ');
40366 if(this.resizeEl != this.el){
40367 width -= this.el.getFrameWidth('lr');
40368 height -= this.el.getFrameWidth('tb');
40371 var te = this.toolbar.getEl();
40372 te.setWidth(width);
40373 height -= te.getHeight();
40376 var te = this.footer.getEl();
40377 te.setWidth(width);
40378 height -= te.getHeight();
40382 if(this.adjustments){
40383 width += this.adjustments[0];
40384 height += this.adjustments[1];
40386 return {"width": width, "height": height};
40389 setSize : function(width, height){
40390 if(this.fitToFrame && !this.ignoreResize(width, height)){
40391 if(this.fitContainer && this.resizeEl != this.el){
40392 this.el.setSize(width, height);
40394 var size = this.adjustForComponents(width, height);
40396 this.iframeEl.setSize(width,height);
40399 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40400 this.fireEvent('resize', this, size.width, size.height);
40407 * Returns this panel's title
40410 getTitle : function(){
40412 if (typeof(this.title) != 'object') {
40417 for (var k in this.title) {
40418 if (!this.title.hasOwnProperty(k)) {
40422 if (k.indexOf('-') >= 0) {
40423 var s = k.split('-');
40424 for (var i = 0; i<s.length; i++) {
40425 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40428 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40435 * Set this panel's title
40436 * @param {String} title
40438 setTitle : function(title){
40439 this.title = title;
40441 this.region.updatePanelTitle(this, title);
40446 * Returns true is this panel was configured to be closable
40447 * @return {Boolean}
40449 isClosable : function(){
40450 return this.closable;
40453 beforeSlide : function(){
40455 this.resizeEl.clip();
40458 afterSlide : function(){
40460 this.resizeEl.unclip();
40464 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40465 * Will fail silently if the {@link #setUrl} method has not been called.
40466 * This does not activate the panel, just updates its content.
40468 refresh : function(){
40469 if(this.refreshDelegate){
40470 this.loaded = false;
40471 this.refreshDelegate();
40476 * Destroys this panel
40478 destroy : function(){
40479 this.el.removeAllListeners();
40480 var tempEl = document.createElement("span");
40481 tempEl.appendChild(this.el.dom);
40482 tempEl.innerHTML = "";
40488 * form - if the content panel contains a form - this is a reference to it.
40489 * @type {Roo.form.Form}
40493 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40494 * This contains a reference to it.
40500 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40510 * @param {Object} cfg Xtype definition of item to add.
40514 getChildContainer: function () {
40515 return this.getEl();
40520 var ret = new Roo.factory(cfg);
40525 if (cfg.xtype.match(/^Form$/)) {
40528 //if (this.footer) {
40529 // el = this.footer.container.insertSibling(false, 'before');
40531 el = this.el.createChild();
40534 this.form = new Roo.form.Form(cfg);
40537 if ( this.form.allItems.length) {
40538 this.form.render(el.dom);
40542 // should only have one of theses..
40543 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40544 // views.. should not be just added - used named prop 'view''
40546 cfg.el = this.el.appendChild(document.createElement("div"));
40549 var ret = new Roo.factory(cfg);
40551 ret.render && ret.render(false, ''); // render blank..
40561 * @class Roo.bootstrap.panel.Grid
40562 * @extends Roo.bootstrap.panel.Content
40564 * Create a new GridPanel.
40565 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40566 * @param {Object} config A the config object
40572 Roo.bootstrap.panel.Grid = function(config)
40576 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40577 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40579 config.el = this.wrapper;
40580 //this.el = this.wrapper;
40582 if (config.container) {
40583 // ctor'ed from a Border/panel.grid
40586 this.wrapper.setStyle("overflow", "hidden");
40587 this.wrapper.addClass('roo-grid-container');
40592 if(config.toolbar){
40593 var tool_el = this.wrapper.createChild();
40594 this.toolbar = Roo.factory(config.toolbar);
40596 if (config.toolbar.items) {
40597 ti = config.toolbar.items ;
40598 delete config.toolbar.items ;
40602 this.toolbar.render(tool_el);
40603 for(var i =0;i < ti.length;i++) {
40604 // Roo.log(['add child', items[i]]);
40605 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40607 this.toolbar.items = nitems;
40609 delete config.toolbar;
40612 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40613 config.grid.scrollBody = true;;
40614 config.grid.monitorWindowResize = false; // turn off autosizing
40615 config.grid.autoHeight = false;
40616 config.grid.autoWidth = false;
40618 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40620 if (config.background) {
40621 // render grid on panel activation (if panel background)
40622 this.on('activate', function(gp) {
40623 if (!gp.grid.rendered) {
40624 gp.grid.render(this.wrapper);
40625 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40630 this.grid.render(this.wrapper);
40631 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40634 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40635 // ??? needed ??? config.el = this.wrapper;
40640 // xtype created footer. - not sure if will work as we normally have to render first..
40641 if (this.footer && !this.footer.el && this.footer.xtype) {
40643 var ctr = this.grid.getView().getFooterPanel(true);
40644 this.footer.dataSource = this.grid.dataSource;
40645 this.footer = Roo.factory(this.footer, Roo);
40646 this.footer.render(ctr);
40656 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40657 getId : function(){
40658 return this.grid.id;
40662 * Returns the grid for this panel
40663 * @return {Roo.bootstrap.Table}
40665 getGrid : function(){
40669 setSize : function(width, height){
40670 if(!this.ignoreResize(width, height)){
40671 var grid = this.grid;
40672 var size = this.adjustForComponents(width, height);
40673 // tfoot is not a footer?
40676 var gridel = grid.getGridEl();
40677 gridel.setSize(size.width, size.height);
40679 var tbd = grid.getGridEl().select('tbody', true).first();
40680 var thd = grid.getGridEl().select('thead',true).first();
40681 var tbf= grid.getGridEl().select('tfoot', true).first();
40684 size.height -= tbf.getHeight();
40687 size.height -= thd.getHeight();
40690 tbd.setSize(size.width, size.height );
40691 // this is for the account management tab -seems to work there.
40692 var thd = grid.getGridEl().select('thead',true).first();
40694 // tbd.setSize(size.width, size.height - thd.getHeight());
40703 beforeSlide : function(){
40704 this.grid.getView().scroller.clip();
40707 afterSlide : function(){
40708 this.grid.getView().scroller.unclip();
40711 destroy : function(){
40712 this.grid.destroy();
40714 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40719 * @class Roo.bootstrap.panel.Nest
40720 * @extends Roo.bootstrap.panel.Content
40722 * Create a new Panel, that can contain a layout.Border.
40725 * @param {Roo.BorderLayout} layout The layout for this panel
40726 * @param {String/Object} config A string to set only the title or a config object
40728 Roo.bootstrap.panel.Nest = function(config)
40730 // construct with only one argument..
40731 /* FIXME - implement nicer consturctors
40732 if (layout.layout) {
40734 layout = config.layout;
40735 delete config.layout;
40737 if (layout.xtype && !layout.getEl) {
40738 // then layout needs constructing..
40739 layout = Roo.factory(layout, Roo);
40743 config.el = config.layout.getEl();
40745 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40747 config.layout.monitorWindowResize = false; // turn off autosizing
40748 this.layout = config.layout;
40749 this.layout.getEl().addClass("roo-layout-nested-layout");
40750 this.layout.parent = this;
40757 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40759 setSize : function(width, height){
40760 if(!this.ignoreResize(width, height)){
40761 var size = this.adjustForComponents(width, height);
40762 var el = this.layout.getEl();
40763 if (size.height < 1) {
40764 el.setWidth(size.width);
40766 el.setSize(size.width, size.height);
40768 var touch = el.dom.offsetWidth;
40769 this.layout.layout();
40770 // ie requires a double layout on the first pass
40771 if(Roo.isIE && !this.initialized){
40772 this.initialized = true;
40773 this.layout.layout();
40778 // activate all subpanels if not currently active..
40780 setActiveState : function(active){
40781 this.active = active;
40782 this.setActiveClass(active);
40785 this.fireEvent("deactivate", this);
40789 this.fireEvent("activate", this);
40790 // not sure if this should happen before or after..
40791 if (!this.layout) {
40792 return; // should not happen..
40795 for (var r in this.layout.regions) {
40796 reg = this.layout.getRegion(r);
40797 if (reg.getActivePanel()) {
40798 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40799 reg.setActivePanel(reg.getActivePanel());
40802 if (!reg.panels.length) {
40805 reg.showPanel(reg.getPanel(0));
40814 * Returns the nested BorderLayout for this panel
40815 * @return {Roo.BorderLayout}
40817 getLayout : function(){
40818 return this.layout;
40822 * Adds a xtype elements to the layout of the nested panel
40826 xtype : 'ContentPanel',
40833 xtype : 'NestedLayoutPanel',
40839 items : [ ... list of content panels or nested layout panels.. ]
40843 * @param {Object} cfg Xtype definition of item to add.
40845 addxtype : function(cfg) {
40846 return this.layout.addxtype(cfg);
40851 * Ext JS Library 1.1.1
40852 * Copyright(c) 2006-2007, Ext JS, LLC.
40854 * Originally Released Under LGPL - original licence link has changed is not relivant.
40857 * <script type="text/javascript">
40860 * @class Roo.TabPanel
40861 * @extends Roo.util.Observable
40862 * A lightweight tab container.
40866 // basic tabs 1, built from existing content
40867 var tabs = new Roo.TabPanel("tabs1");
40868 tabs.addTab("script", "View Script");
40869 tabs.addTab("markup", "View Markup");
40870 tabs.activate("script");
40872 // more advanced tabs, built from javascript
40873 var jtabs = new Roo.TabPanel("jtabs");
40874 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40876 // set up the UpdateManager
40877 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40878 var updater = tab2.getUpdateManager();
40879 updater.setDefaultUrl("ajax1.htm");
40880 tab2.on('activate', updater.refresh, updater, true);
40882 // Use setUrl for Ajax loading
40883 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40884 tab3.setUrl("ajax2.htm", null, true);
40887 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40890 jtabs.activate("jtabs-1");
40893 * Create a new TabPanel.
40894 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40895 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40897 Roo.bootstrap.panel.Tabs = function(config){
40899 * The container element for this TabPanel.
40900 * @type Roo.Element
40902 this.el = Roo.get(config.el);
40905 if(typeof config == "boolean"){
40906 this.tabPosition = config ? "bottom" : "top";
40908 Roo.apply(this, config);
40912 if(this.tabPosition == "bottom"){
40913 // if tabs are at the bottom = create the body first.
40914 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40915 this.el.addClass("roo-tabs-bottom");
40917 // next create the tabs holders
40919 if (this.tabPosition == "west"){
40921 var reg = this.region; // fake it..
40923 if (!reg.mgr.parent) {
40926 reg = reg.mgr.parent.region;
40928 Roo.log("got nest?");
40930 if (reg.mgr.getRegion('west')) {
40931 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40932 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40933 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40934 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40935 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40943 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40944 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40945 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40946 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40951 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40954 // finally - if tabs are at the top, then create the body last..
40955 if(this.tabPosition != "bottom"){
40956 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40957 * @type Roo.Element
40959 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40960 this.el.addClass("roo-tabs-top");
40964 this.bodyEl.setStyle("position", "relative");
40966 this.active = null;
40967 this.activateDelegate = this.activate.createDelegate(this);
40972 * Fires when the active tab changes
40973 * @param {Roo.TabPanel} this
40974 * @param {Roo.TabPanelItem} activePanel The new active tab
40978 * @event beforetabchange
40979 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40980 * @param {Roo.TabPanel} this
40981 * @param {Object} e Set cancel to true on this object to cancel the tab change
40982 * @param {Roo.TabPanelItem} tab The tab being changed to
40984 "beforetabchange" : true
40987 Roo.EventManager.onWindowResize(this.onResize, this);
40988 this.cpad = this.el.getPadding("lr");
40989 this.hiddenCount = 0;
40992 // toolbar on the tabbar support...
40993 if (this.toolbar) {
40994 alert("no toolbar support yet");
40995 this.toolbar = false;
40997 var tcfg = this.toolbar;
40998 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40999 this.toolbar = new Roo.Toolbar(tcfg);
41000 if (Roo.isSafari) {
41001 var tbl = tcfg.container.child('table', true);
41002 tbl.setAttribute('width', '100%');
41010 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41013 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41015 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41017 tabPosition : "top",
41019 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41021 currentTabWidth : 0,
41023 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41027 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41031 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41033 preferredTabWidth : 175,
41035 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41037 resizeTabs : false,
41039 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41041 monitorResize : true,
41043 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41045 toolbar : false, // set by caller..
41047 region : false, /// set by caller
41049 disableTooltips : true, // not used yet...
41052 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41053 * @param {String} id The id of the div to use <b>or create</b>
41054 * @param {String} text The text for the tab
41055 * @param {String} content (optional) Content to put in the TabPanelItem body
41056 * @param {Boolean} closable (optional) True to create a close icon on the tab
41057 * @return {Roo.TabPanelItem} The created TabPanelItem
41059 addTab : function(id, text, content, closable, tpl)
41061 var item = new Roo.bootstrap.panel.TabItem({
41065 closable : closable,
41068 this.addTabItem(item);
41070 item.setContent(content);
41076 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41077 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41078 * @return {Roo.TabPanelItem}
41080 getTab : function(id){
41081 return this.items[id];
41085 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41086 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41088 hideTab : function(id){
41089 var t = this.items[id];
41092 this.hiddenCount++;
41093 this.autoSizeTabs();
41098 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41099 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41101 unhideTab : function(id){
41102 var t = this.items[id];
41104 t.setHidden(false);
41105 this.hiddenCount--;
41106 this.autoSizeTabs();
41111 * Adds an existing {@link Roo.TabPanelItem}.
41112 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41114 addTabItem : function(item)
41116 this.items[item.id] = item;
41117 this.items.push(item);
41118 this.autoSizeTabs();
41119 // if(this.resizeTabs){
41120 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41121 // this.autoSizeTabs();
41123 // item.autoSize();
41128 * Removes a {@link Roo.TabPanelItem}.
41129 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41131 removeTab : function(id){
41132 var items = this.items;
41133 var tab = items[id];
41134 if(!tab) { return; }
41135 var index = items.indexOf(tab);
41136 if(this.active == tab && items.length > 1){
41137 var newTab = this.getNextAvailable(index);
41142 this.stripEl.dom.removeChild(tab.pnode.dom);
41143 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41144 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41146 items.splice(index, 1);
41147 delete this.items[tab.id];
41148 tab.fireEvent("close", tab);
41149 tab.purgeListeners();
41150 this.autoSizeTabs();
41153 getNextAvailable : function(start){
41154 var items = this.items;
41156 // look for a next tab that will slide over to
41157 // replace the one being removed
41158 while(index < items.length){
41159 var item = items[++index];
41160 if(item && !item.isHidden()){
41164 // if one isn't found select the previous tab (on the left)
41167 var item = items[--index];
41168 if(item && !item.isHidden()){
41176 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41177 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41179 disableTab : function(id){
41180 var tab = this.items[id];
41181 if(tab && this.active != tab){
41187 * Enables a {@link Roo.TabPanelItem} that is disabled.
41188 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41190 enableTab : function(id){
41191 var tab = this.items[id];
41196 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41197 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41198 * @return {Roo.TabPanelItem} The TabPanelItem.
41200 activate : function(id)
41202 //Roo.log('activite:' + id);
41204 var tab = this.items[id];
41208 if(tab == this.active || tab.disabled){
41212 this.fireEvent("beforetabchange", this, e, tab);
41213 if(e.cancel !== true && !tab.disabled){
41215 this.active.hide();
41217 this.active = this.items[id];
41218 this.active.show();
41219 this.fireEvent("tabchange", this, this.active);
41225 * Gets the active {@link Roo.TabPanelItem}.
41226 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41228 getActiveTab : function(){
41229 return this.active;
41233 * Updates the tab body element to fit the height of the container element
41234 * for overflow scrolling
41235 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41237 syncHeight : function(targetHeight){
41238 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41239 var bm = this.bodyEl.getMargins();
41240 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41241 this.bodyEl.setHeight(newHeight);
41245 onResize : function(){
41246 if(this.monitorResize){
41247 this.autoSizeTabs();
41252 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41254 beginUpdate : function(){
41255 this.updating = true;
41259 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41261 endUpdate : function(){
41262 this.updating = false;
41263 this.autoSizeTabs();
41267 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41269 autoSizeTabs : function()
41271 var count = this.items.length;
41272 var vcount = count - this.hiddenCount;
41275 this.stripEl.hide();
41277 this.stripEl.show();
41280 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41285 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41286 var availWidth = Math.floor(w / vcount);
41287 var b = this.stripBody;
41288 if(b.getWidth() > w){
41289 var tabs = this.items;
41290 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41291 if(availWidth < this.minTabWidth){
41292 /*if(!this.sleft){ // incomplete scrolling code
41293 this.createScrollButtons();
41296 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41299 if(this.currentTabWidth < this.preferredTabWidth){
41300 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41306 * Returns the number of tabs in this TabPanel.
41309 getCount : function(){
41310 return this.items.length;
41314 * Resizes all the tabs to the passed width
41315 * @param {Number} The new width
41317 setTabWidth : function(width){
41318 this.currentTabWidth = width;
41319 for(var i = 0, len = this.items.length; i < len; i++) {
41320 if(!this.items[i].isHidden()) {
41321 this.items[i].setWidth(width);
41327 * Destroys this TabPanel
41328 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41330 destroy : function(removeEl){
41331 Roo.EventManager.removeResizeListener(this.onResize, this);
41332 for(var i = 0, len = this.items.length; i < len; i++){
41333 this.items[i].purgeListeners();
41335 if(removeEl === true){
41336 this.el.update("");
41341 createStrip : function(container)
41343 var strip = document.createElement("nav");
41344 strip.className = Roo.bootstrap.version == 4 ?
41345 "navbar-light bg-light" :
41346 "navbar navbar-default"; //"x-tabs-wrap";
41347 container.appendChild(strip);
41351 createStripList : function(strip)
41353 // div wrapper for retard IE
41354 // returns the "tr" element.
41355 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41356 //'<div class="x-tabs-strip-wrap">'+
41357 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41358 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41359 return strip.firstChild; //.firstChild.firstChild.firstChild;
41361 createBody : function(container)
41363 var body = document.createElement("div");
41364 Roo.id(body, "tab-body");
41365 //Roo.fly(body).addClass("x-tabs-body");
41366 Roo.fly(body).addClass("tab-content");
41367 container.appendChild(body);
41370 createItemBody :function(bodyEl, id){
41371 var body = Roo.getDom(id);
41373 body = document.createElement("div");
41376 //Roo.fly(body).addClass("x-tabs-item-body");
41377 Roo.fly(body).addClass("tab-pane");
41378 bodyEl.insertBefore(body, bodyEl.firstChild);
41382 createStripElements : function(stripEl, text, closable, tpl)
41384 var td = document.createElement("li"); // was td..
41385 td.className = 'nav-item';
41387 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41390 stripEl.appendChild(td);
41392 td.className = "x-tabs-closable";
41393 if(!this.closeTpl){
41394 this.closeTpl = new Roo.Template(
41395 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41396 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41397 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41400 var el = this.closeTpl.overwrite(td, {"text": text});
41401 var close = el.getElementsByTagName("div")[0];
41402 var inner = el.getElementsByTagName("em")[0];
41403 return {"el": el, "close": close, "inner": inner};
41406 // not sure what this is..
41407 // if(!this.tabTpl){
41408 //this.tabTpl = new Roo.Template(
41409 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41410 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41412 // this.tabTpl = new Roo.Template(
41413 // '<a href="#">' +
41414 // '<span unselectable="on"' +
41415 // (this.disableTooltips ? '' : ' title="{text}"') +
41416 // ' >{text}</span></a>'
41422 var template = tpl || this.tabTpl || false;
41425 template = new Roo.Template(
41426 Roo.bootstrap.version == 4 ?
41428 '<a class="nav-link" href="#" unselectable="on"' +
41429 (this.disableTooltips ? '' : ' title="{text}"') +
41432 '<a class="nav-link" href="#">' +
41433 '<span unselectable="on"' +
41434 (this.disableTooltips ? '' : ' title="{text}"') +
41435 ' >{text}</span></a>'
41440 switch (typeof(template)) {
41444 template = new Roo.Template(template);
41450 var el = template.overwrite(td, {"text": text});
41452 var inner = el.getElementsByTagName("span")[0];
41454 return {"el": el, "inner": inner};
41462 * @class Roo.TabPanelItem
41463 * @extends Roo.util.Observable
41464 * Represents an individual item (tab plus body) in a TabPanel.
41465 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41466 * @param {String} id The id of this TabPanelItem
41467 * @param {String} text The text for the tab of this TabPanelItem
41468 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41470 Roo.bootstrap.panel.TabItem = function(config){
41472 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41473 * @type Roo.TabPanel
41475 this.tabPanel = config.panel;
41477 * The id for this TabPanelItem
41480 this.id = config.id;
41482 this.disabled = false;
41484 this.text = config.text;
41486 this.loaded = false;
41487 this.closable = config.closable;
41490 * The body element for this TabPanelItem.
41491 * @type Roo.Element
41493 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41494 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41495 this.bodyEl.setStyle("display", "block");
41496 this.bodyEl.setStyle("zoom", "1");
41497 //this.hideAction();
41499 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41501 this.el = Roo.get(els.el);
41502 this.inner = Roo.get(els.inner, true);
41503 this.textEl = Roo.bootstrap.version == 4 ?
41504 this.el : Roo.get(this.el.dom.firstChild, true);
41506 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41507 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41510 // this.el.on("mousedown", this.onTabMouseDown, this);
41511 this.el.on("click", this.onTabClick, this);
41513 if(config.closable){
41514 var c = Roo.get(els.close, true);
41515 c.dom.title = this.closeText;
41516 c.addClassOnOver("close-over");
41517 c.on("click", this.closeClick, this);
41523 * Fires when this tab becomes the active tab.
41524 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41525 * @param {Roo.TabPanelItem} this
41529 * @event beforeclose
41530 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41531 * @param {Roo.TabPanelItem} this
41532 * @param {Object} e Set cancel to true on this object to cancel the close.
41534 "beforeclose": true,
41537 * Fires when this tab is closed.
41538 * @param {Roo.TabPanelItem} this
41542 * @event deactivate
41543 * Fires when this tab is no longer the active tab.
41544 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41545 * @param {Roo.TabPanelItem} this
41547 "deactivate" : true
41549 this.hidden = false;
41551 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41554 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41556 purgeListeners : function(){
41557 Roo.util.Observable.prototype.purgeListeners.call(this);
41558 this.el.removeAllListeners();
41561 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41564 this.status_node.addClass("active");
41567 this.tabPanel.stripWrap.repaint();
41569 this.fireEvent("activate", this.tabPanel, this);
41573 * Returns true if this tab is the active tab.
41574 * @return {Boolean}
41576 isActive : function(){
41577 return this.tabPanel.getActiveTab() == this;
41581 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41584 this.status_node.removeClass("active");
41586 this.fireEvent("deactivate", this.tabPanel, this);
41589 hideAction : function(){
41590 this.bodyEl.hide();
41591 this.bodyEl.setStyle("position", "absolute");
41592 this.bodyEl.setLeft("-20000px");
41593 this.bodyEl.setTop("-20000px");
41596 showAction : function(){
41597 this.bodyEl.setStyle("position", "relative");
41598 this.bodyEl.setTop("");
41599 this.bodyEl.setLeft("");
41600 this.bodyEl.show();
41604 * Set the tooltip for the tab.
41605 * @param {String} tooltip The tab's tooltip
41607 setTooltip : function(text){
41608 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41609 this.textEl.dom.qtip = text;
41610 this.textEl.dom.removeAttribute('title');
41612 this.textEl.dom.title = text;
41616 onTabClick : function(e){
41617 e.preventDefault();
41618 this.tabPanel.activate(this.id);
41621 onTabMouseDown : function(e){
41622 e.preventDefault();
41623 this.tabPanel.activate(this.id);
41626 getWidth : function(){
41627 return this.inner.getWidth();
41630 setWidth : function(width){
41631 var iwidth = width - this.linode.getPadding("lr");
41632 this.inner.setWidth(iwidth);
41633 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41634 this.linode.setWidth(width);
41638 * Show or hide the tab
41639 * @param {Boolean} hidden True to hide or false to show.
41641 setHidden : function(hidden){
41642 this.hidden = hidden;
41643 this.linode.setStyle("display", hidden ? "none" : "");
41647 * Returns true if this tab is "hidden"
41648 * @return {Boolean}
41650 isHidden : function(){
41651 return this.hidden;
41655 * Returns the text for this tab
41658 getText : function(){
41662 autoSize : function(){
41663 //this.el.beginMeasure();
41664 this.textEl.setWidth(1);
41666 * #2804 [new] Tabs in Roojs
41667 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41669 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41670 //this.el.endMeasure();
41674 * Sets the text for the tab (Note: this also sets the tooltip text)
41675 * @param {String} text The tab's text and tooltip
41677 setText : function(text){
41679 this.textEl.update(text);
41680 this.setTooltip(text);
41681 //if(!this.tabPanel.resizeTabs){
41682 // this.autoSize();
41686 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41688 activate : function(){
41689 this.tabPanel.activate(this.id);
41693 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41695 disable : function(){
41696 if(this.tabPanel.active != this){
41697 this.disabled = true;
41698 this.status_node.addClass("disabled");
41703 * Enables this TabPanelItem if it was previously disabled.
41705 enable : function(){
41706 this.disabled = false;
41707 this.status_node.removeClass("disabled");
41711 * Sets the content for this TabPanelItem.
41712 * @param {String} content The content
41713 * @param {Boolean} loadScripts true to look for and load scripts
41715 setContent : function(content, loadScripts){
41716 this.bodyEl.update(content, loadScripts);
41720 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41721 * @return {Roo.UpdateManager} The UpdateManager
41723 getUpdateManager : function(){
41724 return this.bodyEl.getUpdateManager();
41728 * Set a URL to be used to load the content for this TabPanelItem.
41729 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41730 * @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)
41731 * @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)
41732 * @return {Roo.UpdateManager} The UpdateManager
41734 setUrl : function(url, params, loadOnce){
41735 if(this.refreshDelegate){
41736 this.un('activate', this.refreshDelegate);
41738 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41739 this.on("activate", this.refreshDelegate);
41740 return this.bodyEl.getUpdateManager();
41744 _handleRefresh : function(url, params, loadOnce){
41745 if(!loadOnce || !this.loaded){
41746 var updater = this.bodyEl.getUpdateManager();
41747 updater.update(url, params, this._setLoaded.createDelegate(this));
41752 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41753 * Will fail silently if the setUrl method has not been called.
41754 * This does not activate the panel, just updates its content.
41756 refresh : function(){
41757 if(this.refreshDelegate){
41758 this.loaded = false;
41759 this.refreshDelegate();
41764 _setLoaded : function(){
41765 this.loaded = true;
41769 closeClick : function(e){
41772 this.fireEvent("beforeclose", this, o);
41773 if(o.cancel !== true){
41774 this.tabPanel.removeTab(this.id);
41778 * The text displayed in the tooltip for the close icon.
41781 closeText : "Close this tab"
41784 * This script refer to:
41785 * Title: International Telephone Input
41786 * Author: Jack O'Connor
41787 * Code version: v12.1.12
41788 * Availability: https://github.com/jackocnr/intl-tel-input.git
41791 Roo.bootstrap.PhoneInputData = function() {
41794 "Afghanistan (افغانستان)",
41799 "Albania (Shqipëri)",
41804 "Algeria (الجزائر)",
41829 "Antigua and Barbuda",
41839 "Armenia (Հայաստան)",
41855 "Austria (Österreich)",
41860 "Azerbaijan (Azərbaycan)",
41870 "Bahrain (البحرين)",
41875 "Bangladesh (বাংলাদেশ)",
41885 "Belarus (Беларусь)",
41890 "Belgium (België)",
41920 "Bosnia and Herzegovina (Босна и Херцеговина)",
41935 "British Indian Ocean Territory",
41940 "British Virgin Islands",
41950 "Bulgaria (България)",
41960 "Burundi (Uburundi)",
41965 "Cambodia (កម្ពុជា)",
41970 "Cameroon (Cameroun)",
41979 ["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"]
41982 "Cape Verde (Kabu Verdi)",
41987 "Caribbean Netherlands",
41998 "Central African Republic (République centrafricaine)",
42018 "Christmas Island",
42024 "Cocos (Keeling) Islands",
42035 "Comoros (جزر القمر)",
42040 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42045 "Congo (Republic) (Congo-Brazzaville)",
42065 "Croatia (Hrvatska)",
42086 "Czech Republic (Česká republika)",
42091 "Denmark (Danmark)",
42106 "Dominican Republic (República Dominicana)",
42110 ["809", "829", "849"]
42128 "Equatorial Guinea (Guinea Ecuatorial)",
42148 "Falkland Islands (Islas Malvinas)",
42153 "Faroe Islands (Føroyar)",
42174 "French Guiana (Guyane française)",
42179 "French Polynesia (Polynésie française)",
42194 "Georgia (საქართველო)",
42199 "Germany (Deutschland)",
42219 "Greenland (Kalaallit Nunaat)",
42256 "Guinea-Bissau (Guiné Bissau)",
42281 "Hungary (Magyarország)",
42286 "Iceland (Ísland)",
42306 "Iraq (العراق)",
42322 "Israel (ישראל)",
42349 "Jordan (الأردن)",
42354 "Kazakhstan (Казахстан)",
42375 "Kuwait (الكويت)",
42380 "Kyrgyzstan (Кыргызстан)",
42390 "Latvia (Latvija)",
42395 "Lebanon (لبنان)",
42410 "Libya (ليبيا)",
42420 "Lithuania (Lietuva)",
42435 "Macedonia (FYROM) (Македонија)",
42440 "Madagascar (Madagasikara)",
42470 "Marshall Islands",
42480 "Mauritania (موريتانيا)",
42485 "Mauritius (Moris)",
42506 "Moldova (Republica Moldova)",
42516 "Mongolia (Монгол)",
42521 "Montenegro (Crna Gora)",
42531 "Morocco (المغرب)",
42537 "Mozambique (Moçambique)",
42542 "Myanmar (Burma) (မြန်မာ)",
42547 "Namibia (Namibië)",
42562 "Netherlands (Nederland)",
42567 "New Caledonia (Nouvelle-Calédonie)",
42602 "North Korea (조선 민주주의 인민 공화국)",
42607 "Northern Mariana Islands",
42623 "Pakistan (پاکستان)",
42633 "Palestine (فلسطين)",
42643 "Papua New Guinea",
42685 "Réunion (La Réunion)",
42691 "Romania (România)",
42707 "Saint Barthélemy",
42718 "Saint Kitts and Nevis",
42728 "Saint Martin (Saint-Martin (partie française))",
42734 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42739 "Saint Vincent and the Grenadines",
42754 "São Tomé and Príncipe (São Tomé e Príncipe)",
42759 "Saudi Arabia (المملكة العربية السعودية)",
42764 "Senegal (Sénégal)",
42794 "Slovakia (Slovensko)",
42799 "Slovenia (Slovenija)",
42809 "Somalia (Soomaaliya)",
42819 "South Korea (대한민국)",
42824 "South Sudan (جنوب السودان)",
42834 "Sri Lanka (ශ්රී ලංකාව)",
42839 "Sudan (السودان)",
42849 "Svalbard and Jan Mayen",
42860 "Sweden (Sverige)",
42865 "Switzerland (Schweiz)",
42870 "Syria (سوريا)",
42915 "Trinidad and Tobago",
42920 "Tunisia (تونس)",
42925 "Turkey (Türkiye)",
42935 "Turks and Caicos Islands",
42945 "U.S. Virgin Islands",
42955 "Ukraine (Україна)",
42960 "United Arab Emirates (الإمارات العربية المتحدة)",
42982 "Uzbekistan (Oʻzbekiston)",
42992 "Vatican City (Città del Vaticano)",
43003 "Vietnam (Việt Nam)",
43008 "Wallis and Futuna (Wallis-et-Futuna)",
43013 "Western Sahara (الصحراء الغربية)",
43019 "Yemen (اليمن)",
43043 * This script refer to:
43044 * Title: International Telephone Input
43045 * Author: Jack O'Connor
43046 * Code version: v12.1.12
43047 * Availability: https://github.com/jackocnr/intl-tel-input.git
43051 * @class Roo.bootstrap.PhoneInput
43052 * @extends Roo.bootstrap.TriggerField
43053 * An input with International dial-code selection
43055 * @cfg {String} defaultDialCode default '+852'
43056 * @cfg {Array} preferedCountries default []
43059 * Create a new PhoneInput.
43060 * @param {Object} config Configuration options
43063 Roo.bootstrap.PhoneInput = function(config) {
43064 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43067 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43069 listWidth: undefined,
43071 selectedClass: 'active',
43073 invalidClass : "has-warning",
43075 validClass: 'has-success',
43077 allowed: '0123456789',
43082 * @cfg {String} defaultDialCode The default dial code when initializing the input
43084 defaultDialCode: '+852',
43087 * @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
43089 preferedCountries: false,
43091 getAutoCreate : function()
43093 var data = Roo.bootstrap.PhoneInputData();
43094 var align = this.labelAlign || this.parentLabelAlign();
43097 this.allCountries = [];
43098 this.dialCodeMapping = [];
43100 for (var i = 0; i < data.length; i++) {
43102 this.allCountries[i] = {
43106 priority: c[3] || 0,
43107 areaCodes: c[4] || null
43109 this.dialCodeMapping[c[2]] = {
43112 priority: c[3] || 0,
43113 areaCodes: c[4] || null
43125 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43126 maxlength: this.max_length,
43127 cls : 'form-control tel-input',
43128 autocomplete: 'new-password'
43131 var hiddenInput = {
43134 cls: 'hidden-tel-input'
43138 hiddenInput.name = this.name;
43141 if (this.disabled) {
43142 input.disabled = true;
43145 var flag_container = {
43162 cls: this.hasFeedback ? 'has-feedback' : '',
43168 cls: 'dial-code-holder',
43175 cls: 'roo-select2-container input-group',
43182 if (this.fieldLabel.length) {
43185 tooltip: 'This field is required'
43191 cls: 'control-label',
43197 html: this.fieldLabel
43200 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43206 if(this.indicatorpos == 'right') {
43207 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43214 if(align == 'left') {
43222 if(this.labelWidth > 12){
43223 label.style = "width: " + this.labelWidth + 'px';
43225 if(this.labelWidth < 13 && this.labelmd == 0){
43226 this.labelmd = this.labelWidth;
43228 if(this.labellg > 0){
43229 label.cls += ' col-lg-' + this.labellg;
43230 input.cls += ' col-lg-' + (12 - this.labellg);
43232 if(this.labelmd > 0){
43233 label.cls += ' col-md-' + this.labelmd;
43234 container.cls += ' col-md-' + (12 - this.labelmd);
43236 if(this.labelsm > 0){
43237 label.cls += ' col-sm-' + this.labelsm;
43238 container.cls += ' col-sm-' + (12 - this.labelsm);
43240 if(this.labelxs > 0){
43241 label.cls += ' col-xs-' + this.labelxs;
43242 container.cls += ' col-xs-' + (12 - this.labelxs);
43252 var settings = this;
43254 ['xs','sm','md','lg'].map(function(size){
43255 if (settings[size]) {
43256 cfg.cls += ' col-' + size + '-' + settings[size];
43260 this.store = new Roo.data.Store({
43261 proxy : new Roo.data.MemoryProxy({}),
43262 reader : new Roo.data.JsonReader({
43273 'name' : 'dialCode',
43277 'name' : 'priority',
43281 'name' : 'areaCodes',
43288 if(!this.preferedCountries) {
43289 this.preferedCountries = [
43296 var p = this.preferedCountries.reverse();
43299 for (var i = 0; i < p.length; i++) {
43300 for (var j = 0; j < this.allCountries.length; j++) {
43301 if(this.allCountries[j].iso2 == p[i]) {
43302 var t = this.allCountries[j];
43303 this.allCountries.splice(j,1);
43304 this.allCountries.unshift(t);
43310 this.store.proxy.data = {
43312 data: this.allCountries
43318 initEvents : function()
43321 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43323 this.indicator = this.indicatorEl();
43324 this.flag = this.flagEl();
43325 this.dialCodeHolder = this.dialCodeHolderEl();
43327 this.trigger = this.el.select('div.flag-box',true).first();
43328 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43333 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43334 _this.list.setWidth(lw);
43337 this.list.on('mouseover', this.onViewOver, this);
43338 this.list.on('mousemove', this.onViewMove, this);
43339 this.inputEl().on("keyup", this.onKeyUp, this);
43340 this.inputEl().on("keypress", this.onKeyPress, this);
43342 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43344 this.view = new Roo.View(this.list, this.tpl, {
43345 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43348 this.view.on('click', this.onViewClick, this);
43349 this.setValue(this.defaultDialCode);
43352 onTriggerClick : function(e)
43354 Roo.log('trigger click');
43359 if(this.isExpanded()){
43361 this.hasFocus = false;
43363 this.store.load({});
43364 this.hasFocus = true;
43369 isExpanded : function()
43371 return this.list.isVisible();
43374 collapse : function()
43376 if(!this.isExpanded()){
43380 Roo.get(document).un('mousedown', this.collapseIf, this);
43381 Roo.get(document).un('mousewheel', this.collapseIf, this);
43382 this.fireEvent('collapse', this);
43386 expand : function()
43390 if(this.isExpanded() || !this.hasFocus){
43394 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43395 this.list.setWidth(lw);
43398 this.restrictHeight();
43400 Roo.get(document).on('mousedown', this.collapseIf, this);
43401 Roo.get(document).on('mousewheel', this.collapseIf, this);
43403 this.fireEvent('expand', this);
43406 restrictHeight : function()
43408 this.list.alignTo(this.inputEl(), this.listAlign);
43409 this.list.alignTo(this.inputEl(), this.listAlign);
43412 onViewOver : function(e, t)
43414 if(this.inKeyMode){
43417 var item = this.view.findItemFromChild(t);
43420 var index = this.view.indexOf(item);
43421 this.select(index, false);
43426 onViewClick : function(view, doFocus, el, e)
43428 var index = this.view.getSelectedIndexes()[0];
43430 var r = this.store.getAt(index);
43433 this.onSelect(r, index);
43435 if(doFocus !== false && !this.blockFocus){
43436 this.inputEl().focus();
43440 onViewMove : function(e, t)
43442 this.inKeyMode = false;
43445 select : function(index, scrollIntoView)
43447 this.selectedIndex = index;
43448 this.view.select(index);
43449 if(scrollIntoView !== false){
43450 var el = this.view.getNode(index);
43452 this.list.scrollChildIntoView(el, false);
43457 createList : function()
43459 this.list = Roo.get(document.body).createChild({
43461 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43462 style: 'display:none'
43465 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43468 collapseIf : function(e)
43470 var in_combo = e.within(this.el);
43471 var in_list = e.within(this.list);
43472 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43474 if (in_combo || in_list || is_list) {
43480 onSelect : function(record, index)
43482 if(this.fireEvent('beforeselect', this, record, index) !== false){
43484 this.setFlagClass(record.data.iso2);
43485 this.setDialCode(record.data.dialCode);
43486 this.hasFocus = false;
43488 this.fireEvent('select', this, record, index);
43492 flagEl : function()
43494 var flag = this.el.select('div.flag',true).first();
43501 dialCodeHolderEl : function()
43503 var d = this.el.select('input.dial-code-holder',true).first();
43510 setDialCode : function(v)
43512 this.dialCodeHolder.dom.value = '+'+v;
43515 setFlagClass : function(n)
43517 this.flag.dom.className = 'flag '+n;
43520 getValue : function()
43522 var v = this.inputEl().getValue();
43523 if(this.dialCodeHolder) {
43524 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43529 setValue : function(v)
43531 var d = this.getDialCode(v);
43533 //invalid dial code
43534 if(v.length == 0 || !d || d.length == 0) {
43536 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43537 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43543 this.setFlagClass(this.dialCodeMapping[d].iso2);
43544 this.setDialCode(d);
43545 this.inputEl().dom.value = v.replace('+'+d,'');
43546 this.hiddenEl().dom.value = this.getValue();
43551 getDialCode : function(v)
43555 if (v.length == 0) {
43556 return this.dialCodeHolder.dom.value;
43560 if (v.charAt(0) != "+") {
43563 var numericChars = "";
43564 for (var i = 1; i < v.length; i++) {
43565 var c = v.charAt(i);
43568 if (this.dialCodeMapping[numericChars]) {
43569 dialCode = v.substr(1, i);
43571 if (numericChars.length == 4) {
43581 this.setValue(this.defaultDialCode);
43585 hiddenEl : function()
43587 return this.el.select('input.hidden-tel-input',true).first();
43590 // after setting val
43591 onKeyUp : function(e){
43592 this.setValue(this.getValue());
43595 onKeyPress : function(e){
43596 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43603 * @class Roo.bootstrap.MoneyField
43604 * @extends Roo.bootstrap.ComboBox
43605 * Bootstrap MoneyField class
43608 * Create a new MoneyField.
43609 * @param {Object} config Configuration options
43612 Roo.bootstrap.MoneyField = function(config) {
43614 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43618 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43621 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43623 allowDecimals : true,
43625 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43627 decimalSeparator : ".",
43629 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43631 decimalPrecision : 0,
43633 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43635 allowNegative : true,
43637 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43641 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43643 minValue : Number.NEGATIVE_INFINITY,
43645 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43647 maxValue : Number.MAX_VALUE,
43649 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43651 minText : "The minimum value for this field is {0}",
43653 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43655 maxText : "The maximum value for this field is {0}",
43657 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43658 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43660 nanText : "{0} is not a valid number",
43662 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43666 * @cfg {String} defaults currency of the MoneyField
43667 * value should be in lkey
43669 defaultCurrency : false,
43671 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43673 thousandsDelimiter : false,
43675 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43686 getAutoCreate : function()
43688 var align = this.labelAlign || this.parentLabelAlign();
43700 cls : 'form-control roo-money-amount-input',
43701 autocomplete: 'new-password'
43704 var hiddenInput = {
43708 cls: 'hidden-number-input'
43711 if(this.max_length) {
43712 input.maxlength = this.max_length;
43716 hiddenInput.name = this.name;
43719 if (this.disabled) {
43720 input.disabled = true;
43723 var clg = 12 - this.inputlg;
43724 var cmd = 12 - this.inputmd;
43725 var csm = 12 - this.inputsm;
43726 var cxs = 12 - this.inputxs;
43730 cls : 'row roo-money-field',
43734 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43738 cls: 'roo-select2-container input-group',
43742 cls : 'form-control roo-money-currency-input',
43743 autocomplete: 'new-password',
43745 name : this.currencyName
43749 cls : 'input-group-addon',
43763 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43767 cls: this.hasFeedback ? 'has-feedback' : '',
43778 if (this.fieldLabel.length) {
43781 tooltip: 'This field is required'
43787 cls: 'control-label',
43793 html: this.fieldLabel
43796 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43802 if(this.indicatorpos == 'right') {
43803 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43810 if(align == 'left') {
43818 if(this.labelWidth > 12){
43819 label.style = "width: " + this.labelWidth + 'px';
43821 if(this.labelWidth < 13 && this.labelmd == 0){
43822 this.labelmd = this.labelWidth;
43824 if(this.labellg > 0){
43825 label.cls += ' col-lg-' + this.labellg;
43826 input.cls += ' col-lg-' + (12 - this.labellg);
43828 if(this.labelmd > 0){
43829 label.cls += ' col-md-' + this.labelmd;
43830 container.cls += ' col-md-' + (12 - this.labelmd);
43832 if(this.labelsm > 0){
43833 label.cls += ' col-sm-' + this.labelsm;
43834 container.cls += ' col-sm-' + (12 - this.labelsm);
43836 if(this.labelxs > 0){
43837 label.cls += ' col-xs-' + this.labelxs;
43838 container.cls += ' col-xs-' + (12 - this.labelxs);
43849 var settings = this;
43851 ['xs','sm','md','lg'].map(function(size){
43852 if (settings[size]) {
43853 cfg.cls += ' col-' + size + '-' + settings[size];
43860 initEvents : function()
43862 this.indicator = this.indicatorEl();
43864 this.initCurrencyEvent();
43866 this.initNumberEvent();
43869 initCurrencyEvent : function()
43872 throw "can not find store for combo";
43875 this.store = Roo.factory(this.store, Roo.data);
43876 this.store.parent = this;
43880 this.triggerEl = this.el.select('.input-group-addon', true).first();
43882 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43887 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43888 _this.list.setWidth(lw);
43891 this.list.on('mouseover', this.onViewOver, this);
43892 this.list.on('mousemove', this.onViewMove, this);
43893 this.list.on('scroll', this.onViewScroll, this);
43896 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43899 this.view = new Roo.View(this.list, this.tpl, {
43900 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43903 this.view.on('click', this.onViewClick, this);
43905 this.store.on('beforeload', this.onBeforeLoad, this);
43906 this.store.on('load', this.onLoad, this);
43907 this.store.on('loadexception', this.onLoadException, this);
43909 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43910 "up" : function(e){
43911 this.inKeyMode = true;
43915 "down" : function(e){
43916 if(!this.isExpanded()){
43917 this.onTriggerClick();
43919 this.inKeyMode = true;
43924 "enter" : function(e){
43927 if(this.fireEvent("specialkey", this, e)){
43928 this.onViewClick(false);
43934 "esc" : function(e){
43938 "tab" : function(e){
43941 if(this.fireEvent("specialkey", this, e)){
43942 this.onViewClick(false);
43950 doRelay : function(foo, bar, hname){
43951 if(hname == 'down' || this.scope.isExpanded()){
43952 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43960 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43964 initNumberEvent : function(e)
43966 this.inputEl().on("keydown" , this.fireKey, this);
43967 this.inputEl().on("focus", this.onFocus, this);
43968 this.inputEl().on("blur", this.onBlur, this);
43970 this.inputEl().relayEvent('keyup', this);
43972 if(this.indicator){
43973 this.indicator.addClass('invisible');
43976 this.originalValue = this.getValue();
43978 if(this.validationEvent == 'keyup'){
43979 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43980 this.inputEl().on('keyup', this.filterValidation, this);
43982 else if(this.validationEvent !== false){
43983 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43986 if(this.selectOnFocus){
43987 this.on("focus", this.preFocus, this);
43990 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43991 this.inputEl().on("keypress", this.filterKeys, this);
43993 this.inputEl().relayEvent('keypress', this);
43996 var allowed = "0123456789";
43998 if(this.allowDecimals){
43999 allowed += this.decimalSeparator;
44002 if(this.allowNegative){
44006 if(this.thousandsDelimiter) {
44010 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44012 var keyPress = function(e){
44014 var k = e.getKey();
44016 var c = e.getCharCode();
44019 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44020 allowed.indexOf(String.fromCharCode(c)) === -1
44026 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44030 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44035 this.inputEl().on("keypress", keyPress, this);
44039 onTriggerClick : function(e)
44046 this.loadNext = false;
44048 if(this.isExpanded()){
44053 this.hasFocus = true;
44055 if(this.triggerAction == 'all') {
44056 this.doQuery(this.allQuery, true);
44060 this.doQuery(this.getRawValue());
44063 getCurrency : function()
44065 var v = this.currencyEl().getValue();
44070 restrictHeight : function()
44072 this.list.alignTo(this.currencyEl(), this.listAlign);
44073 this.list.alignTo(this.currencyEl(), this.listAlign);
44076 onViewClick : function(view, doFocus, el, e)
44078 var index = this.view.getSelectedIndexes()[0];
44080 var r = this.store.getAt(index);
44083 this.onSelect(r, index);
44087 onSelect : function(record, index){
44089 if(this.fireEvent('beforeselect', this, record, index) !== false){
44091 this.setFromCurrencyData(index > -1 ? record.data : false);
44095 this.fireEvent('select', this, record, index);
44099 setFromCurrencyData : function(o)
44103 this.lastCurrency = o;
44105 if (this.currencyField) {
44106 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44108 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44111 this.lastSelectionText = currency;
44113 //setting default currency
44114 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44115 this.setCurrency(this.defaultCurrency);
44119 this.setCurrency(currency);
44122 setFromData : function(o)
44126 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44128 this.setFromCurrencyData(c);
44133 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44135 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44138 this.setValue(value);
44142 setCurrency : function(v)
44144 this.currencyValue = v;
44147 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44152 setValue : function(v)
44154 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44160 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44162 this.inputEl().dom.value = (v == '') ? '' :
44163 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44165 if(!this.allowZero && v === '0') {
44166 this.hiddenEl().dom.value = '';
44167 this.inputEl().dom.value = '';
44174 getRawValue : function()
44176 var v = this.inputEl().getValue();
44181 getValue : function()
44183 return this.fixPrecision(this.parseValue(this.getRawValue()));
44186 parseValue : function(value)
44188 if(this.thousandsDelimiter) {
44190 r = new RegExp(",", "g");
44191 value = value.replace(r, "");
44194 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44195 return isNaN(value) ? '' : value;
44199 fixPrecision : function(value)
44201 if(this.thousandsDelimiter) {
44203 r = new RegExp(",", "g");
44204 value = value.replace(r, "");
44207 var nan = isNaN(value);
44209 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44210 return nan ? '' : value;
44212 return parseFloat(value).toFixed(this.decimalPrecision);
44215 decimalPrecisionFcn : function(v)
44217 return Math.floor(v);
44220 validateValue : function(value)
44222 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44226 var num = this.parseValue(value);
44229 this.markInvalid(String.format(this.nanText, value));
44233 if(num < this.minValue){
44234 this.markInvalid(String.format(this.minText, this.minValue));
44238 if(num > this.maxValue){
44239 this.markInvalid(String.format(this.maxText, this.maxValue));
44246 validate : function()
44248 if(this.disabled || this.allowBlank){
44253 var currency = this.getCurrency();
44255 if(this.validateValue(this.getRawValue()) && currency.length){
44260 this.markInvalid();
44264 getName: function()
44269 beforeBlur : function()
44275 var v = this.parseValue(this.getRawValue());
44282 onBlur : function()
44286 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44287 //this.el.removeClass(this.focusClass);
44290 this.hasFocus = false;
44292 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44296 var v = this.getValue();
44298 if(String(v) !== String(this.startValue)){
44299 this.fireEvent('change', this, v, this.startValue);
44302 this.fireEvent("blur", this);
44305 inputEl : function()
44307 return this.el.select('.roo-money-amount-input', true).first();
44310 currencyEl : function()
44312 return this.el.select('.roo-money-currency-input', true).first();
44315 hiddenEl : function()
44317 return this.el.select('input.hidden-number-input',true).first();
44321 * @class Roo.bootstrap.BezierSignature
44322 * @extends Roo.bootstrap.Component
44323 * Bootstrap BezierSignature class
44324 * This script refer to:
44325 * Title: Signature Pad
44327 * Availability: https://github.com/szimek/signature_pad
44330 * Create a new BezierSignature
44331 * @param {Object} config The config object
44334 Roo.bootstrap.BezierSignature = function(config){
44335 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44341 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44348 mouse_btn_down: true,
44351 * @cfg {int} canvas height
44353 canvas_height: '200px',
44356 * @cfg {float|function} Radius of a single dot.
44361 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44366 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44371 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44376 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44381 * @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.
44383 bg_color: 'rgba(0, 0, 0, 0)',
44386 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44388 dot_color: 'black',
44391 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44393 velocity_filter_weight: 0.7,
44396 * @cfg {function} Callback when stroke begin.
44401 * @cfg {function} Callback when stroke end.
44405 getAutoCreate : function()
44407 var cls = 'roo-signature column';
44410 cls += ' ' + this.cls;
44420 for(var i = 0; i < col_sizes.length; i++) {
44421 if(this[col_sizes[i]]) {
44422 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44432 cls: 'roo-signature-body',
44436 cls: 'roo-signature-body-canvas',
44437 height: this.canvas_height,
44438 width: this.canvas_width
44445 style: 'display: none'
44453 initEvents: function()
44455 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44457 var canvas = this.canvasEl();
44459 // mouse && touch event swapping...
44460 canvas.dom.style.touchAction = 'none';
44461 canvas.dom.style.msTouchAction = 'none';
44463 this.mouse_btn_down = false;
44464 canvas.on('mousedown', this._handleMouseDown, this);
44465 canvas.on('mousemove', this._handleMouseMove, this);
44466 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44468 if (window.PointerEvent) {
44469 canvas.on('pointerdown', this._handleMouseDown, this);
44470 canvas.on('pointermove', this._handleMouseMove, this);
44471 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44474 if ('ontouchstart' in window) {
44475 canvas.on('touchstart', this._handleTouchStart, this);
44476 canvas.on('touchmove', this._handleTouchMove, this);
44477 canvas.on('touchend', this._handleTouchEnd, this);
44480 Roo.EventManager.onWindowResize(this.resize, this, true);
44482 // file input event
44483 this.fileEl().on('change', this.uploadImage, this);
44490 resize: function(){
44492 var canvas = this.canvasEl().dom;
44493 var ctx = this.canvasElCtx();
44494 var img_data = false;
44496 if(canvas.width > 0) {
44497 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44499 // setting canvas width will clean img data
44502 var style = window.getComputedStyle ?
44503 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44505 var padding_left = parseInt(style.paddingLeft) || 0;
44506 var padding_right = parseInt(style.paddingRight) || 0;
44508 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44511 ctx.putImageData(img_data, 0, 0);
44515 _handleMouseDown: function(e)
44517 if (e.browserEvent.which === 1) {
44518 this.mouse_btn_down = true;
44519 this.strokeBegin(e);
44523 _handleMouseMove: function (e)
44525 if (this.mouse_btn_down) {
44526 this.strokeMoveUpdate(e);
44530 _handleMouseUp: function (e)
44532 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44533 this.mouse_btn_down = false;
44538 _handleTouchStart: function (e) {
44540 e.preventDefault();
44541 if (e.browserEvent.targetTouches.length === 1) {
44542 // var touch = e.browserEvent.changedTouches[0];
44543 // this.strokeBegin(touch);
44545 this.strokeBegin(e); // assume e catching the correct xy...
44549 _handleTouchMove: function (e) {
44550 e.preventDefault();
44551 // var touch = event.targetTouches[0];
44552 // _this._strokeMoveUpdate(touch);
44553 this.strokeMoveUpdate(e);
44556 _handleTouchEnd: function (e) {
44557 var wasCanvasTouched = e.target === this.canvasEl().dom;
44558 if (wasCanvasTouched) {
44559 e.preventDefault();
44560 // var touch = event.changedTouches[0];
44561 // _this._strokeEnd(touch);
44566 reset: function () {
44567 this._lastPoints = [];
44568 this._lastVelocity = 0;
44569 this._lastWidth = (this.min_width + this.max_width) / 2;
44570 this.canvasElCtx().fillStyle = this.dot_color;
44573 strokeMoveUpdate: function(e)
44575 this.strokeUpdate(e);
44577 if (this.throttle) {
44578 this.throttleStroke(this.strokeUpdate, this.throttle);
44581 this.strokeUpdate(e);
44585 strokeBegin: function(e)
44587 var newPointGroup = {
44588 color: this.dot_color,
44592 if (typeof this.onBegin === 'function') {
44596 this.curve_data.push(newPointGroup);
44598 this.strokeUpdate(e);
44601 strokeUpdate: function(e)
44603 var rect = this.canvasEl().dom.getBoundingClientRect();
44604 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44605 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44606 var lastPoints = lastPointGroup.points;
44607 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44608 var isLastPointTooClose = lastPoint
44609 ? point.distanceTo(lastPoint) <= this.min_distance
44611 var color = lastPointGroup.color;
44612 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44613 var curve = this.addPoint(point);
44615 this.drawDot({color: color, point: point});
44618 this.drawCurve({color: color, curve: curve});
44628 strokeEnd: function(e)
44630 this.strokeUpdate(e);
44631 if (typeof this.onEnd === 'function') {
44636 addPoint: function (point) {
44637 var _lastPoints = this._lastPoints;
44638 _lastPoints.push(point);
44639 if (_lastPoints.length > 2) {
44640 if (_lastPoints.length === 3) {
44641 _lastPoints.unshift(_lastPoints[0]);
44643 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44644 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44645 _lastPoints.shift();
44651 calculateCurveWidths: function (startPoint, endPoint) {
44652 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44653 (1 - this.velocity_filter_weight) * this._lastVelocity;
44655 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44658 start: this._lastWidth
44661 this._lastVelocity = velocity;
44662 this._lastWidth = newWidth;
44666 drawDot: function (_a) {
44667 var color = _a.color, point = _a.point;
44668 var ctx = this.canvasElCtx();
44669 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44671 this.drawCurveSegment(point.x, point.y, width);
44673 ctx.fillStyle = color;
44677 drawCurve: function (_a) {
44678 var color = _a.color, curve = _a.curve;
44679 var ctx = this.canvasElCtx();
44680 var widthDelta = curve.endWidth - curve.startWidth;
44681 var drawSteps = Math.floor(curve.length()) * 2;
44683 ctx.fillStyle = color;
44684 for (var i = 0; i < drawSteps; i += 1) {
44685 var t = i / drawSteps;
44691 var x = uuu * curve.startPoint.x;
44692 x += 3 * uu * t * curve.control1.x;
44693 x += 3 * u * tt * curve.control2.x;
44694 x += ttt * curve.endPoint.x;
44695 var y = uuu * curve.startPoint.y;
44696 y += 3 * uu * t * curve.control1.y;
44697 y += 3 * u * tt * curve.control2.y;
44698 y += ttt * curve.endPoint.y;
44699 var width = curve.startWidth + ttt * widthDelta;
44700 this.drawCurveSegment(x, y, width);
44706 drawCurveSegment: function (x, y, width) {
44707 var ctx = this.canvasElCtx();
44709 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44710 this.is_empty = false;
44715 var ctx = this.canvasElCtx();
44716 var canvas = this.canvasEl().dom;
44717 ctx.fillStyle = this.bg_color;
44718 ctx.clearRect(0, 0, canvas.width, canvas.height);
44719 ctx.fillRect(0, 0, canvas.width, canvas.height);
44720 this.curve_data = [];
44722 this.is_empty = true;
44727 return this.el.select('input',true).first();
44730 canvasEl: function()
44732 return this.el.select('canvas',true).first();
44735 canvasElCtx: function()
44737 return this.el.select('canvas',true).first().dom.getContext('2d');
44740 getImage: function(type)
44742 if(this.is_empty) {
44747 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44750 drawFromImage: function(img_src)
44752 var img = new Image();
44754 img.onload = function(){
44755 this.canvasElCtx().drawImage(img, 0, 0);
44760 this.is_empty = false;
44763 selectImage: function()
44765 this.fileEl().dom.click();
44768 uploadImage: function(e)
44770 var reader = new FileReader();
44772 reader.onload = function(e){
44773 var img = new Image();
44774 img.onload = function(){
44776 this.canvasElCtx().drawImage(img, 0, 0);
44778 img.src = e.target.result;
44781 reader.readAsDataURL(e.target.files[0]);
44784 // Bezier Point Constructor
44785 Point: (function () {
44786 function Point(x, y, time) {
44789 this.time = time || Date.now();
44791 Point.prototype.distanceTo = function (start) {
44792 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44794 Point.prototype.equals = function (other) {
44795 return this.x === other.x && this.y === other.y && this.time === other.time;
44797 Point.prototype.velocityFrom = function (start) {
44798 return this.time !== start.time
44799 ? this.distanceTo(start) / (this.time - start.time)
44806 // Bezier Constructor
44807 Bezier: (function () {
44808 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44809 this.startPoint = startPoint;
44810 this.control2 = control2;
44811 this.control1 = control1;
44812 this.endPoint = endPoint;
44813 this.startWidth = startWidth;
44814 this.endWidth = endWidth;
44816 Bezier.fromPoints = function (points, widths, scope) {
44817 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44818 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44819 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44821 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44822 var dx1 = s1.x - s2.x;
44823 var dy1 = s1.y - s2.y;
44824 var dx2 = s2.x - s3.x;
44825 var dy2 = s2.y - s3.y;
44826 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44827 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44828 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44829 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44830 var dxm = m1.x - m2.x;
44831 var dym = m1.y - m2.y;
44832 var k = l2 / (l1 + l2);
44833 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44834 var tx = s2.x - cm.x;
44835 var ty = s2.y - cm.y;
44837 c1: new scope.Point(m1.x + tx, m1.y + ty),
44838 c2: new scope.Point(m2.x + tx, m2.y + ty)
44841 Bezier.prototype.length = function () {
44846 for (var i = 0; i <= steps; i += 1) {
44848 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44849 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44851 var xdiff = cx - px;
44852 var ydiff = cy - py;
44853 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44860 Bezier.prototype.point = function (t, start, c1, c2, end) {
44861 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44862 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44863 + (3.0 * c2 * (1.0 - t) * t * t)
44864 + (end * t * t * t);
44869 throttleStroke: function(fn, wait) {
44870 if (wait === void 0) { wait = 250; }
44872 var timeout = null;
44876 var later = function () {
44877 previous = Date.now();
44879 result = fn.apply(storedContext, storedArgs);
44881 storedContext = null;
44885 return function wrapper() {
44887 for (var _i = 0; _i < arguments.length; _i++) {
44888 args[_i] = arguments[_i];
44890 var now = Date.now();
44891 var remaining = wait - (now - previous);
44892 storedContext = this;
44894 if (remaining <= 0 || remaining > wait) {
44896 clearTimeout(timeout);
44900 result = fn.apply(storedContext, storedArgs);
44902 storedContext = null;
44906 else if (!timeout) {
44907 timeout = window.setTimeout(later, remaining);