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 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();
8275 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8276 e.on('click', _this.sort, _this);
8279 this.mainBody.on("click", this.onClick, this);
8280 this.mainBody.on("dblclick", this.onDblClick, this);
8282 // why is this done????? = it breaks dialogs??
8283 //this.parent().el.setStyle('position', 'relative');
8287 this.footer.parentId = this.id;
8288 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8291 this.el.select('tfoot tr td').first().addClass('hide');
8296 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8299 this.store.on('load', this.onLoad, this);
8300 this.store.on('beforeload', this.onBeforeLoad, this);
8301 this.store.on('update', this.onUpdate, this);
8302 this.store.on('add', this.onAdd, this);
8303 this.store.on("clear", this.clear, this);
8305 this.el.on("contextmenu", this.onContextMenu, this);
8307 this.mainBody.on('scroll', this.onBodyScroll, this);
8309 this.cm.on("headerchange", this.onHeaderChange, this);
8311 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8315 onContextMenu : function(e, t)
8317 this.processEvent("contextmenu", e);
8320 processEvent : function(name, e)
8322 if (name != 'touchstart' ) {
8323 this.fireEvent(name, e);
8326 var t = e.getTarget();
8328 var cell = Roo.get(t);
8334 if(cell.findParent('tfoot', false, true)){
8338 if(cell.findParent('thead', false, true)){
8340 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8341 cell = Roo.get(t).findParent('th', false, true);
8343 Roo.log("failed to find th in thead?");
8344 Roo.log(e.getTarget());
8349 var cellIndex = cell.dom.cellIndex;
8351 var ename = name == 'touchstart' ? 'click' : name;
8352 this.fireEvent("header" + ename, this, cellIndex, e);
8357 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8358 cell = Roo.get(t).findParent('td', false, true);
8360 Roo.log("failed to find th in tbody?");
8361 Roo.log(e.getTarget());
8366 var row = cell.findParent('tr', false, true);
8367 var cellIndex = cell.dom.cellIndex;
8368 var rowIndex = row.dom.rowIndex - 1;
8372 this.fireEvent("row" + name, this, rowIndex, e);
8376 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8382 onMouseover : function(e, el)
8384 var cell = Roo.get(el);
8390 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8391 cell = cell.findParent('td', false, true);
8394 var row = cell.findParent('tr', false, true);
8395 var cellIndex = cell.dom.cellIndex;
8396 var rowIndex = row.dom.rowIndex - 1; // start from 0
8398 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8402 onMouseout : function(e, el)
8404 var cell = Roo.get(el);
8410 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8411 cell = cell.findParent('td', false, true);
8414 var row = cell.findParent('tr', false, true);
8415 var cellIndex = cell.dom.cellIndex;
8416 var rowIndex = row.dom.rowIndex - 1; // start from 0
8418 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8422 onClick : function(e, el)
8424 var cell = Roo.get(el);
8426 if(!cell || (!this.cellSelection && !this.rowSelection)){
8430 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8431 cell = cell.findParent('td', false, true);
8434 if(!cell || typeof(cell) == 'undefined'){
8438 var row = cell.findParent('tr', false, true);
8440 if(!row || typeof(row) == 'undefined'){
8444 var cellIndex = cell.dom.cellIndex;
8445 var rowIndex = this.getRowIndex(row);
8447 // why??? - should these not be based on SelectionModel?
8448 if(this.cellSelection){
8449 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8452 if(this.rowSelection){
8453 this.fireEvent('rowclick', this, row, rowIndex, e);
8459 onDblClick : function(e,el)
8461 var cell = Roo.get(el);
8463 if(!cell || (!this.cellSelection && !this.rowSelection)){
8467 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8468 cell = cell.findParent('td', false, true);
8471 if(!cell || typeof(cell) == 'undefined'){
8475 var row = cell.findParent('tr', false, true);
8477 if(!row || typeof(row) == 'undefined'){
8481 var cellIndex = cell.dom.cellIndex;
8482 var rowIndex = this.getRowIndex(row);
8484 if(this.cellSelection){
8485 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8488 if(this.rowSelection){
8489 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8493 sort : function(e,el)
8495 var col = Roo.get(el);
8497 if(!col.hasClass('sortable')){
8501 var sort = col.attr('sort');
8504 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8508 this.store.sortInfo = {field : sort, direction : dir};
8511 Roo.log("calling footer first");
8512 this.footer.onClick('first');
8515 this.store.load({ params : { start : 0 } });
8519 renderHeader : function()
8527 this.totalWidth = 0;
8529 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8531 var config = cm.config[i];
8535 cls : 'x-hcol-' + i,
8537 html: cm.getColumnHeader(i)
8542 if(typeof(config.sortable) != 'undefined' && config.sortable){
8544 c.html = '<i class="glyphicon"></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(['glyphicon-arrow-up', 'glyphicon-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(['glyphicon-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(['glyphicon-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 cls : rowcfg.rowClass + ' x-col-' + i,
8887 html: (typeof(value) === 'object') ? '' : value
8894 if(typeof(config.colspan) != 'undefined'){
8895 td.colspan = config.colspan;
8898 if(typeof(config.hidden) != 'undefined' && config.hidden){
8899 td.style += ' display:none;';
8902 if(typeof(config.align) != 'undefined' && config.align.length){
8903 td.style += ' text-align:' + config.align + ';';
8905 if(typeof(config.valign) != 'undefined' && config.valign.length){
8906 td.style += ' vertical-align:' + config.valign + ';';
8909 if(typeof(config.width) != 'undefined'){
8910 td.style += ' width:' + config.width + 'px;';
8913 if(typeof(config.cursor) != 'undefined'){
8914 td.style += ' cursor:' + config.cursor + ';';
8917 if(typeof(config.cls) != 'undefined'){
8918 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8921 ['xs','sm','md','lg'].map(function(size){
8923 if(typeof(config[size]) == 'undefined'){
8929 if (!config[size]) { // 0 = hidden
8930 // BS 4 '0' is treated as hide that column and below.
8931 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8935 td.cls += ' col-' + size + '-' + config[size] + (
8936 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8946 row.cellObjects = cellObjects;
8954 onBeforeLoad : function()
8963 this.el.select('tbody', true).first().dom.innerHTML = '';
8966 * Show or hide a row.
8967 * @param {Number} rowIndex to show or hide
8968 * @param {Boolean} state hide
8970 setRowVisibility : function(rowIndex, state)
8972 var bt = this.mainBody.dom;
8974 var rows = this.el.select('tbody > tr', true).elements;
8976 if(typeof(rows[rowIndex]) == 'undefined'){
8979 rows[rowIndex].dom.style.display = state ? '' : 'none';
8983 getSelectionModel : function(){
8985 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8987 return this.selModel;
8990 * Render the Roo.bootstrap object from renderder
8992 renderCellObject : function(r)
8996 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8998 var t = r.cfg.render(r.container);
9001 Roo.each(r.cfg.cn, function(c){
9003 container: t.getChildContainer(),
9006 _this.renderCellObject(child);
9011 getRowIndex : function(row)
9015 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9026 * Returns the grid's underlying element = used by panel.Grid
9027 * @return {Element} The element
9029 getGridEl : function(){
9033 * Forces a resize - used by panel.Grid
9034 * @return {Element} The element
9036 autoSize : function()
9038 //var ctr = Roo.get(this.container.dom.parentElement);
9039 var ctr = Roo.get(this.el.dom);
9041 var thd = this.getGridEl().select('thead',true).first();
9042 var tbd = this.getGridEl().select('tbody', true).first();
9043 var tfd = this.getGridEl().select('tfoot', true).first();
9045 var cw = ctr.getWidth();
9046 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9050 tbd.setWidth(ctr.getWidth());
9051 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9052 // this needs fixing for various usage - currently only hydra job advers I think..
9054 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9056 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9059 cw = Math.max(cw, this.totalWidth);
9060 this.getGridEl().select('tbody tr',true).setWidth(cw);
9062 // resize 'expandable coloumn?
9064 return; // we doe not have a view in this design..
9067 onBodyScroll: function()
9069 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9071 this.mainHead.setStyle({
9072 'position' : 'relative',
9073 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9079 var scrollHeight = this.mainBody.dom.scrollHeight;
9081 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9083 var height = this.mainBody.getHeight();
9085 if(scrollHeight - height == scrollTop) {
9087 var total = this.ds.getTotalCount();
9089 if(this.footer.cursor + this.footer.pageSize < total){
9091 this.footer.ds.load({
9093 start : this.footer.cursor + this.footer.pageSize,
9094 limit : this.footer.pageSize
9104 onHeaderChange : function()
9106 var header = this.renderHeader();
9107 var table = this.el.select('table', true).first();
9109 this.mainHead.remove();
9110 this.mainHead = table.createChild(header, this.mainBody, false);
9113 onHiddenChange : function(colModel, colIndex, hidden)
9115 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9116 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9118 this.CSS.updateRule(thSelector, "display", "");
9119 this.CSS.updateRule(tdSelector, "display", "");
9122 this.CSS.updateRule(thSelector, "display", "none");
9123 this.CSS.updateRule(tdSelector, "display", "none");
9126 this.onHeaderChange();
9130 setColumnWidth: function(col_index, width)
9132 // width = "md-2 xs-2..."
9133 if(!this.colModel.config[col_index]) {
9137 var w = width.split(" ");
9139 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9141 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9144 for(var j = 0; j < w.length; j++) {
9150 var size_cls = w[j].split("-");
9152 if(!Number.isInteger(size_cls[1] * 1)) {
9156 if(!this.colModel.config[col_index][size_cls[0]]) {
9160 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9164 h_row[0].classList.replace(
9165 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9166 "col-"+size_cls[0]+"-"+size_cls[1]
9169 for(var i = 0; i < rows.length; i++) {
9171 var size_cls = w[j].split("-");
9173 if(!Number.isInteger(size_cls[1] * 1)) {
9177 if(!this.colModel.config[col_index][size_cls[0]]) {
9181 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9185 rows[i].classList.replace(
9186 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9187 "col-"+size_cls[0]+"-"+size_cls[1]
9191 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9206 * @class Roo.bootstrap.TableCell
9207 * @extends Roo.bootstrap.Component
9208 * Bootstrap TableCell class
9209 * @cfg {String} html cell contain text
9210 * @cfg {String} cls cell class
9211 * @cfg {String} tag cell tag (td|th) default td
9212 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9213 * @cfg {String} align Aligns the content in a cell
9214 * @cfg {String} axis Categorizes cells
9215 * @cfg {String} bgcolor Specifies the background color of a cell
9216 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9217 * @cfg {Number} colspan Specifies the number of columns a cell should span
9218 * @cfg {String} headers Specifies one or more header cells a cell is related to
9219 * @cfg {Number} height Sets the height of a cell
9220 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9221 * @cfg {Number} rowspan Sets the number of rows a cell should span
9222 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9223 * @cfg {String} valign Vertical aligns the content in a cell
9224 * @cfg {Number} width Specifies the width of a cell
9227 * Create a new TableCell
9228 * @param {Object} config The config object
9231 Roo.bootstrap.TableCell = function(config){
9232 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9235 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9255 getAutoCreate : function(){
9256 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9276 cfg.align=this.align
9282 cfg.bgcolor=this.bgcolor
9285 cfg.charoff=this.charoff
9288 cfg.colspan=this.colspan
9291 cfg.headers=this.headers
9294 cfg.height=this.height
9297 cfg.nowrap=this.nowrap
9300 cfg.rowspan=this.rowspan
9303 cfg.scope=this.scope
9306 cfg.valign=this.valign
9309 cfg.width=this.width
9328 * @class Roo.bootstrap.TableRow
9329 * @extends Roo.bootstrap.Component
9330 * Bootstrap TableRow class
9331 * @cfg {String} cls row class
9332 * @cfg {String} align Aligns the content in a table row
9333 * @cfg {String} bgcolor Specifies a background color for a table row
9334 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9335 * @cfg {String} valign Vertical aligns the content in a table row
9338 * Create a new TableRow
9339 * @param {Object} config The config object
9342 Roo.bootstrap.TableRow = function(config){
9343 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9346 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9354 getAutoCreate : function(){
9355 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9365 cfg.align = this.align;
9368 cfg.bgcolor = this.bgcolor;
9371 cfg.charoff = this.charoff;
9374 cfg.valign = this.valign;
9392 * @class Roo.bootstrap.TableBody
9393 * @extends Roo.bootstrap.Component
9394 * Bootstrap TableBody class
9395 * @cfg {String} cls element class
9396 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9397 * @cfg {String} align Aligns the content inside the element
9398 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9399 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9402 * Create a new TableBody
9403 * @param {Object} config The config object
9406 Roo.bootstrap.TableBody = function(config){
9407 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9410 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9418 getAutoCreate : function(){
9419 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9433 cfg.align = this.align;
9436 cfg.charoff = this.charoff;
9439 cfg.valign = this.valign;
9446 // initEvents : function()
9453 // this.store = Roo.factory(this.store, Roo.data);
9454 // this.store.on('load', this.onLoad, this);
9456 // this.store.load();
9460 // onLoad: function ()
9462 // this.fireEvent('load', this);
9472 * Ext JS Library 1.1.1
9473 * Copyright(c) 2006-2007, Ext JS, LLC.
9475 * Originally Released Under LGPL - original licence link has changed is not relivant.
9478 * <script type="text/javascript">
9481 // as we use this in bootstrap.
9482 Roo.namespace('Roo.form');
9484 * @class Roo.form.Action
9485 * Internal Class used to handle form actions
9487 * @param {Roo.form.BasicForm} el The form element or its id
9488 * @param {Object} config Configuration options
9493 // define the action interface
9494 Roo.form.Action = function(form, options){
9496 this.options = options || {};
9499 * Client Validation Failed
9502 Roo.form.Action.CLIENT_INVALID = 'client';
9504 * Server Validation Failed
9507 Roo.form.Action.SERVER_INVALID = 'server';
9509 * Connect to Server Failed
9512 Roo.form.Action.CONNECT_FAILURE = 'connect';
9514 * Reading Data from Server Failed
9517 Roo.form.Action.LOAD_FAILURE = 'load';
9519 Roo.form.Action.prototype = {
9521 failureType : undefined,
9522 response : undefined,
9526 run : function(options){
9531 success : function(response){
9536 handleResponse : function(response){
9540 // default connection failure
9541 failure : function(response){
9543 this.response = response;
9544 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9545 this.form.afterAction(this, false);
9548 processResponse : function(response){
9549 this.response = response;
9550 if(!response.responseText){
9553 this.result = this.handleResponse(response);
9557 // utility functions used internally
9558 getUrl : function(appendParams){
9559 var url = this.options.url || this.form.url || this.form.el.dom.action;
9561 var p = this.getParams();
9563 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9569 getMethod : function(){
9570 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9573 getParams : function(){
9574 var bp = this.form.baseParams;
9575 var p = this.options.params;
9577 if(typeof p == "object"){
9578 p = Roo.urlEncode(Roo.applyIf(p, bp));
9579 }else if(typeof p == 'string' && bp){
9580 p += '&' + Roo.urlEncode(bp);
9583 p = Roo.urlEncode(bp);
9588 createCallback : function(){
9590 success: this.success,
9591 failure: this.failure,
9593 timeout: (this.form.timeout*1000),
9594 upload: this.form.fileUpload ? this.success : undefined
9599 Roo.form.Action.Submit = function(form, options){
9600 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9603 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9606 haveProgress : false,
9607 uploadComplete : false,
9609 // uploadProgress indicator.
9610 uploadProgress : function()
9612 if (!this.form.progressUrl) {
9616 if (!this.haveProgress) {
9617 Roo.MessageBox.progress("Uploading", "Uploading");
9619 if (this.uploadComplete) {
9620 Roo.MessageBox.hide();
9624 this.haveProgress = true;
9626 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9628 var c = new Roo.data.Connection();
9630 url : this.form.progressUrl,
9635 success : function(req){
9636 //console.log(data);
9640 rdata = Roo.decode(req.responseText)
9642 Roo.log("Invalid data from server..");
9646 if (!rdata || !rdata.success) {
9648 Roo.MessageBox.alert(Roo.encode(rdata));
9651 var data = rdata.data;
9653 if (this.uploadComplete) {
9654 Roo.MessageBox.hide();
9659 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9660 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9663 this.uploadProgress.defer(2000,this);
9666 failure: function(data) {
9667 Roo.log('progress url failed ');
9678 // run get Values on the form, so it syncs any secondary forms.
9679 this.form.getValues();
9681 var o = this.options;
9682 var method = this.getMethod();
9683 var isPost = method == 'POST';
9684 if(o.clientValidation === false || this.form.isValid()){
9686 if (this.form.progressUrl) {
9687 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9688 (new Date() * 1) + '' + Math.random());
9693 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9694 form:this.form.el.dom,
9695 url:this.getUrl(!isPost),
9697 params:isPost ? this.getParams() : null,
9698 isUpload: this.form.fileUpload,
9699 formData : this.form.formData
9702 this.uploadProgress();
9704 }else if (o.clientValidation !== false){ // client validation failed
9705 this.failureType = Roo.form.Action.CLIENT_INVALID;
9706 this.form.afterAction(this, false);
9710 success : function(response)
9712 this.uploadComplete= true;
9713 if (this.haveProgress) {
9714 Roo.MessageBox.hide();
9718 var result = this.processResponse(response);
9719 if(result === true || result.success){
9720 this.form.afterAction(this, true);
9724 this.form.markInvalid(result.errors);
9725 this.failureType = Roo.form.Action.SERVER_INVALID;
9727 this.form.afterAction(this, false);
9729 failure : function(response)
9731 this.uploadComplete= true;
9732 if (this.haveProgress) {
9733 Roo.MessageBox.hide();
9736 this.response = response;
9737 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9738 this.form.afterAction(this, false);
9741 handleResponse : function(response){
9742 if(this.form.errorReader){
9743 var rs = this.form.errorReader.read(response);
9746 for(var i = 0, len = rs.records.length; i < len; i++) {
9747 var r = rs.records[i];
9751 if(errors.length < 1){
9755 success : rs.success,
9761 ret = Roo.decode(response.responseText);
9765 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9775 Roo.form.Action.Load = function(form, options){
9776 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9777 this.reader = this.form.reader;
9780 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9785 Roo.Ajax.request(Roo.apply(
9786 this.createCallback(), {
9787 method:this.getMethod(),
9788 url:this.getUrl(false),
9789 params:this.getParams()
9793 success : function(response){
9795 var result = this.processResponse(response);
9796 if(result === true || !result.success || !result.data){
9797 this.failureType = Roo.form.Action.LOAD_FAILURE;
9798 this.form.afterAction(this, false);
9801 this.form.clearInvalid();
9802 this.form.setValues(result.data);
9803 this.form.afterAction(this, true);
9806 handleResponse : function(response){
9807 if(this.form.reader){
9808 var rs = this.form.reader.read(response);
9809 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9811 success : rs.success,
9815 return Roo.decode(response.responseText);
9819 Roo.form.Action.ACTION_TYPES = {
9820 'load' : Roo.form.Action.Load,
9821 'submit' : Roo.form.Action.Submit
9830 * @class Roo.bootstrap.Form
9831 * @extends Roo.bootstrap.Component
9832 * Bootstrap Form class
9833 * @cfg {String} method GET | POST (default POST)
9834 * @cfg {String} labelAlign top | left (default top)
9835 * @cfg {String} align left | right - for navbars
9836 * @cfg {Boolean} loadMask load mask when submit (default true)
9841 * @param {Object} config The config object
9845 Roo.bootstrap.Form = function(config){
9847 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9849 Roo.bootstrap.Form.popover.apply();
9853 * @event clientvalidation
9854 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9855 * @param {Form} this
9856 * @param {Boolean} valid true if the form has passed client-side validation
9858 clientvalidation: true,
9860 * @event beforeaction
9861 * Fires before any action is performed. Return false to cancel the action.
9862 * @param {Form} this
9863 * @param {Action} action The action to be performed
9867 * @event actionfailed
9868 * Fires when an action fails.
9869 * @param {Form} this
9870 * @param {Action} action The action that failed
9872 actionfailed : true,
9874 * @event actioncomplete
9875 * Fires when an action is completed.
9876 * @param {Form} this
9877 * @param {Action} action The action that completed
9879 actioncomplete : true
9883 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9886 * @cfg {String} method
9887 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9892 * The URL to use for form actions if one isn't supplied in the action options.
9895 * @cfg {Boolean} fileUpload
9896 * Set to true if this form is a file upload.
9900 * @cfg {Object} baseParams
9901 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9905 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9909 * @cfg {Sting} align (left|right) for navbar forms
9914 activeAction : null,
9917 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9918 * element by passing it or its id or mask the form itself by passing in true.
9921 waitMsgTarget : false,
9926 * @cfg {Boolean} errorMask (true|false) default false
9931 * @cfg {Number} maskOffset Default 100
9936 * @cfg {Boolean} maskBody
9940 getAutoCreate : function(){
9944 method : this.method || 'POST',
9945 id : this.id || Roo.id(),
9948 if (this.parent().xtype.match(/^Nav/)) {
9949 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9953 if (this.labelAlign == 'left' ) {
9954 cfg.cls += ' form-horizontal';
9960 initEvents : function()
9962 this.el.on('submit', this.onSubmit, this);
9963 // this was added as random key presses on the form where triggering form submit.
9964 this.el.on('keypress', function(e) {
9965 if (e.getCharCode() != 13) {
9968 // we might need to allow it for textareas.. and some other items.
9969 // check e.getTarget().
9971 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9975 Roo.log("keypress blocked");
9983 onSubmit : function(e){
9988 * Returns true if client-side validation on the form is successful.
9991 isValid : function(){
9992 var items = this.getItems();
9996 items.each(function(f){
10002 Roo.log('invalid field: ' + f.name);
10006 if(!target && f.el.isVisible(true)){
10012 if(this.errorMask && !valid){
10013 Roo.bootstrap.Form.popover.mask(this, target);
10020 * Returns true if any fields in this form have changed since their original load.
10023 isDirty : function(){
10025 var items = this.getItems();
10026 items.each(function(f){
10036 * Performs a predefined action (submit or load) or custom actions you define on this form.
10037 * @param {String} actionName The name of the action type
10038 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10039 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10040 * accept other config options):
10042 Property Type Description
10043 ---------------- --------------- ----------------------------------------------------------------------------------
10044 url String The url for the action (defaults to the form's url)
10045 method String The form method to use (defaults to the form's method, or POST if not defined)
10046 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10047 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10048 validate the form on the client (defaults to false)
10050 * @return {BasicForm} this
10052 doAction : function(action, options){
10053 if(typeof action == 'string'){
10054 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10056 if(this.fireEvent('beforeaction', this, action) !== false){
10057 this.beforeAction(action);
10058 action.run.defer(100, action);
10064 beforeAction : function(action){
10065 var o = action.options;
10070 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10072 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10075 // not really supported yet.. ??
10077 //if(this.waitMsgTarget === true){
10078 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10079 //}else if(this.waitMsgTarget){
10080 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10081 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10083 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10089 afterAction : function(action, success){
10090 this.activeAction = null;
10091 var o = action.options;
10096 Roo.get(document.body).unmask();
10102 //if(this.waitMsgTarget === true){
10103 // this.el.unmask();
10104 //}else if(this.waitMsgTarget){
10105 // this.waitMsgTarget.unmask();
10107 // Roo.MessageBox.updateProgress(1);
10108 // Roo.MessageBox.hide();
10115 Roo.callback(o.success, o.scope, [this, action]);
10116 this.fireEvent('actioncomplete', this, action);
10120 // failure condition..
10121 // we have a scenario where updates need confirming.
10122 // eg. if a locking scenario exists..
10123 // we look for { errors : { needs_confirm : true }} in the response.
10125 (typeof(action.result) != 'undefined') &&
10126 (typeof(action.result.errors) != 'undefined') &&
10127 (typeof(action.result.errors.needs_confirm) != 'undefined')
10130 Roo.log("not supported yet");
10133 Roo.MessageBox.confirm(
10134 "Change requires confirmation",
10135 action.result.errorMsg,
10140 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10150 Roo.callback(o.failure, o.scope, [this, action]);
10151 // show an error message if no failed handler is set..
10152 if (!this.hasListener('actionfailed')) {
10153 Roo.log("need to add dialog support");
10155 Roo.MessageBox.alert("Error",
10156 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10157 action.result.errorMsg :
10158 "Saving Failed, please check your entries or try again"
10163 this.fireEvent('actionfailed', this, action);
10168 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10169 * @param {String} id The value to search for
10172 findField : function(id){
10173 var items = this.getItems();
10174 var field = items.get(id);
10176 items.each(function(f){
10177 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10184 return field || null;
10187 * Mark fields in this form invalid in bulk.
10188 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10189 * @return {BasicForm} this
10191 markInvalid : function(errors){
10192 if(errors instanceof Array){
10193 for(var i = 0, len = errors.length; i < len; i++){
10194 var fieldError = errors[i];
10195 var f = this.findField(fieldError.id);
10197 f.markInvalid(fieldError.msg);
10203 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10204 field.markInvalid(errors[id]);
10208 //Roo.each(this.childForms || [], function (f) {
10209 // f.markInvalid(errors);
10216 * Set values for fields in this form in bulk.
10217 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10218 * @return {BasicForm} this
10220 setValues : function(values){
10221 if(values instanceof Array){ // array of objects
10222 for(var i = 0, len = values.length; i < len; i++){
10224 var f = this.findField(v.id);
10226 f.setValue(v.value);
10227 if(this.trackResetOnLoad){
10228 f.originalValue = f.getValue();
10232 }else{ // object hash
10235 if(typeof values[id] != 'function' && (field = this.findField(id))){
10237 if (field.setFromData &&
10238 field.valueField &&
10239 field.displayField &&
10240 // combos' with local stores can
10241 // be queried via setValue()
10242 // to set their value..
10243 (field.store && !field.store.isLocal)
10247 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10248 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10249 field.setFromData(sd);
10251 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10253 field.setFromData(values);
10256 field.setValue(values[id]);
10260 if(this.trackResetOnLoad){
10261 field.originalValue = field.getValue();
10267 //Roo.each(this.childForms || [], function (f) {
10268 // f.setValues(values);
10275 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10276 * they are returned as an array.
10277 * @param {Boolean} asString
10280 getValues : function(asString){
10281 //if (this.childForms) {
10282 // copy values from the child forms
10283 // Roo.each(this.childForms, function (f) {
10284 // this.setValues(f.getValues());
10290 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10291 if(asString === true){
10294 return Roo.urlDecode(fs);
10298 * Returns the fields in this form as an object with key/value pairs.
10299 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10302 getFieldValues : function(with_hidden)
10304 var items = this.getItems();
10306 items.each(function(f){
10308 if (!f.getName()) {
10312 var v = f.getValue();
10314 if (f.inputType =='radio') {
10315 if (typeof(ret[f.getName()]) == 'undefined') {
10316 ret[f.getName()] = ''; // empty..
10319 if (!f.el.dom.checked) {
10323 v = f.el.dom.value;
10327 if(f.xtype == 'MoneyField'){
10328 ret[f.currencyName] = f.getCurrency();
10331 // not sure if this supported any more..
10332 if ((typeof(v) == 'object') && f.getRawValue) {
10333 v = f.getRawValue() ; // dates..
10335 // combo boxes where name != hiddenName...
10336 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10337 ret[f.name] = f.getRawValue();
10339 ret[f.getName()] = v;
10346 * Clears all invalid messages in this form.
10347 * @return {BasicForm} this
10349 clearInvalid : function(){
10350 var items = this.getItems();
10352 items.each(function(f){
10360 * Resets this form.
10361 * @return {BasicForm} this
10363 reset : function(){
10364 var items = this.getItems();
10365 items.each(function(f){
10369 Roo.each(this.childForms || [], function (f) {
10377 getItems : function()
10379 var r=new Roo.util.MixedCollection(false, function(o){
10380 return o.id || (o.id = Roo.id());
10382 var iter = function(el) {
10389 Roo.each(el.items,function(e) {
10398 hideFields : function(items)
10400 Roo.each(items, function(i){
10402 var f = this.findField(i);
10413 showFields : function(items)
10415 Roo.each(items, function(i){
10417 var f = this.findField(i);
10430 Roo.apply(Roo.bootstrap.Form, {
10446 intervalID : false,
10452 if(this.isApplied){
10457 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10458 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10459 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10460 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10463 this.maskEl.top.enableDisplayMode("block");
10464 this.maskEl.left.enableDisplayMode("block");
10465 this.maskEl.bottom.enableDisplayMode("block");
10466 this.maskEl.right.enableDisplayMode("block");
10468 this.toolTip = new Roo.bootstrap.Tooltip({
10469 cls : 'roo-form-error-popover',
10471 'left' : ['r-l', [-2,0], 'right'],
10472 'right' : ['l-r', [2,0], 'left'],
10473 'bottom' : ['tl-bl', [0,2], 'top'],
10474 'top' : [ 'bl-tl', [0,-2], 'bottom']
10478 this.toolTip.render(Roo.get(document.body));
10480 this.toolTip.el.enableDisplayMode("block");
10482 Roo.get(document.body).on('click', function(){
10486 Roo.get(document.body).on('touchstart', function(){
10490 this.isApplied = true
10493 mask : function(form, target)
10497 this.target = target;
10499 if(!this.form.errorMask || !target.el){
10503 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10505 Roo.log(scrollable);
10507 var ot = this.target.el.calcOffsetsTo(scrollable);
10509 var scrollTo = ot[1] - this.form.maskOffset;
10511 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10513 scrollable.scrollTo('top', scrollTo);
10515 var box = this.target.el.getBox();
10517 var zIndex = Roo.bootstrap.Modal.zIndex++;
10520 this.maskEl.top.setStyle('position', 'absolute');
10521 this.maskEl.top.setStyle('z-index', zIndex);
10522 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10523 this.maskEl.top.setLeft(0);
10524 this.maskEl.top.setTop(0);
10525 this.maskEl.top.show();
10527 this.maskEl.left.setStyle('position', 'absolute');
10528 this.maskEl.left.setStyle('z-index', zIndex);
10529 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10530 this.maskEl.left.setLeft(0);
10531 this.maskEl.left.setTop(box.y - this.padding);
10532 this.maskEl.left.show();
10534 this.maskEl.bottom.setStyle('position', 'absolute');
10535 this.maskEl.bottom.setStyle('z-index', zIndex);
10536 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10537 this.maskEl.bottom.setLeft(0);
10538 this.maskEl.bottom.setTop(box.bottom + this.padding);
10539 this.maskEl.bottom.show();
10541 this.maskEl.right.setStyle('position', 'absolute');
10542 this.maskEl.right.setStyle('z-index', zIndex);
10543 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10544 this.maskEl.right.setLeft(box.right + this.padding);
10545 this.maskEl.right.setTop(box.y - this.padding);
10546 this.maskEl.right.show();
10548 this.toolTip.bindEl = this.target.el;
10550 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10552 var tip = this.target.blankText;
10554 if(this.target.getValue() !== '' ) {
10556 if (this.target.invalidText.length) {
10557 tip = this.target.invalidText;
10558 } else if (this.target.regexText.length){
10559 tip = this.target.regexText;
10563 this.toolTip.show(tip);
10565 this.intervalID = window.setInterval(function() {
10566 Roo.bootstrap.Form.popover.unmask();
10569 window.onwheel = function(){ return false;};
10571 (function(){ this.isMasked = true; }).defer(500, this);
10575 unmask : function()
10577 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10581 this.maskEl.top.setStyle('position', 'absolute');
10582 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10583 this.maskEl.top.hide();
10585 this.maskEl.left.setStyle('position', 'absolute');
10586 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10587 this.maskEl.left.hide();
10589 this.maskEl.bottom.setStyle('position', 'absolute');
10590 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10591 this.maskEl.bottom.hide();
10593 this.maskEl.right.setStyle('position', 'absolute');
10594 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10595 this.maskEl.right.hide();
10597 this.toolTip.hide();
10599 this.toolTip.el.hide();
10601 window.onwheel = function(){ return true;};
10603 if(this.intervalID){
10604 window.clearInterval(this.intervalID);
10605 this.intervalID = false;
10608 this.isMasked = false;
10618 * Ext JS Library 1.1.1
10619 * Copyright(c) 2006-2007, Ext JS, LLC.
10621 * Originally Released Under LGPL - original licence link has changed is not relivant.
10624 * <script type="text/javascript">
10627 * @class Roo.form.VTypes
10628 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10631 Roo.form.VTypes = function(){
10632 // closure these in so they are only created once.
10633 var alpha = /^[a-zA-Z_]+$/;
10634 var alphanum = /^[a-zA-Z0-9_]+$/;
10635 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10636 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10638 // All these messages and functions are configurable
10641 * The function used to validate email addresses
10642 * @param {String} value The email address
10644 'email' : function(v){
10645 return email.test(v);
10648 * The error text to display when the email validation function returns false
10651 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10653 * The keystroke filter mask to be applied on email input
10656 'emailMask' : /[a-z0-9_\.\-@]/i,
10659 * The function used to validate URLs
10660 * @param {String} value The URL
10662 'url' : function(v){
10663 return url.test(v);
10666 * The error text to display when the url validation function returns false
10669 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10672 * The function used to validate alpha values
10673 * @param {String} value The value
10675 'alpha' : function(v){
10676 return alpha.test(v);
10679 * The error text to display when the alpha validation function returns false
10682 'alphaText' : 'This field should only contain letters and _',
10684 * The keystroke filter mask to be applied on alpha input
10687 'alphaMask' : /[a-z_]/i,
10690 * The function used to validate alphanumeric values
10691 * @param {String} value The value
10693 'alphanum' : function(v){
10694 return alphanum.test(v);
10697 * The error text to display when the alphanumeric validation function returns false
10700 'alphanumText' : 'This field should only contain letters, numbers and _',
10702 * The keystroke filter mask to be applied on alphanumeric input
10705 'alphanumMask' : /[a-z0-9_]/i
10715 * @class Roo.bootstrap.Input
10716 * @extends Roo.bootstrap.Component
10717 * Bootstrap Input class
10718 * @cfg {Boolean} disabled is it disabled
10719 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10720 * @cfg {String} name name of the input
10721 * @cfg {string} fieldLabel - the label associated
10722 * @cfg {string} placeholder - placeholder to put in text.
10723 * @cfg {string} before - input group add on before
10724 * @cfg {string} after - input group add on after
10725 * @cfg {string} size - (lg|sm) or leave empty..
10726 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10727 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10728 * @cfg {Number} md colspan out of 12 for computer-sized screens
10729 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10730 * @cfg {string} value default value of the input
10731 * @cfg {Number} labelWidth set the width of label
10732 * @cfg {Number} labellg set the width of label (1-12)
10733 * @cfg {Number} labelmd set the width of label (1-12)
10734 * @cfg {Number} labelsm set the width of label (1-12)
10735 * @cfg {Number} labelxs set the width of label (1-12)
10736 * @cfg {String} labelAlign (top|left)
10737 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10738 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10739 * @cfg {String} indicatorpos (left|right) default left
10740 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10741 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10742 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10744 * @cfg {String} align (left|center|right) Default left
10745 * @cfg {Boolean} forceFeedback (true|false) Default false
10748 * Create a new Input
10749 * @param {Object} config The config object
10752 Roo.bootstrap.Input = function(config){
10754 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10759 * Fires when this field receives input focus.
10760 * @param {Roo.form.Field} this
10765 * Fires when this field loses input focus.
10766 * @param {Roo.form.Field} this
10770 * @event specialkey
10771 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10772 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10773 * @param {Roo.form.Field} this
10774 * @param {Roo.EventObject} e The event object
10779 * Fires just before the field blurs if the field value has changed.
10780 * @param {Roo.form.Field} this
10781 * @param {Mixed} newValue The new value
10782 * @param {Mixed} oldValue The original value
10787 * Fires after the field has been marked as invalid.
10788 * @param {Roo.form.Field} this
10789 * @param {String} msg The validation message
10794 * Fires after the field has been validated with no errors.
10795 * @param {Roo.form.Field} this
10800 * Fires after the key up
10801 * @param {Roo.form.Field} this
10802 * @param {Roo.EventObject} e The event Object
10807 * Fires after the user pastes into input
10808 * @param {Roo.form.Field} this
10809 * @param {Roo.EventObject} e The event Object
10815 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10817 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10818 automatic validation (defaults to "keyup").
10820 validationEvent : "keyup",
10822 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10824 validateOnBlur : true,
10826 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10828 validationDelay : 250,
10830 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10832 focusClass : "x-form-focus", // not needed???
10836 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10838 invalidClass : "has-warning",
10841 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10843 validClass : "has-success",
10846 * @cfg {Boolean} hasFeedback (true|false) default true
10848 hasFeedback : true,
10851 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10853 invalidFeedbackClass : "glyphicon-warning-sign",
10856 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10858 validFeedbackClass : "glyphicon-ok",
10861 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10863 selectOnFocus : false,
10866 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10870 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10875 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10877 disableKeyFilter : false,
10880 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10884 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10888 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10890 blankText : "Please complete this mandatory field",
10893 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10897 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10899 maxLength : Number.MAX_VALUE,
10901 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10903 minLengthText : "The minimum length for this field is {0}",
10905 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10907 maxLengthText : "The maximum length for this field is {0}",
10911 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10912 * If available, this function will be called only after the basic validators all return true, and will be passed the
10913 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10917 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10918 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10919 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10923 * @cfg {String} regexText -- Depricated - use Invalid Text
10928 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10934 autocomplete: false,
10938 inputType : 'text',
10941 placeholder: false,
10946 preventMark: false,
10947 isFormField : true,
10950 labelAlign : false,
10953 formatedValue : false,
10954 forceFeedback : false,
10956 indicatorpos : 'left',
10966 parentLabelAlign : function()
10969 while (parent.parent()) {
10970 parent = parent.parent();
10971 if (typeof(parent.labelAlign) !='undefined') {
10972 return parent.labelAlign;
10979 getAutoCreate : function()
10981 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10987 if(this.inputType != 'hidden'){
10988 cfg.cls = 'form-group' //input-group
10994 type : this.inputType,
10995 value : this.value,
10996 cls : 'form-control',
10997 placeholder : this.placeholder || '',
10998 autocomplete : this.autocomplete || 'new-password'
11000 if (this.inputType == 'file') {
11001 input.style = 'overflow:hidden'; // why not in CSS?
11004 if(this.capture.length){
11005 input.capture = this.capture;
11008 if(this.accept.length){
11009 input.accept = this.accept + "/*";
11013 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11016 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11017 input.maxLength = this.maxLength;
11020 if (this.disabled) {
11021 input.disabled=true;
11024 if (this.readOnly) {
11025 input.readonly=true;
11029 input.name = this.name;
11033 input.cls += ' input-' + this.size;
11037 ['xs','sm','md','lg'].map(function(size){
11038 if (settings[size]) {
11039 cfg.cls += ' col-' + size + '-' + settings[size];
11043 var inputblock = input;
11047 cls: 'glyphicon form-control-feedback'
11050 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11053 cls : 'has-feedback',
11061 if (this.before || this.after) {
11064 cls : 'input-group',
11068 if (this.before && typeof(this.before) == 'string') {
11070 inputblock.cn.push({
11072 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11076 if (this.before && typeof(this.before) == 'object') {
11077 this.before = Roo.factory(this.before);
11079 inputblock.cn.push({
11081 cls : 'roo-input-before input-group-prepend input-group-' +
11082 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11086 inputblock.cn.push(input);
11088 if (this.after && typeof(this.after) == 'string') {
11089 inputblock.cn.push({
11091 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11095 if (this.after && typeof(this.after) == 'object') {
11096 this.after = Roo.factory(this.after);
11098 inputblock.cn.push({
11100 cls : 'roo-input-after input-group-append input-group-' +
11101 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11105 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11106 inputblock.cls += ' has-feedback';
11107 inputblock.cn.push(feedback);
11112 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11113 tooltip : 'This field is required'
11115 if (this.allowBlank ) {
11116 indicator.style = this.allowBlank ? ' display:none' : '';
11118 if (align ==='left' && this.fieldLabel.length) {
11120 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11127 cls : 'control-label col-form-label',
11128 html : this.fieldLabel
11139 var labelCfg = cfg.cn[1];
11140 var contentCfg = cfg.cn[2];
11142 if(this.indicatorpos == 'right'){
11147 cls : 'control-label col-form-label',
11151 html : this.fieldLabel
11165 labelCfg = cfg.cn[0];
11166 contentCfg = cfg.cn[1];
11170 if(this.labelWidth > 12){
11171 labelCfg.style = "width: " + this.labelWidth + 'px';
11174 if(this.labelWidth < 13 && this.labelmd == 0){
11175 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11178 if(this.labellg > 0){
11179 labelCfg.cls += ' col-lg-' + this.labellg;
11180 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11183 if(this.labelmd > 0){
11184 labelCfg.cls += ' col-md-' + this.labelmd;
11185 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11188 if(this.labelsm > 0){
11189 labelCfg.cls += ' col-sm-' + this.labelsm;
11190 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11193 if(this.labelxs > 0){
11194 labelCfg.cls += ' col-xs-' + this.labelxs;
11195 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11199 } else if ( this.fieldLabel.length) {
11206 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11207 tooltip : 'This field is required',
11208 style : this.allowBlank ? ' display:none' : ''
11212 //cls : 'input-group-addon',
11213 html : this.fieldLabel
11221 if(this.indicatorpos == 'right'){
11226 //cls : 'input-group-addon',
11227 html : this.fieldLabel
11232 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11233 tooltip : 'This field is required',
11234 style : this.allowBlank ? ' display:none' : ''
11254 if (this.parentType === 'Navbar' && this.parent().bar) {
11255 cfg.cls += ' navbar-form';
11258 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11259 // on BS4 we do this only if not form
11260 cfg.cls += ' navbar-form';
11268 * return the real input element.
11270 inputEl: function ()
11272 return this.el.select('input.form-control',true).first();
11275 tooltipEl : function()
11277 return this.inputEl();
11280 indicatorEl : function()
11282 if (Roo.bootstrap.version == 4) {
11283 return false; // not enabled in v4 yet.
11286 var indicator = this.el.select('i.roo-required-indicator',true).first();
11296 setDisabled : function(v)
11298 var i = this.inputEl().dom;
11300 i.removeAttribute('disabled');
11304 i.setAttribute('disabled','true');
11306 initEvents : function()
11309 this.inputEl().on("keydown" , this.fireKey, this);
11310 this.inputEl().on("focus", this.onFocus, this);
11311 this.inputEl().on("blur", this.onBlur, this);
11313 this.inputEl().relayEvent('keyup', this);
11314 this.inputEl().relayEvent('paste', this);
11316 this.indicator = this.indicatorEl();
11318 if(this.indicator){
11319 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11322 // reference to original value for reset
11323 this.originalValue = this.getValue();
11324 //Roo.form.TextField.superclass.initEvents.call(this);
11325 if(this.validationEvent == 'keyup'){
11326 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11327 this.inputEl().on('keyup', this.filterValidation, this);
11329 else if(this.validationEvent !== false){
11330 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11333 if(this.selectOnFocus){
11334 this.on("focus", this.preFocus, this);
11337 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11338 this.inputEl().on("keypress", this.filterKeys, this);
11340 this.inputEl().relayEvent('keypress', this);
11343 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11344 this.el.on("click", this.autoSize, this);
11347 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11348 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11351 if (typeof(this.before) == 'object') {
11352 this.before.render(this.el.select('.roo-input-before',true).first());
11354 if (typeof(this.after) == 'object') {
11355 this.after.render(this.el.select('.roo-input-after',true).first());
11358 this.inputEl().on('change', this.onChange, this);
11361 filterValidation : function(e){
11362 if(!e.isNavKeyPress()){
11363 this.validationTask.delay(this.validationDelay);
11367 * Validates the field value
11368 * @return {Boolean} True if the value is valid, else false
11370 validate : function(){
11371 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11372 if(this.disabled || this.validateValue(this.getRawValue())){
11377 this.markInvalid();
11383 * Validates a value according to the field's validation rules and marks the field as invalid
11384 * if the validation fails
11385 * @param {Mixed} value The value to validate
11386 * @return {Boolean} True if the value is valid, else false
11388 validateValue : function(value)
11390 if(this.getVisibilityEl().hasClass('hidden')){
11394 if(value.length < 1) { // if it's blank
11395 if(this.allowBlank){
11401 if(value.length < this.minLength){
11404 if(value.length > this.maxLength){
11408 var vt = Roo.form.VTypes;
11409 if(!vt[this.vtype](value, this)){
11413 if(typeof this.validator == "function"){
11414 var msg = this.validator(value);
11418 if (typeof(msg) == 'string') {
11419 this.invalidText = msg;
11423 if(this.regex && !this.regex.test(value)){
11431 fireKey : function(e){
11432 //Roo.log('field ' + e.getKey());
11433 if(e.isNavKeyPress()){
11434 this.fireEvent("specialkey", this, e);
11437 focus : function (selectText){
11439 this.inputEl().focus();
11440 if(selectText === true){
11441 this.inputEl().dom.select();
11447 onFocus : function(){
11448 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11449 // this.el.addClass(this.focusClass);
11451 if(!this.hasFocus){
11452 this.hasFocus = true;
11453 this.startValue = this.getValue();
11454 this.fireEvent("focus", this);
11458 beforeBlur : Roo.emptyFn,
11462 onBlur : function(){
11464 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11465 //this.el.removeClass(this.focusClass);
11467 this.hasFocus = false;
11468 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11471 var v = this.getValue();
11472 if(String(v) !== String(this.startValue)){
11473 this.fireEvent('change', this, v, this.startValue);
11475 this.fireEvent("blur", this);
11478 onChange : function(e)
11480 var v = this.getValue();
11481 if(String(v) !== String(this.startValue)){
11482 this.fireEvent('change', this, v, this.startValue);
11488 * Resets the current field value to the originally loaded value and clears any validation messages
11490 reset : function(){
11491 this.setValue(this.originalValue);
11495 * Returns the name of the field
11496 * @return {Mixed} name The name field
11498 getName: function(){
11502 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11503 * @return {Mixed} value The field value
11505 getValue : function(){
11507 var v = this.inputEl().getValue();
11512 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11513 * @return {Mixed} value The field value
11515 getRawValue : function(){
11516 var v = this.inputEl().getValue();
11522 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11523 * @param {Mixed} value The value to set
11525 setRawValue : function(v){
11526 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11529 selectText : function(start, end){
11530 var v = this.getRawValue();
11532 start = start === undefined ? 0 : start;
11533 end = end === undefined ? v.length : end;
11534 var d = this.inputEl().dom;
11535 if(d.setSelectionRange){
11536 d.setSelectionRange(start, end);
11537 }else if(d.createTextRange){
11538 var range = d.createTextRange();
11539 range.moveStart("character", start);
11540 range.moveEnd("character", v.length-end);
11547 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11548 * @param {Mixed} value The value to set
11550 setValue : function(v){
11553 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11559 processValue : function(value){
11560 if(this.stripCharsRe){
11561 var newValue = value.replace(this.stripCharsRe, '');
11562 if(newValue !== value){
11563 this.setRawValue(newValue);
11570 preFocus : function(){
11572 if(this.selectOnFocus){
11573 this.inputEl().dom.select();
11576 filterKeys : function(e){
11577 var k = e.getKey();
11578 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11581 var c = e.getCharCode(), cc = String.fromCharCode(c);
11582 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11585 if(!this.maskRe.test(cc)){
11590 * Clear any invalid styles/messages for this field
11592 clearInvalid : function(){
11594 if(!this.el || this.preventMark){ // not rendered
11599 this.el.removeClass([this.invalidClass, 'is-invalid']);
11601 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11603 var feedback = this.el.select('.form-control-feedback', true).first();
11606 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11611 if(this.indicator){
11612 this.indicator.removeClass('visible');
11613 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11616 this.fireEvent('valid', this);
11620 * Mark this field as valid
11622 markValid : function()
11624 if(!this.el || this.preventMark){ // not rendered...
11628 this.el.removeClass([this.invalidClass, this.validClass]);
11629 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11631 var feedback = this.el.select('.form-control-feedback', true).first();
11634 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11637 if(this.indicator){
11638 this.indicator.removeClass('visible');
11639 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11647 if(this.allowBlank && !this.getRawValue().length){
11650 if (Roo.bootstrap.version == 3) {
11651 this.el.addClass(this.validClass);
11653 this.inputEl().addClass('is-valid');
11656 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11658 var feedback = this.el.select('.form-control-feedback', true).first();
11661 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11662 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11667 this.fireEvent('valid', this);
11671 * Mark this field as invalid
11672 * @param {String} msg The validation message
11674 markInvalid : function(msg)
11676 if(!this.el || this.preventMark){ // not rendered
11680 this.el.removeClass([this.invalidClass, this.validClass]);
11681 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11683 var feedback = this.el.select('.form-control-feedback', true).first();
11686 this.el.select('.form-control-feedback', true).first().removeClass(
11687 [this.invalidFeedbackClass, this.validFeedbackClass]);
11694 if(this.allowBlank && !this.getRawValue().length){
11698 if(this.indicator){
11699 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11700 this.indicator.addClass('visible');
11702 if (Roo.bootstrap.version == 3) {
11703 this.el.addClass(this.invalidClass);
11705 this.inputEl().addClass('is-invalid');
11710 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11712 var feedback = this.el.select('.form-control-feedback', true).first();
11715 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11717 if(this.getValue().length || this.forceFeedback){
11718 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11725 this.fireEvent('invalid', this, msg);
11728 SafariOnKeyDown : function(event)
11730 // this is a workaround for a password hang bug on chrome/ webkit.
11731 if (this.inputEl().dom.type != 'password') {
11735 var isSelectAll = false;
11737 if(this.inputEl().dom.selectionEnd > 0){
11738 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11740 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11741 event.preventDefault();
11746 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11748 event.preventDefault();
11749 // this is very hacky as keydown always get's upper case.
11751 var cc = String.fromCharCode(event.getCharCode());
11752 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11756 adjustWidth : function(tag, w){
11757 tag = tag.toLowerCase();
11758 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11759 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11760 if(tag == 'input'){
11763 if(tag == 'textarea'){
11766 }else if(Roo.isOpera){
11767 if(tag == 'input'){
11770 if(tag == 'textarea'){
11778 setFieldLabel : function(v)
11780 if(!this.rendered){
11784 if(this.indicatorEl()){
11785 var ar = this.el.select('label > span',true);
11787 if (ar.elements.length) {
11788 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11789 this.fieldLabel = v;
11793 var br = this.el.select('label',true);
11795 if(br.elements.length) {
11796 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11797 this.fieldLabel = v;
11801 Roo.log('Cannot Found any of label > span || label in input');
11805 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11806 this.fieldLabel = v;
11821 * @class Roo.bootstrap.TextArea
11822 * @extends Roo.bootstrap.Input
11823 * Bootstrap TextArea class
11824 * @cfg {Number} cols Specifies the visible width of a text area
11825 * @cfg {Number} rows Specifies the visible number of lines in a text area
11826 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11827 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11828 * @cfg {string} html text
11831 * Create a new TextArea
11832 * @param {Object} config The config object
11835 Roo.bootstrap.TextArea = function(config){
11836 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11840 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11850 getAutoCreate : function(){
11852 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11858 if(this.inputType != 'hidden'){
11859 cfg.cls = 'form-group' //input-group
11867 value : this.value || '',
11868 html: this.html || '',
11869 cls : 'form-control',
11870 placeholder : this.placeholder || ''
11874 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11875 input.maxLength = this.maxLength;
11879 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11883 input.cols = this.cols;
11886 if (this.readOnly) {
11887 input.readonly = true;
11891 input.name = this.name;
11895 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11899 ['xs','sm','md','lg'].map(function(size){
11900 if (settings[size]) {
11901 cfg.cls += ' col-' + size + '-' + settings[size];
11905 var inputblock = input;
11907 if(this.hasFeedback && !this.allowBlank){
11911 cls: 'glyphicon form-control-feedback'
11915 cls : 'has-feedback',
11924 if (this.before || this.after) {
11927 cls : 'input-group',
11931 inputblock.cn.push({
11933 cls : 'input-group-addon',
11938 inputblock.cn.push(input);
11940 if(this.hasFeedback && !this.allowBlank){
11941 inputblock.cls += ' has-feedback';
11942 inputblock.cn.push(feedback);
11946 inputblock.cn.push({
11948 cls : 'input-group-addon',
11955 if (align ==='left' && this.fieldLabel.length) {
11960 cls : 'control-label',
11961 html : this.fieldLabel
11972 if(this.labelWidth > 12){
11973 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11976 if(this.labelWidth < 13 && this.labelmd == 0){
11977 this.labelmd = this.labelWidth;
11980 if(this.labellg > 0){
11981 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11982 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11985 if(this.labelmd > 0){
11986 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11987 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11990 if(this.labelsm > 0){
11991 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11992 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11995 if(this.labelxs > 0){
11996 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11997 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12000 } else if ( this.fieldLabel.length) {
12005 //cls : 'input-group-addon',
12006 html : this.fieldLabel
12024 if (this.disabled) {
12025 input.disabled=true;
12032 * return the real textarea element.
12034 inputEl: function ()
12036 return this.el.select('textarea.form-control',true).first();
12040 * Clear any invalid styles/messages for this field
12042 clearInvalid : function()
12045 if(!this.el || this.preventMark){ // not rendered
12049 var label = this.el.select('label', true).first();
12050 var icon = this.el.select('i.fa-star', true).first();
12055 this.el.removeClass( this.validClass);
12056 this.inputEl().removeClass('is-invalid');
12058 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12060 var feedback = this.el.select('.form-control-feedback', true).first();
12063 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12068 this.fireEvent('valid', this);
12072 * Mark this field as valid
12074 markValid : function()
12076 if(!this.el || this.preventMark){ // not rendered
12080 this.el.removeClass([this.invalidClass, this.validClass]);
12081 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12083 var feedback = this.el.select('.form-control-feedback', true).first();
12086 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12089 if(this.disabled || this.allowBlank){
12093 var label = this.el.select('label', true).first();
12094 var icon = this.el.select('i.fa-star', true).first();
12099 if (Roo.bootstrap.version == 3) {
12100 this.el.addClass(this.validClass);
12102 this.inputEl().addClass('is-valid');
12106 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12108 var feedback = this.el.select('.form-control-feedback', true).first();
12111 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12112 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12117 this.fireEvent('valid', this);
12121 * Mark this field as invalid
12122 * @param {String} msg The validation message
12124 markInvalid : function(msg)
12126 if(!this.el || this.preventMark){ // not rendered
12130 this.el.removeClass([this.invalidClass, this.validClass]);
12131 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12133 var feedback = this.el.select('.form-control-feedback', true).first();
12136 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12139 if(this.disabled || this.allowBlank){
12143 var label = this.el.select('label', true).first();
12144 var icon = this.el.select('i.fa-star', true).first();
12146 if(!this.getValue().length && label && !icon){
12147 this.el.createChild({
12149 cls : 'text-danger fa fa-lg fa-star',
12150 tooltip : 'This field is required',
12151 style : 'margin-right:5px;'
12155 if (Roo.bootstrap.version == 3) {
12156 this.el.addClass(this.invalidClass);
12158 this.inputEl().addClass('is-invalid');
12161 // fixme ... this may be depricated need to test..
12162 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12164 var feedback = this.el.select('.form-control-feedback', true).first();
12167 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12169 if(this.getValue().length || this.forceFeedback){
12170 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12177 this.fireEvent('invalid', this, msg);
12185 * trigger field - base class for combo..
12190 * @class Roo.bootstrap.TriggerField
12191 * @extends Roo.bootstrap.Input
12192 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12193 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12194 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12195 * for which you can provide a custom implementation. For example:
12197 var trigger = new Roo.bootstrap.TriggerField();
12198 trigger.onTriggerClick = myTriggerFn;
12199 trigger.applyTo('my-field');
12202 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12203 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12204 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12205 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12206 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12209 * Create a new TriggerField.
12210 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12211 * to the base TextField)
12213 Roo.bootstrap.TriggerField = function(config){
12214 this.mimicing = false;
12215 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12218 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12220 * @cfg {String} triggerClass A CSS class to apply to the trigger
12223 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12228 * @cfg {Boolean} removable (true|false) special filter default false
12232 /** @cfg {Boolean} grow @hide */
12233 /** @cfg {Number} growMin @hide */
12234 /** @cfg {Number} growMax @hide */
12240 autoSize: Roo.emptyFn,
12244 deferHeight : true,
12247 actionMode : 'wrap',
12252 getAutoCreate : function(){
12254 var align = this.labelAlign || this.parentLabelAlign();
12259 cls: 'form-group' //input-group
12266 type : this.inputType,
12267 cls : 'form-control',
12268 autocomplete: 'new-password',
12269 placeholder : this.placeholder || ''
12273 input.name = this.name;
12276 input.cls += ' input-' + this.size;
12279 if (this.disabled) {
12280 input.disabled=true;
12283 var inputblock = input;
12285 if(this.hasFeedback && !this.allowBlank){
12289 cls: 'glyphicon form-control-feedback'
12292 if(this.removable && !this.editable ){
12294 cls : 'has-feedback',
12300 cls : 'roo-combo-removable-btn close'
12307 cls : 'has-feedback',
12316 if(this.removable && !this.editable ){
12318 cls : 'roo-removable',
12324 cls : 'roo-combo-removable-btn close'
12331 if (this.before || this.after) {
12334 cls : 'input-group',
12338 inputblock.cn.push({
12340 cls : 'input-group-addon input-group-prepend input-group-text',
12345 inputblock.cn.push(input);
12347 if(this.hasFeedback && !this.allowBlank){
12348 inputblock.cls += ' has-feedback';
12349 inputblock.cn.push(feedback);
12353 inputblock.cn.push({
12355 cls : 'input-group-addon input-group-append input-group-text',
12364 var ibwrap = inputblock;
12369 cls: 'roo-select2-choices',
12373 cls: 'roo-select2-search-field',
12385 cls: 'roo-select2-container input-group',
12390 cls: 'form-hidden-field'
12396 if(!this.multiple && this.showToggleBtn){
12402 if (this.caret != false) {
12405 cls: 'fa fa-' + this.caret
12412 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12414 Roo.bootstrap.version == 3 ? caret : '',
12417 cls: 'combobox-clear',
12431 combobox.cls += ' roo-select2-container-multi';
12435 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12436 tooltip : 'This field is required'
12438 if (Roo.bootstrap.version == 4) {
12441 style : 'display:none'
12446 if (align ==='left' && this.fieldLabel.length) {
12448 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12455 cls : 'control-label',
12456 html : this.fieldLabel
12468 var labelCfg = cfg.cn[1];
12469 var contentCfg = cfg.cn[2];
12471 if(this.indicatorpos == 'right'){
12476 cls : 'control-label',
12480 html : this.fieldLabel
12494 labelCfg = cfg.cn[0];
12495 contentCfg = cfg.cn[1];
12498 if(this.labelWidth > 12){
12499 labelCfg.style = "width: " + this.labelWidth + 'px';
12502 if(this.labelWidth < 13 && this.labelmd == 0){
12503 this.labelmd = this.labelWidth;
12506 if(this.labellg > 0){
12507 labelCfg.cls += ' col-lg-' + this.labellg;
12508 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12511 if(this.labelmd > 0){
12512 labelCfg.cls += ' col-md-' + this.labelmd;
12513 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12516 if(this.labelsm > 0){
12517 labelCfg.cls += ' col-sm-' + this.labelsm;
12518 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12521 if(this.labelxs > 0){
12522 labelCfg.cls += ' col-xs-' + this.labelxs;
12523 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12526 } else if ( this.fieldLabel.length) {
12527 // Roo.log(" label");
12532 //cls : 'input-group-addon',
12533 html : this.fieldLabel
12541 if(this.indicatorpos == 'right'){
12549 html : this.fieldLabel
12563 // Roo.log(" no label && no align");
12570 ['xs','sm','md','lg'].map(function(size){
12571 if (settings[size]) {
12572 cfg.cls += ' col-' + size + '-' + settings[size];
12583 onResize : function(w, h){
12584 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12585 // if(typeof w == 'number'){
12586 // var x = w - this.trigger.getWidth();
12587 // this.inputEl().setWidth(this.adjustWidth('input', x));
12588 // this.trigger.setStyle('left', x+'px');
12593 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12596 getResizeEl : function(){
12597 return this.inputEl();
12601 getPositionEl : function(){
12602 return this.inputEl();
12606 alignErrorIcon : function(){
12607 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12611 initEvents : function(){
12615 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12616 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12617 if(!this.multiple && this.showToggleBtn){
12618 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12619 if(this.hideTrigger){
12620 this.trigger.setDisplayed(false);
12622 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12626 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12629 if(this.removable && !this.editable && !this.tickable){
12630 var close = this.closeTriggerEl();
12633 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12634 close.on('click', this.removeBtnClick, this, close);
12638 //this.trigger.addClassOnOver('x-form-trigger-over');
12639 //this.trigger.addClassOnClick('x-form-trigger-click');
12642 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12646 closeTriggerEl : function()
12648 var close = this.el.select('.roo-combo-removable-btn', true).first();
12649 return close ? close : false;
12652 removeBtnClick : function(e, h, el)
12654 e.preventDefault();
12656 if(this.fireEvent("remove", this) !== false){
12658 this.fireEvent("afterremove", this)
12662 createList : function()
12664 this.list = Roo.get(document.body).createChild({
12665 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12666 cls: 'typeahead typeahead-long dropdown-menu shadow',
12667 style: 'display:none'
12670 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12675 initTrigger : function(){
12680 onDestroy : function(){
12682 this.trigger.removeAllListeners();
12683 // this.trigger.remove();
12686 // this.wrap.remove();
12688 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12692 onFocus : function(){
12693 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12695 if(!this.mimicing){
12696 this.wrap.addClass('x-trigger-wrap-focus');
12697 this.mimicing = true;
12698 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12699 if(this.monitorTab){
12700 this.el.on("keydown", this.checkTab, this);
12707 checkTab : function(e){
12708 if(e.getKey() == e.TAB){
12709 this.triggerBlur();
12714 onBlur : function(){
12719 mimicBlur : function(e, t){
12721 if(!this.wrap.contains(t) && this.validateBlur()){
12722 this.triggerBlur();
12728 triggerBlur : function(){
12729 this.mimicing = false;
12730 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12731 if(this.monitorTab){
12732 this.el.un("keydown", this.checkTab, this);
12734 //this.wrap.removeClass('x-trigger-wrap-focus');
12735 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12739 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12740 validateBlur : function(e, t){
12745 onDisable : function(){
12746 this.inputEl().dom.disabled = true;
12747 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12749 // this.wrap.addClass('x-item-disabled');
12754 onEnable : function(){
12755 this.inputEl().dom.disabled = false;
12756 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12758 // this.el.removeClass('x-item-disabled');
12763 onShow : function(){
12764 var ae = this.getActionEl();
12767 ae.dom.style.display = '';
12768 ae.dom.style.visibility = 'visible';
12774 onHide : function(){
12775 var ae = this.getActionEl();
12776 ae.dom.style.display = 'none';
12780 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12781 * by an implementing function.
12783 * @param {EventObject} e
12785 onTriggerClick : Roo.emptyFn
12793 * @class Roo.bootstrap.CardUploader
12794 * @extends Roo.bootstrap.Button
12795 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12796 * @cfg {Number} errorTimeout default 3000
12797 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12798 * @cfg {Array} html The button text.
12802 * Create a new CardUploader
12803 * @param {Object} config The config object
12806 Roo.bootstrap.CardUploader = function(config){
12810 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12813 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12821 * When a image is clicked on - and needs to display a slideshow or similar..
12822 * @param {Roo.bootstrap.Card} this
12823 * @param {Object} The image information data
12829 * When a the download link is clicked
12830 * @param {Roo.bootstrap.Card} this
12831 * @param {Object} The image information data contains
12838 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12841 errorTimeout : 3000,
12845 fileCollection : false,
12848 getAutoCreate : function()
12852 cls :'form-group' ,
12857 //cls : 'input-group-addon',
12858 html : this.fieldLabel
12866 value : this.value,
12867 cls : 'd-none form-control'
12872 multiple : 'multiple',
12874 cls : 'd-none roo-card-upload-selector'
12878 cls : 'roo-card-uploader-button-container w-100 mb-2'
12881 cls : 'card-columns roo-card-uploader-container'
12891 getChildContainer : function() /// what children are added to.
12893 return this.containerEl;
12896 getButtonContainer : function() /// what children are added to.
12898 return this.el.select(".roo-card-uploader-button-container").first();
12901 initEvents : function()
12904 Roo.bootstrap.Input.prototype.initEvents.call(this);
12908 xns: Roo.bootstrap,
12911 container_method : 'getButtonContainer' ,
12912 html : this.html, // fix changable?
12915 'click' : function(btn, e) {
12924 this.urlAPI = (window.createObjectURL && window) ||
12925 (window.URL && URL.revokeObjectURL && URL) ||
12926 (window.webkitURL && webkitURL);
12931 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12933 this.selectorEl.on('change', this.onFileSelected, this);
12936 this.images.forEach(function(img) {
12939 this.images = false;
12941 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12947 onClick : function(e)
12949 e.preventDefault();
12951 this.selectorEl.dom.click();
12955 onFileSelected : function(e)
12957 e.preventDefault();
12959 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12963 Roo.each(this.selectorEl.dom.files, function(file){
12964 this.addFile(file);
12973 addFile : function(file)
12976 if(typeof(file) === 'string'){
12977 throw "Add file by name?"; // should not happen
12981 if(!file || !this.urlAPI){
12991 var url = _this.urlAPI.createObjectURL( file);
12994 id : Roo.bootstrap.CardUploader.ID--,
12995 is_uploaded : false,
12999 mimetype : file.type,
13007 * addCard - add an Attachment to the uploader
13008 * @param data - the data about the image to upload
13012 title : "Title of file",
13013 is_uploaded : false,
13014 src : "http://.....",
13015 srcfile : { the File upload object },
13016 mimetype : file.type,
13019 .. any other data...
13025 addCard : function (data)
13027 // hidden input element?
13028 // if the file is not an image...
13029 //then we need to use something other that and header_image
13034 xns : Roo.bootstrap,
13035 xtype : 'CardFooter',
13038 xns : Roo.bootstrap,
13044 xns : Roo.bootstrap,
13046 html : String.format("<small>{0}</small>", data.title),
13047 cls : 'col-10 text-left',
13052 click : function() {
13054 t.fireEvent( "download", t, data );
13060 xns : Roo.bootstrap,
13062 style: 'max-height: 28px; ',
13068 click : function() {
13069 t.removeCard(data.id)
13081 var cn = this.addxtype(
13084 xns : Roo.bootstrap,
13087 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13088 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13089 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13094 initEvents : function() {
13095 Roo.bootstrap.Card.prototype.initEvents.call(this);
13097 this.imgEl = this.el.select('.card-img-top').first();
13099 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13100 this.imgEl.set({ 'pointer' : 'cursor' });
13103 this.getCardFooter().addClass('p-1');
13110 // dont' really need ot update items.
13111 // this.items.push(cn);
13112 this.fileCollection.add(cn);
13114 if (!data.srcfile) {
13115 this.updateInput();
13120 var reader = new FileReader();
13121 reader.addEventListener("load", function() {
13122 data.srcdata = reader.result;
13125 reader.readAsDataURL(data.srcfile);
13130 removeCard : function(id)
13133 var card = this.fileCollection.get(id);
13134 card.data.is_deleted = 1;
13135 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13136 //this.fileCollection.remove(card);
13137 //this.items = this.items.filter(function(e) { return e != card });
13138 // dont' really need ot update items.
13139 card.el.dom.parentNode.removeChild(card.el.dom);
13140 this.updateInput();
13146 this.fileCollection.each(function(card) {
13147 if (card.el.dom && card.el.dom.parentNode) {
13148 card.el.dom.parentNode.removeChild(card.el.dom);
13151 this.fileCollection.clear();
13152 this.updateInput();
13155 updateInput : function()
13158 this.fileCollection.each(function(e) {
13162 this.inputEl().dom.value = JSON.stringify(data);
13172 Roo.bootstrap.CardUploader.ID = -1;/*
13174 * Ext JS Library 1.1.1
13175 * Copyright(c) 2006-2007, Ext JS, LLC.
13177 * Originally Released Under LGPL - original licence link has changed is not relivant.
13180 * <script type="text/javascript">
13185 * @class Roo.data.SortTypes
13187 * Defines the default sorting (casting?) comparison functions used when sorting data.
13189 Roo.data.SortTypes = {
13191 * Default sort that does nothing
13192 * @param {Mixed} s The value being converted
13193 * @return {Mixed} The comparison value
13195 none : function(s){
13200 * The regular expression used to strip tags
13204 stripTagsRE : /<\/?[^>]+>/gi,
13207 * Strips all HTML tags to sort on text only
13208 * @param {Mixed} s The value being converted
13209 * @return {String} The comparison value
13211 asText : function(s){
13212 return String(s).replace(this.stripTagsRE, "");
13216 * Strips all HTML tags to sort on text only - Case insensitive
13217 * @param {Mixed} s The value being converted
13218 * @return {String} The comparison value
13220 asUCText : function(s){
13221 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13225 * Case insensitive string
13226 * @param {Mixed} s The value being converted
13227 * @return {String} The comparison value
13229 asUCString : function(s) {
13230 return String(s).toUpperCase();
13235 * @param {Mixed} s The value being converted
13236 * @return {Number} The comparison value
13238 asDate : function(s) {
13242 if(s instanceof Date){
13243 return s.getTime();
13245 return Date.parse(String(s));
13250 * @param {Mixed} s The value being converted
13251 * @return {Float} The comparison value
13253 asFloat : function(s) {
13254 var val = parseFloat(String(s).replace(/,/g, ""));
13263 * @param {Mixed} s The value being converted
13264 * @return {Number} The comparison value
13266 asInt : function(s) {
13267 var val = parseInt(String(s).replace(/,/g, ""));
13275 * Ext JS Library 1.1.1
13276 * Copyright(c) 2006-2007, Ext JS, LLC.
13278 * Originally Released Under LGPL - original licence link has changed is not relivant.
13281 * <script type="text/javascript">
13285 * @class Roo.data.Record
13286 * Instances of this class encapsulate both record <em>definition</em> information, and record
13287 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13288 * to access Records cached in an {@link Roo.data.Store} object.<br>
13290 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13291 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13294 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13296 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13297 * {@link #create}. The parameters are the same.
13298 * @param {Array} data An associative Array of data values keyed by the field name.
13299 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13300 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13301 * not specified an integer id is generated.
13303 Roo.data.Record = function(data, id){
13304 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13309 * Generate a constructor for a specific record layout.
13310 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13311 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13312 * Each field definition object may contain the following properties: <ul>
13313 * <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,
13314 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13315 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13316 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13317 * is being used, then this is a string containing the javascript expression to reference the data relative to
13318 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13319 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13320 * this may be omitted.</p></li>
13321 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13322 * <ul><li>auto (Default, implies no conversion)</li>
13327 * <li>date</li></ul></p></li>
13328 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13329 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13330 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13331 * by the Reader into an object that will be stored in the Record. It is passed the
13332 * following parameters:<ul>
13333 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13335 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13337 * <br>usage:<br><pre><code>
13338 var TopicRecord = Roo.data.Record.create(
13339 {name: 'title', mapping: 'topic_title'},
13340 {name: 'author', mapping: 'username'},
13341 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13342 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13343 {name: 'lastPoster', mapping: 'user2'},
13344 {name: 'excerpt', mapping: 'post_text'}
13347 var myNewRecord = new TopicRecord({
13348 title: 'Do my job please',
13351 lastPost: new Date(),
13352 lastPoster: 'Animal',
13353 excerpt: 'No way dude!'
13355 myStore.add(myNewRecord);
13360 Roo.data.Record.create = function(o){
13361 var f = function(){
13362 f.superclass.constructor.apply(this, arguments);
13364 Roo.extend(f, Roo.data.Record);
13365 var p = f.prototype;
13366 p.fields = new Roo.util.MixedCollection(false, function(field){
13369 for(var i = 0, len = o.length; i < len; i++){
13370 p.fields.add(new Roo.data.Field(o[i]));
13372 f.getField = function(name){
13373 return p.fields.get(name);
13378 Roo.data.Record.AUTO_ID = 1000;
13379 Roo.data.Record.EDIT = 'edit';
13380 Roo.data.Record.REJECT = 'reject';
13381 Roo.data.Record.COMMIT = 'commit';
13383 Roo.data.Record.prototype = {
13385 * Readonly flag - true if this record has been modified.
13394 join : function(store){
13395 this.store = store;
13399 * Set the named field to the specified value.
13400 * @param {String} name The name of the field to set.
13401 * @param {Object} value The value to set the field to.
13403 set : function(name, value){
13404 if(this.data[name] == value){
13408 if(!this.modified){
13409 this.modified = {};
13411 if(typeof this.modified[name] == 'undefined'){
13412 this.modified[name] = this.data[name];
13414 this.data[name] = value;
13415 if(!this.editing && this.store){
13416 this.store.afterEdit(this);
13421 * Get the value of the named field.
13422 * @param {String} name The name of the field to get the value of.
13423 * @return {Object} The value of the field.
13425 get : function(name){
13426 return this.data[name];
13430 beginEdit : function(){
13431 this.editing = true;
13432 this.modified = {};
13436 cancelEdit : function(){
13437 this.editing = false;
13438 delete this.modified;
13442 endEdit : function(){
13443 this.editing = false;
13444 if(this.dirty && this.store){
13445 this.store.afterEdit(this);
13450 * Usually called by the {@link Roo.data.Store} which owns the Record.
13451 * Rejects all changes made to the Record since either creation, or the last commit operation.
13452 * Modified fields are reverted to their original values.
13454 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13455 * of reject operations.
13457 reject : function(){
13458 var m = this.modified;
13460 if(typeof m[n] != "function"){
13461 this.data[n] = m[n];
13464 this.dirty = false;
13465 delete this.modified;
13466 this.editing = false;
13468 this.store.afterReject(this);
13473 * Usually called by the {@link Roo.data.Store} which owns the Record.
13474 * Commits all changes made to the Record since either creation, or the last commit operation.
13476 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13477 * of commit operations.
13479 commit : function(){
13480 this.dirty = false;
13481 delete this.modified;
13482 this.editing = false;
13484 this.store.afterCommit(this);
13489 hasError : function(){
13490 return this.error != null;
13494 clearError : function(){
13499 * Creates a copy of this record.
13500 * @param {String} id (optional) A new record id if you don't want to use this record's id
13503 copy : function(newId) {
13504 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13508 * Ext JS Library 1.1.1
13509 * Copyright(c) 2006-2007, Ext JS, LLC.
13511 * Originally Released Under LGPL - original licence link has changed is not relivant.
13514 * <script type="text/javascript">
13520 * @class Roo.data.Store
13521 * @extends Roo.util.Observable
13522 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13523 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13525 * 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
13526 * has no knowledge of the format of the data returned by the Proxy.<br>
13528 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13529 * instances from the data object. These records are cached and made available through accessor functions.
13531 * Creates a new Store.
13532 * @param {Object} config A config object containing the objects needed for the Store to access data,
13533 * and read the data into Records.
13535 Roo.data.Store = function(config){
13536 this.data = new Roo.util.MixedCollection(false);
13537 this.data.getKey = function(o){
13540 this.baseParams = {};
13542 this.paramNames = {
13547 "multisort" : "_multisort"
13550 if(config && config.data){
13551 this.inlineData = config.data;
13552 delete config.data;
13555 Roo.apply(this, config);
13557 if(this.reader){ // reader passed
13558 this.reader = Roo.factory(this.reader, Roo.data);
13559 this.reader.xmodule = this.xmodule || false;
13560 if(!this.recordType){
13561 this.recordType = this.reader.recordType;
13563 if(this.reader.onMetaChange){
13564 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13568 if(this.recordType){
13569 this.fields = this.recordType.prototype.fields;
13571 this.modified = [];
13575 * @event datachanged
13576 * Fires when the data cache has changed, and a widget which is using this Store
13577 * as a Record cache should refresh its view.
13578 * @param {Store} this
13580 datachanged : true,
13582 * @event metachange
13583 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13584 * @param {Store} this
13585 * @param {Object} meta The JSON metadata
13590 * Fires when Records have been added to the Store
13591 * @param {Store} this
13592 * @param {Roo.data.Record[]} records The array of Records added
13593 * @param {Number} index The index at which the record(s) were added
13598 * Fires when a Record has been removed from the Store
13599 * @param {Store} this
13600 * @param {Roo.data.Record} record The Record that was removed
13601 * @param {Number} index The index at which the record was removed
13606 * Fires when a Record has been updated
13607 * @param {Store} this
13608 * @param {Roo.data.Record} record The Record that was updated
13609 * @param {String} operation The update operation being performed. Value may be one of:
13611 Roo.data.Record.EDIT
13612 Roo.data.Record.REJECT
13613 Roo.data.Record.COMMIT
13619 * Fires when the data cache has been cleared.
13620 * @param {Store} this
13624 * @event beforeload
13625 * Fires before a request is made for a new data object. If the beforeload handler returns false
13626 * the load action will be canceled.
13627 * @param {Store} this
13628 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13632 * @event beforeloadadd
13633 * Fires after a new set of Records has been loaded.
13634 * @param {Store} this
13635 * @param {Roo.data.Record[]} records The Records that were loaded
13636 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13638 beforeloadadd : true,
13641 * Fires after a new set of Records has been loaded, before they are added to the store.
13642 * @param {Store} this
13643 * @param {Roo.data.Record[]} records The Records that were loaded
13644 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13645 * @params {Object} return from reader
13649 * @event loadexception
13650 * Fires if an exception occurs in the Proxy during loading.
13651 * Called with the signature of the Proxy's "loadexception" event.
13652 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13655 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13656 * @param {Object} load options
13657 * @param {Object} jsonData from your request (normally this contains the Exception)
13659 loadexception : true
13663 this.proxy = Roo.factory(this.proxy, Roo.data);
13664 this.proxy.xmodule = this.xmodule || false;
13665 this.relayEvents(this.proxy, ["loadexception"]);
13667 this.sortToggle = {};
13668 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13670 Roo.data.Store.superclass.constructor.call(this);
13672 if(this.inlineData){
13673 this.loadData(this.inlineData);
13674 delete this.inlineData;
13678 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13680 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13681 * without a remote query - used by combo/forms at present.
13685 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13688 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13691 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13692 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13695 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13696 * on any HTTP request
13699 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13702 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13706 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13707 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13709 remoteSort : false,
13712 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13713 * loaded or when a record is removed. (defaults to false).
13715 pruneModifiedRecords : false,
13718 lastOptions : null,
13721 * Add Records to the Store and fires the add event.
13722 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13724 add : function(records){
13725 records = [].concat(records);
13726 for(var i = 0, len = records.length; i < len; i++){
13727 records[i].join(this);
13729 var index = this.data.length;
13730 this.data.addAll(records);
13731 this.fireEvent("add", this, records, index);
13735 * Remove a Record from the Store and fires the remove event.
13736 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13738 remove : function(record){
13739 var index = this.data.indexOf(record);
13740 this.data.removeAt(index);
13742 if(this.pruneModifiedRecords){
13743 this.modified.remove(record);
13745 this.fireEvent("remove", this, record, index);
13749 * Remove all Records from the Store and fires the clear event.
13751 removeAll : function(){
13753 if(this.pruneModifiedRecords){
13754 this.modified = [];
13756 this.fireEvent("clear", this);
13760 * Inserts Records to the Store at the given index and fires the add event.
13761 * @param {Number} index The start index at which to insert the passed Records.
13762 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13764 insert : function(index, records){
13765 records = [].concat(records);
13766 for(var i = 0, len = records.length; i < len; i++){
13767 this.data.insert(index, records[i]);
13768 records[i].join(this);
13770 this.fireEvent("add", this, records, index);
13774 * Get the index within the cache of the passed Record.
13775 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13776 * @return {Number} The index of the passed Record. Returns -1 if not found.
13778 indexOf : function(record){
13779 return this.data.indexOf(record);
13783 * Get the index within the cache of the Record with the passed id.
13784 * @param {String} id The id of the Record to find.
13785 * @return {Number} The index of the Record. Returns -1 if not found.
13787 indexOfId : function(id){
13788 return this.data.indexOfKey(id);
13792 * Get the Record with the specified id.
13793 * @param {String} id The id of the Record to find.
13794 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13796 getById : function(id){
13797 return this.data.key(id);
13801 * Get the Record at the specified index.
13802 * @param {Number} index The index of the Record to find.
13803 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13805 getAt : function(index){
13806 return this.data.itemAt(index);
13810 * Returns a range of Records between specified indices.
13811 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13812 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13813 * @return {Roo.data.Record[]} An array of Records
13815 getRange : function(start, end){
13816 return this.data.getRange(start, end);
13820 storeOptions : function(o){
13821 o = Roo.apply({}, o);
13824 this.lastOptions = o;
13828 * Loads the Record cache from the configured Proxy using the configured Reader.
13830 * If using remote paging, then the first load call must specify the <em>start</em>
13831 * and <em>limit</em> properties in the options.params property to establish the initial
13832 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13834 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13835 * and this call will return before the new data has been loaded. Perform any post-processing
13836 * in a callback function, or in a "load" event handler.</strong>
13838 * @param {Object} options An object containing properties which control loading options:<ul>
13839 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13840 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13841 * passed the following arguments:<ul>
13842 * <li>r : Roo.data.Record[]</li>
13843 * <li>options: Options object from the load call</li>
13844 * <li>success: Boolean success indicator</li></ul></li>
13845 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13846 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13849 load : function(options){
13850 options = options || {};
13851 if(this.fireEvent("beforeload", this, options) !== false){
13852 this.storeOptions(options);
13853 var p = Roo.apply(options.params || {}, this.baseParams);
13854 // if meta was not loaded from remote source.. try requesting it.
13855 if (!this.reader.metaFromRemote) {
13856 p._requestMeta = 1;
13858 if(this.sortInfo && this.remoteSort){
13859 var pn = this.paramNames;
13860 p[pn["sort"]] = this.sortInfo.field;
13861 p[pn["dir"]] = this.sortInfo.direction;
13863 if (this.multiSort) {
13864 var pn = this.paramNames;
13865 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13868 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13873 * Reloads the Record cache from the configured Proxy using the configured Reader and
13874 * the options from the last load operation performed.
13875 * @param {Object} options (optional) An object containing properties which may override the options
13876 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13877 * the most recently used options are reused).
13879 reload : function(options){
13880 this.load(Roo.applyIf(options||{}, this.lastOptions));
13884 // Called as a callback by the Reader during a load operation.
13885 loadRecords : function(o, options, success){
13886 if(!o || success === false){
13887 if(success !== false){
13888 this.fireEvent("load", this, [], options, o);
13890 if(options.callback){
13891 options.callback.call(options.scope || this, [], options, false);
13895 // if data returned failure - throw an exception.
13896 if (o.success === false) {
13897 // show a message if no listener is registered.
13898 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13899 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13901 // loadmask wil be hooked into this..
13902 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13905 var r = o.records, t = o.totalRecords || r.length;
13907 this.fireEvent("beforeloadadd", this, r, options, o);
13909 if(!options || options.add !== true){
13910 if(this.pruneModifiedRecords){
13911 this.modified = [];
13913 for(var i = 0, len = r.length; i < len; i++){
13917 this.data = this.snapshot;
13918 delete this.snapshot;
13921 this.data.addAll(r);
13922 this.totalLength = t;
13924 this.fireEvent("datachanged", this);
13926 this.totalLength = Math.max(t, this.data.length+r.length);
13930 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13932 var e = new Roo.data.Record({});
13934 e.set(this.parent.displayField, this.parent.emptyTitle);
13935 e.set(this.parent.valueField, '');
13940 this.fireEvent("load", this, r, options, o);
13941 if(options.callback){
13942 options.callback.call(options.scope || this, r, options, true);
13948 * Loads data from a passed data block. A Reader which understands the format of the data
13949 * must have been configured in the constructor.
13950 * @param {Object} data The data block from which to read the Records. The format of the data expected
13951 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13952 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13954 loadData : function(o, append){
13955 var r = this.reader.readRecords(o);
13956 this.loadRecords(r, {add: append}, true);
13960 * using 'cn' the nested child reader read the child array into it's child stores.
13961 * @param {Object} rec The record with a 'children array
13963 loadDataFromChildren : function(rec)
13965 this.loadData(this.reader.toLoadData(rec));
13970 * Gets the number of cached records.
13972 * <em>If using paging, this may not be the total size of the dataset. If the data object
13973 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13974 * the data set size</em>
13976 getCount : function(){
13977 return this.data.length || 0;
13981 * Gets the total number of records in the dataset as returned by the server.
13983 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13984 * the dataset size</em>
13986 getTotalCount : function(){
13987 return this.totalLength || 0;
13991 * Returns the sort state of the Store as an object with two properties:
13993 field {String} The name of the field by which the Records are sorted
13994 direction {String} The sort order, "ASC" or "DESC"
13997 getSortState : function(){
13998 return this.sortInfo;
14002 applySort : function(){
14003 if(this.sortInfo && !this.remoteSort){
14004 var s = this.sortInfo, f = s.field;
14005 var st = this.fields.get(f).sortType;
14006 var fn = function(r1, r2){
14007 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14008 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14010 this.data.sort(s.direction, fn);
14011 if(this.snapshot && this.snapshot != this.data){
14012 this.snapshot.sort(s.direction, fn);
14018 * Sets the default sort column and order to be used by the next load operation.
14019 * @param {String} fieldName The name of the field to sort by.
14020 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14022 setDefaultSort : function(field, dir){
14023 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14027 * Sort the Records.
14028 * If remote sorting is used, the sort is performed on the server, and the cache is
14029 * reloaded. If local sorting is used, the cache is sorted internally.
14030 * @param {String} fieldName The name of the field to sort by.
14031 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14033 sort : function(fieldName, dir){
14034 var f = this.fields.get(fieldName);
14036 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14038 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14039 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14044 this.sortToggle[f.name] = dir;
14045 this.sortInfo = {field: f.name, direction: dir};
14046 if(!this.remoteSort){
14048 this.fireEvent("datachanged", this);
14050 this.load(this.lastOptions);
14055 * Calls the specified function for each of the Records in the cache.
14056 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14057 * Returning <em>false</em> aborts and exits the iteration.
14058 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14060 each : function(fn, scope){
14061 this.data.each(fn, scope);
14065 * Gets all records modified since the last commit. Modified records are persisted across load operations
14066 * (e.g., during paging).
14067 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14069 getModifiedRecords : function(){
14070 return this.modified;
14074 createFilterFn : function(property, value, anyMatch){
14075 if(!value.exec){ // not a regex
14076 value = String(value);
14077 if(value.length == 0){
14080 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14082 return function(r){
14083 return value.test(r.data[property]);
14088 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14089 * @param {String} property A field on your records
14090 * @param {Number} start The record index to start at (defaults to 0)
14091 * @param {Number} end The last record index to include (defaults to length - 1)
14092 * @return {Number} The sum
14094 sum : function(property, start, end){
14095 var rs = this.data.items, v = 0;
14096 start = start || 0;
14097 end = (end || end === 0) ? end : rs.length-1;
14099 for(var i = start; i <= end; i++){
14100 v += (rs[i].data[property] || 0);
14106 * Filter the records by a specified property.
14107 * @param {String} field A field on your records
14108 * @param {String/RegExp} value Either a string that the field
14109 * should start with or a RegExp to test against the field
14110 * @param {Boolean} anyMatch True to match any part not just the beginning
14112 filter : function(property, value, anyMatch){
14113 var fn = this.createFilterFn(property, value, anyMatch);
14114 return fn ? this.filterBy(fn) : this.clearFilter();
14118 * Filter by a function. The specified function will be called with each
14119 * record in this data source. If the function returns true the record is included,
14120 * otherwise it is filtered.
14121 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14122 * @param {Object} scope (optional) The scope of the function (defaults to this)
14124 filterBy : function(fn, scope){
14125 this.snapshot = this.snapshot || this.data;
14126 this.data = this.queryBy(fn, scope||this);
14127 this.fireEvent("datachanged", this);
14131 * Query the records by a specified property.
14132 * @param {String} field A field on your records
14133 * @param {String/RegExp} value Either a string that the field
14134 * should start with or a RegExp to test against the field
14135 * @param {Boolean} anyMatch True to match any part not just the beginning
14136 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14138 query : function(property, value, anyMatch){
14139 var fn = this.createFilterFn(property, value, anyMatch);
14140 return fn ? this.queryBy(fn) : this.data.clone();
14144 * Query by a function. The specified function will be called with each
14145 * record in this data source. If the function returns true the record is included
14147 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14148 * @param {Object} scope (optional) The scope of the function (defaults to this)
14149 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14151 queryBy : function(fn, scope){
14152 var data = this.snapshot || this.data;
14153 return data.filterBy(fn, scope||this);
14157 * Collects unique values for a particular dataIndex from this store.
14158 * @param {String} dataIndex The property to collect
14159 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14160 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14161 * @return {Array} An array of the unique values
14163 collect : function(dataIndex, allowNull, bypassFilter){
14164 var d = (bypassFilter === true && this.snapshot) ?
14165 this.snapshot.items : this.data.items;
14166 var v, sv, r = [], l = {};
14167 for(var i = 0, len = d.length; i < len; i++){
14168 v = d[i].data[dataIndex];
14170 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14179 * Revert to a view of the Record cache with no filtering applied.
14180 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14182 clearFilter : function(suppressEvent){
14183 if(this.snapshot && this.snapshot != this.data){
14184 this.data = this.snapshot;
14185 delete this.snapshot;
14186 if(suppressEvent !== true){
14187 this.fireEvent("datachanged", this);
14193 afterEdit : function(record){
14194 if(this.modified.indexOf(record) == -1){
14195 this.modified.push(record);
14197 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14201 afterReject : function(record){
14202 this.modified.remove(record);
14203 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14207 afterCommit : function(record){
14208 this.modified.remove(record);
14209 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14213 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14214 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14216 commitChanges : function(){
14217 var m = this.modified.slice(0);
14218 this.modified = [];
14219 for(var i = 0, len = m.length; i < len; i++){
14225 * Cancel outstanding changes on all changed records.
14227 rejectChanges : function(){
14228 var m = this.modified.slice(0);
14229 this.modified = [];
14230 for(var i = 0, len = m.length; i < len; i++){
14235 onMetaChange : function(meta, rtype, o){
14236 this.recordType = rtype;
14237 this.fields = rtype.prototype.fields;
14238 delete this.snapshot;
14239 this.sortInfo = meta.sortInfo || this.sortInfo;
14240 this.modified = [];
14241 this.fireEvent('metachange', this, this.reader.meta);
14244 moveIndex : function(data, type)
14246 var index = this.indexOf(data);
14248 var newIndex = index + type;
14252 this.insert(newIndex, data);
14257 * Ext JS Library 1.1.1
14258 * Copyright(c) 2006-2007, Ext JS, LLC.
14260 * Originally Released Under LGPL - original licence link has changed is not relivant.
14263 * <script type="text/javascript">
14267 * @class Roo.data.SimpleStore
14268 * @extends Roo.data.Store
14269 * Small helper class to make creating Stores from Array data easier.
14270 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14271 * @cfg {Array} fields An array of field definition objects, or field name strings.
14272 * @cfg {Object} an existing reader (eg. copied from another store)
14273 * @cfg {Array} data The multi-dimensional array of data
14275 * @param {Object} config
14277 Roo.data.SimpleStore = function(config)
14279 Roo.data.SimpleStore.superclass.constructor.call(this, {
14281 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14284 Roo.data.Record.create(config.fields)
14286 proxy : new Roo.data.MemoryProxy(config.data)
14290 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14292 * Ext JS Library 1.1.1
14293 * Copyright(c) 2006-2007, Ext JS, LLC.
14295 * Originally Released Under LGPL - original licence link has changed is not relivant.
14298 * <script type="text/javascript">
14303 * @extends Roo.data.Store
14304 * @class Roo.data.JsonStore
14305 * Small helper class to make creating Stores for JSON data easier. <br/>
14307 var store = new Roo.data.JsonStore({
14308 url: 'get-images.php',
14310 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14313 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14314 * JsonReader and HttpProxy (unless inline data is provided).</b>
14315 * @cfg {Array} fields An array of field definition objects, or field name strings.
14317 * @param {Object} config
14319 Roo.data.JsonStore = function(c){
14320 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14321 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14322 reader: new Roo.data.JsonReader(c, c.fields)
14325 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14327 * Ext JS Library 1.1.1
14328 * Copyright(c) 2006-2007, Ext JS, LLC.
14330 * Originally Released Under LGPL - original licence link has changed is not relivant.
14333 * <script type="text/javascript">
14337 Roo.data.Field = function(config){
14338 if(typeof config == "string"){
14339 config = {name: config};
14341 Roo.apply(this, config);
14344 this.type = "auto";
14347 var st = Roo.data.SortTypes;
14348 // named sortTypes are supported, here we look them up
14349 if(typeof this.sortType == "string"){
14350 this.sortType = st[this.sortType];
14353 // set default sortType for strings and dates
14354 if(!this.sortType){
14357 this.sortType = st.asUCString;
14360 this.sortType = st.asDate;
14363 this.sortType = st.none;
14368 var stripRe = /[\$,%]/g;
14370 // prebuilt conversion function for this field, instead of
14371 // switching every time we're reading a value
14373 var cv, dateFormat = this.dateFormat;
14378 cv = function(v){ return v; };
14381 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14385 return v !== undefined && v !== null && v !== '' ?
14386 parseInt(String(v).replace(stripRe, ""), 10) : '';
14391 return v !== undefined && v !== null && v !== '' ?
14392 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14397 cv = function(v){ return v === true || v === "true" || v == 1; };
14404 if(v instanceof Date){
14408 if(dateFormat == "timestamp"){
14409 return new Date(v*1000);
14411 return Date.parseDate(v, dateFormat);
14413 var parsed = Date.parse(v);
14414 return parsed ? new Date(parsed) : null;
14423 Roo.data.Field.prototype = {
14431 * Ext JS Library 1.1.1
14432 * Copyright(c) 2006-2007, Ext JS, LLC.
14434 * Originally Released Under LGPL - original licence link has changed is not relivant.
14437 * <script type="text/javascript">
14440 // Base class for reading structured data from a data source. This class is intended to be
14441 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14444 * @class Roo.data.DataReader
14445 * Base class for reading structured data from a data source. This class is intended to be
14446 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14449 Roo.data.DataReader = function(meta, recordType){
14453 this.recordType = recordType instanceof Array ?
14454 Roo.data.Record.create(recordType) : recordType;
14457 Roo.data.DataReader.prototype = {
14460 readerType : 'Data',
14462 * Create an empty record
14463 * @param {Object} data (optional) - overlay some values
14464 * @return {Roo.data.Record} record created.
14466 newRow : function(d) {
14468 this.recordType.prototype.fields.each(function(c) {
14470 case 'int' : da[c.name] = 0; break;
14471 case 'date' : da[c.name] = new Date(); break;
14472 case 'float' : da[c.name] = 0.0; break;
14473 case 'boolean' : da[c.name] = false; break;
14474 default : da[c.name] = ""; break;
14478 return new this.recordType(Roo.apply(da, d));
14484 * Ext JS Library 1.1.1
14485 * Copyright(c) 2006-2007, Ext JS, LLC.
14487 * Originally Released Under LGPL - original licence link has changed is not relivant.
14490 * <script type="text/javascript">
14494 * @class Roo.data.DataProxy
14495 * @extends Roo.data.Observable
14496 * This class is an abstract base class for implementations which provide retrieval of
14497 * unformatted data objects.<br>
14499 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14500 * (of the appropriate type which knows how to parse the data object) to provide a block of
14501 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14503 * Custom implementations must implement the load method as described in
14504 * {@link Roo.data.HttpProxy#load}.
14506 Roo.data.DataProxy = function(){
14509 * @event beforeload
14510 * Fires before a network request is made to retrieve a data object.
14511 * @param {Object} This DataProxy object.
14512 * @param {Object} params The params parameter to the load function.
14517 * Fires before the load method's callback is called.
14518 * @param {Object} This DataProxy object.
14519 * @param {Object} o The data object.
14520 * @param {Object} arg The callback argument object passed to the load function.
14524 * @event loadexception
14525 * Fires if an Exception occurs during data retrieval.
14526 * @param {Object} This DataProxy object.
14527 * @param {Object} o The data object.
14528 * @param {Object} arg The callback argument object passed to the load function.
14529 * @param {Object} e The Exception.
14531 loadexception : true
14533 Roo.data.DataProxy.superclass.constructor.call(this);
14536 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14539 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14543 * Ext JS Library 1.1.1
14544 * Copyright(c) 2006-2007, Ext JS, LLC.
14546 * Originally Released Under LGPL - original licence link has changed is not relivant.
14549 * <script type="text/javascript">
14552 * @class Roo.data.MemoryProxy
14553 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14554 * to the Reader when its load method is called.
14556 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14558 Roo.data.MemoryProxy = function(data){
14562 Roo.data.MemoryProxy.superclass.constructor.call(this);
14566 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14569 * Load data from the requested source (in this case an in-memory
14570 * data object passed to the constructor), read the data object into
14571 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14572 * process that block using the passed callback.
14573 * @param {Object} params This parameter is not used by the MemoryProxy class.
14574 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14575 * object into a block of Roo.data.Records.
14576 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14577 * The function must be passed <ul>
14578 * <li>The Record block object</li>
14579 * <li>The "arg" argument from the load function</li>
14580 * <li>A boolean success indicator</li>
14582 * @param {Object} scope The scope in which to call the callback
14583 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14585 load : function(params, reader, callback, scope, arg){
14586 params = params || {};
14589 result = reader.readRecords(params.data ? params.data :this.data);
14591 this.fireEvent("loadexception", this, arg, null, e);
14592 callback.call(scope, null, arg, false);
14595 callback.call(scope, result, arg, true);
14599 update : function(params, records){
14604 * Ext JS Library 1.1.1
14605 * Copyright(c) 2006-2007, Ext JS, LLC.
14607 * Originally Released Under LGPL - original licence link has changed is not relivant.
14610 * <script type="text/javascript">
14613 * @class Roo.data.HttpProxy
14614 * @extends Roo.data.DataProxy
14615 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14616 * configured to reference a certain URL.<br><br>
14618 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14619 * from which the running page was served.<br><br>
14621 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14623 * Be aware that to enable the browser to parse an XML document, the server must set
14624 * the Content-Type header in the HTTP response to "text/xml".
14626 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14627 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14628 * will be used to make the request.
14630 Roo.data.HttpProxy = function(conn){
14631 Roo.data.HttpProxy.superclass.constructor.call(this);
14632 // is conn a conn config or a real conn?
14634 this.useAjax = !conn || !conn.events;
14638 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14639 // thse are take from connection...
14642 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14645 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14646 * extra parameters to each request made by this object. (defaults to undefined)
14649 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14650 * to each request made by this object. (defaults to undefined)
14653 * @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)
14656 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14659 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14665 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14669 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14670 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14671 * a finer-grained basis than the DataProxy events.
14673 getConnection : function(){
14674 return this.useAjax ? Roo.Ajax : this.conn;
14678 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14679 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14680 * process that block using the passed callback.
14681 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14682 * for the request to the remote server.
14683 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14684 * object into a block of Roo.data.Records.
14685 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14686 * The function must be passed <ul>
14687 * <li>The Record block object</li>
14688 * <li>The "arg" argument from the load function</li>
14689 * <li>A boolean success indicator</li>
14691 * @param {Object} scope The scope in which to call the callback
14692 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14694 load : function(params, reader, callback, scope, arg){
14695 if(this.fireEvent("beforeload", this, params) !== false){
14697 params : params || {},
14699 callback : callback,
14704 callback : this.loadResponse,
14708 Roo.applyIf(o, this.conn);
14709 if(this.activeRequest){
14710 Roo.Ajax.abort(this.activeRequest);
14712 this.activeRequest = Roo.Ajax.request(o);
14714 this.conn.request(o);
14717 callback.call(scope||this, null, arg, false);
14722 loadResponse : function(o, success, response){
14723 delete this.activeRequest;
14725 this.fireEvent("loadexception", this, o, response);
14726 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14731 result = o.reader.read(response);
14733 this.fireEvent("loadexception", this, o, response, e);
14734 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14738 this.fireEvent("load", this, o, o.request.arg);
14739 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14743 update : function(dataSet){
14748 updateResponse : function(dataSet){
14753 * Ext JS Library 1.1.1
14754 * Copyright(c) 2006-2007, Ext JS, LLC.
14756 * Originally Released Under LGPL - original licence link has changed is not relivant.
14759 * <script type="text/javascript">
14763 * @class Roo.data.ScriptTagProxy
14764 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14765 * other than the originating domain of the running page.<br><br>
14767 * <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
14768 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14770 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14771 * source code that is used as the source inside a <script> tag.<br><br>
14773 * In order for the browser to process the returned data, the server must wrap the data object
14774 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14775 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14776 * depending on whether the callback name was passed:
14779 boolean scriptTag = false;
14780 String cb = request.getParameter("callback");
14783 response.setContentType("text/javascript");
14785 response.setContentType("application/x-json");
14787 Writer out = response.getWriter();
14789 out.write(cb + "(");
14791 out.print(dataBlock.toJsonString());
14798 * @param {Object} config A configuration object.
14800 Roo.data.ScriptTagProxy = function(config){
14801 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14802 Roo.apply(this, config);
14803 this.head = document.getElementsByTagName("head")[0];
14806 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14808 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14810 * @cfg {String} url The URL from which to request the data object.
14813 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14817 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14818 * the server the name of the callback function set up by the load call to process the returned data object.
14819 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14820 * javascript output which calls this named function passing the data object as its only parameter.
14822 callbackParam : "callback",
14824 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14825 * name to the request.
14830 * Load data from the configured URL, read the data object into
14831 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14832 * process that block using the passed callback.
14833 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14834 * for the request to the remote server.
14835 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14836 * object into a block of Roo.data.Records.
14837 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14838 * The function must be passed <ul>
14839 * <li>The Record block object</li>
14840 * <li>The "arg" argument from the load function</li>
14841 * <li>A boolean success indicator</li>
14843 * @param {Object} scope The scope in which to call the callback
14844 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14846 load : function(params, reader, callback, scope, arg){
14847 if(this.fireEvent("beforeload", this, params) !== false){
14849 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14851 var url = this.url;
14852 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14854 url += "&_dc=" + (new Date().getTime());
14856 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14859 cb : "stcCallback"+transId,
14860 scriptId : "stcScript"+transId,
14864 callback : callback,
14870 window[trans.cb] = function(o){
14871 conn.handleResponse(o, trans);
14874 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14876 if(this.autoAbort !== false){
14880 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14882 var script = document.createElement("script");
14883 script.setAttribute("src", url);
14884 script.setAttribute("type", "text/javascript");
14885 script.setAttribute("id", trans.scriptId);
14886 this.head.appendChild(script);
14888 this.trans = trans;
14890 callback.call(scope||this, null, arg, false);
14895 isLoading : function(){
14896 return this.trans ? true : false;
14900 * Abort the current server request.
14902 abort : function(){
14903 if(this.isLoading()){
14904 this.destroyTrans(this.trans);
14909 destroyTrans : function(trans, isLoaded){
14910 this.head.removeChild(document.getElementById(trans.scriptId));
14911 clearTimeout(trans.timeoutId);
14913 window[trans.cb] = undefined;
14915 delete window[trans.cb];
14918 // if hasn't been loaded, wait for load to remove it to prevent script error
14919 window[trans.cb] = function(){
14920 window[trans.cb] = undefined;
14922 delete window[trans.cb];
14929 handleResponse : function(o, trans){
14930 this.trans = false;
14931 this.destroyTrans(trans, true);
14934 result = trans.reader.readRecords(o);
14936 this.fireEvent("loadexception", this, o, trans.arg, e);
14937 trans.callback.call(trans.scope||window, null, trans.arg, false);
14940 this.fireEvent("load", this, o, trans.arg);
14941 trans.callback.call(trans.scope||window, result, trans.arg, true);
14945 handleFailure : function(trans){
14946 this.trans = false;
14947 this.destroyTrans(trans, false);
14948 this.fireEvent("loadexception", this, null, trans.arg);
14949 trans.callback.call(trans.scope||window, null, trans.arg, false);
14953 * Ext JS Library 1.1.1
14954 * Copyright(c) 2006-2007, Ext JS, LLC.
14956 * Originally Released Under LGPL - original licence link has changed is not relivant.
14959 * <script type="text/javascript">
14963 * @class Roo.data.JsonReader
14964 * @extends Roo.data.DataReader
14965 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14966 * based on mappings in a provided Roo.data.Record constructor.
14968 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14969 * in the reply previously.
14974 var RecordDef = Roo.data.Record.create([
14975 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14976 {name: 'occupation'} // This field will use "occupation" as the mapping.
14978 var myReader = new Roo.data.JsonReader({
14979 totalProperty: "results", // The property which contains the total dataset size (optional)
14980 root: "rows", // The property which contains an Array of row objects
14981 id: "id" // The property within each row object that provides an ID for the record (optional)
14985 * This would consume a JSON file like this:
14987 { 'results': 2, 'rows': [
14988 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14989 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14992 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14993 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14994 * paged from the remote server.
14995 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14996 * @cfg {String} root name of the property which contains the Array of row objects.
14997 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14998 * @cfg {Array} fields Array of field definition objects
15000 * Create a new JsonReader
15001 * @param {Object} meta Metadata configuration options
15002 * @param {Object} recordType Either an Array of field definition objects,
15003 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15005 Roo.data.JsonReader = function(meta, recordType){
15008 // set some defaults:
15009 Roo.applyIf(meta, {
15010 totalProperty: 'total',
15011 successProperty : 'success',
15016 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15018 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15020 readerType : 'Json',
15023 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15024 * Used by Store query builder to append _requestMeta to params.
15027 metaFromRemote : false,
15029 * This method is only used by a DataProxy which has retrieved data from a remote server.
15030 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15031 * @return {Object} data A data block which is used by an Roo.data.Store object as
15032 * a cache of Roo.data.Records.
15034 read : function(response){
15035 var json = response.responseText;
15037 var o = /* eval:var:o */ eval("("+json+")");
15039 throw {message: "JsonReader.read: Json object not found"};
15045 this.metaFromRemote = true;
15046 this.meta = o.metaData;
15047 this.recordType = Roo.data.Record.create(o.metaData.fields);
15048 this.onMetaChange(this.meta, this.recordType, o);
15050 return this.readRecords(o);
15053 // private function a store will implement
15054 onMetaChange : function(meta, recordType, o){
15061 simpleAccess: function(obj, subsc) {
15068 getJsonAccessor: function(){
15070 return function(expr) {
15072 return(re.test(expr))
15073 ? new Function("obj", "return obj." + expr)
15078 return Roo.emptyFn;
15083 * Create a data block containing Roo.data.Records from an XML document.
15084 * @param {Object} o An object which contains an Array of row objects in the property specified
15085 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15086 * which contains the total size of the dataset.
15087 * @return {Object} data A data block which is used by an Roo.data.Store object as
15088 * a cache of Roo.data.Records.
15090 readRecords : function(o){
15092 * After any data loads, the raw JSON data is available for further custom processing.
15096 var s = this.meta, Record = this.recordType,
15097 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15099 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15101 if(s.totalProperty) {
15102 this.getTotal = this.getJsonAccessor(s.totalProperty);
15104 if(s.successProperty) {
15105 this.getSuccess = this.getJsonAccessor(s.successProperty);
15107 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15109 var g = this.getJsonAccessor(s.id);
15110 this.getId = function(rec) {
15112 return (r === undefined || r === "") ? null : r;
15115 this.getId = function(){return null;};
15118 for(var jj = 0; jj < fl; jj++){
15120 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15121 this.ef[jj] = this.getJsonAccessor(map);
15125 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15126 if(s.totalProperty){
15127 var vt = parseInt(this.getTotal(o), 10);
15132 if(s.successProperty){
15133 var vs = this.getSuccess(o);
15134 if(vs === false || vs === 'false'){
15139 for(var i = 0; i < c; i++){
15142 var id = this.getId(n);
15143 for(var j = 0; j < fl; j++){
15145 var v = this.ef[j](n);
15147 Roo.log('missing convert for ' + f.name);
15151 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15153 var record = new Record(values, id);
15155 records[i] = record;
15161 totalRecords : totalRecords
15164 // used when loading children.. @see loadDataFromChildren
15165 toLoadData: function(rec)
15167 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15168 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15169 return { data : data, total : data.length };
15174 * Ext JS Library 1.1.1
15175 * Copyright(c) 2006-2007, Ext JS, LLC.
15177 * Originally Released Under LGPL - original licence link has changed is not relivant.
15180 * <script type="text/javascript">
15184 * @class Roo.data.ArrayReader
15185 * @extends Roo.data.DataReader
15186 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15187 * Each element of that Array represents a row of data fields. The
15188 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15189 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15193 var RecordDef = Roo.data.Record.create([
15194 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15195 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15197 var myReader = new Roo.data.ArrayReader({
15198 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15202 * This would consume an Array like this:
15204 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15208 * Create a new JsonReader
15209 * @param {Object} meta Metadata configuration options.
15210 * @param {Object|Array} recordType Either an Array of field definition objects
15212 * @cfg {Array} fields Array of field definition objects
15213 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15214 * as specified to {@link Roo.data.Record#create},
15215 * or an {@link Roo.data.Record} object
15218 * created using {@link Roo.data.Record#create}.
15220 Roo.data.ArrayReader = function(meta, recordType)
15222 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15225 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15228 * Create a data block containing Roo.data.Records from an XML document.
15229 * @param {Object} o An Array of row objects which represents the dataset.
15230 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15231 * a cache of Roo.data.Records.
15233 readRecords : function(o)
15235 var sid = this.meta ? this.meta.id : null;
15236 var recordType = this.recordType, fields = recordType.prototype.fields;
15239 for(var i = 0; i < root.length; i++){
15242 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15243 for(var j = 0, jlen = fields.length; j < jlen; j++){
15244 var f = fields.items[j];
15245 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15246 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15248 values[f.name] = v;
15250 var record = new recordType(values, id);
15252 records[records.length] = record;
15256 totalRecords : records.length
15259 // used when loading children.. @see loadDataFromChildren
15260 toLoadData: function(rec)
15262 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15263 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15274 * @class Roo.bootstrap.ComboBox
15275 * @extends Roo.bootstrap.TriggerField
15276 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15277 * @cfg {Boolean} append (true|false) default false
15278 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15279 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15280 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15281 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15282 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15283 * @cfg {Boolean} animate default true
15284 * @cfg {Boolean} emptyResultText only for touch device
15285 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15286 * @cfg {String} emptyTitle default ''
15287 * @cfg {Number} width fixed with? experimental
15289 * Create a new ComboBox.
15290 * @param {Object} config Configuration options
15292 Roo.bootstrap.ComboBox = function(config){
15293 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15297 * Fires when the dropdown list is expanded
15298 * @param {Roo.bootstrap.ComboBox} combo This combo box
15303 * Fires when the dropdown list is collapsed
15304 * @param {Roo.bootstrap.ComboBox} combo This combo box
15308 * @event beforeselect
15309 * Fires before a list item is selected. Return false to cancel the selection.
15310 * @param {Roo.bootstrap.ComboBox} combo This combo box
15311 * @param {Roo.data.Record} record The data record returned from the underlying store
15312 * @param {Number} index The index of the selected item in the dropdown list
15314 'beforeselect' : true,
15317 * Fires when a list item is selected
15318 * @param {Roo.bootstrap.ComboBox} combo This combo box
15319 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15320 * @param {Number} index The index of the selected item in the dropdown list
15324 * @event beforequery
15325 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15326 * The event object passed has these properties:
15327 * @param {Roo.bootstrap.ComboBox} combo This combo box
15328 * @param {String} query The query
15329 * @param {Boolean} forceAll true to force "all" query
15330 * @param {Boolean} cancel true to cancel the query
15331 * @param {Object} e The query event object
15333 'beforequery': true,
15336 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15337 * @param {Roo.bootstrap.ComboBox} combo This combo box
15342 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15343 * @param {Roo.bootstrap.ComboBox} combo This combo box
15344 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15349 * Fires when the remove value from the combobox array
15350 * @param {Roo.bootstrap.ComboBox} combo This combo box
15354 * @event afterremove
15355 * Fires when the remove value from the combobox array
15356 * @param {Roo.bootstrap.ComboBox} combo This combo box
15358 'afterremove' : true,
15360 * @event specialfilter
15361 * Fires when specialfilter
15362 * @param {Roo.bootstrap.ComboBox} combo This combo box
15364 'specialfilter' : true,
15367 * Fires when tick the element
15368 * @param {Roo.bootstrap.ComboBox} combo This combo box
15372 * @event touchviewdisplay
15373 * Fires when touch view require special display (default is using displayField)
15374 * @param {Roo.bootstrap.ComboBox} combo This combo box
15375 * @param {Object} cfg set html .
15377 'touchviewdisplay' : true
15382 this.tickItems = [];
15384 this.selectedIndex = -1;
15385 if(this.mode == 'local'){
15386 if(config.queryDelay === undefined){
15387 this.queryDelay = 10;
15389 if(config.minChars === undefined){
15395 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15398 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15399 * rendering into an Roo.Editor, defaults to false)
15402 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15403 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15406 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15409 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15410 * the dropdown list (defaults to undefined, with no header element)
15414 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15418 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15420 listWidth: undefined,
15422 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15423 * mode = 'remote' or 'text' if mode = 'local')
15425 displayField: undefined,
15428 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15429 * mode = 'remote' or 'value' if mode = 'local').
15430 * Note: use of a valueField requires the user make a selection
15431 * in order for a value to be mapped.
15433 valueField: undefined,
15435 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15440 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15441 * field's data value (defaults to the underlying DOM element's name)
15443 hiddenName: undefined,
15445 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15449 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15451 selectedClass: 'active',
15454 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15458 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15459 * anchor positions (defaults to 'tl-bl')
15461 listAlign: 'tl-bl?',
15463 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15467 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15468 * query specified by the allQuery config option (defaults to 'query')
15470 triggerAction: 'query',
15472 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15473 * (defaults to 4, does not apply if editable = false)
15477 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15478 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15482 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15483 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15487 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15488 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15492 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15493 * when editable = true (defaults to false)
15495 selectOnFocus:false,
15497 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15499 queryParam: 'query',
15501 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15502 * when mode = 'remote' (defaults to 'Loading...')
15504 loadingText: 'Loading...',
15506 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15510 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15514 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15515 * traditional select (defaults to true)
15519 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15523 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15527 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15528 * listWidth has a higher value)
15532 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15533 * allow the user to set arbitrary text into the field (defaults to false)
15535 forceSelection:false,
15537 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15538 * if typeAhead = true (defaults to 250)
15540 typeAheadDelay : 250,
15542 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15543 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15545 valueNotFoundText : undefined,
15547 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15549 blockFocus : false,
15552 * @cfg {Boolean} disableClear Disable showing of clear button.
15554 disableClear : false,
15556 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15558 alwaysQuery : false,
15561 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15566 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15568 invalidClass : "has-warning",
15571 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15573 validClass : "has-success",
15576 * @cfg {Boolean} specialFilter (true|false) special filter default false
15578 specialFilter : false,
15581 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15583 mobileTouchView : true,
15586 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15588 useNativeIOS : false,
15591 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15593 mobile_restrict_height : false,
15595 ios_options : false,
15607 btnPosition : 'right',
15608 triggerList : true,
15609 showToggleBtn : true,
15611 emptyResultText: 'Empty',
15612 triggerText : 'Select',
15616 // element that contains real text value.. (when hidden is used..)
15618 getAutoCreate : function()
15623 * Render classic select for iso
15626 if(Roo.isIOS && this.useNativeIOS){
15627 cfg = this.getAutoCreateNativeIOS();
15635 if(Roo.isTouch && this.mobileTouchView){
15636 cfg = this.getAutoCreateTouchView();
15643 if(!this.tickable){
15644 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15649 * ComboBox with tickable selections
15652 var align = this.labelAlign || this.parentLabelAlign();
15655 cls : 'form-group roo-combobox-tickable' //input-group
15658 var btn_text_select = '';
15659 var btn_text_done = '';
15660 var btn_text_cancel = '';
15662 if (this.btn_text_show) {
15663 btn_text_select = 'Select';
15664 btn_text_done = 'Done';
15665 btn_text_cancel = 'Cancel';
15670 cls : 'tickable-buttons',
15675 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15676 //html : this.triggerText
15677 html: btn_text_select
15683 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15685 html: btn_text_done
15691 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15693 html: btn_text_cancel
15699 buttons.cn.unshift({
15701 cls: 'roo-select2-search-field-input'
15707 Roo.each(buttons.cn, function(c){
15709 c.cls += ' btn-' + _this.size;
15712 if (_this.disabled) {
15719 style : 'display: contents',
15724 cls: 'form-hidden-field'
15728 cls: 'roo-select2-choices',
15732 cls: 'roo-select2-search-field',
15743 cls: 'roo-select2-container input-group roo-select2-container-multi',
15749 // cls: 'typeahead typeahead-long dropdown-menu',
15750 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15755 if(this.hasFeedback && !this.allowBlank){
15759 cls: 'glyphicon form-control-feedback'
15762 combobox.cn.push(feedback);
15769 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15770 tooltip : 'This field is required'
15772 if (Roo.bootstrap.version == 4) {
15775 style : 'display:none'
15778 if (align ==='left' && this.fieldLabel.length) {
15780 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15787 cls : 'control-label col-form-label',
15788 html : this.fieldLabel
15800 var labelCfg = cfg.cn[1];
15801 var contentCfg = cfg.cn[2];
15804 if(this.indicatorpos == 'right'){
15810 cls : 'control-label col-form-label',
15814 html : this.fieldLabel
15830 labelCfg = cfg.cn[0];
15831 contentCfg = cfg.cn[1];
15835 if(this.labelWidth > 12){
15836 labelCfg.style = "width: " + this.labelWidth + 'px';
15838 if(this.width * 1 > 0){
15839 contentCfg.style = "width: " + this.width + 'px';
15841 if(this.labelWidth < 13 && this.labelmd == 0){
15842 this.labelmd = this.labelWidth;
15845 if(this.labellg > 0){
15846 labelCfg.cls += ' col-lg-' + this.labellg;
15847 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15850 if(this.labelmd > 0){
15851 labelCfg.cls += ' col-md-' + this.labelmd;
15852 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15855 if(this.labelsm > 0){
15856 labelCfg.cls += ' col-sm-' + this.labelsm;
15857 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15860 if(this.labelxs > 0){
15861 labelCfg.cls += ' col-xs-' + this.labelxs;
15862 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15866 } else if ( this.fieldLabel.length) {
15867 // Roo.log(" label");
15872 //cls : 'input-group-addon',
15873 html : this.fieldLabel
15878 if(this.indicatorpos == 'right'){
15882 //cls : 'input-group-addon',
15883 html : this.fieldLabel
15893 // Roo.log(" no label && no align");
15900 ['xs','sm','md','lg'].map(function(size){
15901 if (settings[size]) {
15902 cfg.cls += ' col-' + size + '-' + settings[size];
15910 _initEventsCalled : false,
15913 initEvents: function()
15915 if (this._initEventsCalled) { // as we call render... prevent looping...
15918 this._initEventsCalled = true;
15921 throw "can not find store for combo";
15924 this.indicator = this.indicatorEl();
15926 this.store = Roo.factory(this.store, Roo.data);
15927 this.store.parent = this;
15929 // if we are building from html. then this element is so complex, that we can not really
15930 // use the rendered HTML.
15931 // so we have to trash and replace the previous code.
15932 if (Roo.XComponent.build_from_html) {
15933 // remove this element....
15934 var e = this.el.dom, k=0;
15935 while (e ) { e = e.previousSibling; ++k;}
15940 this.rendered = false;
15942 this.render(this.parent().getChildContainer(true), k);
15945 if(Roo.isIOS && this.useNativeIOS){
15946 this.initIOSView();
15954 if(Roo.isTouch && this.mobileTouchView){
15955 this.initTouchView();
15960 this.initTickableEvents();
15964 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15966 if(this.hiddenName){
15968 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15970 this.hiddenField.dom.value =
15971 this.hiddenValue !== undefined ? this.hiddenValue :
15972 this.value !== undefined ? this.value : '';
15974 // prevent input submission
15975 this.el.dom.removeAttribute('name');
15976 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15981 // this.el.dom.setAttribute('autocomplete', 'off');
15984 var cls = 'x-combo-list';
15986 //this.list = new Roo.Layer({
15987 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15993 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15994 _this.list.setWidth(lw);
15997 this.list.on('mouseover', this.onViewOver, this);
15998 this.list.on('mousemove', this.onViewMove, this);
15999 this.list.on('scroll', this.onViewScroll, this);
16002 this.list.swallowEvent('mousewheel');
16003 this.assetHeight = 0;
16006 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16007 this.assetHeight += this.header.getHeight();
16010 this.innerList = this.list.createChild({cls:cls+'-inner'});
16011 this.innerList.on('mouseover', this.onViewOver, this);
16012 this.innerList.on('mousemove', this.onViewMove, this);
16013 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16015 if(this.allowBlank && !this.pageSize && !this.disableClear){
16016 this.footer = this.list.createChild({cls:cls+'-ft'});
16017 this.pageTb = new Roo.Toolbar(this.footer);
16021 this.footer = this.list.createChild({cls:cls+'-ft'});
16022 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16023 {pageSize: this.pageSize});
16027 if (this.pageTb && this.allowBlank && !this.disableClear) {
16029 this.pageTb.add(new Roo.Toolbar.Fill(), {
16030 cls: 'x-btn-icon x-btn-clear',
16032 handler: function()
16035 _this.clearValue();
16036 _this.onSelect(false, -1);
16041 this.assetHeight += this.footer.getHeight();
16046 this.tpl = Roo.bootstrap.version == 4 ?
16047 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16048 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16051 this.view = new Roo.View(this.list, this.tpl, {
16052 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16054 //this.view.wrapEl.setDisplayed(false);
16055 this.view.on('click', this.onViewClick, this);
16058 this.store.on('beforeload', this.onBeforeLoad, this);
16059 this.store.on('load', this.onLoad, this);
16060 this.store.on('loadexception', this.onLoadException, this);
16062 if(this.resizable){
16063 this.resizer = new Roo.Resizable(this.list, {
16064 pinned:true, handles:'se'
16066 this.resizer.on('resize', function(r, w, h){
16067 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16068 this.listWidth = w;
16069 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16070 this.restrictHeight();
16072 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16075 if(!this.editable){
16076 this.editable = true;
16077 this.setEditable(false);
16082 if (typeof(this.events.add.listeners) != 'undefined') {
16084 this.addicon = this.wrap.createChild(
16085 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16087 this.addicon.on('click', function(e) {
16088 this.fireEvent('add', this);
16091 if (typeof(this.events.edit.listeners) != 'undefined') {
16093 this.editicon = this.wrap.createChild(
16094 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16095 if (this.addicon) {
16096 this.editicon.setStyle('margin-left', '40px');
16098 this.editicon.on('click', function(e) {
16100 // we fire even if inothing is selected..
16101 this.fireEvent('edit', this, this.lastData );
16107 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16108 "up" : function(e){
16109 this.inKeyMode = true;
16113 "down" : function(e){
16114 if(!this.isExpanded()){
16115 this.onTriggerClick();
16117 this.inKeyMode = true;
16122 "enter" : function(e){
16123 // this.onViewClick();
16127 if(this.fireEvent("specialkey", this, e)){
16128 this.onViewClick(false);
16134 "esc" : function(e){
16138 "tab" : function(e){
16141 if(this.fireEvent("specialkey", this, e)){
16142 this.onViewClick(false);
16150 doRelay : function(foo, bar, hname){
16151 if(hname == 'down' || this.scope.isExpanded()){
16152 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16161 this.queryDelay = Math.max(this.queryDelay || 10,
16162 this.mode == 'local' ? 10 : 250);
16165 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16167 if(this.typeAhead){
16168 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16170 if(this.editable !== false){
16171 this.inputEl().on("keyup", this.onKeyUp, this);
16173 if(this.forceSelection){
16174 this.inputEl().on('blur', this.doForce, this);
16178 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16179 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16183 initTickableEvents: function()
16187 if(this.hiddenName){
16189 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16191 this.hiddenField.dom.value =
16192 this.hiddenValue !== undefined ? this.hiddenValue :
16193 this.value !== undefined ? this.value : '';
16195 // prevent input submission
16196 this.el.dom.removeAttribute('name');
16197 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16202 // this.list = this.el.select('ul.dropdown-menu',true).first();
16204 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16205 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16206 if(this.triggerList){
16207 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16210 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16211 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16213 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16214 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16216 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16217 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16219 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16220 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16221 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16224 this.cancelBtn.hide();
16229 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16230 _this.list.setWidth(lw);
16233 this.list.on('mouseover', this.onViewOver, this);
16234 this.list.on('mousemove', this.onViewMove, this);
16236 this.list.on('scroll', this.onViewScroll, this);
16239 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16240 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16243 this.view = new Roo.View(this.list, this.tpl, {
16248 selectedClass: this.selectedClass
16251 //this.view.wrapEl.setDisplayed(false);
16252 this.view.on('click', this.onViewClick, this);
16256 this.store.on('beforeload', this.onBeforeLoad, this);
16257 this.store.on('load', this.onLoad, this);
16258 this.store.on('loadexception', this.onLoadException, this);
16261 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16262 "up" : function(e){
16263 this.inKeyMode = true;
16267 "down" : function(e){
16268 this.inKeyMode = true;
16272 "enter" : function(e){
16273 if(this.fireEvent("specialkey", this, e)){
16274 this.onViewClick(false);
16280 "esc" : function(e){
16281 this.onTickableFooterButtonClick(e, false, false);
16284 "tab" : function(e){
16285 this.fireEvent("specialkey", this, e);
16287 this.onTickableFooterButtonClick(e, false, false);
16294 doRelay : function(e, fn, key){
16295 if(this.scope.isExpanded()){
16296 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16305 this.queryDelay = Math.max(this.queryDelay || 10,
16306 this.mode == 'local' ? 10 : 250);
16309 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16311 if(this.typeAhead){
16312 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16315 if(this.editable !== false){
16316 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16319 this.indicator = this.indicatorEl();
16321 if(this.indicator){
16322 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16323 this.indicator.hide();
16328 onDestroy : function(){
16330 this.view.setStore(null);
16331 this.view.el.removeAllListeners();
16332 this.view.el.remove();
16333 this.view.purgeListeners();
16336 this.list.dom.innerHTML = '';
16340 this.store.un('beforeload', this.onBeforeLoad, this);
16341 this.store.un('load', this.onLoad, this);
16342 this.store.un('loadexception', this.onLoadException, this);
16344 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16348 fireKey : function(e){
16349 if(e.isNavKeyPress() && !this.list.isVisible()){
16350 this.fireEvent("specialkey", this, e);
16355 onResize: function(w, h)
16359 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16361 // if(typeof w != 'number'){
16362 // // we do not handle it!?!?
16365 // var tw = this.trigger.getWidth();
16366 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16367 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16369 // this.inputEl().setWidth( this.adjustWidth('input', x));
16371 // //this.trigger.setStyle('left', x+'px');
16373 // if(this.list && this.listWidth === undefined){
16374 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16375 // this.list.setWidth(lw);
16376 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16384 * Allow or prevent the user from directly editing the field text. If false is passed,
16385 * the user will only be able to select from the items defined in the dropdown list. This method
16386 * is the runtime equivalent of setting the 'editable' config option at config time.
16387 * @param {Boolean} value True to allow the user to directly edit the field text
16389 setEditable : function(value){
16390 if(value == this.editable){
16393 this.editable = value;
16395 this.inputEl().dom.setAttribute('readOnly', true);
16396 this.inputEl().on('mousedown', this.onTriggerClick, this);
16397 this.inputEl().addClass('x-combo-noedit');
16399 this.inputEl().dom.removeAttribute('readOnly');
16400 this.inputEl().un('mousedown', this.onTriggerClick, this);
16401 this.inputEl().removeClass('x-combo-noedit');
16407 onBeforeLoad : function(combo,opts){
16408 if(!this.hasFocus){
16412 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16414 this.restrictHeight();
16415 this.selectedIndex = -1;
16419 onLoad : function(){
16421 this.hasQuery = false;
16423 if(!this.hasFocus){
16427 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16428 this.loading.hide();
16431 if(this.store.getCount() > 0){
16434 this.restrictHeight();
16435 if(this.lastQuery == this.allQuery){
16436 if(this.editable && !this.tickable){
16437 this.inputEl().dom.select();
16441 !this.selectByValue(this.value, true) &&
16444 !this.store.lastOptions ||
16445 typeof(this.store.lastOptions.add) == 'undefined' ||
16446 this.store.lastOptions.add != true
16449 this.select(0, true);
16452 if(this.autoFocus){
16455 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16456 this.taTask.delay(this.typeAheadDelay);
16460 this.onEmptyResults();
16466 onLoadException : function()
16468 this.hasQuery = false;
16470 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16471 this.loading.hide();
16474 if(this.tickable && this.editable){
16479 // only causes errors at present
16480 //Roo.log(this.store.reader.jsonData);
16481 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16483 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16489 onTypeAhead : function(){
16490 if(this.store.getCount() > 0){
16491 var r = this.store.getAt(0);
16492 var newValue = r.data[this.displayField];
16493 var len = newValue.length;
16494 var selStart = this.getRawValue().length;
16496 if(selStart != len){
16497 this.setRawValue(newValue);
16498 this.selectText(selStart, newValue.length);
16504 onSelect : function(record, index){
16506 if(this.fireEvent('beforeselect', this, record, index) !== false){
16508 this.setFromData(index > -1 ? record.data : false);
16511 this.fireEvent('select', this, record, index);
16516 * Returns the currently selected field value or empty string if no value is set.
16517 * @return {String} value The selected value
16519 getValue : function()
16521 if(Roo.isIOS && this.useNativeIOS){
16522 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16526 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16529 if(this.valueField){
16530 return typeof this.value != 'undefined' ? this.value : '';
16532 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16536 getRawValue : function()
16538 if(Roo.isIOS && this.useNativeIOS){
16539 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16542 var v = this.inputEl().getValue();
16548 * Clears any text/value currently set in the field
16550 clearValue : function(){
16552 if(this.hiddenField){
16553 this.hiddenField.dom.value = '';
16556 this.setRawValue('');
16557 this.lastSelectionText = '';
16558 this.lastData = false;
16560 var close = this.closeTriggerEl();
16571 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16572 * will be displayed in the field. If the value does not match the data value of an existing item,
16573 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16574 * Otherwise the field will be blank (although the value will still be set).
16575 * @param {String} value The value to match
16577 setValue : function(v)
16579 if(Roo.isIOS && this.useNativeIOS){
16580 this.setIOSValue(v);
16590 if(this.valueField){
16591 var r = this.findRecord(this.valueField, v);
16593 text = r.data[this.displayField];
16594 }else if(this.valueNotFoundText !== undefined){
16595 text = this.valueNotFoundText;
16598 this.lastSelectionText = text;
16599 if(this.hiddenField){
16600 this.hiddenField.dom.value = v;
16602 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16605 var close = this.closeTriggerEl();
16608 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16614 * @property {Object} the last set data for the element
16619 * Sets the value of the field based on a object which is related to the record format for the store.
16620 * @param {Object} value the value to set as. or false on reset?
16622 setFromData : function(o){
16629 var dv = ''; // display value
16630 var vv = ''; // value value..
16632 if (this.displayField) {
16633 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16635 // this is an error condition!!!
16636 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16639 if(this.valueField){
16640 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16643 var close = this.closeTriggerEl();
16646 if(dv.length || vv * 1 > 0){
16648 this.blockFocus=true;
16654 if(this.hiddenField){
16655 this.hiddenField.dom.value = vv;
16657 this.lastSelectionText = dv;
16658 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16662 // no hidden field.. - we store the value in 'value', but still display
16663 // display field!!!!
16664 this.lastSelectionText = dv;
16665 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16672 reset : function(){
16673 // overridden so that last data is reset..
16680 this.setValue(this.originalValue);
16681 //this.clearInvalid();
16682 this.lastData = false;
16684 this.view.clearSelections();
16690 findRecord : function(prop, value){
16692 if(this.store.getCount() > 0){
16693 this.store.each(function(r){
16694 if(r.data[prop] == value){
16704 getName: function()
16706 // returns hidden if it's set..
16707 if (!this.rendered) {return ''};
16708 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16712 onViewMove : function(e, t){
16713 this.inKeyMode = false;
16717 onViewOver : function(e, t){
16718 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16721 var item = this.view.findItemFromChild(t);
16724 var index = this.view.indexOf(item);
16725 this.select(index, false);
16730 onViewClick : function(view, doFocus, el, e)
16732 var index = this.view.getSelectedIndexes()[0];
16734 var r = this.store.getAt(index);
16738 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16745 Roo.each(this.tickItems, function(v,k){
16747 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16749 _this.tickItems.splice(k, 1);
16751 if(typeof(e) == 'undefined' && view == false){
16752 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16764 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16765 this.tickItems.push(r.data);
16768 if(typeof(e) == 'undefined' && view == false){
16769 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16776 this.onSelect(r, index);
16778 if(doFocus !== false && !this.blockFocus){
16779 this.inputEl().focus();
16784 restrictHeight : function(){
16785 //this.innerList.dom.style.height = '';
16786 //var inner = this.innerList.dom;
16787 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16788 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16789 //this.list.beginUpdate();
16790 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16791 this.list.alignTo(this.inputEl(), this.listAlign);
16792 this.list.alignTo(this.inputEl(), this.listAlign);
16793 //this.list.endUpdate();
16797 onEmptyResults : function(){
16799 if(this.tickable && this.editable){
16800 this.hasFocus = false;
16801 this.restrictHeight();
16809 * Returns true if the dropdown list is expanded, else false.
16811 isExpanded : function(){
16812 return this.list.isVisible();
16816 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16817 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16818 * @param {String} value The data value of the item to select
16819 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16820 * selected item if it is not currently in view (defaults to true)
16821 * @return {Boolean} True if the value matched an item in the list, else false
16823 selectByValue : function(v, scrollIntoView){
16824 if(v !== undefined && v !== null){
16825 var r = this.findRecord(this.valueField || this.displayField, v);
16827 this.select(this.store.indexOf(r), scrollIntoView);
16835 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16836 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16837 * @param {Number} index The zero-based index of the list item to select
16838 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16839 * selected item if it is not currently in view (defaults to true)
16841 select : function(index, scrollIntoView){
16842 this.selectedIndex = index;
16843 this.view.select(index);
16844 if(scrollIntoView !== false){
16845 var el = this.view.getNode(index);
16847 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16850 this.list.scrollChildIntoView(el, false);
16856 selectNext : function(){
16857 var ct = this.store.getCount();
16859 if(this.selectedIndex == -1){
16861 }else if(this.selectedIndex < ct-1){
16862 this.select(this.selectedIndex+1);
16868 selectPrev : function(){
16869 var ct = this.store.getCount();
16871 if(this.selectedIndex == -1){
16873 }else if(this.selectedIndex != 0){
16874 this.select(this.selectedIndex-1);
16880 onKeyUp : function(e){
16881 if(this.editable !== false && !e.isSpecialKey()){
16882 this.lastKey = e.getKey();
16883 this.dqTask.delay(this.queryDelay);
16888 validateBlur : function(){
16889 return !this.list || !this.list.isVisible();
16893 initQuery : function(){
16895 var v = this.getRawValue();
16897 if(this.tickable && this.editable){
16898 v = this.tickableInputEl().getValue();
16905 doForce : function(){
16906 if(this.inputEl().dom.value.length > 0){
16907 this.inputEl().dom.value =
16908 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16914 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16915 * query allowing the query action to be canceled if needed.
16916 * @param {String} query The SQL query to execute
16917 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16918 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16919 * saved in the current store (defaults to false)
16921 doQuery : function(q, forceAll){
16923 if(q === undefined || q === null){
16928 forceAll: forceAll,
16932 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16937 forceAll = qe.forceAll;
16938 if(forceAll === true || (q.length >= this.minChars)){
16940 this.hasQuery = true;
16942 if(this.lastQuery != q || this.alwaysQuery){
16943 this.lastQuery = q;
16944 if(this.mode == 'local'){
16945 this.selectedIndex = -1;
16947 this.store.clearFilter();
16950 if(this.specialFilter){
16951 this.fireEvent('specialfilter', this);
16956 this.store.filter(this.displayField, q);
16959 this.store.fireEvent("datachanged", this.store);
16966 this.store.baseParams[this.queryParam] = q;
16968 var options = {params : this.getParams(q)};
16971 options.add = true;
16972 options.params.start = this.page * this.pageSize;
16975 this.store.load(options);
16978 * this code will make the page width larger, at the beginning, the list not align correctly,
16979 * we should expand the list on onLoad
16980 * so command out it
16985 this.selectedIndex = -1;
16990 this.loadNext = false;
16994 getParams : function(q){
16996 //p[this.queryParam] = q;
17000 p.limit = this.pageSize;
17006 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17008 collapse : function(){
17009 if(!this.isExpanded()){
17015 this.hasFocus = false;
17019 this.cancelBtn.hide();
17020 this.trigger.show();
17023 this.tickableInputEl().dom.value = '';
17024 this.tickableInputEl().blur();
17029 Roo.get(document).un('mousedown', this.collapseIf, this);
17030 Roo.get(document).un('mousewheel', this.collapseIf, this);
17031 if (!this.editable) {
17032 Roo.get(document).un('keydown', this.listKeyPress, this);
17034 this.fireEvent('collapse', this);
17040 collapseIf : function(e){
17041 var in_combo = e.within(this.el);
17042 var in_list = e.within(this.list);
17043 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17045 if (in_combo || in_list || is_list) {
17046 //e.stopPropagation();
17051 this.onTickableFooterButtonClick(e, false, false);
17059 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17061 expand : function(){
17063 if(this.isExpanded() || !this.hasFocus){
17067 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17068 this.list.setWidth(lw);
17074 this.restrictHeight();
17078 this.tickItems = Roo.apply([], this.item);
17081 this.cancelBtn.show();
17082 this.trigger.hide();
17085 this.tickableInputEl().focus();
17090 Roo.get(document).on('mousedown', this.collapseIf, this);
17091 Roo.get(document).on('mousewheel', this.collapseIf, this);
17092 if (!this.editable) {
17093 Roo.get(document).on('keydown', this.listKeyPress, this);
17096 this.fireEvent('expand', this);
17100 // Implements the default empty TriggerField.onTriggerClick function
17101 onTriggerClick : function(e)
17103 Roo.log('trigger click');
17105 if(this.disabled || !this.triggerList){
17110 this.loadNext = false;
17112 if(this.isExpanded()){
17114 if (!this.blockFocus) {
17115 this.inputEl().focus();
17119 this.hasFocus = true;
17120 if(this.triggerAction == 'all') {
17121 this.doQuery(this.allQuery, true);
17123 this.doQuery(this.getRawValue());
17125 if (!this.blockFocus) {
17126 this.inputEl().focus();
17131 onTickableTriggerClick : function(e)
17138 this.loadNext = false;
17139 this.hasFocus = true;
17141 if(this.triggerAction == 'all') {
17142 this.doQuery(this.allQuery, true);
17144 this.doQuery(this.getRawValue());
17148 onSearchFieldClick : function(e)
17150 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17151 this.onTickableFooterButtonClick(e, false, false);
17155 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17160 this.loadNext = false;
17161 this.hasFocus = true;
17163 if(this.triggerAction == 'all') {
17164 this.doQuery(this.allQuery, true);
17166 this.doQuery(this.getRawValue());
17170 listKeyPress : function(e)
17172 //Roo.log('listkeypress');
17173 // scroll to first matching element based on key pres..
17174 if (e.isSpecialKey()) {
17177 var k = String.fromCharCode(e.getKey()).toUpperCase();
17180 var csel = this.view.getSelectedNodes();
17181 var cselitem = false;
17183 var ix = this.view.indexOf(csel[0]);
17184 cselitem = this.store.getAt(ix);
17185 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17191 this.store.each(function(v) {
17193 // start at existing selection.
17194 if (cselitem.id == v.id) {
17200 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17201 match = this.store.indexOf(v);
17207 if (match === false) {
17208 return true; // no more action?
17211 this.view.select(match);
17212 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17213 sn.scrollIntoView(sn.dom.parentNode, false);
17216 onViewScroll : function(e, t){
17218 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){
17222 this.hasQuery = true;
17224 this.loading = this.list.select('.loading', true).first();
17226 if(this.loading === null){
17227 this.list.createChild({
17229 cls: 'loading roo-select2-more-results roo-select2-active',
17230 html: 'Loading more results...'
17233 this.loading = this.list.select('.loading', true).first();
17235 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17237 this.loading.hide();
17240 this.loading.show();
17245 this.loadNext = true;
17247 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17252 addItem : function(o)
17254 var dv = ''; // display value
17256 if (this.displayField) {
17257 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17259 // this is an error condition!!!
17260 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17267 var choice = this.choices.createChild({
17269 cls: 'roo-select2-search-choice',
17278 cls: 'roo-select2-search-choice-close fa fa-times',
17283 }, this.searchField);
17285 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17287 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17295 this.inputEl().dom.value = '';
17300 onRemoveItem : function(e, _self, o)
17302 e.preventDefault();
17304 this.lastItem = Roo.apply([], this.item);
17306 var index = this.item.indexOf(o.data) * 1;
17309 Roo.log('not this item?!');
17313 this.item.splice(index, 1);
17318 this.fireEvent('remove', this, e);
17324 syncValue : function()
17326 if(!this.item.length){
17333 Roo.each(this.item, function(i){
17334 if(_this.valueField){
17335 value.push(i[_this.valueField]);
17342 this.value = value.join(',');
17344 if(this.hiddenField){
17345 this.hiddenField.dom.value = this.value;
17348 this.store.fireEvent("datachanged", this.store);
17353 clearItem : function()
17355 if(!this.multiple){
17361 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17369 if(this.tickable && !Roo.isTouch){
17370 this.view.refresh();
17374 inputEl: function ()
17376 if(Roo.isIOS && this.useNativeIOS){
17377 return this.el.select('select.roo-ios-select', true).first();
17380 if(Roo.isTouch && this.mobileTouchView){
17381 return this.el.select('input.form-control',true).first();
17385 return this.searchField;
17388 return this.el.select('input.form-control',true).first();
17391 onTickableFooterButtonClick : function(e, btn, el)
17393 e.preventDefault();
17395 this.lastItem = Roo.apply([], this.item);
17397 if(btn && btn.name == 'cancel'){
17398 this.tickItems = Roo.apply([], this.item);
17407 Roo.each(this.tickItems, function(o){
17415 validate : function()
17417 if(this.getVisibilityEl().hasClass('hidden')){
17421 var v = this.getRawValue();
17424 v = this.getValue();
17427 if(this.disabled || this.allowBlank || v.length){
17432 this.markInvalid();
17436 tickableInputEl : function()
17438 if(!this.tickable || !this.editable){
17439 return this.inputEl();
17442 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17446 getAutoCreateTouchView : function()
17451 cls: 'form-group' //input-group
17457 type : this.inputType,
17458 cls : 'form-control x-combo-noedit',
17459 autocomplete: 'new-password',
17460 placeholder : this.placeholder || '',
17465 input.name = this.name;
17469 input.cls += ' input-' + this.size;
17472 if (this.disabled) {
17473 input.disabled = true;
17477 cls : 'roo-combobox-wrap',
17484 inputblock.cls += ' input-group';
17486 inputblock.cn.unshift({
17488 cls : 'input-group-addon input-group-prepend input-group-text',
17493 if(this.removable && !this.multiple){
17494 inputblock.cls += ' roo-removable';
17496 inputblock.cn.push({
17499 cls : 'roo-combo-removable-btn close'
17503 if(this.hasFeedback && !this.allowBlank){
17505 inputblock.cls += ' has-feedback';
17507 inputblock.cn.push({
17509 cls: 'glyphicon form-control-feedback'
17516 inputblock.cls += (this.before) ? '' : ' input-group';
17518 inputblock.cn.push({
17520 cls : 'input-group-addon input-group-append input-group-text',
17526 var ibwrap = inputblock;
17531 cls: 'roo-select2-choices',
17535 cls: 'roo-select2-search-field',
17548 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17553 cls: 'form-hidden-field'
17559 if(!this.multiple && this.showToggleBtn){
17565 if (this.caret != false) {
17568 cls: 'fa fa-' + this.caret
17575 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17577 Roo.bootstrap.version == 3 ? caret : '',
17580 cls: 'combobox-clear',
17594 combobox.cls += ' roo-select2-container-multi';
17597 var required = this.allowBlank ? {
17599 style: 'display: none'
17602 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17603 tooltip : 'This field is required'
17606 var align = this.labelAlign || this.parentLabelAlign();
17608 if (align ==='left' && this.fieldLabel.length) {
17614 cls : 'control-label col-form-label',
17615 html : this.fieldLabel
17619 cls : 'roo-combobox-wrap ',
17626 var labelCfg = cfg.cn[1];
17627 var contentCfg = cfg.cn[2];
17630 if(this.indicatorpos == 'right'){
17635 cls : 'control-label col-form-label',
17639 html : this.fieldLabel
17645 cls : "roo-combobox-wrap ",
17653 labelCfg = cfg.cn[0];
17654 contentCfg = cfg.cn[1];
17659 if(this.labelWidth > 12){
17660 labelCfg.style = "width: " + this.labelWidth + 'px';
17663 if(this.labelWidth < 13 && this.labelmd == 0){
17664 this.labelmd = this.labelWidth;
17667 if(this.labellg > 0){
17668 labelCfg.cls += ' col-lg-' + this.labellg;
17669 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17672 if(this.labelmd > 0){
17673 labelCfg.cls += ' col-md-' + this.labelmd;
17674 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17677 if(this.labelsm > 0){
17678 labelCfg.cls += ' col-sm-' + this.labelsm;
17679 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17682 if(this.labelxs > 0){
17683 labelCfg.cls += ' col-xs-' + this.labelxs;
17684 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17688 } else if ( this.fieldLabel.length) {
17693 cls : 'control-label',
17694 html : this.fieldLabel
17705 if(this.indicatorpos == 'right'){
17709 cls : 'control-label',
17710 html : this.fieldLabel,
17728 var settings = this;
17730 ['xs','sm','md','lg'].map(function(size){
17731 if (settings[size]) {
17732 cfg.cls += ' col-' + size + '-' + settings[size];
17739 initTouchView : function()
17741 this.renderTouchView();
17743 this.touchViewEl.on('scroll', function(){
17744 this.el.dom.scrollTop = 0;
17747 this.originalValue = this.getValue();
17749 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17751 this.inputEl().on("click", this.showTouchView, this);
17752 if (this.triggerEl) {
17753 this.triggerEl.on("click", this.showTouchView, this);
17757 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17758 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17760 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17762 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17763 this.store.on('load', this.onTouchViewLoad, this);
17764 this.store.on('loadexception', this.onTouchViewLoadException, this);
17766 if(this.hiddenName){
17768 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17770 this.hiddenField.dom.value =
17771 this.hiddenValue !== undefined ? this.hiddenValue :
17772 this.value !== undefined ? this.value : '';
17774 this.el.dom.removeAttribute('name');
17775 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17779 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17780 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17783 if(this.removable && !this.multiple){
17784 var close = this.closeTriggerEl();
17786 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17787 close.on('click', this.removeBtnClick, this, close);
17791 * fix the bug in Safari iOS8
17793 this.inputEl().on("focus", function(e){
17794 document.activeElement.blur();
17797 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17804 renderTouchView : function()
17806 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17807 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17809 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17810 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17812 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17813 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17814 this.touchViewBodyEl.setStyle('overflow', 'auto');
17816 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17817 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17819 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17820 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17824 showTouchView : function()
17830 this.touchViewHeaderEl.hide();
17832 if(this.modalTitle.length){
17833 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17834 this.touchViewHeaderEl.show();
17837 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17838 this.touchViewEl.show();
17840 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17842 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17843 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17845 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17847 if(this.modalTitle.length){
17848 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17851 this.touchViewBodyEl.setHeight(bodyHeight);
17855 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17857 this.touchViewEl.addClass(['in','show']);
17860 if(this._touchViewMask){
17861 Roo.get(document.body).addClass("x-body-masked");
17862 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17863 this._touchViewMask.setStyle('z-index', 10000);
17864 this._touchViewMask.addClass('show');
17867 this.doTouchViewQuery();
17871 hideTouchView : function()
17873 this.touchViewEl.removeClass(['in','show']);
17877 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17879 this.touchViewEl.setStyle('display', 'none');
17882 if(this._touchViewMask){
17883 this._touchViewMask.removeClass('show');
17884 Roo.get(document.body).removeClass("x-body-masked");
17888 setTouchViewValue : function()
17895 Roo.each(this.tickItems, function(o){
17900 this.hideTouchView();
17903 doTouchViewQuery : function()
17912 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17916 if(!this.alwaysQuery || this.mode == 'local'){
17917 this.onTouchViewLoad();
17924 onTouchViewBeforeLoad : function(combo,opts)
17930 onTouchViewLoad : function()
17932 if(this.store.getCount() < 1){
17933 this.onTouchViewEmptyResults();
17937 this.clearTouchView();
17939 var rawValue = this.getRawValue();
17941 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17943 this.tickItems = [];
17945 this.store.data.each(function(d, rowIndex){
17946 var row = this.touchViewListGroup.createChild(template);
17948 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17949 row.addClass(d.data.cls);
17952 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17955 html : d.data[this.displayField]
17958 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17959 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17962 row.removeClass('selected');
17963 if(!this.multiple && this.valueField &&
17964 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17967 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17968 row.addClass('selected');
17971 if(this.multiple && this.valueField &&
17972 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17976 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17977 this.tickItems.push(d.data);
17980 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17984 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17986 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17988 if(this.modalTitle.length){
17989 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17992 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17994 if(this.mobile_restrict_height && listHeight < bodyHeight){
17995 this.touchViewBodyEl.setHeight(listHeight);
18000 if(firstChecked && listHeight > bodyHeight){
18001 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18006 onTouchViewLoadException : function()
18008 this.hideTouchView();
18011 onTouchViewEmptyResults : function()
18013 this.clearTouchView();
18015 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18017 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18021 clearTouchView : function()
18023 this.touchViewListGroup.dom.innerHTML = '';
18026 onTouchViewClick : function(e, el, o)
18028 e.preventDefault();
18031 var rowIndex = o.rowIndex;
18033 var r = this.store.getAt(rowIndex);
18035 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18037 if(!this.multiple){
18038 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18039 c.dom.removeAttribute('checked');
18042 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18044 this.setFromData(r.data);
18046 var close = this.closeTriggerEl();
18052 this.hideTouchView();
18054 this.fireEvent('select', this, r, rowIndex);
18059 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18060 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18061 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18065 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18066 this.addItem(r.data);
18067 this.tickItems.push(r.data);
18071 getAutoCreateNativeIOS : function()
18074 cls: 'form-group' //input-group,
18079 cls : 'roo-ios-select'
18083 combobox.name = this.name;
18086 if (this.disabled) {
18087 combobox.disabled = true;
18090 var settings = this;
18092 ['xs','sm','md','lg'].map(function(size){
18093 if (settings[size]) {
18094 cfg.cls += ' col-' + size + '-' + settings[size];
18104 initIOSView : function()
18106 this.store.on('load', this.onIOSViewLoad, this);
18111 onIOSViewLoad : function()
18113 if(this.store.getCount() < 1){
18117 this.clearIOSView();
18119 if(this.allowBlank) {
18121 var default_text = '-- SELECT --';
18123 if(this.placeholder.length){
18124 default_text = this.placeholder;
18127 if(this.emptyTitle.length){
18128 default_text += ' - ' + this.emptyTitle + ' -';
18131 var opt = this.inputEl().createChild({
18134 html : default_text
18138 o[this.valueField] = 0;
18139 o[this.displayField] = default_text;
18141 this.ios_options.push({
18148 this.store.data.each(function(d, rowIndex){
18152 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18153 html = d.data[this.displayField];
18158 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18159 value = d.data[this.valueField];
18168 if(this.value == d.data[this.valueField]){
18169 option['selected'] = true;
18172 var opt = this.inputEl().createChild(option);
18174 this.ios_options.push({
18181 this.inputEl().on('change', function(){
18182 this.fireEvent('select', this);
18187 clearIOSView: function()
18189 this.inputEl().dom.innerHTML = '';
18191 this.ios_options = [];
18194 setIOSValue: function(v)
18198 if(!this.ios_options){
18202 Roo.each(this.ios_options, function(opts){
18204 opts.el.dom.removeAttribute('selected');
18206 if(opts.data[this.valueField] != v){
18210 opts.el.dom.setAttribute('selected', true);
18216 * @cfg {Boolean} grow
18220 * @cfg {Number} growMin
18224 * @cfg {Number} growMax
18233 Roo.apply(Roo.bootstrap.ComboBox, {
18237 cls: 'modal-header',
18259 cls: 'list-group-item',
18263 cls: 'roo-combobox-list-group-item-value'
18267 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18281 listItemCheckbox : {
18283 cls: 'list-group-item',
18287 cls: 'roo-combobox-list-group-item-value'
18291 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18307 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18312 cls: 'modal-footer',
18320 cls: 'col-xs-6 text-left',
18323 cls: 'btn btn-danger roo-touch-view-cancel',
18329 cls: 'col-xs-6 text-right',
18332 cls: 'btn btn-success roo-touch-view-ok',
18343 Roo.apply(Roo.bootstrap.ComboBox, {
18345 touchViewTemplate : {
18347 cls: 'modal fade roo-combobox-touch-view',
18351 cls: 'modal-dialog',
18352 style : 'position:fixed', // we have to fix position....
18356 cls: 'modal-content',
18358 Roo.bootstrap.ComboBox.header,
18359 Roo.bootstrap.ComboBox.body,
18360 Roo.bootstrap.ComboBox.footer
18369 * Ext JS Library 1.1.1
18370 * Copyright(c) 2006-2007, Ext JS, LLC.
18372 * Originally Released Under LGPL - original licence link has changed is not relivant.
18375 * <script type="text/javascript">
18380 * @extends Roo.util.Observable
18381 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18382 * This class also supports single and multi selection modes. <br>
18383 * Create a data model bound view:
18385 var store = new Roo.data.Store(...);
18387 var view = new Roo.View({
18389 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18391 singleSelect: true,
18392 selectedClass: "ydataview-selected",
18396 // listen for node click?
18397 view.on("click", function(vw, index, node, e){
18398 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18402 dataModel.load("foobar.xml");
18404 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18406 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18407 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18409 * Note: old style constructor is still suported (container, template, config)
18412 * Create a new View
18413 * @param {Object} config The config object
18416 Roo.View = function(config, depreciated_tpl, depreciated_config){
18418 this.parent = false;
18420 if (typeof(depreciated_tpl) == 'undefined') {
18421 // new way.. - universal constructor.
18422 Roo.apply(this, config);
18423 this.el = Roo.get(this.el);
18426 this.el = Roo.get(config);
18427 this.tpl = depreciated_tpl;
18428 Roo.apply(this, depreciated_config);
18430 this.wrapEl = this.el.wrap().wrap();
18431 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18434 if(typeof(this.tpl) == "string"){
18435 this.tpl = new Roo.Template(this.tpl);
18437 // support xtype ctors..
18438 this.tpl = new Roo.factory(this.tpl, Roo);
18442 this.tpl.compile();
18447 * @event beforeclick
18448 * Fires before a click is processed. Returns false to cancel the default action.
18449 * @param {Roo.View} this
18450 * @param {Number} index The index of the target node
18451 * @param {HTMLElement} node The target node
18452 * @param {Roo.EventObject} e The raw event object
18454 "beforeclick" : true,
18457 * Fires when a template node is clicked.
18458 * @param {Roo.View} this
18459 * @param {Number} index The index of the target node
18460 * @param {HTMLElement} node The target node
18461 * @param {Roo.EventObject} e The raw event object
18466 * Fires when a template node is double 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
18474 * @event contextmenu
18475 * Fires when a template node is right 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
18481 "contextmenu" : true,
18483 * @event selectionchange
18484 * Fires when the selected nodes change.
18485 * @param {Roo.View} this
18486 * @param {Array} selections Array of the selected nodes
18488 "selectionchange" : true,
18491 * @event beforeselect
18492 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18493 * @param {Roo.View} this
18494 * @param {HTMLElement} node The node to be selected
18495 * @param {Array} selections Array of currently selected nodes
18497 "beforeselect" : true,
18499 * @event preparedata
18500 * Fires on every row to render, to allow you to change the data.
18501 * @param {Roo.View} this
18502 * @param {Object} data to be rendered (change this)
18504 "preparedata" : true
18512 "click": this.onClick,
18513 "dblclick": this.onDblClick,
18514 "contextmenu": this.onContextMenu,
18518 this.selections = [];
18520 this.cmp = new Roo.CompositeElementLite([]);
18522 this.store = Roo.factory(this.store, Roo.data);
18523 this.setStore(this.store, true);
18526 if ( this.footer && this.footer.xtype) {
18528 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18530 this.footer.dataSource = this.store;
18531 this.footer.container = fctr;
18532 this.footer = Roo.factory(this.footer, Roo);
18533 fctr.insertFirst(this.el);
18535 // this is a bit insane - as the paging toolbar seems to detach the el..
18536 // dom.parentNode.parentNode.parentNode
18537 // they get detached?
18541 Roo.View.superclass.constructor.call(this);
18546 Roo.extend(Roo.View, Roo.util.Observable, {
18549 * @cfg {Roo.data.Store} store Data store to load data from.
18554 * @cfg {String|Roo.Element} el The container element.
18559 * @cfg {String|Roo.Template} tpl The template used by this View
18563 * @cfg {String} dataName the named area of the template to use as the data area
18564 * Works with domtemplates roo-name="name"
18568 * @cfg {String} selectedClass The css class to add to selected nodes
18570 selectedClass : "x-view-selected",
18572 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18577 * @cfg {String} text to display on mask (default Loading)
18581 * @cfg {Boolean} multiSelect Allow multiple selection
18583 multiSelect : false,
18585 * @cfg {Boolean} singleSelect Allow single selection
18587 singleSelect: false,
18590 * @cfg {Boolean} toggleSelect - selecting
18592 toggleSelect : false,
18595 * @cfg {Boolean} tickable - selecting
18600 * Returns the element this view is bound to.
18601 * @return {Roo.Element}
18603 getEl : function(){
18604 return this.wrapEl;
18610 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18612 refresh : function(){
18613 //Roo.log('refresh');
18616 // if we are using something like 'domtemplate', then
18617 // the what gets used is:
18618 // t.applySubtemplate(NAME, data, wrapping data..)
18619 // the outer template then get' applied with
18620 // the store 'extra data'
18621 // and the body get's added to the
18622 // roo-name="data" node?
18623 // <span class='roo-tpl-{name}'></span> ?????
18627 this.clearSelections();
18628 this.el.update("");
18630 var records = this.store.getRange();
18631 if(records.length < 1) {
18633 // is this valid?? = should it render a template??
18635 this.el.update(this.emptyText);
18639 if (this.dataName) {
18640 this.el.update(t.apply(this.store.meta)); //????
18641 el = this.el.child('.roo-tpl-' + this.dataName);
18644 for(var i = 0, len = records.length; i < len; i++){
18645 var data = this.prepareData(records[i].data, i, records[i]);
18646 this.fireEvent("preparedata", this, data, i, records[i]);
18648 var d = Roo.apply({}, data);
18651 Roo.apply(d, {'roo-id' : Roo.id()});
18655 Roo.each(this.parent.item, function(item){
18656 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18659 Roo.apply(d, {'roo-data-checked' : 'checked'});
18663 html[html.length] = Roo.util.Format.trim(
18665 t.applySubtemplate(this.dataName, d, this.store.meta) :
18672 el.update(html.join(""));
18673 this.nodes = el.dom.childNodes;
18674 this.updateIndexes(0);
18679 * Function to override to reformat the data that is sent to
18680 * the template for each node.
18681 * DEPRICATED - use the preparedata event handler.
18682 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18683 * a JSON object for an UpdateManager bound view).
18685 prepareData : function(data, index, record)
18687 this.fireEvent("preparedata", this, data, index, record);
18691 onUpdate : function(ds, record){
18692 // Roo.log('on update');
18693 this.clearSelections();
18694 var index = this.store.indexOf(record);
18695 var n = this.nodes[index];
18696 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18697 n.parentNode.removeChild(n);
18698 this.updateIndexes(index, index);
18704 onAdd : function(ds, records, index)
18706 //Roo.log(['on Add', ds, records, index] );
18707 this.clearSelections();
18708 if(this.nodes.length == 0){
18712 var n = this.nodes[index];
18713 for(var i = 0, len = records.length; i < len; i++){
18714 var d = this.prepareData(records[i].data, i, records[i]);
18716 this.tpl.insertBefore(n, d);
18719 this.tpl.append(this.el, d);
18722 this.updateIndexes(index);
18725 onRemove : function(ds, record, index){
18726 // Roo.log('onRemove');
18727 this.clearSelections();
18728 var el = this.dataName ?
18729 this.el.child('.roo-tpl-' + this.dataName) :
18732 el.dom.removeChild(this.nodes[index]);
18733 this.updateIndexes(index);
18737 * Refresh an individual node.
18738 * @param {Number} index
18740 refreshNode : function(index){
18741 this.onUpdate(this.store, this.store.getAt(index));
18744 updateIndexes : function(startIndex, endIndex){
18745 var ns = this.nodes;
18746 startIndex = startIndex || 0;
18747 endIndex = endIndex || ns.length - 1;
18748 for(var i = startIndex; i <= endIndex; i++){
18749 ns[i].nodeIndex = i;
18754 * Changes the data store this view uses and refresh the view.
18755 * @param {Store} store
18757 setStore : function(store, initial){
18758 if(!initial && this.store){
18759 this.store.un("datachanged", this.refresh);
18760 this.store.un("add", this.onAdd);
18761 this.store.un("remove", this.onRemove);
18762 this.store.un("update", this.onUpdate);
18763 this.store.un("clear", this.refresh);
18764 this.store.un("beforeload", this.onBeforeLoad);
18765 this.store.un("load", this.onLoad);
18766 this.store.un("loadexception", this.onLoad);
18770 store.on("datachanged", this.refresh, this);
18771 store.on("add", this.onAdd, this);
18772 store.on("remove", this.onRemove, this);
18773 store.on("update", this.onUpdate, this);
18774 store.on("clear", this.refresh, this);
18775 store.on("beforeload", this.onBeforeLoad, this);
18776 store.on("load", this.onLoad, this);
18777 store.on("loadexception", this.onLoad, this);
18785 * onbeforeLoad - masks the loading area.
18788 onBeforeLoad : function(store,opts)
18790 //Roo.log('onBeforeLoad');
18792 this.el.update("");
18794 this.el.mask(this.mask ? this.mask : "Loading" );
18796 onLoad : function ()
18803 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18804 * @param {HTMLElement} node
18805 * @return {HTMLElement} The template node
18807 findItemFromChild : function(node){
18808 var el = this.dataName ?
18809 this.el.child('.roo-tpl-' + this.dataName,true) :
18812 if(!node || node.parentNode == el){
18815 var p = node.parentNode;
18816 while(p && p != el){
18817 if(p.parentNode == el){
18826 onClick : function(e){
18827 var item = this.findItemFromChild(e.getTarget());
18829 var index = this.indexOf(item);
18830 if(this.onItemClick(item, index, e) !== false){
18831 this.fireEvent("click", this, index, item, e);
18834 this.clearSelections();
18839 onContextMenu : function(e){
18840 var item = this.findItemFromChild(e.getTarget());
18842 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18847 onDblClick : function(e){
18848 var item = this.findItemFromChild(e.getTarget());
18850 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18854 onItemClick : function(item, index, e)
18856 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18859 if (this.toggleSelect) {
18860 var m = this.isSelected(item) ? 'unselect' : 'select';
18863 _t[m](item, true, false);
18866 if(this.multiSelect || this.singleSelect){
18867 if(this.multiSelect && e.shiftKey && this.lastSelection){
18868 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18870 this.select(item, this.multiSelect && e.ctrlKey);
18871 this.lastSelection = item;
18874 if(!this.tickable){
18875 e.preventDefault();
18883 * Get the number of selected nodes.
18886 getSelectionCount : function(){
18887 return this.selections.length;
18891 * Get the currently selected nodes.
18892 * @return {Array} An array of HTMLElements
18894 getSelectedNodes : function(){
18895 return this.selections;
18899 * Get the indexes of the selected nodes.
18902 getSelectedIndexes : function(){
18903 var indexes = [], s = this.selections;
18904 for(var i = 0, len = s.length; i < len; i++){
18905 indexes.push(s[i].nodeIndex);
18911 * Clear all selections
18912 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18914 clearSelections : function(suppressEvent){
18915 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18916 this.cmp.elements = this.selections;
18917 this.cmp.removeClass(this.selectedClass);
18918 this.selections = [];
18919 if(!suppressEvent){
18920 this.fireEvent("selectionchange", this, this.selections);
18926 * Returns true if the passed node is selected
18927 * @param {HTMLElement/Number} node The node or node index
18928 * @return {Boolean}
18930 isSelected : function(node){
18931 var s = this.selections;
18935 node = this.getNode(node);
18936 return s.indexOf(node) !== -1;
18941 * @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
18942 * @param {Boolean} keepExisting (optional) true to keep existing selections
18943 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18945 select : function(nodeInfo, keepExisting, suppressEvent){
18946 if(nodeInfo instanceof Array){
18948 this.clearSelections(true);
18950 for(var i = 0, len = nodeInfo.length; i < len; i++){
18951 this.select(nodeInfo[i], true, true);
18955 var node = this.getNode(nodeInfo);
18956 if(!node || this.isSelected(node)){
18957 return; // already selected.
18960 this.clearSelections(true);
18963 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18964 Roo.fly(node).addClass(this.selectedClass);
18965 this.selections.push(node);
18966 if(!suppressEvent){
18967 this.fireEvent("selectionchange", this, this.selections);
18975 * @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
18976 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18977 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18979 unselect : function(nodeInfo, keepExisting, suppressEvent)
18981 if(nodeInfo instanceof Array){
18982 Roo.each(this.selections, function(s) {
18983 this.unselect(s, nodeInfo);
18987 var node = this.getNode(nodeInfo);
18988 if(!node || !this.isSelected(node)){
18989 //Roo.log("not selected");
18990 return; // not selected.
18994 Roo.each(this.selections, function(s) {
18996 Roo.fly(node).removeClass(this.selectedClass);
19003 this.selections= ns;
19004 this.fireEvent("selectionchange", this, this.selections);
19008 * Gets a template node.
19009 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19010 * @return {HTMLElement} The node or null if it wasn't found
19012 getNode : function(nodeInfo){
19013 if(typeof nodeInfo == "string"){
19014 return document.getElementById(nodeInfo);
19015 }else if(typeof nodeInfo == "number"){
19016 return this.nodes[nodeInfo];
19022 * Gets a range template nodes.
19023 * @param {Number} startIndex
19024 * @param {Number} endIndex
19025 * @return {Array} An array of nodes
19027 getNodes : function(start, end){
19028 var ns = this.nodes;
19029 start = start || 0;
19030 end = typeof end == "undefined" ? ns.length - 1 : end;
19033 for(var i = start; i <= end; i++){
19037 for(var i = start; i >= end; i--){
19045 * Finds the index of the passed node
19046 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19047 * @return {Number} The index of the node or -1
19049 indexOf : function(node){
19050 node = this.getNode(node);
19051 if(typeof node.nodeIndex == "number"){
19052 return node.nodeIndex;
19054 var ns = this.nodes;
19055 for(var i = 0, len = ns.length; i < len; i++){
19066 * based on jquery fullcalendar
19070 Roo.bootstrap = Roo.bootstrap || {};
19072 * @class Roo.bootstrap.Calendar
19073 * @extends Roo.bootstrap.Component
19074 * Bootstrap Calendar class
19075 * @cfg {Boolean} loadMask (true|false) default false
19076 * @cfg {Object} header generate the user specific header of the calendar, default false
19079 * Create a new Container
19080 * @param {Object} config The config object
19085 Roo.bootstrap.Calendar = function(config){
19086 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19090 * Fires when a date is selected
19091 * @param {DatePicker} this
19092 * @param {Date} date The selected date
19096 * @event monthchange
19097 * Fires when the displayed month changes
19098 * @param {DatePicker} this
19099 * @param {Date} date The selected month
19101 'monthchange': true,
19103 * @event evententer
19104 * Fires when mouse over an event
19105 * @param {Calendar} this
19106 * @param {event} Event
19108 'evententer': true,
19110 * @event eventleave
19111 * Fires when the mouse leaves an
19112 * @param {Calendar} this
19115 'eventleave': true,
19117 * @event eventclick
19118 * Fires when the mouse click an
19119 * @param {Calendar} this
19128 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19131 * @cfg {Number} startDay
19132 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19140 getAutoCreate : function(){
19143 var fc_button = function(name, corner, style, content ) {
19144 return Roo.apply({},{
19146 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19148 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19151 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19162 style : 'width:100%',
19169 cls : 'fc-header-left',
19171 fc_button('prev', 'left', 'arrow', '‹' ),
19172 fc_button('next', 'right', 'arrow', '›' ),
19173 { tag: 'span', cls: 'fc-header-space' },
19174 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19182 cls : 'fc-header-center',
19186 cls: 'fc-header-title',
19189 html : 'month / year'
19197 cls : 'fc-header-right',
19199 /* fc_button('month', 'left', '', 'month' ),
19200 fc_button('week', '', '', 'week' ),
19201 fc_button('day', 'right', '', 'day' )
19213 header = this.header;
19216 var cal_heads = function() {
19218 // fixme - handle this.
19220 for (var i =0; i < Date.dayNames.length; i++) {
19221 var d = Date.dayNames[i];
19224 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19225 html : d.substring(0,3)
19229 ret[0].cls += ' fc-first';
19230 ret[6].cls += ' fc-last';
19233 var cal_cell = function(n) {
19236 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19241 cls: 'fc-day-number',
19245 cls: 'fc-day-content',
19249 style: 'position: relative;' // height: 17px;
19261 var cal_rows = function() {
19264 for (var r = 0; r < 6; r++) {
19271 for (var i =0; i < Date.dayNames.length; i++) {
19272 var d = Date.dayNames[i];
19273 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19276 row.cn[0].cls+=' fc-first';
19277 row.cn[0].cn[0].style = 'min-height:90px';
19278 row.cn[6].cls+=' fc-last';
19282 ret[0].cls += ' fc-first';
19283 ret[4].cls += ' fc-prev-last';
19284 ret[5].cls += ' fc-last';
19291 cls: 'fc-border-separate',
19292 style : 'width:100%',
19300 cls : 'fc-first fc-last',
19318 cls : 'fc-content',
19319 style : "position: relative;",
19322 cls : 'fc-view fc-view-month fc-grid',
19323 style : 'position: relative',
19324 unselectable : 'on',
19327 cls : 'fc-event-container',
19328 style : 'position:absolute;z-index:8;top:0;left:0;'
19346 initEvents : function()
19349 throw "can not find store for calendar";
19355 style: "text-align:center",
19359 style: "background-color:white;width:50%;margin:250 auto",
19363 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19374 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19376 var size = this.el.select('.fc-content', true).first().getSize();
19377 this.maskEl.setSize(size.width, size.height);
19378 this.maskEl.enableDisplayMode("block");
19379 if(!this.loadMask){
19380 this.maskEl.hide();
19383 this.store = Roo.factory(this.store, Roo.data);
19384 this.store.on('load', this.onLoad, this);
19385 this.store.on('beforeload', this.onBeforeLoad, this);
19389 this.cells = this.el.select('.fc-day',true);
19390 //Roo.log(this.cells);
19391 this.textNodes = this.el.query('.fc-day-number');
19392 this.cells.addClassOnOver('fc-state-hover');
19394 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19395 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19396 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19397 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19399 this.on('monthchange', this.onMonthChange, this);
19401 this.update(new Date().clearTime());
19404 resize : function() {
19405 var sz = this.el.getSize();
19407 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19408 this.el.select('.fc-day-content div',true).setHeight(34);
19413 showPrevMonth : function(e){
19414 this.update(this.activeDate.add("mo", -1));
19416 showToday : function(e){
19417 this.update(new Date().clearTime());
19420 showNextMonth : function(e){
19421 this.update(this.activeDate.add("mo", 1));
19425 showPrevYear : function(){
19426 this.update(this.activeDate.add("y", -1));
19430 showNextYear : function(){
19431 this.update(this.activeDate.add("y", 1));
19436 update : function(date)
19438 var vd = this.activeDate;
19439 this.activeDate = date;
19440 // if(vd && this.el){
19441 // var t = date.getTime();
19442 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19443 // Roo.log('using add remove');
19445 // this.fireEvent('monthchange', this, date);
19447 // this.cells.removeClass("fc-state-highlight");
19448 // this.cells.each(function(c){
19449 // if(c.dateValue == t){
19450 // c.addClass("fc-state-highlight");
19451 // setTimeout(function(){
19452 // try{c.dom.firstChild.focus();}catch(e){}
19462 var days = date.getDaysInMonth();
19464 var firstOfMonth = date.getFirstDateOfMonth();
19465 var startingPos = firstOfMonth.getDay()-this.startDay;
19467 if(startingPos < this.startDay){
19471 var pm = date.add(Date.MONTH, -1);
19472 var prevStart = pm.getDaysInMonth()-startingPos;
19474 this.cells = this.el.select('.fc-day',true);
19475 this.textNodes = this.el.query('.fc-day-number');
19476 this.cells.addClassOnOver('fc-state-hover');
19478 var cells = this.cells.elements;
19479 var textEls = this.textNodes;
19481 Roo.each(cells, function(cell){
19482 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19485 days += startingPos;
19487 // convert everything to numbers so it's fast
19488 var day = 86400000;
19489 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19492 //Roo.log(prevStart);
19494 var today = new Date().clearTime().getTime();
19495 var sel = date.clearTime().getTime();
19496 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19497 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19498 var ddMatch = this.disabledDatesRE;
19499 var ddText = this.disabledDatesText;
19500 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19501 var ddaysText = this.disabledDaysText;
19502 var format = this.format;
19504 var setCellClass = function(cal, cell){
19508 //Roo.log('set Cell Class');
19510 var t = d.getTime();
19514 cell.dateValue = t;
19516 cell.className += " fc-today";
19517 cell.className += " fc-state-highlight";
19518 cell.title = cal.todayText;
19521 // disable highlight in other month..
19522 //cell.className += " fc-state-highlight";
19527 cell.className = " fc-state-disabled";
19528 cell.title = cal.minText;
19532 cell.className = " fc-state-disabled";
19533 cell.title = cal.maxText;
19537 if(ddays.indexOf(d.getDay()) != -1){
19538 cell.title = ddaysText;
19539 cell.className = " fc-state-disabled";
19542 if(ddMatch && format){
19543 var fvalue = d.dateFormat(format);
19544 if(ddMatch.test(fvalue)){
19545 cell.title = ddText.replace("%0", fvalue);
19546 cell.className = " fc-state-disabled";
19550 if (!cell.initialClassName) {
19551 cell.initialClassName = cell.dom.className;
19554 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19559 for(; i < startingPos; i++) {
19560 textEls[i].innerHTML = (++prevStart);
19561 d.setDate(d.getDate()+1);
19563 cells[i].className = "fc-past fc-other-month";
19564 setCellClass(this, cells[i]);
19569 for(; i < days; i++){
19570 intDay = i - startingPos + 1;
19571 textEls[i].innerHTML = (intDay);
19572 d.setDate(d.getDate()+1);
19574 cells[i].className = ''; // "x-date-active";
19575 setCellClass(this, cells[i]);
19579 for(; i < 42; i++) {
19580 textEls[i].innerHTML = (++extraDays);
19581 d.setDate(d.getDate()+1);
19583 cells[i].className = "fc-future fc-other-month";
19584 setCellClass(this, cells[i]);
19587 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19589 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19591 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19592 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19594 if(totalRows != 6){
19595 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19596 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19599 this.fireEvent('monthchange', this, date);
19603 if(!this.internalRender){
19604 var main = this.el.dom.firstChild;
19605 var w = main.offsetWidth;
19606 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19607 Roo.fly(main).setWidth(w);
19608 this.internalRender = true;
19609 // opera does not respect the auto grow header center column
19610 // then, after it gets a width opera refuses to recalculate
19611 // without a second pass
19612 if(Roo.isOpera && !this.secondPass){
19613 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19614 this.secondPass = true;
19615 this.update.defer(10, this, [date]);
19622 findCell : function(dt) {
19623 dt = dt.clearTime().getTime();
19625 this.cells.each(function(c){
19626 //Roo.log("check " +c.dateValue + '?=' + dt);
19627 if(c.dateValue == dt){
19637 findCells : function(ev) {
19638 var s = ev.start.clone().clearTime().getTime();
19640 var e= ev.end.clone().clearTime().getTime();
19643 this.cells.each(function(c){
19644 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19646 if(c.dateValue > e){
19649 if(c.dateValue < s){
19658 // findBestRow: function(cells)
19662 // for (var i =0 ; i < cells.length;i++) {
19663 // ret = Math.max(cells[i].rows || 0,ret);
19670 addItem : function(ev)
19672 // look for vertical location slot in
19673 var cells = this.findCells(ev);
19675 // ev.row = this.findBestRow(cells);
19677 // work out the location.
19681 for(var i =0; i < cells.length; i++) {
19683 cells[i].row = cells[0].row;
19686 cells[i].row = cells[i].row + 1;
19696 if (crow.start.getY() == cells[i].getY()) {
19698 crow.end = cells[i];
19715 cells[0].events.push(ev);
19717 this.calevents.push(ev);
19720 clearEvents: function() {
19722 if(!this.calevents){
19726 Roo.each(this.cells.elements, function(c){
19732 Roo.each(this.calevents, function(e) {
19733 Roo.each(e.els, function(el) {
19734 el.un('mouseenter' ,this.onEventEnter, this);
19735 el.un('mouseleave' ,this.onEventLeave, this);
19740 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19746 renderEvents: function()
19750 this.cells.each(function(c) {
19759 if(c.row != c.events.length){
19760 r = 4 - (4 - (c.row - c.events.length));
19763 c.events = ev.slice(0, r);
19764 c.more = ev.slice(r);
19766 if(c.more.length && c.more.length == 1){
19767 c.events.push(c.more.pop());
19770 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19774 this.cells.each(function(c) {
19776 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19779 for (var e = 0; e < c.events.length; e++){
19780 var ev = c.events[e];
19781 var rows = ev.rows;
19783 for(var i = 0; i < rows.length; i++) {
19785 // how many rows should it span..
19788 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19789 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19791 unselectable : "on",
19794 cls: 'fc-event-inner',
19798 // cls: 'fc-event-time',
19799 // html : cells.length > 1 ? '' : ev.time
19803 cls: 'fc-event-title',
19804 html : String.format('{0}', ev.title)
19811 cls: 'ui-resizable-handle ui-resizable-e',
19812 html : '  '
19819 cfg.cls += ' fc-event-start';
19821 if ((i+1) == rows.length) {
19822 cfg.cls += ' fc-event-end';
19825 var ctr = _this.el.select('.fc-event-container',true).first();
19826 var cg = ctr.createChild(cfg);
19828 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19829 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19831 var r = (c.more.length) ? 1 : 0;
19832 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19833 cg.setWidth(ebox.right - sbox.x -2);
19835 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19836 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19837 cg.on('click', _this.onEventClick, _this, ev);
19848 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19849 style : 'position: absolute',
19850 unselectable : "on",
19853 cls: 'fc-event-inner',
19857 cls: 'fc-event-title',
19865 cls: 'ui-resizable-handle ui-resizable-e',
19866 html : '  '
19872 var ctr = _this.el.select('.fc-event-container',true).first();
19873 var cg = ctr.createChild(cfg);
19875 var sbox = c.select('.fc-day-content',true).first().getBox();
19876 var ebox = c.select('.fc-day-content',true).first().getBox();
19878 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19879 cg.setWidth(ebox.right - sbox.x -2);
19881 cg.on('click', _this.onMoreEventClick, _this, c.more);
19891 onEventEnter: function (e, el,event,d) {
19892 this.fireEvent('evententer', this, el, event);
19895 onEventLeave: function (e, el,event,d) {
19896 this.fireEvent('eventleave', this, el, event);
19899 onEventClick: function (e, el,event,d) {
19900 this.fireEvent('eventclick', this, el, event);
19903 onMonthChange: function () {
19907 onMoreEventClick: function(e, el, more)
19911 this.calpopover.placement = 'right';
19912 this.calpopover.setTitle('More');
19914 this.calpopover.setContent('');
19916 var ctr = this.calpopover.el.select('.popover-content', true).first();
19918 Roo.each(more, function(m){
19920 cls : 'fc-event-hori fc-event-draggable',
19923 var cg = ctr.createChild(cfg);
19925 cg.on('click', _this.onEventClick, _this, m);
19928 this.calpopover.show(el);
19933 onLoad: function ()
19935 this.calevents = [];
19938 if(this.store.getCount() > 0){
19939 this.store.data.each(function(d){
19942 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19943 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19944 time : d.data.start_time,
19945 title : d.data.title,
19946 description : d.data.description,
19947 venue : d.data.venue
19952 this.renderEvents();
19954 if(this.calevents.length && this.loadMask){
19955 this.maskEl.hide();
19959 onBeforeLoad: function()
19961 this.clearEvents();
19963 this.maskEl.show();
19977 * @class Roo.bootstrap.Popover
19978 * @extends Roo.bootstrap.Component
19979 * Bootstrap Popover class
19980 * @cfg {String} html contents of the popover (or false to use children..)
19981 * @cfg {String} title of popover (or false to hide)
19982 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19983 * @cfg {String} trigger click || hover (or false to trigger manually)
19984 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19985 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19986 * - if false and it has a 'parent' then it will be automatically added to that element
19987 * - if string - Roo.get will be called
19988 * @cfg {Number} delay - delay before showing
19991 * Create a new Popover
19992 * @param {Object} config The config object
19995 Roo.bootstrap.Popover = function(config){
19996 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20002 * After the popover show
20004 * @param {Roo.bootstrap.Popover} this
20009 * After the popover hide
20011 * @param {Roo.bootstrap.Popover} this
20017 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20022 placement : 'right',
20023 trigger : 'hover', // hover
20029 can_build_overlaid : false,
20031 maskEl : false, // the mask element
20034 alignEl : false, // when show is called with an element - this get's stored.
20036 getChildContainer : function()
20038 return this.contentEl;
20041 getPopoverHeader : function()
20043 this.title = true; // flag not to hide it..
20044 this.headerEl.addClass('p-0');
20045 return this.headerEl
20049 getAutoCreate : function(){
20052 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20053 style: 'display:block',
20059 cls : 'popover-inner ',
20063 cls: 'popover-title popover-header',
20064 html : this.title === false ? '' : this.title
20067 cls : 'popover-content popover-body ' + (this.cls || ''),
20068 html : this.html || ''
20079 * @param {string} the title
20081 setTitle: function(str)
20085 this.headerEl.dom.innerHTML = str;
20090 * @param {string} the body content
20092 setContent: function(str)
20095 if (this.contentEl) {
20096 this.contentEl.dom.innerHTML = str;
20100 // as it get's added to the bottom of the page.
20101 onRender : function(ct, position)
20103 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20108 var cfg = Roo.apply({}, this.getAutoCreate());
20112 cfg.cls += ' ' + this.cls;
20115 cfg.style = this.style;
20117 //Roo.log("adding to ");
20118 this.el = Roo.get(document.body).createChild(cfg, position);
20119 // Roo.log(this.el);
20122 this.contentEl = this.el.select('.popover-content',true).first();
20123 this.headerEl = this.el.select('.popover-title',true).first();
20126 if(typeof(this.items) != 'undefined'){
20127 var items = this.items;
20130 for(var i =0;i < items.length;i++) {
20131 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20135 this.items = nitems;
20137 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20138 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20145 resizeMask : function()
20147 this.maskEl.setSize(
20148 Roo.lib.Dom.getViewWidth(true),
20149 Roo.lib.Dom.getViewHeight(true)
20153 initEvents : function()
20157 Roo.bootstrap.Popover.register(this);
20160 this.arrowEl = this.el.select('.arrow',true).first();
20161 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20162 this.el.enableDisplayMode('block');
20166 if (this.over === false && !this.parent()) {
20169 if (this.triggers === false) {
20174 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20175 var triggers = this.trigger ? this.trigger.split(' ') : [];
20176 Roo.each(triggers, function(trigger) {
20178 if (trigger == 'click') {
20179 on_el.on('click', this.toggle, this);
20180 } else if (trigger != 'manual') {
20181 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20182 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20184 on_el.on(eventIn ,this.enter, this);
20185 on_el.on(eventOut, this.leave, this);
20195 toggle : function () {
20196 this.hoverState == 'in' ? this.leave() : this.enter();
20199 enter : function () {
20201 clearTimeout(this.timeout);
20203 this.hoverState = 'in';
20205 if (!this.delay || !this.delay.show) {
20210 this.timeout = setTimeout(function () {
20211 if (_t.hoverState == 'in') {
20214 }, this.delay.show)
20217 leave : function() {
20218 clearTimeout(this.timeout);
20220 this.hoverState = 'out';
20222 if (!this.delay || !this.delay.hide) {
20227 this.timeout = setTimeout(function () {
20228 if (_t.hoverState == 'out') {
20231 }, this.delay.hide)
20235 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20236 * @param {string} (left|right|top|bottom) position
20238 show : function (on_el, placement)
20240 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20241 on_el = on_el || false; // default to false
20244 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20245 on_el = this.parent().el;
20246 } else if (this.over) {
20247 on_el = Roo.get(this.over);
20252 this.alignEl = Roo.get( on_el );
20255 this.render(document.body);
20261 if (this.title === false) {
20262 this.headerEl.hide();
20267 this.el.dom.style.display = 'block';
20270 if (this.alignEl) {
20271 this.updatePosition(this.placement, true);
20274 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20275 var es = this.el.getSize();
20276 var x = Roo.lib.Dom.getViewWidth()/2;
20277 var y = Roo.lib.Dom.getViewHeight()/2;
20278 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20283 //var arrow = this.el.select('.arrow',true).first();
20284 //arrow.set(align[2],
20286 this.el.addClass('in');
20290 this.hoverState = 'in';
20293 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20294 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20295 this.maskEl.dom.style.display = 'block';
20296 this.maskEl.addClass('show');
20298 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20300 this.fireEvent('show', this);
20304 * fire this manually after loading a grid in the table for example
20305 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20306 * @param {Boolean} try and move it if we cant get right position.
20308 updatePosition : function(placement, try_move)
20310 // allow for calling with no parameters
20311 placement = placement ? placement : this.placement;
20312 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20314 this.el.removeClass([
20315 'fade','top','bottom', 'left', 'right','in',
20316 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20318 this.el.addClass(placement + ' bs-popover-' + placement);
20320 if (!this.alignEl ) {
20324 switch (placement) {
20326 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20327 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20328 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20329 //normal display... or moved up/down.
20330 this.el.setXY(offset);
20331 var xy = this.alignEl.getAnchorXY('tr', false);
20333 this.arrowEl.setXY(xy);
20336 // continue through...
20337 return this.updatePosition('left', false);
20341 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20342 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20343 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20344 //normal display... or moved up/down.
20345 this.el.setXY(offset);
20346 var xy = this.alignEl.getAnchorXY('tl', false);
20347 xy[0]-=10;xy[1]+=5; // << fix me
20348 this.arrowEl.setXY(xy);
20352 return this.updatePosition('right', false);
20355 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20356 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20357 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20358 //normal display... or moved up/down.
20359 this.el.setXY(offset);
20360 var xy = this.alignEl.getAnchorXY('t', false);
20361 xy[1]-=10; // << fix me
20362 this.arrowEl.setXY(xy);
20366 return this.updatePosition('bottom', false);
20369 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20370 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20371 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20372 //normal display... or moved up/down.
20373 this.el.setXY(offset);
20374 var xy = this.alignEl.getAnchorXY('b', false);
20375 xy[1]+=2; // << fix me
20376 this.arrowEl.setXY(xy);
20380 return this.updatePosition('top', false);
20391 this.el.setXY([0,0]);
20392 this.el.removeClass('in');
20394 this.hoverState = null;
20395 this.maskEl.hide(); // always..
20396 this.fireEvent('hide', this);
20402 Roo.apply(Roo.bootstrap.Popover, {
20405 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20406 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20407 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20408 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20413 clickHander : false,
20417 onMouseDown : function(e)
20419 if (this.popups.length && !e.getTarget(".roo-popover")) {
20420 /// what is nothing is showing..
20429 register : function(popup)
20431 if (!Roo.bootstrap.Popover.clickHandler) {
20432 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20434 // hide other popups.
20435 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
20436 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
20437 this.hideAll(); //<< why?
20438 //this.popups.push(popup);
20440 hideAll : function()
20442 this.popups.forEach(function(p) {
20446 onShow : function() {
20447 Roo.bootstrap.Popover.popups.push(this);
20449 onHide : function() {
20450 Roo.bootstrap.Popover.popups.remove(this);
20456 * Card header - holder for the card header elements.
20461 * @class Roo.bootstrap.PopoverNav
20462 * @extends Roo.bootstrap.NavGroup
20463 * Bootstrap Popover header navigation class
20465 * Create a new Popover Header Navigation
20466 * @param {Object} config The config object
20469 Roo.bootstrap.PopoverNav = function(config){
20470 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20473 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20476 container_method : 'getPopoverHeader'
20494 * @class Roo.bootstrap.Progress
20495 * @extends Roo.bootstrap.Component
20496 * Bootstrap Progress class
20497 * @cfg {Boolean} striped striped of the progress bar
20498 * @cfg {Boolean} active animated of the progress bar
20502 * Create a new Progress
20503 * @param {Object} config The config object
20506 Roo.bootstrap.Progress = function(config){
20507 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20510 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20515 getAutoCreate : function(){
20523 cfg.cls += ' progress-striped';
20527 cfg.cls += ' active';
20546 * @class Roo.bootstrap.ProgressBar
20547 * @extends Roo.bootstrap.Component
20548 * Bootstrap ProgressBar class
20549 * @cfg {Number} aria_valuenow aria-value now
20550 * @cfg {Number} aria_valuemin aria-value min
20551 * @cfg {Number} aria_valuemax aria-value max
20552 * @cfg {String} label label for the progress bar
20553 * @cfg {String} panel (success | info | warning | danger )
20554 * @cfg {String} role role of the progress bar
20555 * @cfg {String} sr_only text
20559 * Create a new ProgressBar
20560 * @param {Object} config The config object
20563 Roo.bootstrap.ProgressBar = function(config){
20564 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20567 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20571 aria_valuemax : 100,
20577 getAutoCreate : function()
20582 cls: 'progress-bar',
20583 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20595 cfg.role = this.role;
20598 if(this.aria_valuenow){
20599 cfg['aria-valuenow'] = this.aria_valuenow;
20602 if(this.aria_valuemin){
20603 cfg['aria-valuemin'] = this.aria_valuemin;
20606 if(this.aria_valuemax){
20607 cfg['aria-valuemax'] = this.aria_valuemax;
20610 if(this.label && !this.sr_only){
20611 cfg.html = this.label;
20615 cfg.cls += ' progress-bar-' + this.panel;
20621 update : function(aria_valuenow)
20623 this.aria_valuenow = aria_valuenow;
20625 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20640 * @class Roo.bootstrap.TabGroup
20641 * @extends Roo.bootstrap.Column
20642 * Bootstrap Column class
20643 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20644 * @cfg {Boolean} carousel true to make the group behave like a carousel
20645 * @cfg {Boolean} bullets show bullets for the panels
20646 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20647 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20648 * @cfg {Boolean} showarrow (true|false) show arrow default true
20651 * Create a new TabGroup
20652 * @param {Object} config The config object
20655 Roo.bootstrap.TabGroup = function(config){
20656 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20658 this.navId = Roo.id();
20661 Roo.bootstrap.TabGroup.register(this);
20665 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20668 transition : false,
20673 slideOnTouch : false,
20676 getAutoCreate : function()
20678 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20680 cfg.cls += ' tab-content';
20682 if (this.carousel) {
20683 cfg.cls += ' carousel slide';
20686 cls : 'carousel-inner',
20690 if(this.bullets && !Roo.isTouch){
20693 cls : 'carousel-bullets',
20697 if(this.bullets_cls){
20698 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20705 cfg.cn[0].cn.push(bullets);
20708 if(this.showarrow){
20709 cfg.cn[0].cn.push({
20711 class : 'carousel-arrow',
20715 class : 'carousel-prev',
20719 class : 'fa fa-chevron-left'
20725 class : 'carousel-next',
20729 class : 'fa fa-chevron-right'
20742 initEvents: function()
20744 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20745 // this.el.on("touchstart", this.onTouchStart, this);
20748 if(this.autoslide){
20751 this.slideFn = window.setInterval(function() {
20752 _this.showPanelNext();
20756 if(this.showarrow){
20757 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20758 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20764 // onTouchStart : function(e, el, o)
20766 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20770 // this.showPanelNext();
20774 getChildContainer : function()
20776 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20780 * register a Navigation item
20781 * @param {Roo.bootstrap.NavItem} the navitem to add
20783 register : function(item)
20785 this.tabs.push( item);
20786 item.navId = this.navId; // not really needed..
20791 getActivePanel : function()
20794 Roo.each(this.tabs, function(t) {
20804 getPanelByName : function(n)
20807 Roo.each(this.tabs, function(t) {
20808 if (t.tabId == n) {
20816 indexOfPanel : function(p)
20819 Roo.each(this.tabs, function(t,i) {
20820 if (t.tabId == p.tabId) {
20829 * show a specific panel
20830 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20831 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20833 showPanel : function (pan)
20835 if(this.transition || typeof(pan) == 'undefined'){
20836 Roo.log("waiting for the transitionend");
20840 if (typeof(pan) == 'number') {
20841 pan = this.tabs[pan];
20844 if (typeof(pan) == 'string') {
20845 pan = this.getPanelByName(pan);
20848 var cur = this.getActivePanel();
20851 Roo.log('pan or acitve pan is undefined');
20855 if (pan.tabId == this.getActivePanel().tabId) {
20859 if (false === cur.fireEvent('beforedeactivate')) {
20863 if(this.bullets > 0 && !Roo.isTouch){
20864 this.setActiveBullet(this.indexOfPanel(pan));
20867 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20869 //class="carousel-item carousel-item-next carousel-item-left"
20871 this.transition = true;
20872 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20873 var lr = dir == 'next' ? 'left' : 'right';
20874 pan.el.addClass(dir); // or prev
20875 pan.el.addClass('carousel-item-' + dir); // or prev
20876 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20877 cur.el.addClass(lr); // or right
20878 pan.el.addClass(lr);
20879 cur.el.addClass('carousel-item-' +lr); // or right
20880 pan.el.addClass('carousel-item-' +lr);
20884 cur.el.on('transitionend', function() {
20885 Roo.log("trans end?");
20887 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20888 pan.setActive(true);
20890 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20891 cur.setActive(false);
20893 _this.transition = false;
20895 }, this, { single: true } );
20900 cur.setActive(false);
20901 pan.setActive(true);
20906 showPanelNext : function()
20908 var i = this.indexOfPanel(this.getActivePanel());
20910 if (i >= this.tabs.length - 1 && !this.autoslide) {
20914 if (i >= this.tabs.length - 1 && this.autoslide) {
20918 this.showPanel(this.tabs[i+1]);
20921 showPanelPrev : function()
20923 var i = this.indexOfPanel(this.getActivePanel());
20925 if (i < 1 && !this.autoslide) {
20929 if (i < 1 && this.autoslide) {
20930 i = this.tabs.length;
20933 this.showPanel(this.tabs[i-1]);
20937 addBullet: function()
20939 if(!this.bullets || Roo.isTouch){
20942 var ctr = this.el.select('.carousel-bullets',true).first();
20943 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20944 var bullet = ctr.createChild({
20945 cls : 'bullet bullet-' + i
20946 },ctr.dom.lastChild);
20951 bullet.on('click', (function(e, el, o, ii, t){
20953 e.preventDefault();
20955 this.showPanel(ii);
20957 if(this.autoslide && this.slideFn){
20958 clearInterval(this.slideFn);
20959 this.slideFn = window.setInterval(function() {
20960 _this.showPanelNext();
20964 }).createDelegate(this, [i, bullet], true));
20969 setActiveBullet : function(i)
20975 Roo.each(this.el.select('.bullet', true).elements, function(el){
20976 el.removeClass('selected');
20979 var bullet = this.el.select('.bullet-' + i, true).first();
20985 bullet.addClass('selected');
20996 Roo.apply(Roo.bootstrap.TabGroup, {
21000 * register a Navigation Group
21001 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21003 register : function(navgrp)
21005 this.groups[navgrp.navId] = navgrp;
21009 * fetch a Navigation Group based on the navigation ID
21010 * if one does not exist , it will get created.
21011 * @param {string} the navgroup to add
21012 * @returns {Roo.bootstrap.NavGroup} the navgroup
21014 get: function(navId) {
21015 if (typeof(this.groups[navId]) == 'undefined') {
21016 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21018 return this.groups[navId] ;
21033 * @class Roo.bootstrap.TabPanel
21034 * @extends Roo.bootstrap.Component
21035 * Bootstrap TabPanel class
21036 * @cfg {Boolean} active panel active
21037 * @cfg {String} html panel content
21038 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21039 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21040 * @cfg {String} href click to link..
21041 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21045 * Create a new TabPanel
21046 * @param {Object} config The config object
21049 Roo.bootstrap.TabPanel = function(config){
21050 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21054 * Fires when the active status changes
21055 * @param {Roo.bootstrap.TabPanel} this
21056 * @param {Boolean} state the new state
21061 * @event beforedeactivate
21062 * Fires before a tab is de-activated - can be used to do validation on a form.
21063 * @param {Roo.bootstrap.TabPanel} this
21064 * @return {Boolean} false if there is an error
21067 'beforedeactivate': true
21070 this.tabId = this.tabId || Roo.id();
21074 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21081 touchSlide : false,
21082 getAutoCreate : function(){
21087 // item is needed for carousel - not sure if it has any effect otherwise
21088 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21089 html: this.html || ''
21093 cfg.cls += ' active';
21097 cfg.tabId = this.tabId;
21105 initEvents: function()
21107 var p = this.parent();
21109 this.navId = this.navId || p.navId;
21111 if (typeof(this.navId) != 'undefined') {
21112 // not really needed.. but just in case.. parent should be a NavGroup.
21113 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21117 var i = tg.tabs.length - 1;
21119 if(this.active && tg.bullets > 0 && i < tg.bullets){
21120 tg.setActiveBullet(i);
21124 this.el.on('click', this.onClick, this);
21126 if(Roo.isTouch && this.touchSlide){
21127 this.el.on("touchstart", this.onTouchStart, this);
21128 this.el.on("touchmove", this.onTouchMove, this);
21129 this.el.on("touchend", this.onTouchEnd, this);
21134 onRender : function(ct, position)
21136 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21139 setActive : function(state)
21141 Roo.log("panel - set active " + this.tabId + "=" + state);
21143 this.active = state;
21145 this.el.removeClass('active');
21147 } else if (!this.el.hasClass('active')) {
21148 this.el.addClass('active');
21151 this.fireEvent('changed', this, state);
21154 onClick : function(e)
21156 e.preventDefault();
21158 if(!this.href.length){
21162 window.location.href = this.href;
21171 onTouchStart : function(e)
21173 this.swiping = false;
21175 this.startX = e.browserEvent.touches[0].clientX;
21176 this.startY = e.browserEvent.touches[0].clientY;
21179 onTouchMove : function(e)
21181 this.swiping = true;
21183 this.endX = e.browserEvent.touches[0].clientX;
21184 this.endY = e.browserEvent.touches[0].clientY;
21187 onTouchEnd : function(e)
21194 var tabGroup = this.parent();
21196 if(this.endX > this.startX){ // swiping right
21197 tabGroup.showPanelPrev();
21201 if(this.startX > this.endX){ // swiping left
21202 tabGroup.showPanelNext();
21221 * @class Roo.bootstrap.DateField
21222 * @extends Roo.bootstrap.Input
21223 * Bootstrap DateField class
21224 * @cfg {Number} weekStart default 0
21225 * @cfg {String} viewMode default empty, (months|years)
21226 * @cfg {String} minViewMode default empty, (months|years)
21227 * @cfg {Number} startDate default -Infinity
21228 * @cfg {Number} endDate default Infinity
21229 * @cfg {Boolean} todayHighlight default false
21230 * @cfg {Boolean} todayBtn default false
21231 * @cfg {Boolean} calendarWeeks default false
21232 * @cfg {Object} daysOfWeekDisabled default empty
21233 * @cfg {Boolean} singleMode default false (true | false)
21235 * @cfg {Boolean} keyboardNavigation default true
21236 * @cfg {String} language default en
21239 * Create a new DateField
21240 * @param {Object} config The config object
21243 Roo.bootstrap.DateField = function(config){
21244 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21248 * Fires when this field show.
21249 * @param {Roo.bootstrap.DateField} this
21250 * @param {Mixed} date The date value
21255 * Fires when this field hide.
21256 * @param {Roo.bootstrap.DateField} this
21257 * @param {Mixed} date The date value
21262 * Fires when select a date.
21263 * @param {Roo.bootstrap.DateField} this
21264 * @param {Mixed} date The date value
21268 * @event beforeselect
21269 * Fires when before select a date.
21270 * @param {Roo.bootstrap.DateField} this
21271 * @param {Mixed} date The date value
21273 beforeselect : true
21277 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21280 * @cfg {String} format
21281 * The default date format string which can be overriden for localization support. The format must be
21282 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21286 * @cfg {String} altFormats
21287 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21288 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21290 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21298 todayHighlight : false,
21304 keyboardNavigation: true,
21306 calendarWeeks: false,
21308 startDate: -Infinity,
21312 daysOfWeekDisabled: [],
21316 singleMode : false,
21318 UTCDate: function()
21320 return new Date(Date.UTC.apply(Date, arguments));
21323 UTCToday: function()
21325 var today = new Date();
21326 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21329 getDate: function() {
21330 var d = this.getUTCDate();
21331 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21334 getUTCDate: function() {
21338 setDate: function(d) {
21339 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21342 setUTCDate: function(d) {
21344 this.setValue(this.formatDate(this.date));
21347 onRender: function(ct, position)
21350 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21352 this.language = this.language || 'en';
21353 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21354 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21356 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21357 this.format = this.format || 'm/d/y';
21358 this.isInline = false;
21359 this.isInput = true;
21360 this.component = this.el.select('.add-on', true).first() || false;
21361 this.component = (this.component && this.component.length === 0) ? false : this.component;
21362 this.hasInput = this.component && this.inputEl().length;
21364 if (typeof(this.minViewMode === 'string')) {
21365 switch (this.minViewMode) {
21367 this.minViewMode = 1;
21370 this.minViewMode = 2;
21373 this.minViewMode = 0;
21378 if (typeof(this.viewMode === 'string')) {
21379 switch (this.viewMode) {
21392 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21394 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21396 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21398 this.picker().on('mousedown', this.onMousedown, this);
21399 this.picker().on('click', this.onClick, this);
21401 this.picker().addClass('datepicker-dropdown');
21403 this.startViewMode = this.viewMode;
21405 if(this.singleMode){
21406 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21407 v.setVisibilityMode(Roo.Element.DISPLAY);
21411 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21412 v.setStyle('width', '189px');
21416 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21417 if(!this.calendarWeeks){
21422 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21423 v.attr('colspan', function(i, val){
21424 return parseInt(val) + 1;
21429 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21431 this.setStartDate(this.startDate);
21432 this.setEndDate(this.endDate);
21434 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21441 if(this.isInline) {
21446 picker : function()
21448 return this.pickerEl;
21449 // return this.el.select('.datepicker', true).first();
21452 fillDow: function()
21454 var dowCnt = this.weekStart;
21463 if(this.calendarWeeks){
21471 while (dowCnt < this.weekStart + 7) {
21475 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21479 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21482 fillMonths: function()
21485 var months = this.picker().select('>.datepicker-months td', true).first();
21487 months.dom.innerHTML = '';
21493 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21496 months.createChild(month);
21503 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;
21505 if (this.date < this.startDate) {
21506 this.viewDate = new Date(this.startDate);
21507 } else if (this.date > this.endDate) {
21508 this.viewDate = new Date(this.endDate);
21510 this.viewDate = new Date(this.date);
21518 var d = new Date(this.viewDate),
21519 year = d.getUTCFullYear(),
21520 month = d.getUTCMonth(),
21521 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21522 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21523 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21524 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21525 currentDate = this.date && this.date.valueOf(),
21526 today = this.UTCToday();
21528 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21530 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21532 // this.picker.select('>tfoot th.today').
21533 // .text(dates[this.language].today)
21534 // .toggle(this.todayBtn !== false);
21536 this.updateNavArrows();
21539 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21541 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21543 prevMonth.setUTCDate(day);
21545 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21547 var nextMonth = new Date(prevMonth);
21549 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21551 nextMonth = nextMonth.valueOf();
21553 var fillMonths = false;
21555 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21557 while(prevMonth.valueOf() <= nextMonth) {
21560 if (prevMonth.getUTCDay() === this.weekStart) {
21562 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21570 if(this.calendarWeeks){
21571 // ISO 8601: First week contains first thursday.
21572 // ISO also states week starts on Monday, but we can be more abstract here.
21574 // Start of current week: based on weekstart/current date
21575 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21576 // Thursday of this week
21577 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21578 // First Thursday of year, year from thursday
21579 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21580 // Calendar week: ms between thursdays, div ms per day, div 7 days
21581 calWeek = (th - yth) / 864e5 / 7 + 1;
21583 fillMonths.cn.push({
21591 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21593 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21596 if (this.todayHighlight &&
21597 prevMonth.getUTCFullYear() == today.getFullYear() &&
21598 prevMonth.getUTCMonth() == today.getMonth() &&
21599 prevMonth.getUTCDate() == today.getDate()) {
21600 clsName += ' today';
21603 if (currentDate && prevMonth.valueOf() === currentDate) {
21604 clsName += ' active';
21607 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21608 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21609 clsName += ' disabled';
21612 fillMonths.cn.push({
21614 cls: 'day ' + clsName,
21615 html: prevMonth.getDate()
21618 prevMonth.setDate(prevMonth.getDate()+1);
21621 var currentYear = this.date && this.date.getUTCFullYear();
21622 var currentMonth = this.date && this.date.getUTCMonth();
21624 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21626 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21627 v.removeClass('active');
21629 if(currentYear === year && k === currentMonth){
21630 v.addClass('active');
21633 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21634 v.addClass('disabled');
21640 year = parseInt(year/10, 10) * 10;
21642 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21644 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21647 for (var i = -1; i < 11; i++) {
21648 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21650 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21658 showMode: function(dir)
21661 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21664 Roo.each(this.picker().select('>div',true).elements, function(v){
21665 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21668 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21673 if(this.isInline) {
21677 this.picker().removeClass(['bottom', 'top']);
21679 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21681 * place to the top of element!
21685 this.picker().addClass('top');
21686 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21691 this.picker().addClass('bottom');
21693 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21696 parseDate : function(value)
21698 if(!value || value instanceof Date){
21701 var v = Date.parseDate(value, this.format);
21702 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21703 v = Date.parseDate(value, 'Y-m-d');
21705 if(!v && this.altFormats){
21706 if(!this.altFormatsArray){
21707 this.altFormatsArray = this.altFormats.split("|");
21709 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21710 v = Date.parseDate(value, this.altFormatsArray[i]);
21716 formatDate : function(date, fmt)
21718 return (!date || !(date instanceof Date)) ?
21719 date : date.dateFormat(fmt || this.format);
21722 onFocus : function()
21724 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21728 onBlur : function()
21730 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21732 var d = this.inputEl().getValue();
21739 showPopup : function()
21741 this.picker().show();
21745 this.fireEvent('showpopup', this, this.date);
21748 hidePopup : function()
21750 if(this.isInline) {
21753 this.picker().hide();
21754 this.viewMode = this.startViewMode;
21757 this.fireEvent('hidepopup', this, this.date);
21761 onMousedown: function(e)
21763 e.stopPropagation();
21764 e.preventDefault();
21769 Roo.bootstrap.DateField.superclass.keyup.call(this);
21773 setValue: function(v)
21775 if(this.fireEvent('beforeselect', this, v) !== false){
21776 var d = new Date(this.parseDate(v) ).clearTime();
21778 if(isNaN(d.getTime())){
21779 this.date = this.viewDate = '';
21780 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21784 v = this.formatDate(d);
21786 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21788 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21792 this.fireEvent('select', this, this.date);
21796 getValue: function()
21798 return this.formatDate(this.date);
21801 fireKey: function(e)
21803 if (!this.picker().isVisible()){
21804 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21810 var dateChanged = false,
21812 newDate, newViewDate;
21817 e.preventDefault();
21821 if (!this.keyboardNavigation) {
21824 dir = e.keyCode == 37 ? -1 : 1;
21827 newDate = this.moveYear(this.date, dir);
21828 newViewDate = this.moveYear(this.viewDate, dir);
21829 } else if (e.shiftKey){
21830 newDate = this.moveMonth(this.date, dir);
21831 newViewDate = this.moveMonth(this.viewDate, dir);
21833 newDate = new Date(this.date);
21834 newDate.setUTCDate(this.date.getUTCDate() + dir);
21835 newViewDate = new Date(this.viewDate);
21836 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21838 if (this.dateWithinRange(newDate)){
21839 this.date = newDate;
21840 this.viewDate = newViewDate;
21841 this.setValue(this.formatDate(this.date));
21843 e.preventDefault();
21844 dateChanged = true;
21849 if (!this.keyboardNavigation) {
21852 dir = e.keyCode == 38 ? -1 : 1;
21854 newDate = this.moveYear(this.date, dir);
21855 newViewDate = this.moveYear(this.viewDate, dir);
21856 } else if (e.shiftKey){
21857 newDate = this.moveMonth(this.date, dir);
21858 newViewDate = this.moveMonth(this.viewDate, dir);
21860 newDate = new Date(this.date);
21861 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21862 newViewDate = new Date(this.viewDate);
21863 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21865 if (this.dateWithinRange(newDate)){
21866 this.date = newDate;
21867 this.viewDate = newViewDate;
21868 this.setValue(this.formatDate(this.date));
21870 e.preventDefault();
21871 dateChanged = true;
21875 this.setValue(this.formatDate(this.date));
21877 e.preventDefault();
21880 this.setValue(this.formatDate(this.date));
21894 onClick: function(e)
21896 e.stopPropagation();
21897 e.preventDefault();
21899 var target = e.getTarget();
21901 if(target.nodeName.toLowerCase() === 'i'){
21902 target = Roo.get(target).dom.parentNode;
21905 var nodeName = target.nodeName;
21906 var className = target.className;
21907 var html = target.innerHTML;
21908 //Roo.log(nodeName);
21910 switch(nodeName.toLowerCase()) {
21912 switch(className) {
21918 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21919 switch(this.viewMode){
21921 this.viewDate = this.moveMonth(this.viewDate, dir);
21925 this.viewDate = this.moveYear(this.viewDate, dir);
21931 var date = new Date();
21932 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21934 this.setValue(this.formatDate(this.date));
21941 if (className.indexOf('disabled') < 0) {
21942 this.viewDate.setUTCDate(1);
21943 if (className.indexOf('month') > -1) {
21944 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21946 var year = parseInt(html, 10) || 0;
21947 this.viewDate.setUTCFullYear(year);
21951 if(this.singleMode){
21952 this.setValue(this.formatDate(this.viewDate));
21963 //Roo.log(className);
21964 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21965 var day = parseInt(html, 10) || 1;
21966 var year = (this.viewDate || new Date()).getUTCFullYear(),
21967 month = (this.viewDate || new Date()).getUTCMonth();
21969 if (className.indexOf('old') > -1) {
21976 } else if (className.indexOf('new') > -1) {
21984 //Roo.log([year,month,day]);
21985 this.date = this.UTCDate(year, month, day,0,0,0,0);
21986 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21988 //Roo.log(this.formatDate(this.date));
21989 this.setValue(this.formatDate(this.date));
21996 setStartDate: function(startDate)
21998 this.startDate = startDate || -Infinity;
21999 if (this.startDate !== -Infinity) {
22000 this.startDate = this.parseDate(this.startDate);
22003 this.updateNavArrows();
22006 setEndDate: function(endDate)
22008 this.endDate = endDate || Infinity;
22009 if (this.endDate !== Infinity) {
22010 this.endDate = this.parseDate(this.endDate);
22013 this.updateNavArrows();
22016 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22018 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22019 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22020 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22022 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22023 return parseInt(d, 10);
22026 this.updateNavArrows();
22029 updateNavArrows: function()
22031 if(this.singleMode){
22035 var d = new Date(this.viewDate),
22036 year = d.getUTCFullYear(),
22037 month = d.getUTCMonth();
22039 Roo.each(this.picker().select('.prev', true).elements, function(v){
22041 switch (this.viewMode) {
22044 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22050 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22057 Roo.each(this.picker().select('.next', true).elements, function(v){
22059 switch (this.viewMode) {
22062 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22068 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22076 moveMonth: function(date, dir)
22081 var new_date = new Date(date.valueOf()),
22082 day = new_date.getUTCDate(),
22083 month = new_date.getUTCMonth(),
22084 mag = Math.abs(dir),
22086 dir = dir > 0 ? 1 : -1;
22089 // If going back one month, make sure month is not current month
22090 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22092 return new_date.getUTCMonth() == month;
22094 // If going forward one month, make sure month is as expected
22095 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22097 return new_date.getUTCMonth() != new_month;
22099 new_month = month + dir;
22100 new_date.setUTCMonth(new_month);
22101 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22102 if (new_month < 0 || new_month > 11) {
22103 new_month = (new_month + 12) % 12;
22106 // For magnitudes >1, move one month at a time...
22107 for (var i=0; i<mag; i++) {
22108 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22109 new_date = this.moveMonth(new_date, dir);
22111 // ...then reset the day, keeping it in the new month
22112 new_month = new_date.getUTCMonth();
22113 new_date.setUTCDate(day);
22115 return new_month != new_date.getUTCMonth();
22118 // Common date-resetting loop -- if date is beyond end of month, make it
22121 new_date.setUTCDate(--day);
22122 new_date.setUTCMonth(new_month);
22127 moveYear: function(date, dir)
22129 return this.moveMonth(date, dir*12);
22132 dateWithinRange: function(date)
22134 return date >= this.startDate && date <= this.endDate;
22140 this.picker().remove();
22143 validateValue : function(value)
22145 if(this.getVisibilityEl().hasClass('hidden')){
22149 if(value.length < 1) {
22150 if(this.allowBlank){
22156 if(value.length < this.minLength){
22159 if(value.length > this.maxLength){
22163 var vt = Roo.form.VTypes;
22164 if(!vt[this.vtype](value, this)){
22168 if(typeof this.validator == "function"){
22169 var msg = this.validator(value);
22175 if(this.regex && !this.regex.test(value)){
22179 if(typeof(this.parseDate(value)) == 'undefined'){
22183 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22187 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22197 this.date = this.viewDate = '';
22199 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22204 Roo.apply(Roo.bootstrap.DateField, {
22215 html: '<i class="fa fa-arrow-left"/>'
22225 html: '<i class="fa fa-arrow-right"/>'
22267 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22268 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22269 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22270 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22271 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22284 navFnc: 'FullYear',
22289 navFnc: 'FullYear',
22294 Roo.apply(Roo.bootstrap.DateField, {
22298 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22302 cls: 'datepicker-days',
22306 cls: 'table-condensed',
22308 Roo.bootstrap.DateField.head,
22312 Roo.bootstrap.DateField.footer
22319 cls: 'datepicker-months',
22323 cls: 'table-condensed',
22325 Roo.bootstrap.DateField.head,
22326 Roo.bootstrap.DateField.content,
22327 Roo.bootstrap.DateField.footer
22334 cls: 'datepicker-years',
22338 cls: 'table-condensed',
22340 Roo.bootstrap.DateField.head,
22341 Roo.bootstrap.DateField.content,
22342 Roo.bootstrap.DateField.footer
22361 * @class Roo.bootstrap.TimeField
22362 * @extends Roo.bootstrap.Input
22363 * Bootstrap DateField class
22367 * Create a new TimeField
22368 * @param {Object} config The config object
22371 Roo.bootstrap.TimeField = function(config){
22372 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22376 * Fires when this field show.
22377 * @param {Roo.bootstrap.DateField} thisthis
22378 * @param {Mixed} date The date value
22383 * Fires when this field hide.
22384 * @param {Roo.bootstrap.DateField} this
22385 * @param {Mixed} date The date value
22390 * Fires when select a date.
22391 * @param {Roo.bootstrap.DateField} this
22392 * @param {Mixed} date The date value
22398 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22401 * @cfg {String} format
22402 * The default time format string which can be overriden for localization support. The format must be
22403 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22407 getAutoCreate : function()
22409 this.after = '<i class="fa far fa-clock"></i>';
22410 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22414 onRender: function(ct, position)
22417 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22419 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22421 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22423 this.pop = this.picker().select('>.datepicker-time',true).first();
22424 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22426 this.picker().on('mousedown', this.onMousedown, this);
22427 this.picker().on('click', this.onClick, this);
22429 this.picker().addClass('datepicker-dropdown');
22434 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22435 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22436 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22437 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22438 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22439 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22443 fireKey: function(e){
22444 if (!this.picker().isVisible()){
22445 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22451 e.preventDefault();
22459 this.onTogglePeriod();
22462 this.onIncrementMinutes();
22465 this.onDecrementMinutes();
22474 onClick: function(e) {
22475 e.stopPropagation();
22476 e.preventDefault();
22479 picker : function()
22481 return this.pickerEl;
22484 fillTime: function()
22486 var time = this.pop.select('tbody', true).first();
22488 time.dom.innerHTML = '';
22503 cls: 'hours-up fa fas fa-chevron-up'
22523 cls: 'minutes-up fa fas fa-chevron-up'
22544 cls: 'timepicker-hour',
22559 cls: 'timepicker-minute',
22574 cls: 'btn btn-primary period',
22596 cls: 'hours-down fa fas fa-chevron-down'
22616 cls: 'minutes-down fa fas fa-chevron-down'
22634 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22641 var hours = this.time.getHours();
22642 var minutes = this.time.getMinutes();
22655 hours = hours - 12;
22659 hours = '0' + hours;
22663 minutes = '0' + minutes;
22666 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22667 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22668 this.pop.select('button', true).first().dom.innerHTML = period;
22674 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22676 var cls = ['bottom'];
22678 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22685 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22689 //this.picker().setXY(20000,20000);
22690 this.picker().addClass(cls.join('-'));
22694 Roo.each(cls, function(c){
22699 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22700 //_this.picker().setTop(_this.inputEl().getHeight());
22704 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22706 //_this.picker().setTop(0 - _this.picker().getHeight());
22711 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22715 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22723 onFocus : function()
22725 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22729 onBlur : function()
22731 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22737 this.picker().show();
22742 this.fireEvent('show', this, this.date);
22747 this.picker().hide();
22750 this.fireEvent('hide', this, this.date);
22753 setTime : function()
22756 this.setValue(this.time.format(this.format));
22758 this.fireEvent('select', this, this.date);
22763 onMousedown: function(e){
22764 e.stopPropagation();
22765 e.preventDefault();
22768 onIncrementHours: function()
22770 Roo.log('onIncrementHours');
22771 this.time = this.time.add(Date.HOUR, 1);
22776 onDecrementHours: function()
22778 Roo.log('onDecrementHours');
22779 this.time = this.time.add(Date.HOUR, -1);
22783 onIncrementMinutes: function()
22785 Roo.log('onIncrementMinutes');
22786 this.time = this.time.add(Date.MINUTE, 1);
22790 onDecrementMinutes: function()
22792 Roo.log('onDecrementMinutes');
22793 this.time = this.time.add(Date.MINUTE, -1);
22797 onTogglePeriod: function()
22799 Roo.log('onTogglePeriod');
22800 this.time = this.time.add(Date.HOUR, 12);
22808 Roo.apply(Roo.bootstrap.TimeField, {
22812 cls: 'datepicker dropdown-menu',
22816 cls: 'datepicker-time',
22820 cls: 'table-condensed',
22849 cls: 'btn btn-info ok',
22877 * @class Roo.bootstrap.MonthField
22878 * @extends Roo.bootstrap.Input
22879 * Bootstrap MonthField class
22881 * @cfg {String} language default en
22884 * Create a new MonthField
22885 * @param {Object} config The config object
22888 Roo.bootstrap.MonthField = function(config){
22889 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22894 * Fires when this field show.
22895 * @param {Roo.bootstrap.MonthField} this
22896 * @param {Mixed} date The date value
22901 * Fires when this field hide.
22902 * @param {Roo.bootstrap.MonthField} this
22903 * @param {Mixed} date The date value
22908 * Fires when select a date.
22909 * @param {Roo.bootstrap.MonthField} this
22910 * @param {String} oldvalue The old value
22911 * @param {String} newvalue The new value
22917 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22919 onRender: function(ct, position)
22922 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22924 this.language = this.language || 'en';
22925 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22926 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22928 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22929 this.isInline = false;
22930 this.isInput = true;
22931 this.component = this.el.select('.add-on', true).first() || false;
22932 this.component = (this.component && this.component.length === 0) ? false : this.component;
22933 this.hasInput = this.component && this.inputEL().length;
22935 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22937 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22939 this.picker().on('mousedown', this.onMousedown, this);
22940 this.picker().on('click', this.onClick, this);
22942 this.picker().addClass('datepicker-dropdown');
22944 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22945 v.setStyle('width', '189px');
22952 if(this.isInline) {
22958 setValue: function(v, suppressEvent)
22960 var o = this.getValue();
22962 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22966 if(suppressEvent !== true){
22967 this.fireEvent('select', this, o, v);
22972 getValue: function()
22977 onClick: function(e)
22979 e.stopPropagation();
22980 e.preventDefault();
22982 var target = e.getTarget();
22984 if(target.nodeName.toLowerCase() === 'i'){
22985 target = Roo.get(target).dom.parentNode;
22988 var nodeName = target.nodeName;
22989 var className = target.className;
22990 var html = target.innerHTML;
22992 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22996 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22998 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23004 picker : function()
23006 return this.pickerEl;
23009 fillMonths: function()
23012 var months = this.picker().select('>.datepicker-months td', true).first();
23014 months.dom.innerHTML = '';
23020 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23023 months.createChild(month);
23032 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23033 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23036 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23037 e.removeClass('active');
23039 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23040 e.addClass('active');
23047 if(this.isInline) {
23051 this.picker().removeClass(['bottom', 'top']);
23053 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23055 * place to the top of element!
23059 this.picker().addClass('top');
23060 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23065 this.picker().addClass('bottom');
23067 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23070 onFocus : function()
23072 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23076 onBlur : function()
23078 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23080 var d = this.inputEl().getValue();
23089 this.picker().show();
23090 this.picker().select('>.datepicker-months', true).first().show();
23094 this.fireEvent('show', this, this.date);
23099 if(this.isInline) {
23102 this.picker().hide();
23103 this.fireEvent('hide', this, this.date);
23107 onMousedown: function(e)
23109 e.stopPropagation();
23110 e.preventDefault();
23115 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23119 fireKey: function(e)
23121 if (!this.picker().isVisible()){
23122 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23133 e.preventDefault();
23137 dir = e.keyCode == 37 ? -1 : 1;
23139 this.vIndex = this.vIndex + dir;
23141 if(this.vIndex < 0){
23145 if(this.vIndex > 11){
23149 if(isNaN(this.vIndex)){
23153 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23159 dir = e.keyCode == 38 ? -1 : 1;
23161 this.vIndex = this.vIndex + dir * 4;
23163 if(this.vIndex < 0){
23167 if(this.vIndex > 11){
23171 if(isNaN(this.vIndex)){
23175 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23180 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23181 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23185 e.preventDefault();
23188 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23189 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23205 this.picker().remove();
23210 Roo.apply(Roo.bootstrap.MonthField, {
23229 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23230 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23235 Roo.apply(Roo.bootstrap.MonthField, {
23239 cls: 'datepicker dropdown-menu roo-dynamic',
23243 cls: 'datepicker-months',
23247 cls: 'table-condensed',
23249 Roo.bootstrap.DateField.content
23269 * @class Roo.bootstrap.CheckBox
23270 * @extends Roo.bootstrap.Input
23271 * Bootstrap CheckBox class
23273 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23274 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23275 * @cfg {String} boxLabel The text that appears beside the checkbox
23276 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23277 * @cfg {Boolean} checked initnal the element
23278 * @cfg {Boolean} inline inline the element (default false)
23279 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23280 * @cfg {String} tooltip label tooltip
23283 * Create a new CheckBox
23284 * @param {Object} config The config object
23287 Roo.bootstrap.CheckBox = function(config){
23288 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23293 * Fires when the element is checked or unchecked.
23294 * @param {Roo.bootstrap.CheckBox} this This input
23295 * @param {Boolean} checked The new checked value
23300 * Fires when the element is click.
23301 * @param {Roo.bootstrap.CheckBox} this This input
23308 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23310 inputType: 'checkbox',
23319 // checkbox success does not make any sense really..
23324 getAutoCreate : function()
23326 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23332 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23335 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23341 type : this.inputType,
23342 value : this.inputValue,
23343 cls : 'roo-' + this.inputType, //'form-box',
23344 placeholder : this.placeholder || ''
23348 if(this.inputType != 'radio'){
23352 cls : 'roo-hidden-value',
23353 value : this.checked ? this.inputValue : this.valueOff
23358 if (this.weight) { // Validity check?
23359 cfg.cls += " " + this.inputType + "-" + this.weight;
23362 if (this.disabled) {
23363 input.disabled=true;
23367 input.checked = this.checked;
23372 input.name = this.name;
23374 if(this.inputType != 'radio'){
23375 hidden.name = this.name;
23376 input.name = '_hidden_' + this.name;
23381 input.cls += ' input-' + this.size;
23386 ['xs','sm','md','lg'].map(function(size){
23387 if (settings[size]) {
23388 cfg.cls += ' col-' + size + '-' + settings[size];
23392 var inputblock = input;
23394 if (this.before || this.after) {
23397 cls : 'input-group',
23402 inputblock.cn.push({
23404 cls : 'input-group-addon',
23409 inputblock.cn.push(input);
23411 if(this.inputType != 'radio'){
23412 inputblock.cn.push(hidden);
23416 inputblock.cn.push({
23418 cls : 'input-group-addon',
23424 var boxLabelCfg = false;
23430 //'for': id, // box label is handled by onclick - so no for...
23432 html: this.boxLabel
23435 boxLabelCfg.tooltip = this.tooltip;
23441 if (align ==='left' && this.fieldLabel.length) {
23442 // Roo.log("left and has label");
23447 cls : 'control-label',
23448 html : this.fieldLabel
23459 cfg.cn[1].cn.push(boxLabelCfg);
23462 if(this.labelWidth > 12){
23463 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23466 if(this.labelWidth < 13 && this.labelmd == 0){
23467 this.labelmd = this.labelWidth;
23470 if(this.labellg > 0){
23471 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23472 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23475 if(this.labelmd > 0){
23476 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23477 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23480 if(this.labelsm > 0){
23481 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23482 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23485 if(this.labelxs > 0){
23486 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23487 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23490 } else if ( this.fieldLabel.length) {
23491 // Roo.log(" label");
23495 tag: this.boxLabel ? 'span' : 'label',
23497 cls: 'control-label box-input-label',
23498 //cls : 'input-group-addon',
23499 html : this.fieldLabel
23506 cfg.cn.push(boxLabelCfg);
23511 // Roo.log(" no label && no align");
23512 cfg.cn = [ inputblock ] ;
23514 cfg.cn.push(boxLabelCfg);
23522 if(this.inputType != 'radio'){
23523 cfg.cn.push(hidden);
23531 * return the real input element.
23533 inputEl: function ()
23535 return this.el.select('input.roo-' + this.inputType,true).first();
23537 hiddenEl: function ()
23539 return this.el.select('input.roo-hidden-value',true).first();
23542 labelEl: function()
23544 return this.el.select('label.control-label',true).first();
23546 /* depricated... */
23550 return this.labelEl();
23553 boxLabelEl: function()
23555 return this.el.select('label.box-label',true).first();
23558 initEvents : function()
23560 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23562 this.inputEl().on('click', this.onClick, this);
23564 if (this.boxLabel) {
23565 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23568 this.startValue = this.getValue();
23571 Roo.bootstrap.CheckBox.register(this);
23575 onClick : function(e)
23577 if(this.fireEvent('click', this, e) !== false){
23578 this.setChecked(!this.checked);
23583 setChecked : function(state,suppressEvent)
23585 this.startValue = this.getValue();
23587 if(this.inputType == 'radio'){
23589 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23590 e.dom.checked = false;
23593 this.inputEl().dom.checked = true;
23595 this.inputEl().dom.value = this.inputValue;
23597 if(suppressEvent !== true){
23598 this.fireEvent('check', this, true);
23606 this.checked = state;
23608 this.inputEl().dom.checked = state;
23611 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23613 if(suppressEvent !== true){
23614 this.fireEvent('check', this, state);
23620 getValue : function()
23622 if(this.inputType == 'radio'){
23623 return this.getGroupValue();
23626 return this.hiddenEl().dom.value;
23630 getGroupValue : function()
23632 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23636 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23639 setValue : function(v,suppressEvent)
23641 if(this.inputType == 'radio'){
23642 this.setGroupValue(v, suppressEvent);
23646 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23651 setGroupValue : function(v, suppressEvent)
23653 this.startValue = this.getValue();
23655 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23656 e.dom.checked = false;
23658 if(e.dom.value == v){
23659 e.dom.checked = true;
23663 if(suppressEvent !== true){
23664 this.fireEvent('check', this, true);
23672 validate : function()
23674 if(this.getVisibilityEl().hasClass('hidden')){
23680 (this.inputType == 'radio' && this.validateRadio()) ||
23681 (this.inputType == 'checkbox' && this.validateCheckbox())
23687 this.markInvalid();
23691 validateRadio : function()
23693 if(this.getVisibilityEl().hasClass('hidden')){
23697 if(this.allowBlank){
23703 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23704 if(!e.dom.checked){
23716 validateCheckbox : function()
23719 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23720 //return (this.getValue() == this.inputValue) ? true : false;
23723 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23731 for(var i in group){
23732 if(group[i].el.isVisible(true)){
23740 for(var i in group){
23745 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23752 * Mark this field as valid
23754 markValid : function()
23758 this.fireEvent('valid', this);
23760 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23763 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23770 if(this.inputType == 'radio'){
23771 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23772 var fg = e.findParent('.form-group', false, true);
23773 if (Roo.bootstrap.version == 3) {
23774 fg.removeClass([_this.invalidClass, _this.validClass]);
23775 fg.addClass(_this.validClass);
23777 fg.removeClass(['is-valid', 'is-invalid']);
23778 fg.addClass('is-valid');
23786 var fg = this.el.findParent('.form-group', false, true);
23787 if (Roo.bootstrap.version == 3) {
23788 fg.removeClass([this.invalidClass, this.validClass]);
23789 fg.addClass(this.validClass);
23791 fg.removeClass(['is-valid', 'is-invalid']);
23792 fg.addClass('is-valid');
23797 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23803 for(var i in group){
23804 var fg = group[i].el.findParent('.form-group', false, true);
23805 if (Roo.bootstrap.version == 3) {
23806 fg.removeClass([this.invalidClass, this.validClass]);
23807 fg.addClass(this.validClass);
23809 fg.removeClass(['is-valid', 'is-invalid']);
23810 fg.addClass('is-valid');
23816 * Mark this field as invalid
23817 * @param {String} msg The validation message
23819 markInvalid : function(msg)
23821 if(this.allowBlank){
23827 this.fireEvent('invalid', this, msg);
23829 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23832 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23836 label.markInvalid();
23839 if(this.inputType == 'radio'){
23841 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23842 var fg = e.findParent('.form-group', false, true);
23843 if (Roo.bootstrap.version == 3) {
23844 fg.removeClass([_this.invalidClass, _this.validClass]);
23845 fg.addClass(_this.invalidClass);
23847 fg.removeClass(['is-invalid', 'is-valid']);
23848 fg.addClass('is-invalid');
23856 var fg = this.el.findParent('.form-group', false, true);
23857 if (Roo.bootstrap.version == 3) {
23858 fg.removeClass([_this.invalidClass, _this.validClass]);
23859 fg.addClass(_this.invalidClass);
23861 fg.removeClass(['is-invalid', 'is-valid']);
23862 fg.addClass('is-invalid');
23867 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23873 for(var i in group){
23874 var fg = group[i].el.findParent('.form-group', false, true);
23875 if (Roo.bootstrap.version == 3) {
23876 fg.removeClass([_this.invalidClass, _this.validClass]);
23877 fg.addClass(_this.invalidClass);
23879 fg.removeClass(['is-invalid', 'is-valid']);
23880 fg.addClass('is-invalid');
23886 clearInvalid : function()
23888 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23890 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23892 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23894 if (label && label.iconEl) {
23895 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23896 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23900 disable : function()
23902 if(this.inputType != 'radio'){
23903 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23910 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23911 _this.getActionEl().addClass(this.disabledClass);
23912 e.dom.disabled = true;
23916 this.disabled = true;
23917 this.fireEvent("disable", this);
23921 enable : function()
23923 if(this.inputType != 'radio'){
23924 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23931 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23932 _this.getActionEl().removeClass(this.disabledClass);
23933 e.dom.disabled = false;
23937 this.disabled = false;
23938 this.fireEvent("enable", this);
23942 setBoxLabel : function(v)
23947 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23953 Roo.apply(Roo.bootstrap.CheckBox, {
23958 * register a CheckBox Group
23959 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23961 register : function(checkbox)
23963 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23964 this.groups[checkbox.groupId] = {};
23967 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23971 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23975 * fetch a CheckBox Group based on the group ID
23976 * @param {string} the group ID
23977 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23979 get: function(groupId) {
23980 if (typeof(this.groups[groupId]) == 'undefined') {
23984 return this.groups[groupId] ;
23997 * @class Roo.bootstrap.Radio
23998 * @extends Roo.bootstrap.Component
23999 * Bootstrap Radio class
24000 * @cfg {String} boxLabel - the label associated
24001 * @cfg {String} value - the value of radio
24004 * Create a new Radio
24005 * @param {Object} config The config object
24007 Roo.bootstrap.Radio = function(config){
24008 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24012 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24018 getAutoCreate : function()
24022 cls : 'form-group radio',
24027 html : this.boxLabel
24035 initEvents : function()
24037 this.parent().register(this);
24039 this.el.on('click', this.onClick, this);
24043 onClick : function(e)
24045 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24046 this.setChecked(true);
24050 setChecked : function(state, suppressEvent)
24052 this.parent().setValue(this.value, suppressEvent);
24056 setBoxLabel : function(v)
24061 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24076 * @class Roo.bootstrap.SecurePass
24077 * @extends Roo.bootstrap.Input
24078 * Bootstrap SecurePass class
24082 * Create a new SecurePass
24083 * @param {Object} config The config object
24086 Roo.bootstrap.SecurePass = function (config) {
24087 // these go here, so the translation tool can replace them..
24089 PwdEmpty: "Please type a password, and then retype it to confirm.",
24090 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24091 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24092 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24093 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24094 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24095 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24096 TooWeak: "Your password is Too Weak."
24098 this.meterLabel = "Password strength:";
24099 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24100 this.meterClass = [
24101 "roo-password-meter-tooweak",
24102 "roo-password-meter-weak",
24103 "roo-password-meter-medium",
24104 "roo-password-meter-strong",
24105 "roo-password-meter-grey"
24110 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24113 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24115 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24117 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24118 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24119 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24120 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24121 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24122 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24123 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24133 * @cfg {String/Object} Label for the strength meter (defaults to
24134 * 'Password strength:')
24139 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24140 * ['Weak', 'Medium', 'Strong'])
24143 pwdStrengths: false,
24156 initEvents: function ()
24158 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24160 if (this.el.is('input[type=password]') && Roo.isSafari) {
24161 this.el.on('keydown', this.SafariOnKeyDown, this);
24164 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24167 onRender: function (ct, position)
24169 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24170 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24171 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24173 this.trigger.createChild({
24178 cls: 'roo-password-meter-grey col-xs-12',
24181 //width: this.meterWidth + 'px'
24185 cls: 'roo-password-meter-text'
24191 if (this.hideTrigger) {
24192 this.trigger.setDisplayed(false);
24194 this.setSize(this.width || '', this.height || '');
24197 onDestroy: function ()
24199 if (this.trigger) {
24200 this.trigger.removeAllListeners();
24201 this.trigger.remove();
24204 this.wrap.remove();
24206 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24209 checkStrength: function ()
24211 var pwd = this.inputEl().getValue();
24212 if (pwd == this._lastPwd) {
24217 if (this.ClientSideStrongPassword(pwd)) {
24219 } else if (this.ClientSideMediumPassword(pwd)) {
24221 } else if (this.ClientSideWeakPassword(pwd)) {
24227 Roo.log('strength1: ' + strength);
24229 //var pm = this.trigger.child('div/div/div').dom;
24230 var pm = this.trigger.child('div/div');
24231 pm.removeClass(this.meterClass);
24232 pm.addClass(this.meterClass[strength]);
24235 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24237 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24239 this._lastPwd = pwd;
24243 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24245 this._lastPwd = '';
24247 var pm = this.trigger.child('div/div');
24248 pm.removeClass(this.meterClass);
24249 pm.addClass('roo-password-meter-grey');
24252 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24255 this.inputEl().dom.type='password';
24258 validateValue: function (value)
24260 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24263 if (value.length == 0) {
24264 if (this.allowBlank) {
24265 this.clearInvalid();
24269 this.markInvalid(this.errors.PwdEmpty);
24270 this.errorMsg = this.errors.PwdEmpty;
24278 if (!value.match(/[\x21-\x7e]+/)) {
24279 this.markInvalid(this.errors.PwdBadChar);
24280 this.errorMsg = this.errors.PwdBadChar;
24283 if (value.length < 6) {
24284 this.markInvalid(this.errors.PwdShort);
24285 this.errorMsg = this.errors.PwdShort;
24288 if (value.length > 16) {
24289 this.markInvalid(this.errors.PwdLong);
24290 this.errorMsg = this.errors.PwdLong;
24294 if (this.ClientSideStrongPassword(value)) {
24296 } else if (this.ClientSideMediumPassword(value)) {
24298 } else if (this.ClientSideWeakPassword(value)) {
24305 if (strength < 2) {
24306 //this.markInvalid(this.errors.TooWeak);
24307 this.errorMsg = this.errors.TooWeak;
24312 console.log('strength2: ' + strength);
24314 //var pm = this.trigger.child('div/div/div').dom;
24316 var pm = this.trigger.child('div/div');
24317 pm.removeClass(this.meterClass);
24318 pm.addClass(this.meterClass[strength]);
24320 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24322 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24324 this.errorMsg = '';
24328 CharacterSetChecks: function (type)
24331 this.fResult = false;
24334 isctype: function (character, type)
24337 case this.kCapitalLetter:
24338 if (character >= 'A' && character <= 'Z') {
24343 case this.kSmallLetter:
24344 if (character >= 'a' && character <= 'z') {
24350 if (character >= '0' && character <= '9') {
24355 case this.kPunctuation:
24356 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24367 IsLongEnough: function (pwd, size)
24369 return !(pwd == null || isNaN(size) || pwd.length < size);
24372 SpansEnoughCharacterSets: function (word, nb)
24374 if (!this.IsLongEnough(word, nb))
24379 var characterSetChecks = new Array(
24380 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24381 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24384 for (var index = 0; index < word.length; ++index) {
24385 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24386 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24387 characterSetChecks[nCharSet].fResult = true;
24394 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24395 if (characterSetChecks[nCharSet].fResult) {
24400 if (nCharSets < nb) {
24406 ClientSideStrongPassword: function (pwd)
24408 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24411 ClientSideMediumPassword: function (pwd)
24413 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24416 ClientSideWeakPassword: function (pwd)
24418 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24421 })//<script type="text/javascript">
24424 * Based Ext JS Library 1.1.1
24425 * Copyright(c) 2006-2007, Ext JS, LLC.
24431 * @class Roo.HtmlEditorCore
24432 * @extends Roo.Component
24433 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24435 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24438 Roo.HtmlEditorCore = function(config){
24441 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24446 * @event initialize
24447 * Fires when the editor is fully initialized (including the iframe)
24448 * @param {Roo.HtmlEditorCore} this
24453 * Fires when the editor is first receives the focus. Any insertion must wait
24454 * until after this event.
24455 * @param {Roo.HtmlEditorCore} this
24459 * @event beforesync
24460 * Fires before the textarea is updated with content from the editor iframe. Return false
24461 * to cancel the sync.
24462 * @param {Roo.HtmlEditorCore} this
24463 * @param {String} html
24467 * @event beforepush
24468 * Fires before the iframe editor is updated with content from the textarea. Return false
24469 * to cancel the push.
24470 * @param {Roo.HtmlEditorCore} this
24471 * @param {String} html
24476 * Fires when the textarea is updated with content from the editor iframe.
24477 * @param {Roo.HtmlEditorCore} this
24478 * @param {String} html
24483 * Fires when the iframe editor is updated with content from the textarea.
24484 * @param {Roo.HtmlEditorCore} this
24485 * @param {String} html
24490 * @event editorevent
24491 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24492 * @param {Roo.HtmlEditorCore} this
24498 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24500 // defaults : white / black...
24501 this.applyBlacklists();
24508 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24512 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24518 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24523 * @cfg {Number} height (in pixels)
24527 * @cfg {Number} width (in pixels)
24532 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24535 stylesheets: false,
24540 // private properties
24541 validationEvent : false,
24543 initialized : false,
24545 sourceEditMode : false,
24546 onFocus : Roo.emptyFn,
24548 hideMode:'offsets',
24552 // blacklist + whitelisted elements..
24559 * Protected method that will not generally be called directly. It
24560 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24561 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24563 getDocMarkup : function(){
24567 // inherit styels from page...??
24568 if (this.stylesheets === false) {
24570 Roo.get(document.head).select('style').each(function(node) {
24571 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24574 Roo.get(document.head).select('link').each(function(node) {
24575 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24578 } else if (!this.stylesheets.length) {
24580 st = '<style type="text/css">' +
24581 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24584 for (var i in this.stylesheets) {
24585 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24590 st += '<style type="text/css">' +
24591 'IMG { cursor: pointer } ' +
24594 var cls = 'roo-htmleditor-body';
24596 if(this.bodyCls.length){
24597 cls += ' ' + this.bodyCls;
24600 return '<html><head>' + st +
24601 //<style type="text/css">' +
24602 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24604 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24608 onRender : function(ct, position)
24611 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24612 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24615 this.el.dom.style.border = '0 none';
24616 this.el.dom.setAttribute('tabIndex', -1);
24617 this.el.addClass('x-hidden hide');
24621 if(Roo.isIE){ // fix IE 1px bogus margin
24622 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24626 this.frameId = Roo.id();
24630 var iframe = this.owner.wrap.createChild({
24632 cls: 'form-control', // bootstrap..
24634 name: this.frameId,
24635 frameBorder : 'no',
24636 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24641 this.iframe = iframe.dom;
24643 this.assignDocWin();
24645 this.doc.designMode = 'on';
24648 this.doc.write(this.getDocMarkup());
24652 var task = { // must defer to wait for browser to be ready
24654 //console.log("run task?" + this.doc.readyState);
24655 this.assignDocWin();
24656 if(this.doc.body || this.doc.readyState == 'complete'){
24658 this.doc.designMode="on";
24662 Roo.TaskMgr.stop(task);
24663 this.initEditor.defer(10, this);
24670 Roo.TaskMgr.start(task);
24675 onResize : function(w, h)
24677 Roo.log('resize: ' +w + ',' + h );
24678 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24682 if(typeof w == 'number'){
24684 this.iframe.style.width = w + 'px';
24686 if(typeof h == 'number'){
24688 this.iframe.style.height = h + 'px';
24690 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24697 * Toggles the editor between standard and source edit mode.
24698 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24700 toggleSourceEdit : function(sourceEditMode){
24702 this.sourceEditMode = sourceEditMode === true;
24704 if(this.sourceEditMode){
24706 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24709 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24710 //this.iframe.className = '';
24713 //this.setSize(this.owner.wrap.getSize());
24714 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24721 * Protected method that will not generally be called directly. If you need/want
24722 * custom HTML cleanup, this is the method you should override.
24723 * @param {String} html The HTML to be cleaned
24724 * return {String} The cleaned HTML
24726 cleanHtml : function(html){
24727 html = String(html);
24728 if(html.length > 5){
24729 if(Roo.isSafari){ // strip safari nonsense
24730 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24733 if(html == ' '){
24740 * HTML Editor -> Textarea
24741 * Protected method that will not generally be called directly. Syncs the contents
24742 * of the editor iframe with the textarea.
24744 syncValue : function(){
24745 if(this.initialized){
24746 var bd = (this.doc.body || this.doc.documentElement);
24747 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24748 var html = bd.innerHTML;
24750 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24751 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24753 html = '<div style="'+m[0]+'">' + html + '</div>';
24756 html = this.cleanHtml(html);
24757 // fix up the special chars.. normaly like back quotes in word...
24758 // however we do not want to do this with chinese..
24759 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24761 var cc = match.charCodeAt();
24763 // Get the character value, handling surrogate pairs
24764 if (match.length == 2) {
24765 // It's a surrogate pair, calculate the Unicode code point
24766 var high = match.charCodeAt(0) - 0xD800;
24767 var low = match.charCodeAt(1) - 0xDC00;
24768 cc = (high * 0x400) + low + 0x10000;
24770 (cc >= 0x4E00 && cc < 0xA000 ) ||
24771 (cc >= 0x3400 && cc < 0x4E00 ) ||
24772 (cc >= 0xf900 && cc < 0xfb00 )
24777 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24778 return "&#" + cc + ";";
24785 if(this.owner.fireEvent('beforesync', this, html) !== false){
24786 this.el.dom.value = html;
24787 this.owner.fireEvent('sync', this, html);
24793 * Protected method that will not generally be called directly. Pushes the value of the textarea
24794 * into the iframe editor.
24796 pushValue : function(){
24797 if(this.initialized){
24798 var v = this.el.dom.value.trim();
24800 // if(v.length < 1){
24804 if(this.owner.fireEvent('beforepush', this, v) !== false){
24805 var d = (this.doc.body || this.doc.documentElement);
24807 this.cleanUpPaste();
24808 this.el.dom.value = d.innerHTML;
24809 this.owner.fireEvent('push', this, v);
24815 deferFocus : function(){
24816 this.focus.defer(10, this);
24820 focus : function(){
24821 if(this.win && !this.sourceEditMode){
24828 assignDocWin: function()
24830 var iframe = this.iframe;
24833 this.doc = iframe.contentWindow.document;
24834 this.win = iframe.contentWindow;
24836 // if (!Roo.get(this.frameId)) {
24839 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24840 // this.win = Roo.get(this.frameId).dom.contentWindow;
24842 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24846 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24847 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24852 initEditor : function(){
24853 //console.log("INIT EDITOR");
24854 this.assignDocWin();
24858 this.doc.designMode="on";
24860 this.doc.write(this.getDocMarkup());
24863 var dbody = (this.doc.body || this.doc.documentElement);
24864 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24865 // this copies styles from the containing element into thsi one..
24866 // not sure why we need all of this..
24867 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24869 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24870 //ss['background-attachment'] = 'fixed'; // w3c
24871 dbody.bgProperties = 'fixed'; // ie
24872 //Roo.DomHelper.applyStyles(dbody, ss);
24873 Roo.EventManager.on(this.doc, {
24874 //'mousedown': this.onEditorEvent,
24875 'mouseup': this.onEditorEvent,
24876 'dblclick': this.onEditorEvent,
24877 'click': this.onEditorEvent,
24878 'keyup': this.onEditorEvent,
24883 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24885 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24886 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24888 this.initialized = true;
24890 this.owner.fireEvent('initialize', this);
24895 onDestroy : function(){
24901 //for (var i =0; i < this.toolbars.length;i++) {
24902 // // fixme - ask toolbars for heights?
24903 // this.toolbars[i].onDestroy();
24906 //this.wrap.dom.innerHTML = '';
24907 //this.wrap.remove();
24912 onFirstFocus : function(){
24914 this.assignDocWin();
24917 this.activated = true;
24920 if(Roo.isGecko){ // prevent silly gecko errors
24922 var s = this.win.getSelection();
24923 if(!s.focusNode || s.focusNode.nodeType != 3){
24924 var r = s.getRangeAt(0);
24925 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24930 this.execCmd('useCSS', true);
24931 this.execCmd('styleWithCSS', false);
24934 this.owner.fireEvent('activate', this);
24938 adjustFont: function(btn){
24939 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24940 //if(Roo.isSafari){ // safari
24943 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24944 if(Roo.isSafari){ // safari
24945 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24946 v = (v < 10) ? 10 : v;
24947 v = (v > 48) ? 48 : v;
24948 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24953 v = Math.max(1, v+adjust);
24955 this.execCmd('FontSize', v );
24958 onEditorEvent : function(e)
24960 this.owner.fireEvent('editorevent', this, e);
24961 // this.updateToolbar();
24962 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24965 insertTag : function(tg)
24967 // could be a bit smarter... -> wrap the current selected tRoo..
24968 if (tg.toLowerCase() == 'span' ||
24969 tg.toLowerCase() == 'code' ||
24970 tg.toLowerCase() == 'sup' ||
24971 tg.toLowerCase() == 'sub'
24974 range = this.createRange(this.getSelection());
24975 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24976 wrappingNode.appendChild(range.extractContents());
24977 range.insertNode(wrappingNode);
24984 this.execCmd("formatblock", tg);
24988 insertText : function(txt)
24992 var range = this.createRange();
24993 range.deleteContents();
24994 //alert(Sender.getAttribute('label'));
24996 range.insertNode(this.doc.createTextNode(txt));
25002 * Executes a Midas editor command on the editor document and performs necessary focus and
25003 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25004 * @param {String} cmd The Midas command
25005 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25007 relayCmd : function(cmd, value){
25009 this.execCmd(cmd, value);
25010 this.owner.fireEvent('editorevent', this);
25011 //this.updateToolbar();
25012 this.owner.deferFocus();
25016 * Executes a Midas editor command directly on the editor document.
25017 * For visual commands, you should use {@link #relayCmd} instead.
25018 * <b>This should only be called after the editor is initialized.</b>
25019 * @param {String} cmd The Midas command
25020 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25022 execCmd : function(cmd, value){
25023 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25030 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25032 * @param {String} text | dom node..
25034 insertAtCursor : function(text)
25037 if(!this.activated){
25043 var r = this.doc.selection.createRange();
25054 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25058 // from jquery ui (MIT licenced)
25060 var win = this.win;
25062 if (win.getSelection && win.getSelection().getRangeAt) {
25063 range = win.getSelection().getRangeAt(0);
25064 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25065 range.insertNode(node);
25066 } else if (win.document.selection && win.document.selection.createRange) {
25067 // no firefox support
25068 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25069 win.document.selection.createRange().pasteHTML(txt);
25071 // no firefox support
25072 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25073 this.execCmd('InsertHTML', txt);
25082 mozKeyPress : function(e){
25084 var c = e.getCharCode(), cmd;
25087 c = String.fromCharCode(c).toLowerCase();
25101 this.cleanUpPaste.defer(100, this);
25109 e.preventDefault();
25117 fixKeys : function(){ // load time branching for fastest keydown performance
25119 return function(e){
25120 var k = e.getKey(), r;
25123 r = this.doc.selection.createRange();
25126 r.pasteHTML('    ');
25133 r = this.doc.selection.createRange();
25135 var target = r.parentElement();
25136 if(!target || target.tagName.toLowerCase() != 'li'){
25138 r.pasteHTML('<br />');
25144 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25145 this.cleanUpPaste.defer(100, this);
25151 }else if(Roo.isOpera){
25152 return function(e){
25153 var k = e.getKey();
25157 this.execCmd('InsertHTML','    ');
25160 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25161 this.cleanUpPaste.defer(100, this);
25166 }else if(Roo.isSafari){
25167 return function(e){
25168 var k = e.getKey();
25172 this.execCmd('InsertText','\t');
25176 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25177 this.cleanUpPaste.defer(100, this);
25185 getAllAncestors: function()
25187 var p = this.getSelectedNode();
25190 a.push(p); // push blank onto stack..
25191 p = this.getParentElement();
25195 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25199 a.push(this.doc.body);
25203 lastSelNode : false,
25206 getSelection : function()
25208 this.assignDocWin();
25209 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25212 getSelectedNode: function()
25214 // this may only work on Gecko!!!
25216 // should we cache this!!!!
25221 var range = this.createRange(this.getSelection()).cloneRange();
25224 var parent = range.parentElement();
25226 var testRange = range.duplicate();
25227 testRange.moveToElementText(parent);
25228 if (testRange.inRange(range)) {
25231 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25234 parent = parent.parentElement;
25239 // is ancestor a text element.
25240 var ac = range.commonAncestorContainer;
25241 if (ac.nodeType == 3) {
25242 ac = ac.parentNode;
25245 var ar = ac.childNodes;
25248 var other_nodes = [];
25249 var has_other_nodes = false;
25250 for (var i=0;i<ar.length;i++) {
25251 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25254 // fullly contained node.
25256 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25261 // probably selected..
25262 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25263 other_nodes.push(ar[i]);
25267 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25272 has_other_nodes = true;
25274 if (!nodes.length && other_nodes.length) {
25275 nodes= other_nodes;
25277 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25283 createRange: function(sel)
25285 // this has strange effects when using with
25286 // top toolbar - not sure if it's a great idea.
25287 //this.editor.contentWindow.focus();
25288 if (typeof sel != "undefined") {
25290 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25292 return this.doc.createRange();
25295 return this.doc.createRange();
25298 getParentElement: function()
25301 this.assignDocWin();
25302 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25304 var range = this.createRange(sel);
25307 var p = range.commonAncestorContainer;
25308 while (p.nodeType == 3) { // text node
25319 * Range intersection.. the hard stuff...
25323 * [ -- selected range --- ]
25327 * if end is before start or hits it. fail.
25328 * if start is after end or hits it fail.
25330 * if either hits (but other is outside. - then it's not
25336 // @see http://www.thismuchiknow.co.uk/?p=64.
25337 rangeIntersectsNode : function(range, node)
25339 var nodeRange = node.ownerDocument.createRange();
25341 nodeRange.selectNode(node);
25343 nodeRange.selectNodeContents(node);
25346 var rangeStartRange = range.cloneRange();
25347 rangeStartRange.collapse(true);
25349 var rangeEndRange = range.cloneRange();
25350 rangeEndRange.collapse(false);
25352 var nodeStartRange = nodeRange.cloneRange();
25353 nodeStartRange.collapse(true);
25355 var nodeEndRange = nodeRange.cloneRange();
25356 nodeEndRange.collapse(false);
25358 return rangeStartRange.compareBoundaryPoints(
25359 Range.START_TO_START, nodeEndRange) == -1 &&
25360 rangeEndRange.compareBoundaryPoints(
25361 Range.START_TO_START, nodeStartRange) == 1;
25365 rangeCompareNode : function(range, node)
25367 var nodeRange = node.ownerDocument.createRange();
25369 nodeRange.selectNode(node);
25371 nodeRange.selectNodeContents(node);
25375 range.collapse(true);
25377 nodeRange.collapse(true);
25379 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25380 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25382 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25384 var nodeIsBefore = ss == 1;
25385 var nodeIsAfter = ee == -1;
25387 if (nodeIsBefore && nodeIsAfter) {
25390 if (!nodeIsBefore && nodeIsAfter) {
25391 return 1; //right trailed.
25394 if (nodeIsBefore && !nodeIsAfter) {
25395 return 2; // left trailed.
25401 // private? - in a new class?
25402 cleanUpPaste : function()
25404 // cleans up the whole document..
25405 Roo.log('cleanuppaste');
25407 this.cleanUpChildren(this.doc.body);
25408 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25409 if (clean != this.doc.body.innerHTML) {
25410 this.doc.body.innerHTML = clean;
25415 cleanWordChars : function(input) {// change the chars to hex code
25416 var he = Roo.HtmlEditorCore;
25418 var output = input;
25419 Roo.each(he.swapCodes, function(sw) {
25420 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25422 output = output.replace(swapper, sw[1]);
25429 cleanUpChildren : function (n)
25431 if (!n.childNodes.length) {
25434 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25435 this.cleanUpChild(n.childNodes[i]);
25442 cleanUpChild : function (node)
25445 //console.log(node);
25446 if (node.nodeName == "#text") {
25447 // clean up silly Windows -- stuff?
25450 if (node.nodeName == "#comment") {
25451 node.parentNode.removeChild(node);
25452 // clean up silly Windows -- stuff?
25455 var lcname = node.tagName.toLowerCase();
25456 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25457 // whitelist of tags..
25459 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25461 node.parentNode.removeChild(node);
25466 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25468 // spans with no attributes - just remove them..
25469 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25470 remove_keep_children = true;
25473 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25474 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25476 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25477 // remove_keep_children = true;
25480 if (remove_keep_children) {
25481 this.cleanUpChildren(node);
25482 // inserts everything just before this node...
25483 while (node.childNodes.length) {
25484 var cn = node.childNodes[0];
25485 node.removeChild(cn);
25486 node.parentNode.insertBefore(cn, node);
25488 node.parentNode.removeChild(node);
25492 if (!node.attributes || !node.attributes.length) {
25497 this.cleanUpChildren(node);
25501 function cleanAttr(n,v)
25504 if (v.match(/^\./) || v.match(/^\//)) {
25507 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25510 if (v.match(/^#/)) {
25513 if (v.match(/^\{/)) { // allow template editing.
25516 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25517 node.removeAttribute(n);
25521 var cwhite = this.cwhite;
25522 var cblack = this.cblack;
25524 function cleanStyle(n,v)
25526 if (v.match(/expression/)) { //XSS?? should we even bother..
25527 node.removeAttribute(n);
25531 var parts = v.split(/;/);
25534 Roo.each(parts, function(p) {
25535 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25539 var l = p.split(':').shift().replace(/\s+/g,'');
25540 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25542 if ( cwhite.length && cblack.indexOf(l) > -1) {
25543 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25544 //node.removeAttribute(n);
25548 // only allow 'c whitelisted system attributes'
25549 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25550 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25551 //node.removeAttribute(n);
25561 if (clean.length) {
25562 node.setAttribute(n, clean.join(';'));
25564 node.removeAttribute(n);
25570 for (var i = node.attributes.length-1; i > -1 ; i--) {
25571 var a = node.attributes[i];
25574 if (a.name.toLowerCase().substr(0,2)=='on') {
25575 node.removeAttribute(a.name);
25578 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25579 node.removeAttribute(a.name);
25582 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25583 cleanAttr(a.name,a.value); // fixme..
25586 if (a.name == 'style') {
25587 cleanStyle(a.name,a.value);
25590 /// clean up MS crap..
25591 // tecnically this should be a list of valid class'es..
25594 if (a.name == 'class') {
25595 if (a.value.match(/^Mso/)) {
25596 node.removeAttribute('class');
25599 if (a.value.match(/^body$/)) {
25600 node.removeAttribute('class');
25611 this.cleanUpChildren(node);
25617 * Clean up MS wordisms...
25619 cleanWord : function(node)
25622 this.cleanWord(this.doc.body);
25627 node.nodeName == 'SPAN' &&
25628 !node.hasAttributes() &&
25629 node.childNodes.length == 1 &&
25630 node.firstChild.nodeName == "#text"
25632 var textNode = node.firstChild;
25633 node.removeChild(textNode);
25634 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25635 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25637 node.parentNode.insertBefore(textNode, node);
25638 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25639 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25641 node.parentNode.removeChild(node);
25644 if (node.nodeName == "#text") {
25645 // clean up silly Windows -- stuff?
25648 if (node.nodeName == "#comment") {
25649 node.parentNode.removeChild(node);
25650 // clean up silly Windows -- stuff?
25654 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25655 node.parentNode.removeChild(node);
25658 //Roo.log(node.tagName);
25659 // remove - but keep children..
25660 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25661 //Roo.log('-- removed');
25662 while (node.childNodes.length) {
25663 var cn = node.childNodes[0];
25664 node.removeChild(cn);
25665 node.parentNode.insertBefore(cn, node);
25666 // move node to parent - and clean it..
25667 this.cleanWord(cn);
25669 node.parentNode.removeChild(node);
25670 /// no need to iterate chidlren = it's got none..
25671 //this.iterateChildren(node, this.cleanWord);
25675 if (node.className.length) {
25677 var cn = node.className.split(/\W+/);
25679 Roo.each(cn, function(cls) {
25680 if (cls.match(/Mso[a-zA-Z]+/)) {
25685 node.className = cna.length ? cna.join(' ') : '';
25687 node.removeAttribute("class");
25691 if (node.hasAttribute("lang")) {
25692 node.removeAttribute("lang");
25695 if (node.hasAttribute("style")) {
25697 var styles = node.getAttribute("style").split(";");
25699 Roo.each(styles, function(s) {
25700 if (!s.match(/:/)) {
25703 var kv = s.split(":");
25704 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25707 // what ever is left... we allow.
25710 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25711 if (!nstyle.length) {
25712 node.removeAttribute('style');
25715 this.iterateChildren(node, this.cleanWord);
25721 * iterateChildren of a Node, calling fn each time, using this as the scole..
25722 * @param {DomNode} node node to iterate children of.
25723 * @param {Function} fn method of this class to call on each item.
25725 iterateChildren : function(node, fn)
25727 if (!node.childNodes.length) {
25730 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25731 fn.call(this, node.childNodes[i])
25737 * cleanTableWidths.
25739 * Quite often pasting from word etc.. results in tables with column and widths.
25740 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25743 cleanTableWidths : function(node)
25748 this.cleanTableWidths(this.doc.body);
25753 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25756 Roo.log(node.tagName);
25757 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25758 this.iterateChildren(node, this.cleanTableWidths);
25761 if (node.hasAttribute('width')) {
25762 node.removeAttribute('width');
25766 if (node.hasAttribute("style")) {
25769 var styles = node.getAttribute("style").split(";");
25771 Roo.each(styles, function(s) {
25772 if (!s.match(/:/)) {
25775 var kv = s.split(":");
25776 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25779 // what ever is left... we allow.
25782 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25783 if (!nstyle.length) {
25784 node.removeAttribute('style');
25788 this.iterateChildren(node, this.cleanTableWidths);
25796 domToHTML : function(currentElement, depth, nopadtext) {
25798 depth = depth || 0;
25799 nopadtext = nopadtext || false;
25801 if (!currentElement) {
25802 return this.domToHTML(this.doc.body);
25805 //Roo.log(currentElement);
25807 var allText = false;
25808 var nodeName = currentElement.nodeName;
25809 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25811 if (nodeName == '#text') {
25813 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25818 if (nodeName != 'BODY') {
25821 // Prints the node tagName, such as <A>, <IMG>, etc
25824 for(i = 0; i < currentElement.attributes.length;i++) {
25826 var aname = currentElement.attributes.item(i).name;
25827 if (!currentElement.attributes.item(i).value.length) {
25830 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25833 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25842 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25845 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25850 // Traverse the tree
25852 var currentElementChild = currentElement.childNodes.item(i);
25853 var allText = true;
25854 var innerHTML = '';
25856 while (currentElementChild) {
25857 // Formatting code (indent the tree so it looks nice on the screen)
25858 var nopad = nopadtext;
25859 if (lastnode == 'SPAN') {
25863 if (currentElementChild.nodeName == '#text') {
25864 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25865 toadd = nopadtext ? toadd : toadd.trim();
25866 if (!nopad && toadd.length > 80) {
25867 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25869 innerHTML += toadd;
25872 currentElementChild = currentElement.childNodes.item(i);
25878 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25880 // Recursively traverse the tree structure of the child node
25881 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25882 lastnode = currentElementChild.nodeName;
25884 currentElementChild=currentElement.childNodes.item(i);
25890 // The remaining code is mostly for formatting the tree
25891 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25896 ret+= "</"+tagName+">";
25902 applyBlacklists : function()
25904 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25905 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25909 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25910 if (b.indexOf(tag) > -1) {
25913 this.white.push(tag);
25917 Roo.each(w, function(tag) {
25918 if (b.indexOf(tag) > -1) {
25921 if (this.white.indexOf(tag) > -1) {
25924 this.white.push(tag);
25929 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25930 if (w.indexOf(tag) > -1) {
25933 this.black.push(tag);
25937 Roo.each(b, function(tag) {
25938 if (w.indexOf(tag) > -1) {
25941 if (this.black.indexOf(tag) > -1) {
25944 this.black.push(tag);
25949 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25950 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25954 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25955 if (b.indexOf(tag) > -1) {
25958 this.cwhite.push(tag);
25962 Roo.each(w, function(tag) {
25963 if (b.indexOf(tag) > -1) {
25966 if (this.cwhite.indexOf(tag) > -1) {
25969 this.cwhite.push(tag);
25974 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25975 if (w.indexOf(tag) > -1) {
25978 this.cblack.push(tag);
25982 Roo.each(b, function(tag) {
25983 if (w.indexOf(tag) > -1) {
25986 if (this.cblack.indexOf(tag) > -1) {
25989 this.cblack.push(tag);
25994 setStylesheets : function(stylesheets)
25996 if(typeof(stylesheets) == 'string'){
25997 Roo.get(this.iframe.contentDocument.head).createChild({
25999 rel : 'stylesheet',
26008 Roo.each(stylesheets, function(s) {
26013 Roo.get(_this.iframe.contentDocument.head).createChild({
26015 rel : 'stylesheet',
26024 removeStylesheets : function()
26028 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26033 setStyle : function(style)
26035 Roo.get(this.iframe.contentDocument.head).createChild({
26044 // hide stuff that is not compatible
26058 * @event specialkey
26062 * @cfg {String} fieldClass @hide
26065 * @cfg {String} focusClass @hide
26068 * @cfg {String} autoCreate @hide
26071 * @cfg {String} inputType @hide
26074 * @cfg {String} invalidClass @hide
26077 * @cfg {String} invalidText @hide
26080 * @cfg {String} msgFx @hide
26083 * @cfg {String} validateOnBlur @hide
26087 Roo.HtmlEditorCore.white = [
26088 'area', 'br', 'img', 'input', 'hr', 'wbr',
26090 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26091 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26092 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26093 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26094 'table', 'ul', 'xmp',
26096 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26099 'dir', 'menu', 'ol', 'ul', 'dl',
26105 Roo.HtmlEditorCore.black = [
26106 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26108 'base', 'basefont', 'bgsound', 'blink', 'body',
26109 'frame', 'frameset', 'head', 'html', 'ilayer',
26110 'iframe', 'layer', 'link', 'meta', 'object',
26111 'script', 'style' ,'title', 'xml' // clean later..
26113 Roo.HtmlEditorCore.clean = [
26114 'script', 'style', 'title', 'xml'
26116 Roo.HtmlEditorCore.remove = [
26121 Roo.HtmlEditorCore.ablack = [
26125 Roo.HtmlEditorCore.aclean = [
26126 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26130 Roo.HtmlEditorCore.pwhite= [
26131 'http', 'https', 'mailto'
26134 // white listed style attributes.
26135 Roo.HtmlEditorCore.cwhite= [
26136 // 'text-align', /// default is to allow most things..
26142 // black listed style attributes.
26143 Roo.HtmlEditorCore.cblack= [
26144 // 'font-size' -- this can be set by the project
26148 Roo.HtmlEditorCore.swapCodes =[
26149 [ 8211, "–" ],
26150 [ 8212, "—" ],
26167 * @class Roo.bootstrap.HtmlEditor
26168 * @extends Roo.bootstrap.TextArea
26169 * Bootstrap HtmlEditor class
26172 * Create a new HtmlEditor
26173 * @param {Object} config The config object
26176 Roo.bootstrap.HtmlEditor = function(config){
26177 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26178 if (!this.toolbars) {
26179 this.toolbars = [];
26182 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26185 * @event initialize
26186 * Fires when the editor is fully initialized (including the iframe)
26187 * @param {HtmlEditor} this
26192 * Fires when the editor is first receives the focus. Any insertion must wait
26193 * until after this event.
26194 * @param {HtmlEditor} this
26198 * @event beforesync
26199 * Fires before the textarea is updated with content from the editor iframe. Return false
26200 * to cancel the sync.
26201 * @param {HtmlEditor} this
26202 * @param {String} html
26206 * @event beforepush
26207 * Fires before the iframe editor is updated with content from the textarea. Return false
26208 * to cancel the push.
26209 * @param {HtmlEditor} this
26210 * @param {String} html
26215 * Fires when the textarea is updated with content from the editor iframe.
26216 * @param {HtmlEditor} this
26217 * @param {String} html
26222 * Fires when the iframe editor is updated with content from the textarea.
26223 * @param {HtmlEditor} this
26224 * @param {String} html
26228 * @event editmodechange
26229 * Fires when the editor switches edit modes
26230 * @param {HtmlEditor} this
26231 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26233 editmodechange: true,
26235 * @event editorevent
26236 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26237 * @param {HtmlEditor} this
26241 * @event firstfocus
26242 * Fires when on first focus - needed by toolbars..
26243 * @param {HtmlEditor} this
26248 * Auto save the htmlEditor value as a file into Events
26249 * @param {HtmlEditor} this
26253 * @event savedpreview
26254 * preview the saved version of htmlEditor
26255 * @param {HtmlEditor} this
26262 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26266 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26271 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26276 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26281 * @cfg {Number} height (in pixels)
26285 * @cfg {Number} width (in pixels)
26290 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26293 stylesheets: false,
26298 // private properties
26299 validationEvent : false,
26301 initialized : false,
26304 onFocus : Roo.emptyFn,
26306 hideMode:'offsets',
26308 tbContainer : false,
26312 toolbarContainer :function() {
26313 return this.wrap.select('.x-html-editor-tb',true).first();
26317 * Protected method that will not generally be called directly. It
26318 * is called when the editor creates its toolbar. Override this method if you need to
26319 * add custom toolbar buttons.
26320 * @param {HtmlEditor} editor
26322 createToolbar : function(){
26323 Roo.log('renewing');
26324 Roo.log("create toolbars");
26326 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26327 this.toolbars[0].render(this.toolbarContainer());
26331 // if (!editor.toolbars || !editor.toolbars.length) {
26332 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26335 // for (var i =0 ; i < editor.toolbars.length;i++) {
26336 // editor.toolbars[i] = Roo.factory(
26337 // typeof(editor.toolbars[i]) == 'string' ?
26338 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26339 // Roo.bootstrap.HtmlEditor);
26340 // editor.toolbars[i].init(editor);
26346 onRender : function(ct, position)
26348 // Roo.log("Call onRender: " + this.xtype);
26350 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26352 this.wrap = this.inputEl().wrap({
26353 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26356 this.editorcore.onRender(ct, position);
26358 if (this.resizable) {
26359 this.resizeEl = new Roo.Resizable(this.wrap, {
26363 minHeight : this.height,
26364 height: this.height,
26365 handles : this.resizable,
26368 resize : function(r, w, h) {
26369 _t.onResize(w,h); // -something
26375 this.createToolbar(this);
26378 if(!this.width && this.resizable){
26379 this.setSize(this.wrap.getSize());
26381 if (this.resizeEl) {
26382 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26383 // should trigger onReize..
26389 onResize : function(w, h)
26391 Roo.log('resize: ' +w + ',' + h );
26392 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26396 if(this.inputEl() ){
26397 if(typeof w == 'number'){
26398 var aw = w - this.wrap.getFrameWidth('lr');
26399 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26402 if(typeof h == 'number'){
26403 var tbh = -11; // fixme it needs to tool bar size!
26404 for (var i =0; i < this.toolbars.length;i++) {
26405 // fixme - ask toolbars for heights?
26406 tbh += this.toolbars[i].el.getHeight();
26407 //if (this.toolbars[i].footer) {
26408 // tbh += this.toolbars[i].footer.el.getHeight();
26416 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26417 ah -= 5; // knock a few pixes off for look..
26418 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26422 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26423 this.editorcore.onResize(ew,eh);
26428 * Toggles the editor between standard and source edit mode.
26429 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26431 toggleSourceEdit : function(sourceEditMode)
26433 this.editorcore.toggleSourceEdit(sourceEditMode);
26435 if(this.editorcore.sourceEditMode){
26436 Roo.log('editor - showing textarea');
26439 // Roo.log(this.syncValue());
26441 this.inputEl().removeClass(['hide', 'x-hidden']);
26442 this.inputEl().dom.removeAttribute('tabIndex');
26443 this.inputEl().focus();
26445 Roo.log('editor - hiding textarea');
26447 // Roo.log(this.pushValue());
26450 this.inputEl().addClass(['hide', 'x-hidden']);
26451 this.inputEl().dom.setAttribute('tabIndex', -1);
26452 //this.deferFocus();
26455 if(this.resizable){
26456 this.setSize(this.wrap.getSize());
26459 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26462 // private (for BoxComponent)
26463 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26465 // private (for BoxComponent)
26466 getResizeEl : function(){
26470 // private (for BoxComponent)
26471 getPositionEl : function(){
26476 initEvents : function(){
26477 this.originalValue = this.getValue();
26481 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26484 // markInvalid : Roo.emptyFn,
26486 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26489 // clearInvalid : Roo.emptyFn,
26491 setValue : function(v){
26492 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26493 this.editorcore.pushValue();
26498 deferFocus : function(){
26499 this.focus.defer(10, this);
26503 focus : function(){
26504 this.editorcore.focus();
26510 onDestroy : function(){
26516 for (var i =0; i < this.toolbars.length;i++) {
26517 // fixme - ask toolbars for heights?
26518 this.toolbars[i].onDestroy();
26521 this.wrap.dom.innerHTML = '';
26522 this.wrap.remove();
26527 onFirstFocus : function(){
26528 //Roo.log("onFirstFocus");
26529 this.editorcore.onFirstFocus();
26530 for (var i =0; i < this.toolbars.length;i++) {
26531 this.toolbars[i].onFirstFocus();
26537 syncValue : function()
26539 this.editorcore.syncValue();
26542 pushValue : function()
26544 this.editorcore.pushValue();
26548 // hide stuff that is not compatible
26562 * @event specialkey
26566 * @cfg {String} fieldClass @hide
26569 * @cfg {String} focusClass @hide
26572 * @cfg {String} autoCreate @hide
26575 * @cfg {String} inputType @hide
26579 * @cfg {String} invalidText @hide
26582 * @cfg {String} msgFx @hide
26585 * @cfg {String} validateOnBlur @hide
26594 Roo.namespace('Roo.bootstrap.htmleditor');
26596 * @class Roo.bootstrap.HtmlEditorToolbar1
26602 new Roo.bootstrap.HtmlEditor({
26605 new Roo.bootstrap.HtmlEditorToolbar1({
26606 disable : { fonts: 1 , format: 1, ..., ... , ...],
26612 * @cfg {Object} disable List of elements to disable..
26613 * @cfg {Array} btns List of additional buttons.
26617 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26620 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26623 Roo.apply(this, config);
26625 // default disabled, based on 'good practice'..
26626 this.disable = this.disable || {};
26627 Roo.applyIf(this.disable, {
26630 specialElements : true
26632 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26634 this.editor = config.editor;
26635 this.editorcore = config.editor.editorcore;
26637 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26639 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26640 // dont call parent... till later.
26642 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26647 editorcore : false,
26652 "h1","h2","h3","h4","h5","h6",
26654 "abbr", "acronym", "address", "cite", "samp", "var",
26658 onRender : function(ct, position)
26660 // Roo.log("Call onRender: " + this.xtype);
26662 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26664 this.el.dom.style.marginBottom = '0';
26666 var editorcore = this.editorcore;
26667 var editor= this.editor;
26670 var btn = function(id,cmd , toggle, handler, html){
26672 var event = toggle ? 'toggle' : 'click';
26677 xns: Roo.bootstrap,
26681 enableToggle:toggle !== false,
26683 pressed : toggle ? false : null,
26686 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26687 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26693 // var cb_box = function...
26698 xns: Roo.bootstrap,
26703 xns: Roo.bootstrap,
26707 Roo.each(this.formats, function(f) {
26708 style.menu.items.push({
26710 xns: Roo.bootstrap,
26711 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26716 editorcore.insertTag(this.tagname);
26723 children.push(style);
26725 btn('bold',false,true);
26726 btn('italic',false,true);
26727 btn('align-left', 'justifyleft',true);
26728 btn('align-center', 'justifycenter',true);
26729 btn('align-right' , 'justifyright',true);
26730 btn('link', false, false, function(btn) {
26731 //Roo.log("create link?");
26732 var url = prompt(this.createLinkText, this.defaultLinkValue);
26733 if(url && url != 'http:/'+'/'){
26734 this.editorcore.relayCmd('createlink', url);
26737 btn('list','insertunorderedlist',true);
26738 btn('pencil', false,true, function(btn){
26740 this.toggleSourceEdit(btn.pressed);
26743 if (this.editor.btns.length > 0) {
26744 for (var i = 0; i<this.editor.btns.length; i++) {
26745 children.push(this.editor.btns[i]);
26753 xns: Roo.bootstrap,
26758 xns: Roo.bootstrap,
26763 cog.menu.items.push({
26765 xns: Roo.bootstrap,
26766 html : Clean styles,
26771 editorcore.insertTag(this.tagname);
26780 this.xtype = 'NavSimplebar';
26782 for(var i=0;i< children.length;i++) {
26784 this.buttons.add(this.addxtypeChild(children[i]));
26788 editor.on('editorevent', this.updateToolbar, this);
26790 onBtnClick : function(id)
26792 this.editorcore.relayCmd(id);
26793 this.editorcore.focus();
26797 * Protected method that will not generally be called directly. It triggers
26798 * a toolbar update by reading the markup state of the current selection in the editor.
26800 updateToolbar: function(){
26802 if(!this.editorcore.activated){
26803 this.editor.onFirstFocus(); // is this neeed?
26807 var btns = this.buttons;
26808 var doc = this.editorcore.doc;
26809 btns.get('bold').setActive(doc.queryCommandState('bold'));
26810 btns.get('italic').setActive(doc.queryCommandState('italic'));
26811 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26813 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26814 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26815 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26817 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26818 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26821 var ans = this.editorcore.getAllAncestors();
26822 if (this.formatCombo) {
26825 var store = this.formatCombo.store;
26826 this.formatCombo.setValue("");
26827 for (var i =0; i < ans.length;i++) {
26828 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26830 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26838 // hides menus... - so this cant be on a menu...
26839 Roo.bootstrap.MenuMgr.hideAll();
26841 Roo.bootstrap.MenuMgr.hideAll();
26842 //this.editorsyncValue();
26844 onFirstFocus: function() {
26845 this.buttons.each(function(item){
26849 toggleSourceEdit : function(sourceEditMode){
26852 if(sourceEditMode){
26853 Roo.log("disabling buttons");
26854 this.buttons.each( function(item){
26855 if(item.cmd != 'pencil'){
26861 Roo.log("enabling buttons");
26862 if(this.editorcore.initialized){
26863 this.buttons.each( function(item){
26869 Roo.log("calling toggole on editor");
26870 // tell the editor that it's been pressed..
26871 this.editor.toggleSourceEdit(sourceEditMode);
26885 * @class Roo.bootstrap.Markdown
26886 * @extends Roo.bootstrap.TextArea
26887 * Bootstrap Showdown editable area
26888 * @cfg {string} content
26891 * Create a new Showdown
26894 Roo.bootstrap.Markdown = function(config){
26895 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26899 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26903 initEvents : function()
26906 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26907 this.markdownEl = this.el.createChild({
26908 cls : 'roo-markdown-area'
26910 this.inputEl().addClass('d-none');
26911 if (this.getValue() == '') {
26912 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26915 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26917 this.markdownEl.on('click', this.toggleTextEdit, this);
26918 this.on('blur', this.toggleTextEdit, this);
26919 this.on('specialkey', this.resizeTextArea, this);
26922 toggleTextEdit : function()
26924 var sh = this.markdownEl.getHeight();
26925 this.inputEl().addClass('d-none');
26926 this.markdownEl.addClass('d-none');
26927 if (!this.editing) {
26929 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26930 this.inputEl().removeClass('d-none');
26931 this.inputEl().focus();
26932 this.editing = true;
26935 // show showdown...
26936 this.updateMarkdown();
26937 this.markdownEl.removeClass('d-none');
26938 this.editing = false;
26941 updateMarkdown : function()
26943 if (this.getValue() == '') {
26944 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26948 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26951 resizeTextArea: function () {
26954 Roo.log([sh, this.getValue().split("\n").length * 30]);
26955 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26957 setValue : function(val)
26959 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26960 if (!this.editing) {
26961 this.updateMarkdown();
26967 if (!this.editing) {
26968 this.toggleTextEdit();
26976 * @class Roo.bootstrap.Table.AbstractSelectionModel
26977 * @extends Roo.util.Observable
26978 * Abstract base class for grid SelectionModels. It provides the interface that should be
26979 * implemented by descendant classes. This class should not be directly instantiated.
26982 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26983 this.locked = false;
26984 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26988 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26989 /** @ignore Called by the grid automatically. Do not call directly. */
26990 init : function(grid){
26996 * Locks the selections.
26999 this.locked = true;
27003 * Unlocks the selections.
27005 unlock : function(){
27006 this.locked = false;
27010 * Returns true if the selections are locked.
27011 * @return {Boolean}
27013 isLocked : function(){
27014 return this.locked;
27018 initEvents : function ()
27024 * @extends Roo.bootstrap.Table.AbstractSelectionModel
27025 * @class Roo.bootstrap.Table.RowSelectionModel
27026 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27027 * It supports multiple selections and keyboard selection/navigation.
27029 * @param {Object} config
27032 Roo.bootstrap.Table.RowSelectionModel = function(config){
27033 Roo.apply(this, config);
27034 this.selections = new Roo.util.MixedCollection(false, function(o){
27039 this.lastActive = false;
27043 * @event selectionchange
27044 * Fires when the selection changes
27045 * @param {SelectionModel} this
27047 "selectionchange" : true,
27049 * @event afterselectionchange
27050 * Fires after the selection changes (eg. by key press or clicking)
27051 * @param {SelectionModel} this
27053 "afterselectionchange" : true,
27055 * @event beforerowselect
27056 * Fires when a row is selected being selected, return false to cancel.
27057 * @param {SelectionModel} this
27058 * @param {Number} rowIndex The selected index
27059 * @param {Boolean} keepExisting False if other selections will be cleared
27061 "beforerowselect" : true,
27064 * Fires when a row is selected.
27065 * @param {SelectionModel} this
27066 * @param {Number} rowIndex The selected index
27067 * @param {Roo.data.Record} r The record
27069 "rowselect" : true,
27071 * @event rowdeselect
27072 * Fires when a row is deselected.
27073 * @param {SelectionModel} this
27074 * @param {Number} rowIndex The selected index
27076 "rowdeselect" : true
27078 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27079 this.locked = false;
27082 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27084 * @cfg {Boolean} singleSelect
27085 * True to allow selection of only one row at a time (defaults to false)
27087 singleSelect : false,
27090 initEvents : function()
27093 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27094 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27095 //}else{ // allow click to work like normal
27096 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27098 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27099 this.grid.on("rowclick", this.handleMouseDown, this);
27101 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27102 "up" : function(e){
27104 this.selectPrevious(e.shiftKey);
27105 }else if(this.last !== false && this.lastActive !== false){
27106 var last = this.last;
27107 this.selectRange(this.last, this.lastActive-1);
27108 this.grid.getView().focusRow(this.lastActive);
27109 if(last !== false){
27113 this.selectFirstRow();
27115 this.fireEvent("afterselectionchange", this);
27117 "down" : function(e){
27119 this.selectNext(e.shiftKey);
27120 }else if(this.last !== false && this.lastActive !== false){
27121 var last = this.last;
27122 this.selectRange(this.last, this.lastActive+1);
27123 this.grid.getView().focusRow(this.lastActive);
27124 if(last !== false){
27128 this.selectFirstRow();
27130 this.fireEvent("afterselectionchange", this);
27134 this.grid.store.on('load', function(){
27135 this.selections.clear();
27138 var view = this.grid.view;
27139 view.on("refresh", this.onRefresh, this);
27140 view.on("rowupdated", this.onRowUpdated, this);
27141 view.on("rowremoved", this.onRemove, this);
27146 onRefresh : function()
27148 var ds = this.grid.store, i, v = this.grid.view;
27149 var s = this.selections;
27150 s.each(function(r){
27151 if((i = ds.indexOfId(r.id)) != -1){
27160 onRemove : function(v, index, r){
27161 this.selections.remove(r);
27165 onRowUpdated : function(v, index, r){
27166 if(this.isSelected(r)){
27167 v.onRowSelect(index);
27173 * @param {Array} records The records to select
27174 * @param {Boolean} keepExisting (optional) True to keep existing selections
27176 selectRecords : function(records, keepExisting)
27179 this.clearSelections();
27181 var ds = this.grid.store;
27182 for(var i = 0, len = records.length; i < len; i++){
27183 this.selectRow(ds.indexOf(records[i]), true);
27188 * Gets the number of selected rows.
27191 getCount : function(){
27192 return this.selections.length;
27196 * Selects the first row in the grid.
27198 selectFirstRow : function(){
27203 * Select the last row.
27204 * @param {Boolean} keepExisting (optional) True to keep existing selections
27206 selectLastRow : function(keepExisting){
27207 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27208 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27212 * Selects the row immediately following the last selected row.
27213 * @param {Boolean} keepExisting (optional) True to keep existing selections
27215 selectNext : function(keepExisting)
27217 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27218 this.selectRow(this.last+1, keepExisting);
27219 this.grid.getView().focusRow(this.last);
27224 * Selects the row that precedes the last selected row.
27225 * @param {Boolean} keepExisting (optional) True to keep existing selections
27227 selectPrevious : function(keepExisting){
27229 this.selectRow(this.last-1, keepExisting);
27230 this.grid.getView().focusRow(this.last);
27235 * Returns the selected records
27236 * @return {Array} Array of selected records
27238 getSelections : function(){
27239 return [].concat(this.selections.items);
27243 * Returns the first selected record.
27246 getSelected : function(){
27247 return this.selections.itemAt(0);
27252 * Clears all selections.
27254 clearSelections : function(fast)
27260 var ds = this.grid.store;
27261 var s = this.selections;
27262 s.each(function(r){
27263 this.deselectRow(ds.indexOfId(r.id));
27267 this.selections.clear();
27274 * Selects all rows.
27276 selectAll : function(){
27280 this.selections.clear();
27281 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27282 this.selectRow(i, true);
27287 * Returns True if there is a selection.
27288 * @return {Boolean}
27290 hasSelection : function(){
27291 return this.selections.length > 0;
27295 * Returns True if the specified row is selected.
27296 * @param {Number/Record} record The record or index of the record to check
27297 * @return {Boolean}
27299 isSelected : function(index){
27300 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27301 return (r && this.selections.key(r.id) ? true : false);
27305 * Returns True if the specified record id is selected.
27306 * @param {String} id The id of record to check
27307 * @return {Boolean}
27309 isIdSelected : function(id){
27310 return (this.selections.key(id) ? true : false);
27315 handleMouseDBClick : function(e, t){
27319 handleMouseDown : function(e, t)
27321 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27322 if(this.isLocked() || rowIndex < 0 ){
27325 if(e.shiftKey && this.last !== false){
27326 var last = this.last;
27327 this.selectRange(last, rowIndex, e.ctrlKey);
27328 this.last = last; // reset the last
27332 var isSelected = this.isSelected(rowIndex);
27333 //Roo.log("select row:" + rowIndex);
27335 this.deselectRow(rowIndex);
27337 this.selectRow(rowIndex, true);
27341 if(e.button !== 0 && isSelected){
27342 alert('rowIndex 2: ' + rowIndex);
27343 view.focusRow(rowIndex);
27344 }else if(e.ctrlKey && isSelected){
27345 this.deselectRow(rowIndex);
27346 }else if(!isSelected){
27347 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27348 view.focusRow(rowIndex);
27352 this.fireEvent("afterselectionchange", this);
27355 handleDragableRowClick : function(grid, rowIndex, e)
27357 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27358 this.selectRow(rowIndex, false);
27359 grid.view.focusRow(rowIndex);
27360 this.fireEvent("afterselectionchange", this);
27365 * Selects multiple rows.
27366 * @param {Array} rows Array of the indexes of the row to select
27367 * @param {Boolean} keepExisting (optional) True to keep existing selections
27369 selectRows : function(rows, keepExisting){
27371 this.clearSelections();
27373 for(var i = 0, len = rows.length; i < len; i++){
27374 this.selectRow(rows[i], true);
27379 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27380 * @param {Number} startRow The index of the first row in the range
27381 * @param {Number} endRow The index of the last row in the range
27382 * @param {Boolean} keepExisting (optional) True to retain existing selections
27384 selectRange : function(startRow, endRow, keepExisting){
27389 this.clearSelections();
27391 if(startRow <= endRow){
27392 for(var i = startRow; i <= endRow; i++){
27393 this.selectRow(i, true);
27396 for(var i = startRow; i >= endRow; i--){
27397 this.selectRow(i, true);
27403 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27404 * @param {Number} startRow The index of the first row in the range
27405 * @param {Number} endRow The index of the last row in the range
27407 deselectRange : function(startRow, endRow, preventViewNotify){
27411 for(var i = startRow; i <= endRow; i++){
27412 this.deselectRow(i, preventViewNotify);
27418 * @param {Number} row The index of the row to select
27419 * @param {Boolean} keepExisting (optional) True to keep existing selections
27421 selectRow : function(index, keepExisting, preventViewNotify)
27423 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27426 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27427 if(!keepExisting || this.singleSelect){
27428 this.clearSelections();
27431 var r = this.grid.store.getAt(index);
27432 //console.log('selectRow - record id :' + r.id);
27434 this.selections.add(r);
27435 this.last = this.lastActive = index;
27436 if(!preventViewNotify){
27437 var proxy = new Roo.Element(
27438 this.grid.getRowDom(index)
27440 proxy.addClass('bg-info info');
27442 this.fireEvent("rowselect", this, index, r);
27443 this.fireEvent("selectionchange", this);
27449 * @param {Number} row The index of the row to deselect
27451 deselectRow : function(index, preventViewNotify)
27456 if(this.last == index){
27459 if(this.lastActive == index){
27460 this.lastActive = false;
27463 var r = this.grid.store.getAt(index);
27468 this.selections.remove(r);
27469 //.console.log('deselectRow - record id :' + r.id);
27470 if(!preventViewNotify){
27472 var proxy = new Roo.Element(
27473 this.grid.getRowDom(index)
27475 proxy.removeClass('bg-info info');
27477 this.fireEvent("rowdeselect", this, index);
27478 this.fireEvent("selectionchange", this);
27482 restoreLast : function(){
27484 this.last = this._last;
27489 acceptsNav : function(row, col, cm){
27490 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27494 onEditorKey : function(field, e){
27495 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27500 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27502 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27504 }else if(k == e.ENTER && !e.ctrlKey){
27508 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27510 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27512 }else if(k == e.ESC){
27516 g.startEditing(newCell[0], newCell[1]);
27522 * Ext JS Library 1.1.1
27523 * Copyright(c) 2006-2007, Ext JS, LLC.
27525 * Originally Released Under LGPL - original licence link has changed is not relivant.
27528 * <script type="text/javascript">
27532 * @class Roo.bootstrap.PagingToolbar
27533 * @extends Roo.bootstrap.NavSimplebar
27534 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27536 * Create a new PagingToolbar
27537 * @param {Object} config The config object
27538 * @param {Roo.data.Store} store
27540 Roo.bootstrap.PagingToolbar = function(config)
27542 // old args format still supported... - xtype is prefered..
27543 // created from xtype...
27545 this.ds = config.dataSource;
27547 if (config.store && !this.ds) {
27548 this.store= Roo.factory(config.store, Roo.data);
27549 this.ds = this.store;
27550 this.ds.xmodule = this.xmodule || false;
27553 this.toolbarItems = [];
27554 if (config.items) {
27555 this.toolbarItems = config.items;
27558 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27563 this.bind(this.ds);
27566 if (Roo.bootstrap.version == 4) {
27567 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27569 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27574 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27576 * @cfg {Roo.data.Store} dataSource
27577 * The underlying data store providing the paged data
27580 * @cfg {String/HTMLElement/Element} container
27581 * container The id or element that will contain the toolbar
27584 * @cfg {Boolean} displayInfo
27585 * True to display the displayMsg (defaults to false)
27588 * @cfg {Number} pageSize
27589 * The number of records to display per page (defaults to 20)
27593 * @cfg {String} displayMsg
27594 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27596 displayMsg : 'Displaying {0} - {1} of {2}',
27598 * @cfg {String} emptyMsg
27599 * The message to display when no records are found (defaults to "No data to display")
27601 emptyMsg : 'No data to display',
27603 * Customizable piece of the default paging text (defaults to "Page")
27606 beforePageText : "Page",
27608 * Customizable piece of the default paging text (defaults to "of %0")
27611 afterPageText : "of {0}",
27613 * Customizable piece of the default paging text (defaults to "First Page")
27616 firstText : "First Page",
27618 * Customizable piece of the default paging text (defaults to "Previous Page")
27621 prevText : "Previous Page",
27623 * Customizable piece of the default paging text (defaults to "Next Page")
27626 nextText : "Next Page",
27628 * Customizable piece of the default paging text (defaults to "Last Page")
27631 lastText : "Last Page",
27633 * Customizable piece of the default paging text (defaults to "Refresh")
27636 refreshText : "Refresh",
27640 onRender : function(ct, position)
27642 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27643 this.navgroup.parentId = this.id;
27644 this.navgroup.onRender(this.el, null);
27645 // add the buttons to the navgroup
27647 if(this.displayInfo){
27648 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27649 this.displayEl = this.el.select('.x-paging-info', true).first();
27650 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27651 // this.displayEl = navel.el.select('span',true).first();
27657 Roo.each(_this.buttons, function(e){ // this might need to use render????
27658 Roo.factory(e).render(_this.el);
27662 Roo.each(_this.toolbarItems, function(e) {
27663 _this.navgroup.addItem(e);
27667 this.first = this.navgroup.addItem({
27668 tooltip: this.firstText,
27669 cls: "prev btn-outline-secondary",
27670 html : ' <i class="fa fa-step-backward"></i>',
27672 preventDefault: true,
27673 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27676 this.prev = this.navgroup.addItem({
27677 tooltip: this.prevText,
27678 cls: "prev btn-outline-secondary",
27679 html : ' <i class="fa fa-backward"></i>',
27681 preventDefault: true,
27682 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27684 //this.addSeparator();
27687 var field = this.navgroup.addItem( {
27689 cls : 'x-paging-position btn-outline-secondary',
27691 html : this.beforePageText +
27692 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27693 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27696 this.field = field.el.select('input', true).first();
27697 this.field.on("keydown", this.onPagingKeydown, this);
27698 this.field.on("focus", function(){this.dom.select();});
27701 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27702 //this.field.setHeight(18);
27703 //this.addSeparator();
27704 this.next = this.navgroup.addItem({
27705 tooltip: this.nextText,
27706 cls: "next btn-outline-secondary",
27707 html : ' <i class="fa fa-forward"></i>',
27709 preventDefault: true,
27710 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27712 this.last = this.navgroup.addItem({
27713 tooltip: this.lastText,
27714 html : ' <i class="fa fa-step-forward"></i>',
27715 cls: "next btn-outline-secondary",
27717 preventDefault: true,
27718 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27720 //this.addSeparator();
27721 this.loading = this.navgroup.addItem({
27722 tooltip: this.refreshText,
27723 cls: "btn-outline-secondary",
27724 html : ' <i class="fa fa-refresh"></i>',
27725 preventDefault: true,
27726 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27732 updateInfo : function(){
27733 if(this.displayEl){
27734 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27735 var msg = count == 0 ?
27739 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27741 this.displayEl.update(msg);
27746 onLoad : function(ds, r, o)
27748 this.cursor = o.params && o.params.start ? o.params.start : 0;
27750 var d = this.getPageData(),
27755 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27756 this.field.dom.value = ap;
27757 this.first.setDisabled(ap == 1);
27758 this.prev.setDisabled(ap == 1);
27759 this.next.setDisabled(ap == ps);
27760 this.last.setDisabled(ap == ps);
27761 this.loading.enable();
27766 getPageData : function(){
27767 var total = this.ds.getTotalCount();
27770 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27771 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27776 onLoadError : function(){
27777 this.loading.enable();
27781 onPagingKeydown : function(e){
27782 var k = e.getKey();
27783 var d = this.getPageData();
27785 var v = this.field.dom.value, pageNum;
27786 if(!v || isNaN(pageNum = parseInt(v, 10))){
27787 this.field.dom.value = d.activePage;
27790 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27791 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27794 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))
27796 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27797 this.field.dom.value = pageNum;
27798 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27801 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27803 var v = this.field.dom.value, pageNum;
27804 var increment = (e.shiftKey) ? 10 : 1;
27805 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27808 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27809 this.field.dom.value = d.activePage;
27812 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27814 this.field.dom.value = parseInt(v, 10) + increment;
27815 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27816 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27823 beforeLoad : function(){
27825 this.loading.disable();
27830 onClick : function(which){
27839 ds.load({params:{start: 0, limit: this.pageSize}});
27842 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27845 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27848 var total = ds.getTotalCount();
27849 var extra = total % this.pageSize;
27850 var lastStart = extra ? (total - extra) : total-this.pageSize;
27851 ds.load({params:{start: lastStart, limit: this.pageSize}});
27854 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27860 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27861 * @param {Roo.data.Store} store The data store to unbind
27863 unbind : function(ds){
27864 ds.un("beforeload", this.beforeLoad, this);
27865 ds.un("load", this.onLoad, this);
27866 ds.un("loadexception", this.onLoadError, this);
27867 ds.un("remove", this.updateInfo, this);
27868 ds.un("add", this.updateInfo, this);
27869 this.ds = undefined;
27873 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27874 * @param {Roo.data.Store} store The data store to bind
27876 bind : function(ds){
27877 ds.on("beforeload", this.beforeLoad, this);
27878 ds.on("load", this.onLoad, this);
27879 ds.on("loadexception", this.onLoadError, this);
27880 ds.on("remove", this.updateInfo, this);
27881 ds.on("add", this.updateInfo, this);
27892 * @class Roo.bootstrap.MessageBar
27893 * @extends Roo.bootstrap.Component
27894 * Bootstrap MessageBar class
27895 * @cfg {String} html contents of the MessageBar
27896 * @cfg {String} weight (info | success | warning | danger) default info
27897 * @cfg {String} beforeClass insert the bar before the given class
27898 * @cfg {Boolean} closable (true | false) default false
27899 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27902 * Create a new Element
27903 * @param {Object} config The config object
27906 Roo.bootstrap.MessageBar = function(config){
27907 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27910 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27916 beforeClass: 'bootstrap-sticky-wrap',
27918 getAutoCreate : function(){
27922 cls: 'alert alert-dismissable alert-' + this.weight,
27927 html: this.html || ''
27933 cfg.cls += ' alert-messages-fixed';
27947 onRender : function(ct, position)
27949 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27952 var cfg = Roo.apply({}, this.getAutoCreate());
27956 cfg.cls += ' ' + this.cls;
27959 cfg.style = this.style;
27961 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27963 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27966 this.el.select('>button.close').on('click', this.hide, this);
27972 if (!this.rendered) {
27978 this.fireEvent('show', this);
27984 if (!this.rendered) {
27990 this.fireEvent('hide', this);
27993 update : function()
27995 // var e = this.el.dom.firstChild;
27997 // if(this.closable){
27998 // e = e.nextSibling;
28001 // e.data = this.html || '';
28003 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28019 * @class Roo.bootstrap.Graph
28020 * @extends Roo.bootstrap.Component
28021 * Bootstrap Graph class
28025 @cfg {String} graphtype bar | vbar | pie
28026 @cfg {number} g_x coodinator | centre x (pie)
28027 @cfg {number} g_y coodinator | centre y (pie)
28028 @cfg {number} g_r radius (pie)
28029 @cfg {number} g_height height of the chart (respected by all elements in the set)
28030 @cfg {number} g_width width of the chart (respected by all elements in the set)
28031 @cfg {Object} title The title of the chart
28034 -opts (object) options for the chart
28036 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28037 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28039 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.
28040 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28042 o stretch (boolean)
28044 -opts (object) options for the pie
28047 o startAngle (number)
28048 o endAngle (number)
28052 * Create a new Input
28053 * @param {Object} config The config object
28056 Roo.bootstrap.Graph = function(config){
28057 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28063 * The img click event for the img.
28064 * @param {Roo.EventObject} e
28070 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28081 //g_colors: this.colors,
28088 getAutoCreate : function(){
28099 onRender : function(ct,position){
28102 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28104 if (typeof(Raphael) == 'undefined') {
28105 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28109 this.raphael = Raphael(this.el.dom);
28111 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28112 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28113 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28114 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28116 r.text(160, 10, "Single Series Chart").attr(txtattr);
28117 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28118 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28119 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28121 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28122 r.barchart(330, 10, 300, 220, data1);
28123 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28124 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28127 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28128 // r.barchart(30, 30, 560, 250, xdata, {
28129 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28130 // axis : "0 0 1 1",
28131 // axisxlabels : xdata
28132 // //yvalues : cols,
28135 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28137 // this.load(null,xdata,{
28138 // axis : "0 0 1 1",
28139 // axisxlabels : xdata
28144 load : function(graphtype,xdata,opts)
28146 this.raphael.clear();
28148 graphtype = this.graphtype;
28153 var r = this.raphael,
28154 fin = function () {
28155 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28157 fout = function () {
28158 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28160 pfin = function() {
28161 this.sector.stop();
28162 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28165 this.label[0].stop();
28166 this.label[0].attr({ r: 7.5 });
28167 this.label[1].attr({ "font-weight": 800 });
28170 pfout = function() {
28171 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28174 this.label[0].animate({ r: 5 }, 500, "bounce");
28175 this.label[1].attr({ "font-weight": 400 });
28181 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28184 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28187 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28188 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28190 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28197 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28202 setTitle: function(o)
28207 initEvents: function() {
28210 this.el.on('click', this.onClick, this);
28214 onClick : function(e)
28216 Roo.log('img onclick');
28217 this.fireEvent('click', this, e);
28229 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28232 * @class Roo.bootstrap.dash.NumberBox
28233 * @extends Roo.bootstrap.Component
28234 * Bootstrap NumberBox class
28235 * @cfg {String} headline Box headline
28236 * @cfg {String} content Box content
28237 * @cfg {String} icon Box icon
28238 * @cfg {String} footer Footer text
28239 * @cfg {String} fhref Footer href
28242 * Create a new NumberBox
28243 * @param {Object} config The config object
28247 Roo.bootstrap.dash.NumberBox = function(config){
28248 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28252 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28261 getAutoCreate : function(){
28265 cls : 'small-box ',
28273 cls : 'roo-headline',
28274 html : this.headline
28278 cls : 'roo-content',
28279 html : this.content
28293 cls : 'ion ' + this.icon
28302 cls : 'small-box-footer',
28303 href : this.fhref || '#',
28307 cfg.cn.push(footer);
28314 onRender : function(ct,position){
28315 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28322 setHeadline: function (value)
28324 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28327 setFooter: function (value, href)
28329 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28332 this.el.select('a.small-box-footer',true).first().attr('href', href);
28337 setContent: function (value)
28339 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28342 initEvents: function()
28356 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28359 * @class Roo.bootstrap.dash.TabBox
28360 * @extends Roo.bootstrap.Component
28361 * Bootstrap TabBox class
28362 * @cfg {String} title Title of the TabBox
28363 * @cfg {String} icon Icon of the TabBox
28364 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28365 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28368 * Create a new TabBox
28369 * @param {Object} config The config object
28373 Roo.bootstrap.dash.TabBox = function(config){
28374 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28379 * When a pane is added
28380 * @param {Roo.bootstrap.dash.TabPane} pane
28384 * @event activatepane
28385 * When a pane is activated
28386 * @param {Roo.bootstrap.dash.TabPane} pane
28388 "activatepane" : true
28396 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28401 tabScrollable : false,
28403 getChildContainer : function()
28405 return this.el.select('.tab-content', true).first();
28408 getAutoCreate : function(){
28412 cls: 'pull-left header',
28420 cls: 'fa ' + this.icon
28426 cls: 'nav nav-tabs pull-right',
28432 if(this.tabScrollable){
28439 cls: 'nav nav-tabs pull-right',
28450 cls: 'nav-tabs-custom',
28455 cls: 'tab-content no-padding',
28463 initEvents : function()
28465 //Roo.log('add add pane handler');
28466 this.on('addpane', this.onAddPane, this);
28469 * Updates the box title
28470 * @param {String} html to set the title to.
28472 setTitle : function(value)
28474 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28476 onAddPane : function(pane)
28478 this.panes.push(pane);
28479 //Roo.log('addpane');
28481 // tabs are rendere left to right..
28482 if(!this.showtabs){
28486 var ctr = this.el.select('.nav-tabs', true).first();
28489 var existing = ctr.select('.nav-tab',true);
28490 var qty = existing.getCount();;
28493 var tab = ctr.createChild({
28495 cls : 'nav-tab' + (qty ? '' : ' active'),
28503 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28506 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28508 pane.el.addClass('active');
28513 onTabClick : function(ev,un,ob,pane)
28515 //Roo.log('tab - prev default');
28516 ev.preventDefault();
28519 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28520 pane.tab.addClass('active');
28521 //Roo.log(pane.title);
28522 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28523 // technically we should have a deactivate event.. but maybe add later.
28524 // and it should not de-activate the selected tab...
28525 this.fireEvent('activatepane', pane);
28526 pane.el.addClass('active');
28527 pane.fireEvent('activate');
28532 getActivePane : function()
28535 Roo.each(this.panes, function(p) {
28536 if(p.el.hasClass('active')){
28557 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28559 * @class Roo.bootstrap.TabPane
28560 * @extends Roo.bootstrap.Component
28561 * Bootstrap TabPane class
28562 * @cfg {Boolean} active (false | true) Default false
28563 * @cfg {String} title title of panel
28567 * Create a new TabPane
28568 * @param {Object} config The config object
28571 Roo.bootstrap.dash.TabPane = function(config){
28572 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28578 * When a pane is activated
28579 * @param {Roo.bootstrap.dash.TabPane} pane
28586 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28591 // the tabBox that this is attached to.
28594 getAutoCreate : function()
28602 cfg.cls += ' active';
28607 initEvents : function()
28609 //Roo.log('trigger add pane handler');
28610 this.parent().fireEvent('addpane', this)
28614 * Updates the tab title
28615 * @param {String} html to set the title to.
28617 setTitle: function(str)
28623 this.tab.select('a', true).first().dom.innerHTML = str;
28640 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28643 * @class Roo.bootstrap.menu.Menu
28644 * @extends Roo.bootstrap.Component
28645 * Bootstrap Menu class - container for Menu
28646 * @cfg {String} html Text of the menu
28647 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28648 * @cfg {String} icon Font awesome icon
28649 * @cfg {String} pos Menu align to (top | bottom) default bottom
28653 * Create a new Menu
28654 * @param {Object} config The config object
28658 Roo.bootstrap.menu.Menu = function(config){
28659 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28663 * @event beforeshow
28664 * Fires before this menu is displayed
28665 * @param {Roo.bootstrap.menu.Menu} this
28669 * @event beforehide
28670 * Fires before this menu is hidden
28671 * @param {Roo.bootstrap.menu.Menu} this
28676 * Fires after this menu is displayed
28677 * @param {Roo.bootstrap.menu.Menu} this
28682 * Fires after this menu is hidden
28683 * @param {Roo.bootstrap.menu.Menu} this
28688 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28689 * @param {Roo.bootstrap.menu.Menu} this
28690 * @param {Roo.EventObject} e
28697 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28701 weight : 'default',
28706 getChildContainer : function() {
28707 if(this.isSubMenu){
28711 return this.el.select('ul.dropdown-menu', true).first();
28714 getAutoCreate : function()
28719 cls : 'roo-menu-text',
28727 cls : 'fa ' + this.icon
28738 cls : 'dropdown-button btn btn-' + this.weight,
28743 cls : 'dropdown-toggle btn btn-' + this.weight,
28753 cls : 'dropdown-menu'
28759 if(this.pos == 'top'){
28760 cfg.cls += ' dropup';
28763 if(this.isSubMenu){
28766 cls : 'dropdown-menu'
28773 onRender : function(ct, position)
28775 this.isSubMenu = ct.hasClass('dropdown-submenu');
28777 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28780 initEvents : function()
28782 if(this.isSubMenu){
28786 this.hidden = true;
28788 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28789 this.triggerEl.on('click', this.onTriggerPress, this);
28791 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28792 this.buttonEl.on('click', this.onClick, this);
28798 if(this.isSubMenu){
28802 return this.el.select('ul.dropdown-menu', true).first();
28805 onClick : function(e)
28807 this.fireEvent("click", this, e);
28810 onTriggerPress : function(e)
28812 if (this.isVisible()) {
28819 isVisible : function(){
28820 return !this.hidden;
28825 this.fireEvent("beforeshow", this);
28827 this.hidden = false;
28828 this.el.addClass('open');
28830 Roo.get(document).on("mouseup", this.onMouseUp, this);
28832 this.fireEvent("show", this);
28839 this.fireEvent("beforehide", this);
28841 this.hidden = true;
28842 this.el.removeClass('open');
28844 Roo.get(document).un("mouseup", this.onMouseUp);
28846 this.fireEvent("hide", this);
28849 onMouseUp : function()
28863 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28866 * @class Roo.bootstrap.menu.Item
28867 * @extends Roo.bootstrap.Component
28868 * Bootstrap MenuItem class
28869 * @cfg {Boolean} submenu (true | false) default false
28870 * @cfg {String} html text of the item
28871 * @cfg {String} href the link
28872 * @cfg {Boolean} disable (true | false) default false
28873 * @cfg {Boolean} preventDefault (true | false) default true
28874 * @cfg {String} icon Font awesome icon
28875 * @cfg {String} pos Submenu align to (left | right) default right
28879 * Create a new Item
28880 * @param {Object} config The config object
28884 Roo.bootstrap.menu.Item = function(config){
28885 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28889 * Fires when the mouse is hovering over this menu
28890 * @param {Roo.bootstrap.menu.Item} this
28891 * @param {Roo.EventObject} e
28896 * Fires when the mouse exits this menu
28897 * @param {Roo.bootstrap.menu.Item} this
28898 * @param {Roo.EventObject} e
28904 * The raw click event for the entire grid.
28905 * @param {Roo.EventObject} e
28911 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28916 preventDefault: true,
28921 getAutoCreate : function()
28926 cls : 'roo-menu-item-text',
28934 cls : 'fa ' + this.icon
28943 href : this.href || '#',
28950 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28954 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28956 if(this.pos == 'left'){
28957 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28964 initEvents : function()
28966 this.el.on('mouseover', this.onMouseOver, this);
28967 this.el.on('mouseout', this.onMouseOut, this);
28969 this.el.select('a', true).first().on('click', this.onClick, this);
28973 onClick : function(e)
28975 if(this.preventDefault){
28976 e.preventDefault();
28979 this.fireEvent("click", this, e);
28982 onMouseOver : function(e)
28984 if(this.submenu && this.pos == 'left'){
28985 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28988 this.fireEvent("mouseover", this, e);
28991 onMouseOut : function(e)
28993 this.fireEvent("mouseout", this, e);
29005 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29008 * @class Roo.bootstrap.menu.Separator
29009 * @extends Roo.bootstrap.Component
29010 * Bootstrap Separator class
29013 * Create a new Separator
29014 * @param {Object} config The config object
29018 Roo.bootstrap.menu.Separator = function(config){
29019 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29022 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29024 getAutoCreate : function(){
29027 cls: 'dropdown-divider divider'
29045 * @class Roo.bootstrap.Tooltip
29046 * Bootstrap Tooltip class
29047 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29048 * to determine which dom element triggers the tooltip.
29050 * It needs to add support for additional attributes like tooltip-position
29053 * Create a new Toolti
29054 * @param {Object} config The config object
29057 Roo.bootstrap.Tooltip = function(config){
29058 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29060 this.alignment = Roo.bootstrap.Tooltip.alignment;
29062 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29063 this.alignment = config.alignment;
29068 Roo.apply(Roo.bootstrap.Tooltip, {
29070 * @function init initialize tooltip monitoring.
29074 currentTip : false,
29075 currentRegion : false,
29081 Roo.get(document).on('mouseover', this.enter ,this);
29082 Roo.get(document).on('mouseout', this.leave, this);
29085 this.currentTip = new Roo.bootstrap.Tooltip();
29088 enter : function(ev)
29090 var dom = ev.getTarget();
29092 //Roo.log(['enter',dom]);
29093 var el = Roo.fly(dom);
29094 if (this.currentEl) {
29096 //Roo.log(this.currentEl);
29097 //Roo.log(this.currentEl.contains(dom));
29098 if (this.currentEl == el) {
29101 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29107 if (this.currentTip.el) {
29108 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29112 if(!el || el.dom == document){
29118 if (!el.attr('tooltip')) {
29119 pel = el.findParent("[tooltip]");
29121 bindEl = Roo.get(pel);
29127 // you can not look for children, as if el is the body.. then everythign is the child..
29128 if (!pel && !el.attr('tooltip')) { //
29129 if (!el.select("[tooltip]").elements.length) {
29132 // is the mouse over this child...?
29133 bindEl = el.select("[tooltip]").first();
29134 var xy = ev.getXY();
29135 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29136 //Roo.log("not in region.");
29139 //Roo.log("child element over..");
29142 this.currentEl = el;
29143 this.currentTip.bind(bindEl);
29144 this.currentRegion = Roo.lib.Region.getRegion(dom);
29145 this.currentTip.enter();
29148 leave : function(ev)
29150 var dom = ev.getTarget();
29151 //Roo.log(['leave',dom]);
29152 if (!this.currentEl) {
29157 if (dom != this.currentEl.dom) {
29160 var xy = ev.getXY();
29161 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29164 // only activate leave if mouse cursor is outside... bounding box..
29169 if (this.currentTip) {
29170 this.currentTip.leave();
29172 //Roo.log('clear currentEl');
29173 this.currentEl = false;
29178 'left' : ['r-l', [-2,0], 'right'],
29179 'right' : ['l-r', [2,0], 'left'],
29180 'bottom' : ['t-b', [0,2], 'top'],
29181 'top' : [ 'b-t', [0,-2], 'bottom']
29187 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29192 delay : null, // can be { show : 300 , hide: 500}
29196 hoverState : null, //???
29198 placement : 'bottom',
29202 getAutoCreate : function(){
29209 cls : 'tooltip-arrow arrow'
29212 cls : 'tooltip-inner'
29219 bind : function(el)
29224 initEvents : function()
29226 this.arrowEl = this.el.select('.arrow', true).first();
29227 this.innerEl = this.el.select('.tooltip-inner', true).first();
29230 enter : function () {
29232 if (this.timeout != null) {
29233 clearTimeout(this.timeout);
29236 this.hoverState = 'in';
29237 //Roo.log("enter - show");
29238 if (!this.delay || !this.delay.show) {
29243 this.timeout = setTimeout(function () {
29244 if (_t.hoverState == 'in') {
29247 }, this.delay.show);
29251 clearTimeout(this.timeout);
29253 this.hoverState = 'out';
29254 if (!this.delay || !this.delay.hide) {
29260 this.timeout = setTimeout(function () {
29261 //Roo.log("leave - timeout");
29263 if (_t.hoverState == 'out') {
29265 Roo.bootstrap.Tooltip.currentEl = false;
29270 show : function (msg)
29273 this.render(document.body);
29276 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29278 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29280 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29282 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29283 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29285 var placement = typeof this.placement == 'function' ?
29286 this.placement.call(this, this.el, on_el) :
29289 var autoToken = /\s?auto?\s?/i;
29290 var autoPlace = autoToken.test(placement);
29292 placement = placement.replace(autoToken, '') || 'top';
29296 //this.el.setXY([0,0]);
29298 //this.el.dom.style.display='block';
29300 //this.el.appendTo(on_el);
29302 var p = this.getPosition();
29303 var box = this.el.getBox();
29309 var align = this.alignment[placement];
29311 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29313 if(placement == 'top' || placement == 'bottom'){
29315 placement = 'right';
29318 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29319 placement = 'left';
29322 var scroll = Roo.select('body', true).first().getScroll();
29324 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29328 align = this.alignment[placement];
29330 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29334 var elems = document.getElementsByTagName('div');
29335 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29336 for (var i = 0; i < elems.length; i++) {
29337 var zindex = Number.parseInt(
29338 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29341 if (zindex > highest) {
29348 this.el.dom.style.zIndex = highest;
29350 this.el.alignTo(this.bindEl, align[0],align[1]);
29351 //var arrow = this.el.select('.arrow',true).first();
29352 //arrow.set(align[2],
29354 this.el.addClass(placement);
29355 this.el.addClass("bs-tooltip-"+ placement);
29357 this.el.addClass('in fade show');
29359 this.hoverState = null;
29361 if (this.el.hasClass('fade')) {
29376 //this.el.setXY([0,0]);
29377 this.el.removeClass(['show', 'in']);
29393 * @class Roo.bootstrap.LocationPicker
29394 * @extends Roo.bootstrap.Component
29395 * Bootstrap LocationPicker class
29396 * @cfg {Number} latitude Position when init default 0
29397 * @cfg {Number} longitude Position when init default 0
29398 * @cfg {Number} zoom default 15
29399 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29400 * @cfg {Boolean} mapTypeControl default false
29401 * @cfg {Boolean} disableDoubleClickZoom default false
29402 * @cfg {Boolean} scrollwheel default true
29403 * @cfg {Boolean} streetViewControl default false
29404 * @cfg {Number} radius default 0
29405 * @cfg {String} locationName
29406 * @cfg {Boolean} draggable default true
29407 * @cfg {Boolean} enableAutocomplete default false
29408 * @cfg {Boolean} enableReverseGeocode default true
29409 * @cfg {String} markerTitle
29412 * Create a new LocationPicker
29413 * @param {Object} config The config object
29417 Roo.bootstrap.LocationPicker = function(config){
29419 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29424 * Fires when the picker initialized.
29425 * @param {Roo.bootstrap.LocationPicker} this
29426 * @param {Google Location} location
29430 * @event positionchanged
29431 * Fires when the picker position changed.
29432 * @param {Roo.bootstrap.LocationPicker} this
29433 * @param {Google Location} location
29435 positionchanged : true,
29438 * Fires when the map resize.
29439 * @param {Roo.bootstrap.LocationPicker} this
29444 * Fires when the map show.
29445 * @param {Roo.bootstrap.LocationPicker} this
29450 * Fires when the map hide.
29451 * @param {Roo.bootstrap.LocationPicker} this
29456 * Fires when click the map.
29457 * @param {Roo.bootstrap.LocationPicker} this
29458 * @param {Map event} e
29462 * @event mapRightClick
29463 * Fires when right click the map.
29464 * @param {Roo.bootstrap.LocationPicker} this
29465 * @param {Map event} e
29467 mapRightClick : true,
29469 * @event markerClick
29470 * Fires when click the marker.
29471 * @param {Roo.bootstrap.LocationPicker} this
29472 * @param {Map event} e
29474 markerClick : true,
29476 * @event markerRightClick
29477 * Fires when right click the marker.
29478 * @param {Roo.bootstrap.LocationPicker} this
29479 * @param {Map event} e
29481 markerRightClick : true,
29483 * @event OverlayViewDraw
29484 * Fires when OverlayView Draw
29485 * @param {Roo.bootstrap.LocationPicker} this
29487 OverlayViewDraw : true,
29489 * @event OverlayViewOnAdd
29490 * Fires when OverlayView Draw
29491 * @param {Roo.bootstrap.LocationPicker} this
29493 OverlayViewOnAdd : true,
29495 * @event OverlayViewOnRemove
29496 * Fires when OverlayView Draw
29497 * @param {Roo.bootstrap.LocationPicker} this
29499 OverlayViewOnRemove : true,
29501 * @event OverlayViewShow
29502 * Fires when OverlayView Draw
29503 * @param {Roo.bootstrap.LocationPicker} this
29504 * @param {Pixel} cpx
29506 OverlayViewShow : true,
29508 * @event OverlayViewHide
29509 * Fires when OverlayView Draw
29510 * @param {Roo.bootstrap.LocationPicker} this
29512 OverlayViewHide : true,
29514 * @event loadexception
29515 * Fires when load google lib failed.
29516 * @param {Roo.bootstrap.LocationPicker} this
29518 loadexception : true
29523 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29525 gMapContext: false,
29531 mapTypeControl: false,
29532 disableDoubleClickZoom: false,
29534 streetViewControl: false,
29538 enableAutocomplete: false,
29539 enableReverseGeocode: true,
29542 getAutoCreate: function()
29547 cls: 'roo-location-picker'
29553 initEvents: function(ct, position)
29555 if(!this.el.getWidth() || this.isApplied()){
29559 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29564 initial: function()
29566 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29567 this.fireEvent('loadexception', this);
29571 if(!this.mapTypeId){
29572 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29575 this.gMapContext = this.GMapContext();
29577 this.initOverlayView();
29579 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29583 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29584 _this.setPosition(_this.gMapContext.marker.position);
29587 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29588 _this.fireEvent('mapClick', this, event);
29592 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29593 _this.fireEvent('mapRightClick', this, event);
29597 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29598 _this.fireEvent('markerClick', this, event);
29602 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29603 _this.fireEvent('markerRightClick', this, event);
29607 this.setPosition(this.gMapContext.location);
29609 this.fireEvent('initial', this, this.gMapContext.location);
29612 initOverlayView: function()
29616 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29620 _this.fireEvent('OverlayViewDraw', _this);
29625 _this.fireEvent('OverlayViewOnAdd', _this);
29628 onRemove: function()
29630 _this.fireEvent('OverlayViewOnRemove', _this);
29633 show: function(cpx)
29635 _this.fireEvent('OverlayViewShow', _this, cpx);
29640 _this.fireEvent('OverlayViewHide', _this);
29646 fromLatLngToContainerPixel: function(event)
29648 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29651 isApplied: function()
29653 return this.getGmapContext() == false ? false : true;
29656 getGmapContext: function()
29658 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29661 GMapContext: function()
29663 var position = new google.maps.LatLng(this.latitude, this.longitude);
29665 var _map = new google.maps.Map(this.el.dom, {
29668 mapTypeId: this.mapTypeId,
29669 mapTypeControl: this.mapTypeControl,
29670 disableDoubleClickZoom: this.disableDoubleClickZoom,
29671 scrollwheel: this.scrollwheel,
29672 streetViewControl: this.streetViewControl,
29673 locationName: this.locationName,
29674 draggable: this.draggable,
29675 enableAutocomplete: this.enableAutocomplete,
29676 enableReverseGeocode: this.enableReverseGeocode
29679 var _marker = new google.maps.Marker({
29680 position: position,
29682 title: this.markerTitle,
29683 draggable: this.draggable
29690 location: position,
29691 radius: this.radius,
29692 locationName: this.locationName,
29693 addressComponents: {
29694 formatted_address: null,
29695 addressLine1: null,
29696 addressLine2: null,
29698 streetNumber: null,
29702 stateOrProvince: null
29705 domContainer: this.el.dom,
29706 geodecoder: new google.maps.Geocoder()
29710 drawCircle: function(center, radius, options)
29712 if (this.gMapContext.circle != null) {
29713 this.gMapContext.circle.setMap(null);
29717 options = Roo.apply({}, options, {
29718 strokeColor: "#0000FF",
29719 strokeOpacity: .35,
29721 fillColor: "#0000FF",
29725 options.map = this.gMapContext.map;
29726 options.radius = radius;
29727 options.center = center;
29728 this.gMapContext.circle = new google.maps.Circle(options);
29729 return this.gMapContext.circle;
29735 setPosition: function(location)
29737 this.gMapContext.location = location;
29738 this.gMapContext.marker.setPosition(location);
29739 this.gMapContext.map.panTo(location);
29740 this.drawCircle(location, this.gMapContext.radius, {});
29744 if (this.gMapContext.settings.enableReverseGeocode) {
29745 this.gMapContext.geodecoder.geocode({
29746 latLng: this.gMapContext.location
29747 }, function(results, status) {
29749 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29750 _this.gMapContext.locationName = results[0].formatted_address;
29751 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29753 _this.fireEvent('positionchanged', this, location);
29760 this.fireEvent('positionchanged', this, location);
29765 google.maps.event.trigger(this.gMapContext.map, "resize");
29767 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29769 this.fireEvent('resize', this);
29772 setPositionByLatLng: function(latitude, longitude)
29774 this.setPosition(new google.maps.LatLng(latitude, longitude));
29777 getCurrentPosition: function()
29780 latitude: this.gMapContext.location.lat(),
29781 longitude: this.gMapContext.location.lng()
29785 getAddressName: function()
29787 return this.gMapContext.locationName;
29790 getAddressComponents: function()
29792 return this.gMapContext.addressComponents;
29795 address_component_from_google_geocode: function(address_components)
29799 for (var i = 0; i < address_components.length; i++) {
29800 var component = address_components[i];
29801 if (component.types.indexOf("postal_code") >= 0) {
29802 result.postalCode = component.short_name;
29803 } else if (component.types.indexOf("street_number") >= 0) {
29804 result.streetNumber = component.short_name;
29805 } else if (component.types.indexOf("route") >= 0) {
29806 result.streetName = component.short_name;
29807 } else if (component.types.indexOf("neighborhood") >= 0) {
29808 result.city = component.short_name;
29809 } else if (component.types.indexOf("locality") >= 0) {
29810 result.city = component.short_name;
29811 } else if (component.types.indexOf("sublocality") >= 0) {
29812 result.district = component.short_name;
29813 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29814 result.stateOrProvince = component.short_name;
29815 } else if (component.types.indexOf("country") >= 0) {
29816 result.country = component.short_name;
29820 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29821 result.addressLine2 = "";
29825 setZoomLevel: function(zoom)
29827 this.gMapContext.map.setZoom(zoom);
29840 this.fireEvent('show', this);
29851 this.fireEvent('hide', this);
29856 Roo.apply(Roo.bootstrap.LocationPicker, {
29858 OverlayView : function(map, options)
29860 options = options || {};
29867 * @class Roo.bootstrap.Alert
29868 * @extends Roo.bootstrap.Component
29869 * Bootstrap Alert class - shows an alert area box
29871 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29872 Enter a valid email address
29875 * @cfg {String} title The title of alert
29876 * @cfg {String} html The content of alert
29877 * @cfg {String} weight ( success | info | warning | danger )
29878 * @cfg {String} fa font-awesomeicon
29879 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29880 * @cfg {Boolean} close true to show a x closer
29884 * Create a new alert
29885 * @param {Object} config The config object
29889 Roo.bootstrap.Alert = function(config){
29890 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29894 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29900 faicon: false, // BC
29904 getAutoCreate : function()
29916 style : this.close ? '' : 'display:none'
29920 cls : 'roo-alert-icon'
29925 cls : 'roo-alert-title',
29930 cls : 'roo-alert-text',
29937 cfg.cn[0].cls += ' fa ' + this.faicon;
29940 cfg.cn[0].cls += ' fa ' + this.fa;
29944 cfg.cls += ' alert-' + this.weight;
29950 initEvents: function()
29952 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29953 this.titleEl = this.el.select('.roo-alert-title',true).first();
29954 this.iconEl = this.el.select('.roo-alert-icon',true).first();
29955 if (this.seconds > 0) {
29956 this.hide.defer(this.seconds, this);
29960 setTitle : function(str)
29962 this.titleEl.dom.innerHTML = str;
29965 setText : function(str)
29967 this.titleEl.dom.innerHTML = str;
29970 setWeight : function(weight)
29973 this.el.removeClass('alert-' + this.weight);
29976 this.weight = weight;
29978 this.el.addClass('alert-' + this.weight);
29981 setIcon : function(icon)
29984 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29987 this.faicon = icon;
29989 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30010 * @class Roo.bootstrap.UploadCropbox
30011 * @extends Roo.bootstrap.Component
30012 * Bootstrap UploadCropbox class
30013 * @cfg {String} emptyText show when image has been loaded
30014 * @cfg {String} rotateNotify show when image too small to rotate
30015 * @cfg {Number} errorTimeout default 3000
30016 * @cfg {Number} minWidth default 300
30017 * @cfg {Number} minHeight default 300
30018 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30019 * @cfg {Boolean} isDocument (true|false) default false
30020 * @cfg {String} url action url
30021 * @cfg {String} paramName default 'imageUpload'
30022 * @cfg {String} method default POST
30023 * @cfg {Boolean} loadMask (true|false) default true
30024 * @cfg {Boolean} loadingText default 'Loading...'
30027 * Create a new UploadCropbox
30028 * @param {Object} config The config object
30031 Roo.bootstrap.UploadCropbox = function(config){
30032 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30036 * @event beforeselectfile
30037 * Fire before select file
30038 * @param {Roo.bootstrap.UploadCropbox} this
30040 "beforeselectfile" : true,
30043 * Fire after initEvent
30044 * @param {Roo.bootstrap.UploadCropbox} this
30049 * Fire after initEvent
30050 * @param {Roo.bootstrap.UploadCropbox} this
30051 * @param {String} data
30056 * Fire when preparing the file data
30057 * @param {Roo.bootstrap.UploadCropbox} this
30058 * @param {Object} file
30063 * Fire when get exception
30064 * @param {Roo.bootstrap.UploadCropbox} this
30065 * @param {XMLHttpRequest} xhr
30067 "exception" : true,
30069 * @event beforeloadcanvas
30070 * Fire before load the canvas
30071 * @param {Roo.bootstrap.UploadCropbox} this
30072 * @param {String} src
30074 "beforeloadcanvas" : true,
30077 * Fire when trash image
30078 * @param {Roo.bootstrap.UploadCropbox} this
30083 * Fire when download the image
30084 * @param {Roo.bootstrap.UploadCropbox} this
30088 * @event footerbuttonclick
30089 * Fire when footerbuttonclick
30090 * @param {Roo.bootstrap.UploadCropbox} this
30091 * @param {String} type
30093 "footerbuttonclick" : true,
30097 * @param {Roo.bootstrap.UploadCropbox} this
30102 * Fire when rotate the image
30103 * @param {Roo.bootstrap.UploadCropbox} this
30104 * @param {String} pos
30109 * Fire when inspect the file
30110 * @param {Roo.bootstrap.UploadCropbox} this
30111 * @param {Object} file
30116 * Fire when xhr upload the file
30117 * @param {Roo.bootstrap.UploadCropbox} this
30118 * @param {Object} data
30123 * Fire when arrange the file data
30124 * @param {Roo.bootstrap.UploadCropbox} this
30125 * @param {Object} formData
30130 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30133 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30135 emptyText : 'Click to upload image',
30136 rotateNotify : 'Image is too small to rotate',
30137 errorTimeout : 3000,
30151 cropType : 'image/jpeg',
30153 canvasLoaded : false,
30154 isDocument : false,
30156 paramName : 'imageUpload',
30158 loadingText : 'Loading...',
30161 getAutoCreate : function()
30165 cls : 'roo-upload-cropbox',
30169 cls : 'roo-upload-cropbox-selector',
30174 cls : 'roo-upload-cropbox-body',
30175 style : 'cursor:pointer',
30179 cls : 'roo-upload-cropbox-preview'
30183 cls : 'roo-upload-cropbox-thumb'
30187 cls : 'roo-upload-cropbox-empty-notify',
30188 html : this.emptyText
30192 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30193 html : this.rotateNotify
30199 cls : 'roo-upload-cropbox-footer',
30202 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30212 onRender : function(ct, position)
30214 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30216 if (this.buttons.length) {
30218 Roo.each(this.buttons, function(bb) {
30220 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30222 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30228 this.maskEl = this.el;
30232 initEvents : function()
30234 this.urlAPI = (window.createObjectURL && window) ||
30235 (window.URL && URL.revokeObjectURL && URL) ||
30236 (window.webkitURL && webkitURL);
30238 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30239 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30241 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30242 this.selectorEl.hide();
30244 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30245 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30247 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30248 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30249 this.thumbEl.hide();
30251 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30252 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30254 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30255 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30256 this.errorEl.hide();
30258 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30259 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30260 this.footerEl.hide();
30262 this.setThumbBoxSize();
30268 this.fireEvent('initial', this);
30275 window.addEventListener("resize", function() { _this.resize(); } );
30277 this.bodyEl.on('click', this.beforeSelectFile, this);
30280 this.bodyEl.on('touchstart', this.onTouchStart, this);
30281 this.bodyEl.on('touchmove', this.onTouchMove, this);
30282 this.bodyEl.on('touchend', this.onTouchEnd, this);
30286 this.bodyEl.on('mousedown', this.onMouseDown, this);
30287 this.bodyEl.on('mousemove', this.onMouseMove, this);
30288 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30289 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30290 Roo.get(document).on('mouseup', this.onMouseUp, this);
30293 this.selectorEl.on('change', this.onFileSelected, this);
30299 this.baseScale = 1;
30301 this.baseRotate = 1;
30302 this.dragable = false;
30303 this.pinching = false;
30306 this.cropData = false;
30307 this.notifyEl.dom.innerHTML = this.emptyText;
30309 this.selectorEl.dom.value = '';
30313 resize : function()
30315 if(this.fireEvent('resize', this) != false){
30316 this.setThumbBoxPosition();
30317 this.setCanvasPosition();
30321 onFooterButtonClick : function(e, el, o, type)
30324 case 'rotate-left' :
30325 this.onRotateLeft(e);
30327 case 'rotate-right' :
30328 this.onRotateRight(e);
30331 this.beforeSelectFile(e);
30346 this.fireEvent('footerbuttonclick', this, type);
30349 beforeSelectFile : function(e)
30351 e.preventDefault();
30353 if(this.fireEvent('beforeselectfile', this) != false){
30354 this.selectorEl.dom.click();
30358 onFileSelected : function(e)
30360 e.preventDefault();
30362 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30366 var file = this.selectorEl.dom.files[0];
30368 if(this.fireEvent('inspect', this, file) != false){
30369 this.prepare(file);
30374 trash : function(e)
30376 this.fireEvent('trash', this);
30379 download : function(e)
30381 this.fireEvent('download', this);
30384 loadCanvas : function(src)
30386 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30390 this.imageEl = document.createElement('img');
30394 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30396 this.imageEl.src = src;
30400 onLoadCanvas : function()
30402 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30403 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30405 this.bodyEl.un('click', this.beforeSelectFile, this);
30407 this.notifyEl.hide();
30408 this.thumbEl.show();
30409 this.footerEl.show();
30411 this.baseRotateLevel();
30413 if(this.isDocument){
30414 this.setThumbBoxSize();
30417 this.setThumbBoxPosition();
30419 this.baseScaleLevel();
30425 this.canvasLoaded = true;
30428 this.maskEl.unmask();
30433 setCanvasPosition : function()
30435 if(!this.canvasEl){
30439 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30440 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30442 this.previewEl.setLeft(pw);
30443 this.previewEl.setTop(ph);
30447 onMouseDown : function(e)
30451 this.dragable = true;
30452 this.pinching = false;
30454 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30455 this.dragable = false;
30459 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30460 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30464 onMouseMove : function(e)
30468 if(!this.canvasLoaded){
30472 if (!this.dragable){
30476 var minX = Math.ceil(this.thumbEl.getLeft(true));
30477 var minY = Math.ceil(this.thumbEl.getTop(true));
30479 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30480 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30482 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30483 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30485 x = x - this.mouseX;
30486 y = y - this.mouseY;
30488 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30489 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30491 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30492 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30494 this.previewEl.setLeft(bgX);
30495 this.previewEl.setTop(bgY);
30497 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30498 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30501 onMouseUp : function(e)
30505 this.dragable = false;
30508 onMouseWheel : function(e)
30512 this.startScale = this.scale;
30514 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30516 if(!this.zoomable()){
30517 this.scale = this.startScale;
30526 zoomable : function()
30528 var minScale = this.thumbEl.getWidth() / this.minWidth;
30530 if(this.minWidth < this.minHeight){
30531 minScale = this.thumbEl.getHeight() / this.minHeight;
30534 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30535 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30539 (this.rotate == 0 || this.rotate == 180) &&
30541 width > this.imageEl.OriginWidth ||
30542 height > this.imageEl.OriginHeight ||
30543 (width < this.minWidth && height < this.minHeight)
30551 (this.rotate == 90 || this.rotate == 270) &&
30553 width > this.imageEl.OriginWidth ||
30554 height > this.imageEl.OriginHeight ||
30555 (width < this.minHeight && height < this.minWidth)
30562 !this.isDocument &&
30563 (this.rotate == 0 || this.rotate == 180) &&
30565 width < this.minWidth ||
30566 width > this.imageEl.OriginWidth ||
30567 height < this.minHeight ||
30568 height > this.imageEl.OriginHeight
30575 !this.isDocument &&
30576 (this.rotate == 90 || this.rotate == 270) &&
30578 width < this.minHeight ||
30579 width > this.imageEl.OriginWidth ||
30580 height < this.minWidth ||
30581 height > this.imageEl.OriginHeight
30591 onRotateLeft : function(e)
30593 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30595 var minScale = this.thumbEl.getWidth() / this.minWidth;
30597 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30598 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30600 this.startScale = this.scale;
30602 while (this.getScaleLevel() < minScale){
30604 this.scale = this.scale + 1;
30606 if(!this.zoomable()){
30611 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30612 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30617 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30624 this.scale = this.startScale;
30626 this.onRotateFail();
30631 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30633 if(this.isDocument){
30634 this.setThumbBoxSize();
30635 this.setThumbBoxPosition();
30636 this.setCanvasPosition();
30641 this.fireEvent('rotate', this, 'left');
30645 onRotateRight : function(e)
30647 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30649 var minScale = this.thumbEl.getWidth() / this.minWidth;
30651 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30652 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30654 this.startScale = this.scale;
30656 while (this.getScaleLevel() < minScale){
30658 this.scale = this.scale + 1;
30660 if(!this.zoomable()){
30665 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30666 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30671 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30678 this.scale = this.startScale;
30680 this.onRotateFail();
30685 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30687 if(this.isDocument){
30688 this.setThumbBoxSize();
30689 this.setThumbBoxPosition();
30690 this.setCanvasPosition();
30695 this.fireEvent('rotate', this, 'right');
30698 onRotateFail : function()
30700 this.errorEl.show(true);
30704 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30709 this.previewEl.dom.innerHTML = '';
30711 var canvasEl = document.createElement("canvas");
30713 var contextEl = canvasEl.getContext("2d");
30715 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30716 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30717 var center = this.imageEl.OriginWidth / 2;
30719 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30720 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30721 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30722 center = this.imageEl.OriginHeight / 2;
30725 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30727 contextEl.translate(center, center);
30728 contextEl.rotate(this.rotate * Math.PI / 180);
30730 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30732 this.canvasEl = document.createElement("canvas");
30734 this.contextEl = this.canvasEl.getContext("2d");
30736 switch (this.rotate) {
30739 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30740 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30742 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30747 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30748 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30750 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30751 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);
30755 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30760 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30761 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30763 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30764 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30768 this.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);
30773 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30774 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30776 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30777 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30781 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);
30788 this.previewEl.appendChild(this.canvasEl);
30790 this.setCanvasPosition();
30795 if(!this.canvasLoaded){
30799 var imageCanvas = document.createElement("canvas");
30801 var imageContext = imageCanvas.getContext("2d");
30803 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30804 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30806 var center = imageCanvas.width / 2;
30808 imageContext.translate(center, center);
30810 imageContext.rotate(this.rotate * Math.PI / 180);
30812 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30814 var canvas = document.createElement("canvas");
30816 var context = canvas.getContext("2d");
30818 canvas.width = this.minWidth;
30819 canvas.height = this.minHeight;
30821 switch (this.rotate) {
30824 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30825 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30827 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30828 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30830 var targetWidth = this.minWidth - 2 * x;
30831 var targetHeight = this.minHeight - 2 * y;
30835 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30836 scale = targetWidth / width;
30839 if(x > 0 && y == 0){
30840 scale = targetHeight / height;
30843 if(x > 0 && y > 0){
30844 scale = targetWidth / width;
30846 if(width < height){
30847 scale = targetHeight / height;
30851 context.scale(scale, scale);
30853 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30854 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30856 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30857 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30859 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30864 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30865 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30867 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30868 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30870 var targetWidth = this.minWidth - 2 * x;
30871 var targetHeight = this.minHeight - 2 * y;
30875 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30876 scale = targetWidth / width;
30879 if(x > 0 && y == 0){
30880 scale = targetHeight / height;
30883 if(x > 0 && y > 0){
30884 scale = targetWidth / width;
30886 if(width < height){
30887 scale = targetHeight / height;
30891 context.scale(scale, scale);
30893 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30894 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30896 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30897 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30899 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30901 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30906 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30907 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30909 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30910 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30912 var targetWidth = this.minWidth - 2 * x;
30913 var targetHeight = this.minHeight - 2 * y;
30917 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30918 scale = targetWidth / width;
30921 if(x > 0 && y == 0){
30922 scale = targetHeight / height;
30925 if(x > 0 && y > 0){
30926 scale = targetWidth / width;
30928 if(width < height){
30929 scale = targetHeight / height;
30933 context.scale(scale, scale);
30935 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30936 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30938 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30939 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30941 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30942 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30944 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30949 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30950 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30952 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30953 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30955 var targetWidth = this.minWidth - 2 * x;
30956 var targetHeight = this.minHeight - 2 * y;
30960 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30961 scale = targetWidth / width;
30964 if(x > 0 && y == 0){
30965 scale = targetHeight / height;
30968 if(x > 0 && y > 0){
30969 scale = targetWidth / width;
30971 if(width < height){
30972 scale = targetHeight / height;
30976 context.scale(scale, scale);
30978 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30979 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30981 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30982 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30984 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30986 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30993 this.cropData = canvas.toDataURL(this.cropType);
30995 if(this.fireEvent('crop', this, this.cropData) !== false){
30996 this.process(this.file, this.cropData);
31003 setThumbBoxSize : function()
31007 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31008 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31009 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31011 this.minWidth = width;
31012 this.minHeight = height;
31014 if(this.rotate == 90 || this.rotate == 270){
31015 this.minWidth = height;
31016 this.minHeight = width;
31021 width = Math.ceil(this.minWidth * height / this.minHeight);
31023 if(this.minWidth > this.minHeight){
31025 height = Math.ceil(this.minHeight * width / this.minWidth);
31028 this.thumbEl.setStyle({
31029 width : width + 'px',
31030 height : height + 'px'
31037 setThumbBoxPosition : function()
31039 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31040 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31042 this.thumbEl.setLeft(x);
31043 this.thumbEl.setTop(y);
31047 baseRotateLevel : function()
31049 this.baseRotate = 1;
31052 typeof(this.exif) != 'undefined' &&
31053 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31054 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31056 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31059 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31063 baseScaleLevel : function()
31067 if(this.isDocument){
31069 if(this.baseRotate == 6 || this.baseRotate == 8){
31071 height = this.thumbEl.getHeight();
31072 this.baseScale = height / this.imageEl.OriginWidth;
31074 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31075 width = this.thumbEl.getWidth();
31076 this.baseScale = width / this.imageEl.OriginHeight;
31082 height = this.thumbEl.getHeight();
31083 this.baseScale = height / this.imageEl.OriginHeight;
31085 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31086 width = this.thumbEl.getWidth();
31087 this.baseScale = width / this.imageEl.OriginWidth;
31093 if(this.baseRotate == 6 || this.baseRotate == 8){
31095 width = this.thumbEl.getHeight();
31096 this.baseScale = width / this.imageEl.OriginHeight;
31098 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31099 height = this.thumbEl.getWidth();
31100 this.baseScale = height / this.imageEl.OriginHeight;
31103 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31104 height = this.thumbEl.getWidth();
31105 this.baseScale = height / this.imageEl.OriginHeight;
31107 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31108 width = this.thumbEl.getHeight();
31109 this.baseScale = width / this.imageEl.OriginWidth;
31116 width = this.thumbEl.getWidth();
31117 this.baseScale = width / this.imageEl.OriginWidth;
31119 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31120 height = this.thumbEl.getHeight();
31121 this.baseScale = height / this.imageEl.OriginHeight;
31124 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31126 height = this.thumbEl.getHeight();
31127 this.baseScale = height / this.imageEl.OriginHeight;
31129 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31130 width = this.thumbEl.getWidth();
31131 this.baseScale = width / this.imageEl.OriginWidth;
31139 getScaleLevel : function()
31141 return this.baseScale * Math.pow(1.1, this.scale);
31144 onTouchStart : function(e)
31146 if(!this.canvasLoaded){
31147 this.beforeSelectFile(e);
31151 var touches = e.browserEvent.touches;
31157 if(touches.length == 1){
31158 this.onMouseDown(e);
31162 if(touches.length != 2){
31168 for(var i = 0, finger; finger = touches[i]; i++){
31169 coords.push(finger.pageX, finger.pageY);
31172 var x = Math.pow(coords[0] - coords[2], 2);
31173 var y = Math.pow(coords[1] - coords[3], 2);
31175 this.startDistance = Math.sqrt(x + y);
31177 this.startScale = this.scale;
31179 this.pinching = true;
31180 this.dragable = false;
31184 onTouchMove : function(e)
31186 if(!this.pinching && !this.dragable){
31190 var touches = e.browserEvent.touches;
31197 this.onMouseMove(e);
31203 for(var i = 0, finger; finger = touches[i]; i++){
31204 coords.push(finger.pageX, finger.pageY);
31207 var x = Math.pow(coords[0] - coords[2], 2);
31208 var y = Math.pow(coords[1] - coords[3], 2);
31210 this.endDistance = Math.sqrt(x + y);
31212 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31214 if(!this.zoomable()){
31215 this.scale = this.startScale;
31223 onTouchEnd : function(e)
31225 this.pinching = false;
31226 this.dragable = false;
31230 process : function(file, crop)
31233 this.maskEl.mask(this.loadingText);
31236 this.xhr = new XMLHttpRequest();
31238 file.xhr = this.xhr;
31240 this.xhr.open(this.method, this.url, true);
31243 "Accept": "application/json",
31244 "Cache-Control": "no-cache",
31245 "X-Requested-With": "XMLHttpRequest"
31248 for (var headerName in headers) {
31249 var headerValue = headers[headerName];
31251 this.xhr.setRequestHeader(headerName, headerValue);
31257 this.xhr.onload = function()
31259 _this.xhrOnLoad(_this.xhr);
31262 this.xhr.onerror = function()
31264 _this.xhrOnError(_this.xhr);
31267 var formData = new FormData();
31269 formData.append('returnHTML', 'NO');
31272 formData.append('crop', crop);
31275 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31276 formData.append(this.paramName, file, file.name);
31279 if(typeof(file.filename) != 'undefined'){
31280 formData.append('filename', file.filename);
31283 if(typeof(file.mimetype) != 'undefined'){
31284 formData.append('mimetype', file.mimetype);
31287 if(this.fireEvent('arrange', this, formData) != false){
31288 this.xhr.send(formData);
31292 xhrOnLoad : function(xhr)
31295 this.maskEl.unmask();
31298 if (xhr.readyState !== 4) {
31299 this.fireEvent('exception', this, xhr);
31303 var response = Roo.decode(xhr.responseText);
31305 if(!response.success){
31306 this.fireEvent('exception', this, xhr);
31310 var response = Roo.decode(xhr.responseText);
31312 this.fireEvent('upload', this, response);
31316 xhrOnError : function()
31319 this.maskEl.unmask();
31322 Roo.log('xhr on error');
31324 var response = Roo.decode(xhr.responseText);
31330 prepare : function(file)
31333 this.maskEl.mask(this.loadingText);
31339 if(typeof(file) === 'string'){
31340 this.loadCanvas(file);
31344 if(!file || !this.urlAPI){
31349 this.cropType = file.type;
31353 if(this.fireEvent('prepare', this, this.file) != false){
31355 var reader = new FileReader();
31357 reader.onload = function (e) {
31358 if (e.target.error) {
31359 Roo.log(e.target.error);
31363 var buffer = e.target.result,
31364 dataView = new DataView(buffer),
31366 maxOffset = dataView.byteLength - 4,
31370 if (dataView.getUint16(0) === 0xffd8) {
31371 while (offset < maxOffset) {
31372 markerBytes = dataView.getUint16(offset);
31374 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31375 markerLength = dataView.getUint16(offset + 2) + 2;
31376 if (offset + markerLength > dataView.byteLength) {
31377 Roo.log('Invalid meta data: Invalid segment size.');
31381 if(markerBytes == 0xffe1){
31382 _this.parseExifData(
31389 offset += markerLength;
31399 var url = _this.urlAPI.createObjectURL(_this.file);
31401 _this.loadCanvas(url);
31406 reader.readAsArrayBuffer(this.file);
31412 parseExifData : function(dataView, offset, length)
31414 var tiffOffset = offset + 10,
31418 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31419 // No Exif data, might be XMP data instead
31423 // Check for the ASCII code for "Exif" (0x45786966):
31424 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31425 // No Exif data, might be XMP data instead
31428 if (tiffOffset + 8 > dataView.byteLength) {
31429 Roo.log('Invalid Exif data: Invalid segment size.');
31432 // Check for the two null bytes:
31433 if (dataView.getUint16(offset + 8) !== 0x0000) {
31434 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31437 // Check the byte alignment:
31438 switch (dataView.getUint16(tiffOffset)) {
31440 littleEndian = true;
31443 littleEndian = false;
31446 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31449 // Check for the TIFF tag marker (0x002A):
31450 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31451 Roo.log('Invalid Exif data: Missing TIFF marker.');
31454 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31455 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31457 this.parseExifTags(
31460 tiffOffset + dirOffset,
31465 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31470 if (dirOffset + 6 > dataView.byteLength) {
31471 Roo.log('Invalid Exif data: Invalid directory offset.');
31474 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31475 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31476 if (dirEndOffset + 4 > dataView.byteLength) {
31477 Roo.log('Invalid Exif data: Invalid directory size.');
31480 for (i = 0; i < tagsNumber; i += 1) {
31484 dirOffset + 2 + 12 * i, // tag offset
31488 // Return the offset to the next directory:
31489 return dataView.getUint32(dirEndOffset, littleEndian);
31492 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31494 var tag = dataView.getUint16(offset, littleEndian);
31496 this.exif[tag] = this.getExifValue(
31500 dataView.getUint16(offset + 2, littleEndian), // tag type
31501 dataView.getUint32(offset + 4, littleEndian), // tag length
31506 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31508 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31517 Roo.log('Invalid Exif data: Invalid tag type.');
31521 tagSize = tagType.size * length;
31522 // Determine if the value is contained in the dataOffset bytes,
31523 // or if the value at the dataOffset is a pointer to the actual data:
31524 dataOffset = tagSize > 4 ?
31525 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31526 if (dataOffset + tagSize > dataView.byteLength) {
31527 Roo.log('Invalid Exif data: Invalid data offset.');
31530 if (length === 1) {
31531 return tagType.getValue(dataView, dataOffset, littleEndian);
31534 for (i = 0; i < length; i += 1) {
31535 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31538 if (tagType.ascii) {
31540 // Concatenate the chars:
31541 for (i = 0; i < values.length; i += 1) {
31543 // Ignore the terminating NULL byte(s):
31544 if (c === '\u0000') {
31556 Roo.apply(Roo.bootstrap.UploadCropbox, {
31558 'Orientation': 0x0112
31562 1: 0, //'top-left',
31564 3: 180, //'bottom-right',
31565 // 4: 'bottom-left',
31567 6: 90, //'right-top',
31568 // 7: 'right-bottom',
31569 8: 270 //'left-bottom'
31573 // byte, 8-bit unsigned int:
31575 getValue: function (dataView, dataOffset) {
31576 return dataView.getUint8(dataOffset);
31580 // ascii, 8-bit byte:
31582 getValue: function (dataView, dataOffset) {
31583 return String.fromCharCode(dataView.getUint8(dataOffset));
31588 // short, 16 bit int:
31590 getValue: function (dataView, dataOffset, littleEndian) {
31591 return dataView.getUint16(dataOffset, littleEndian);
31595 // long, 32 bit int:
31597 getValue: function (dataView, dataOffset, littleEndian) {
31598 return dataView.getUint32(dataOffset, littleEndian);
31602 // rational = two long values, first is numerator, second is denominator:
31604 getValue: function (dataView, dataOffset, littleEndian) {
31605 return dataView.getUint32(dataOffset, littleEndian) /
31606 dataView.getUint32(dataOffset + 4, littleEndian);
31610 // slong, 32 bit signed int:
31612 getValue: function (dataView, dataOffset, littleEndian) {
31613 return dataView.getInt32(dataOffset, littleEndian);
31617 // srational, two slongs, first is numerator, second is denominator:
31619 getValue: function (dataView, dataOffset, littleEndian) {
31620 return dataView.getInt32(dataOffset, littleEndian) /
31621 dataView.getInt32(dataOffset + 4, littleEndian);
31631 cls : 'btn-group roo-upload-cropbox-rotate-left',
31632 action : 'rotate-left',
31636 cls : 'btn btn-default',
31637 html : '<i class="fa fa-undo"></i>'
31643 cls : 'btn-group roo-upload-cropbox-picture',
31644 action : 'picture',
31648 cls : 'btn btn-default',
31649 html : '<i class="fa fa-picture-o"></i>'
31655 cls : 'btn-group roo-upload-cropbox-rotate-right',
31656 action : 'rotate-right',
31660 cls : 'btn btn-default',
31661 html : '<i class="fa fa-repeat"></i>'
31669 cls : 'btn-group roo-upload-cropbox-rotate-left',
31670 action : 'rotate-left',
31674 cls : 'btn btn-default',
31675 html : '<i class="fa fa-undo"></i>'
31681 cls : 'btn-group roo-upload-cropbox-download',
31682 action : 'download',
31686 cls : 'btn btn-default',
31687 html : '<i class="fa fa-download"></i>'
31693 cls : 'btn-group roo-upload-cropbox-crop',
31698 cls : 'btn btn-default',
31699 html : '<i class="fa fa-crop"></i>'
31705 cls : 'btn-group roo-upload-cropbox-trash',
31710 cls : 'btn btn-default',
31711 html : '<i class="fa fa-trash"></i>'
31717 cls : 'btn-group roo-upload-cropbox-rotate-right',
31718 action : 'rotate-right',
31722 cls : 'btn btn-default',
31723 html : '<i class="fa fa-repeat"></i>'
31731 cls : 'btn-group roo-upload-cropbox-rotate-left',
31732 action : 'rotate-left',
31736 cls : 'btn btn-default',
31737 html : '<i class="fa fa-undo"></i>'
31743 cls : 'btn-group roo-upload-cropbox-rotate-right',
31744 action : 'rotate-right',
31748 cls : 'btn btn-default',
31749 html : '<i class="fa fa-repeat"></i>'
31762 * @class Roo.bootstrap.DocumentManager
31763 * @extends Roo.bootstrap.Component
31764 * Bootstrap DocumentManager class
31765 * @cfg {String} paramName default 'imageUpload'
31766 * @cfg {String} toolTipName default 'filename'
31767 * @cfg {String} method default POST
31768 * @cfg {String} url action url
31769 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31770 * @cfg {Boolean} multiple multiple upload default true
31771 * @cfg {Number} thumbSize default 300
31772 * @cfg {String} fieldLabel
31773 * @cfg {Number} labelWidth default 4
31774 * @cfg {String} labelAlign (left|top) default left
31775 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31776 * @cfg {Number} labellg set the width of label (1-12)
31777 * @cfg {Number} labelmd set the width of label (1-12)
31778 * @cfg {Number} labelsm set the width of label (1-12)
31779 * @cfg {Number} labelxs set the width of label (1-12)
31782 * Create a new DocumentManager
31783 * @param {Object} config The config object
31786 Roo.bootstrap.DocumentManager = function(config){
31787 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31790 this.delegates = [];
31795 * Fire when initial the DocumentManager
31796 * @param {Roo.bootstrap.DocumentManager} this
31801 * inspect selected file
31802 * @param {Roo.bootstrap.DocumentManager} this
31803 * @param {File} file
31808 * Fire when xhr load exception
31809 * @param {Roo.bootstrap.DocumentManager} this
31810 * @param {XMLHttpRequest} xhr
31812 "exception" : true,
31814 * @event afterupload
31815 * Fire when xhr load exception
31816 * @param {Roo.bootstrap.DocumentManager} this
31817 * @param {XMLHttpRequest} xhr
31819 "afterupload" : true,
31822 * prepare the form data
31823 * @param {Roo.bootstrap.DocumentManager} this
31824 * @param {Object} formData
31829 * Fire when remove the file
31830 * @param {Roo.bootstrap.DocumentManager} this
31831 * @param {Object} file
31836 * Fire after refresh the file
31837 * @param {Roo.bootstrap.DocumentManager} this
31842 * Fire after click the image
31843 * @param {Roo.bootstrap.DocumentManager} this
31844 * @param {Object} file
31849 * Fire when upload a image and editable set to true
31850 * @param {Roo.bootstrap.DocumentManager} this
31851 * @param {Object} file
31855 * @event beforeselectfile
31856 * Fire before select file
31857 * @param {Roo.bootstrap.DocumentManager} this
31859 "beforeselectfile" : true,
31862 * Fire before process file
31863 * @param {Roo.bootstrap.DocumentManager} this
31864 * @param {Object} file
31868 * @event previewrendered
31869 * Fire when preview rendered
31870 * @param {Roo.bootstrap.DocumentManager} this
31871 * @param {Object} file
31873 "previewrendered" : true,
31876 "previewResize" : true
31881 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31890 paramName : 'imageUpload',
31891 toolTipName : 'filename',
31894 labelAlign : 'left',
31904 getAutoCreate : function()
31906 var managerWidget = {
31908 cls : 'roo-document-manager',
31912 cls : 'roo-document-manager-selector',
31917 cls : 'roo-document-manager-uploader',
31921 cls : 'roo-document-manager-upload-btn',
31922 html : '<i class="fa fa-plus"></i>'
31933 cls : 'column col-md-12',
31938 if(this.fieldLabel.length){
31943 cls : 'column col-md-12',
31944 html : this.fieldLabel
31948 cls : 'column col-md-12',
31953 if(this.labelAlign == 'left'){
31958 html : this.fieldLabel
31967 if(this.labelWidth > 12){
31968 content[0].style = "width: " + this.labelWidth + 'px';
31971 if(this.labelWidth < 13 && this.labelmd == 0){
31972 this.labelmd = this.labelWidth;
31975 if(this.labellg > 0){
31976 content[0].cls += ' col-lg-' + this.labellg;
31977 content[1].cls += ' col-lg-' + (12 - this.labellg);
31980 if(this.labelmd > 0){
31981 content[0].cls += ' col-md-' + this.labelmd;
31982 content[1].cls += ' col-md-' + (12 - this.labelmd);
31985 if(this.labelsm > 0){
31986 content[0].cls += ' col-sm-' + this.labelsm;
31987 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31990 if(this.labelxs > 0){
31991 content[0].cls += ' col-xs-' + this.labelxs;
31992 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32000 cls : 'row clearfix',
32008 initEvents : function()
32010 this.managerEl = this.el.select('.roo-document-manager', true).first();
32011 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32013 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32014 this.selectorEl.hide();
32017 this.selectorEl.attr('multiple', 'multiple');
32020 this.selectorEl.on('change', this.onFileSelected, this);
32022 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32023 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32025 this.uploader.on('click', this.onUploaderClick, this);
32027 this.renderProgressDialog();
32031 window.addEventListener("resize", function() { _this.refresh(); } );
32033 this.fireEvent('initial', this);
32036 renderProgressDialog : function()
32040 this.progressDialog = new Roo.bootstrap.Modal({
32041 cls : 'roo-document-manager-progress-dialog',
32042 allow_close : false,
32053 btnclick : function() {
32054 _this.uploadCancel();
32060 this.progressDialog.render(Roo.get(document.body));
32062 this.progress = new Roo.bootstrap.Progress({
32063 cls : 'roo-document-manager-progress',
32068 this.progress.render(this.progressDialog.getChildContainer());
32070 this.progressBar = new Roo.bootstrap.ProgressBar({
32071 cls : 'roo-document-manager-progress-bar',
32074 aria_valuemax : 12,
32078 this.progressBar.render(this.progress.getChildContainer());
32081 onUploaderClick : function(e)
32083 e.preventDefault();
32085 if(this.fireEvent('beforeselectfile', this) != false){
32086 this.selectorEl.dom.click();
32091 onFileSelected : function(e)
32093 e.preventDefault();
32095 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32099 Roo.each(this.selectorEl.dom.files, function(file){
32100 if(this.fireEvent('inspect', this, file) != false){
32101 this.files.push(file);
32111 this.selectorEl.dom.value = '';
32113 if(!this.files || !this.files.length){
32117 if(this.boxes > 0 && this.files.length > this.boxes){
32118 this.files = this.files.slice(0, this.boxes);
32121 this.uploader.show();
32123 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32124 this.uploader.hide();
32133 Roo.each(this.files, function(file){
32135 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32136 var f = this.renderPreview(file);
32141 if(file.type.indexOf('image') != -1){
32142 this.delegates.push(
32144 _this.process(file);
32145 }).createDelegate(this)
32153 _this.process(file);
32154 }).createDelegate(this)
32159 this.files = files;
32161 this.delegates = this.delegates.concat(docs);
32163 if(!this.delegates.length){
32168 this.progressBar.aria_valuemax = this.delegates.length;
32175 arrange : function()
32177 if(!this.delegates.length){
32178 this.progressDialog.hide();
32183 var delegate = this.delegates.shift();
32185 this.progressDialog.show();
32187 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32189 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32194 refresh : function()
32196 this.uploader.show();
32198 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32199 this.uploader.hide();
32202 Roo.isTouch ? this.closable(false) : this.closable(true);
32204 this.fireEvent('refresh', this);
32207 onRemove : function(e, el, o)
32209 e.preventDefault();
32211 this.fireEvent('remove', this, o);
32215 remove : function(o)
32219 Roo.each(this.files, function(file){
32220 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32229 this.files = files;
32236 Roo.each(this.files, function(file){
32241 file.target.remove();
32250 onClick : function(e, el, o)
32252 e.preventDefault();
32254 this.fireEvent('click', this, o);
32258 closable : function(closable)
32260 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32262 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32274 xhrOnLoad : function(xhr)
32276 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32280 if (xhr.readyState !== 4) {
32282 this.fireEvent('exception', this, xhr);
32286 var response = Roo.decode(xhr.responseText);
32288 if(!response.success){
32290 this.fireEvent('exception', this, xhr);
32294 var file = this.renderPreview(response.data);
32296 this.files.push(file);
32300 this.fireEvent('afterupload', this, xhr);
32304 xhrOnError : function(xhr)
32306 Roo.log('xhr on error');
32308 var response = Roo.decode(xhr.responseText);
32315 process : function(file)
32317 if(this.fireEvent('process', this, file) !== false){
32318 if(this.editable && file.type.indexOf('image') != -1){
32319 this.fireEvent('edit', this, file);
32323 this.uploadStart(file, false);
32330 uploadStart : function(file, crop)
32332 this.xhr = new XMLHttpRequest();
32334 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32339 file.xhr = this.xhr;
32341 this.managerEl.createChild({
32343 cls : 'roo-document-manager-loading',
32347 tooltip : file.name,
32348 cls : 'roo-document-manager-thumb',
32349 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32355 this.xhr.open(this.method, this.url, true);
32358 "Accept": "application/json",
32359 "Cache-Control": "no-cache",
32360 "X-Requested-With": "XMLHttpRequest"
32363 for (var headerName in headers) {
32364 var headerValue = headers[headerName];
32366 this.xhr.setRequestHeader(headerName, headerValue);
32372 this.xhr.onload = function()
32374 _this.xhrOnLoad(_this.xhr);
32377 this.xhr.onerror = function()
32379 _this.xhrOnError(_this.xhr);
32382 var formData = new FormData();
32384 formData.append('returnHTML', 'NO');
32387 formData.append('crop', crop);
32390 formData.append(this.paramName, file, file.name);
32397 if(this.fireEvent('prepare', this, formData, options) != false){
32399 if(options.manually){
32403 this.xhr.send(formData);
32407 this.uploadCancel();
32410 uploadCancel : function()
32416 this.delegates = [];
32418 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32425 renderPreview : function(file)
32427 if(typeof(file.target) != 'undefined' && file.target){
32431 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32433 var previewEl = this.managerEl.createChild({
32435 cls : 'roo-document-manager-preview',
32439 tooltip : file[this.toolTipName],
32440 cls : 'roo-document-manager-thumb',
32441 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32446 html : '<i class="fa fa-times-circle"></i>'
32451 var close = previewEl.select('button.close', true).first();
32453 close.on('click', this.onRemove, this, file);
32455 file.target = previewEl;
32457 var image = previewEl.select('img', true).first();
32461 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32463 image.on('click', this.onClick, this, file);
32465 this.fireEvent('previewrendered', this, file);
32471 onPreviewLoad : function(file, image)
32473 if(typeof(file.target) == 'undefined' || !file.target){
32477 var width = image.dom.naturalWidth || image.dom.width;
32478 var height = image.dom.naturalHeight || image.dom.height;
32480 if(!this.previewResize) {
32484 if(width > height){
32485 file.target.addClass('wide');
32489 file.target.addClass('tall');
32494 uploadFromSource : function(file, crop)
32496 this.xhr = new XMLHttpRequest();
32498 this.managerEl.createChild({
32500 cls : 'roo-document-manager-loading',
32504 tooltip : file.name,
32505 cls : 'roo-document-manager-thumb',
32506 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32512 this.xhr.open(this.method, this.url, true);
32515 "Accept": "application/json",
32516 "Cache-Control": "no-cache",
32517 "X-Requested-With": "XMLHttpRequest"
32520 for (var headerName in headers) {
32521 var headerValue = headers[headerName];
32523 this.xhr.setRequestHeader(headerName, headerValue);
32529 this.xhr.onload = function()
32531 _this.xhrOnLoad(_this.xhr);
32534 this.xhr.onerror = function()
32536 _this.xhrOnError(_this.xhr);
32539 var formData = new FormData();
32541 formData.append('returnHTML', 'NO');
32543 formData.append('crop', crop);
32545 if(typeof(file.filename) != 'undefined'){
32546 formData.append('filename', file.filename);
32549 if(typeof(file.mimetype) != 'undefined'){
32550 formData.append('mimetype', file.mimetype);
32555 if(this.fireEvent('prepare', this, formData) != false){
32556 this.xhr.send(formData);
32566 * @class Roo.bootstrap.DocumentViewer
32567 * @extends Roo.bootstrap.Component
32568 * Bootstrap DocumentViewer class
32569 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32570 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32573 * Create a new DocumentViewer
32574 * @param {Object} config The config object
32577 Roo.bootstrap.DocumentViewer = function(config){
32578 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32583 * Fire after initEvent
32584 * @param {Roo.bootstrap.DocumentViewer} this
32590 * @param {Roo.bootstrap.DocumentViewer} this
32595 * Fire after download button
32596 * @param {Roo.bootstrap.DocumentViewer} this
32601 * Fire after trash button
32602 * @param {Roo.bootstrap.DocumentViewer} this
32609 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32611 showDownload : true,
32615 getAutoCreate : function()
32619 cls : 'roo-document-viewer',
32623 cls : 'roo-document-viewer-body',
32627 cls : 'roo-document-viewer-thumb',
32631 cls : 'roo-document-viewer-image'
32639 cls : 'roo-document-viewer-footer',
32642 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32646 cls : 'btn-group roo-document-viewer-download',
32650 cls : 'btn btn-default',
32651 html : '<i class="fa fa-download"></i>'
32657 cls : 'btn-group roo-document-viewer-trash',
32661 cls : 'btn btn-default',
32662 html : '<i class="fa fa-trash"></i>'
32675 initEvents : function()
32677 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32678 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32680 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32681 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32683 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32684 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32686 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32687 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32689 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32690 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32692 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32693 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32695 this.bodyEl.on('click', this.onClick, this);
32696 this.downloadBtn.on('click', this.onDownload, this);
32697 this.trashBtn.on('click', this.onTrash, this);
32699 this.downloadBtn.hide();
32700 this.trashBtn.hide();
32702 if(this.showDownload){
32703 this.downloadBtn.show();
32706 if(this.showTrash){
32707 this.trashBtn.show();
32710 if(!this.showDownload && !this.showTrash) {
32711 this.footerEl.hide();
32716 initial : function()
32718 this.fireEvent('initial', this);
32722 onClick : function(e)
32724 e.preventDefault();
32726 this.fireEvent('click', this);
32729 onDownload : function(e)
32731 e.preventDefault();
32733 this.fireEvent('download', this);
32736 onTrash : function(e)
32738 e.preventDefault();
32740 this.fireEvent('trash', this);
32752 * @class Roo.bootstrap.NavProgressBar
32753 * @extends Roo.bootstrap.Component
32754 * Bootstrap NavProgressBar class
32757 * Create a new nav progress bar
32758 * @param {Object} config The config object
32761 Roo.bootstrap.NavProgressBar = function(config){
32762 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32764 this.bullets = this.bullets || [];
32766 // Roo.bootstrap.NavProgressBar.register(this);
32770 * Fires when the active item changes
32771 * @param {Roo.bootstrap.NavProgressBar} this
32772 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32773 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32780 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32785 getAutoCreate : function()
32787 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32791 cls : 'roo-navigation-bar-group',
32795 cls : 'roo-navigation-top-bar'
32799 cls : 'roo-navigation-bullets-bar',
32803 cls : 'roo-navigation-bar'
32810 cls : 'roo-navigation-bottom-bar'
32820 initEvents: function()
32825 onRender : function(ct, position)
32827 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32829 if(this.bullets.length){
32830 Roo.each(this.bullets, function(b){
32839 addItem : function(cfg)
32841 var item = new Roo.bootstrap.NavProgressItem(cfg);
32843 item.parentId = this.id;
32844 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32847 var top = new Roo.bootstrap.Element({
32849 cls : 'roo-navigation-bar-text'
32852 var bottom = new Roo.bootstrap.Element({
32854 cls : 'roo-navigation-bar-text'
32857 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32858 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32860 var topText = new Roo.bootstrap.Element({
32862 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32865 var bottomText = new Roo.bootstrap.Element({
32867 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32870 topText.onRender(top.el, null);
32871 bottomText.onRender(bottom.el, null);
32874 item.bottomEl = bottom;
32877 this.barItems.push(item);
32882 getActive : function()
32884 var active = false;
32886 Roo.each(this.barItems, function(v){
32888 if (!v.isActive()) {
32900 setActiveItem : function(item)
32904 Roo.each(this.barItems, function(v){
32905 if (v.rid == item.rid) {
32909 if (v.isActive()) {
32910 v.setActive(false);
32915 item.setActive(true);
32917 this.fireEvent('changed', this, item, prev);
32920 getBarItem: function(rid)
32924 Roo.each(this.barItems, function(e) {
32925 if (e.rid != rid) {
32936 indexOfItem : function(item)
32940 Roo.each(this.barItems, function(v, i){
32942 if (v.rid != item.rid) {
32953 setActiveNext : function()
32955 var i = this.indexOfItem(this.getActive());
32957 if (i > this.barItems.length) {
32961 this.setActiveItem(this.barItems[i+1]);
32964 setActivePrev : function()
32966 var i = this.indexOfItem(this.getActive());
32972 this.setActiveItem(this.barItems[i-1]);
32975 format : function()
32977 if(!this.barItems.length){
32981 var width = 100 / this.barItems.length;
32983 Roo.each(this.barItems, function(i){
32984 i.el.setStyle('width', width + '%');
32985 i.topEl.el.setStyle('width', width + '%');
32986 i.bottomEl.el.setStyle('width', width + '%');
32995 * Nav Progress Item
33000 * @class Roo.bootstrap.NavProgressItem
33001 * @extends Roo.bootstrap.Component
33002 * Bootstrap NavProgressItem class
33003 * @cfg {String} rid the reference id
33004 * @cfg {Boolean} active (true|false) Is item active default false
33005 * @cfg {Boolean} disabled (true|false) Is item active default false
33006 * @cfg {String} html
33007 * @cfg {String} position (top|bottom) text position default bottom
33008 * @cfg {String} icon show icon instead of number
33011 * Create a new NavProgressItem
33012 * @param {Object} config The config object
33014 Roo.bootstrap.NavProgressItem = function(config){
33015 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33020 * The raw click event for the entire grid.
33021 * @param {Roo.bootstrap.NavProgressItem} this
33022 * @param {Roo.EventObject} e
33029 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33035 position : 'bottom',
33038 getAutoCreate : function()
33040 var iconCls = 'roo-navigation-bar-item-icon';
33042 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33046 cls: 'roo-navigation-bar-item',
33056 cfg.cls += ' active';
33059 cfg.cls += ' disabled';
33065 disable : function()
33067 this.setDisabled(true);
33070 enable : function()
33072 this.setDisabled(false);
33075 initEvents: function()
33077 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33079 this.iconEl.on('click', this.onClick, this);
33082 onClick : function(e)
33084 e.preventDefault();
33090 if(this.fireEvent('click', this, e) === false){
33094 this.parent().setActiveItem(this);
33097 isActive: function ()
33099 return this.active;
33102 setActive : function(state)
33104 if(this.active == state){
33108 this.active = state;
33111 this.el.addClass('active');
33115 this.el.removeClass('active');
33120 setDisabled : function(state)
33122 if(this.disabled == state){
33126 this.disabled = state;
33129 this.el.addClass('disabled');
33133 this.el.removeClass('disabled');
33136 tooltipEl : function()
33138 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33151 * @class Roo.bootstrap.FieldLabel
33152 * @extends Roo.bootstrap.Component
33153 * Bootstrap FieldLabel class
33154 * @cfg {String} html contents of the element
33155 * @cfg {String} tag tag of the element default label
33156 * @cfg {String} cls class of the element
33157 * @cfg {String} target label target
33158 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33159 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33160 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33161 * @cfg {String} iconTooltip default "This field is required"
33162 * @cfg {String} indicatorpos (left|right) default left
33165 * Create a new FieldLabel
33166 * @param {Object} config The config object
33169 Roo.bootstrap.FieldLabel = function(config){
33170 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33175 * Fires after the field has been marked as invalid.
33176 * @param {Roo.form.FieldLabel} this
33177 * @param {String} msg The validation message
33182 * Fires after the field has been validated with no errors.
33183 * @param {Roo.form.FieldLabel} this
33189 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33196 invalidClass : 'has-warning',
33197 validClass : 'has-success',
33198 iconTooltip : 'This field is required',
33199 indicatorpos : 'left',
33201 getAutoCreate : function(){
33204 if (!this.allowBlank) {
33210 cls : 'roo-bootstrap-field-label ' + this.cls,
33215 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33216 tooltip : this.iconTooltip
33225 if(this.indicatorpos == 'right'){
33228 cls : 'roo-bootstrap-field-label ' + this.cls,
33237 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33238 tooltip : this.iconTooltip
33247 initEvents: function()
33249 Roo.bootstrap.Element.superclass.initEvents.call(this);
33251 this.indicator = this.indicatorEl();
33253 if(this.indicator){
33254 this.indicator.removeClass('visible');
33255 this.indicator.addClass('invisible');
33258 Roo.bootstrap.FieldLabel.register(this);
33261 indicatorEl : function()
33263 var indicator = this.el.select('i.roo-required-indicator',true).first();
33274 * Mark this field as valid
33276 markValid : function()
33278 if(this.indicator){
33279 this.indicator.removeClass('visible');
33280 this.indicator.addClass('invisible');
33282 if (Roo.bootstrap.version == 3) {
33283 this.el.removeClass(this.invalidClass);
33284 this.el.addClass(this.validClass);
33286 this.el.removeClass('is-invalid');
33287 this.el.addClass('is-valid');
33291 this.fireEvent('valid', this);
33295 * Mark this field as invalid
33296 * @param {String} msg The validation message
33298 markInvalid : function(msg)
33300 if(this.indicator){
33301 this.indicator.removeClass('invisible');
33302 this.indicator.addClass('visible');
33304 if (Roo.bootstrap.version == 3) {
33305 this.el.removeClass(this.validClass);
33306 this.el.addClass(this.invalidClass);
33308 this.el.removeClass('is-valid');
33309 this.el.addClass('is-invalid');
33313 this.fireEvent('invalid', this, msg);
33319 Roo.apply(Roo.bootstrap.FieldLabel, {
33324 * register a FieldLabel Group
33325 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33327 register : function(label)
33329 if(this.groups.hasOwnProperty(label.target)){
33333 this.groups[label.target] = label;
33337 * fetch a FieldLabel Group based on the target
33338 * @param {string} target
33339 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33341 get: function(target) {
33342 if (typeof(this.groups[target]) == 'undefined') {
33346 return this.groups[target] ;
33355 * page DateSplitField.
33361 * @class Roo.bootstrap.DateSplitField
33362 * @extends Roo.bootstrap.Component
33363 * Bootstrap DateSplitField class
33364 * @cfg {string} fieldLabel - the label associated
33365 * @cfg {Number} labelWidth set the width of label (0-12)
33366 * @cfg {String} labelAlign (top|left)
33367 * @cfg {Boolean} dayAllowBlank (true|false) default false
33368 * @cfg {Boolean} monthAllowBlank (true|false) default false
33369 * @cfg {Boolean} yearAllowBlank (true|false) default false
33370 * @cfg {string} dayPlaceholder
33371 * @cfg {string} monthPlaceholder
33372 * @cfg {string} yearPlaceholder
33373 * @cfg {string} dayFormat default 'd'
33374 * @cfg {string} monthFormat default 'm'
33375 * @cfg {string} yearFormat default 'Y'
33376 * @cfg {Number} labellg set the width of label (1-12)
33377 * @cfg {Number} labelmd set the width of label (1-12)
33378 * @cfg {Number} labelsm set the width of label (1-12)
33379 * @cfg {Number} labelxs set the width of label (1-12)
33383 * Create a new DateSplitField
33384 * @param {Object} config The config object
33387 Roo.bootstrap.DateSplitField = function(config){
33388 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33394 * getting the data of years
33395 * @param {Roo.bootstrap.DateSplitField} this
33396 * @param {Object} years
33401 * getting the data of days
33402 * @param {Roo.bootstrap.DateSplitField} this
33403 * @param {Object} days
33408 * Fires after the field has been marked as invalid.
33409 * @param {Roo.form.Field} this
33410 * @param {String} msg The validation message
33415 * Fires after the field has been validated with no errors.
33416 * @param {Roo.form.Field} this
33422 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33425 labelAlign : 'top',
33427 dayAllowBlank : false,
33428 monthAllowBlank : false,
33429 yearAllowBlank : false,
33430 dayPlaceholder : '',
33431 monthPlaceholder : '',
33432 yearPlaceholder : '',
33436 isFormField : true,
33442 getAutoCreate : function()
33446 cls : 'row roo-date-split-field-group',
33451 cls : 'form-hidden-field roo-date-split-field-group-value',
33457 var labelCls = 'col-md-12';
33458 var contentCls = 'col-md-4';
33460 if(this.fieldLabel){
33464 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33468 html : this.fieldLabel
33473 if(this.labelAlign == 'left'){
33475 if(this.labelWidth > 12){
33476 label.style = "width: " + this.labelWidth + 'px';
33479 if(this.labelWidth < 13 && this.labelmd == 0){
33480 this.labelmd = this.labelWidth;
33483 if(this.labellg > 0){
33484 labelCls = ' col-lg-' + this.labellg;
33485 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33488 if(this.labelmd > 0){
33489 labelCls = ' col-md-' + this.labelmd;
33490 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33493 if(this.labelsm > 0){
33494 labelCls = ' col-sm-' + this.labelsm;
33495 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33498 if(this.labelxs > 0){
33499 labelCls = ' col-xs-' + this.labelxs;
33500 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33504 label.cls += ' ' + labelCls;
33506 cfg.cn.push(label);
33509 Roo.each(['day', 'month', 'year'], function(t){
33512 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33519 inputEl: function ()
33521 return this.el.select('.roo-date-split-field-group-value', true).first();
33524 onRender : function(ct, position)
33528 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33530 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33532 this.dayField = new Roo.bootstrap.ComboBox({
33533 allowBlank : this.dayAllowBlank,
33534 alwaysQuery : true,
33535 displayField : 'value',
33538 forceSelection : true,
33540 placeholder : this.dayPlaceholder,
33541 selectOnFocus : true,
33542 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33543 triggerAction : 'all',
33545 valueField : 'value',
33546 store : new Roo.data.SimpleStore({
33547 data : (function() {
33549 _this.fireEvent('days', _this, days);
33552 fields : [ 'value' ]
33555 select : function (_self, record, index)
33557 _this.setValue(_this.getValue());
33562 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33564 this.monthField = new Roo.bootstrap.MonthField({
33565 after : '<i class=\"fa fa-calendar\"></i>',
33566 allowBlank : this.monthAllowBlank,
33567 placeholder : this.monthPlaceholder,
33570 render : function (_self)
33572 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33573 e.preventDefault();
33577 select : function (_self, oldvalue, newvalue)
33579 _this.setValue(_this.getValue());
33584 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33586 this.yearField = new Roo.bootstrap.ComboBox({
33587 allowBlank : this.yearAllowBlank,
33588 alwaysQuery : true,
33589 displayField : 'value',
33592 forceSelection : true,
33594 placeholder : this.yearPlaceholder,
33595 selectOnFocus : true,
33596 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33597 triggerAction : 'all',
33599 valueField : 'value',
33600 store : new Roo.data.SimpleStore({
33601 data : (function() {
33603 _this.fireEvent('years', _this, years);
33606 fields : [ 'value' ]
33609 select : function (_self, record, index)
33611 _this.setValue(_this.getValue());
33616 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33619 setValue : function(v, format)
33621 this.inputEl.dom.value = v;
33623 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33625 var d = Date.parseDate(v, f);
33632 this.setDay(d.format(this.dayFormat));
33633 this.setMonth(d.format(this.monthFormat));
33634 this.setYear(d.format(this.yearFormat));
33641 setDay : function(v)
33643 this.dayField.setValue(v);
33644 this.inputEl.dom.value = this.getValue();
33649 setMonth : function(v)
33651 this.monthField.setValue(v, true);
33652 this.inputEl.dom.value = this.getValue();
33657 setYear : function(v)
33659 this.yearField.setValue(v);
33660 this.inputEl.dom.value = this.getValue();
33665 getDay : function()
33667 return this.dayField.getValue();
33670 getMonth : function()
33672 return this.monthField.getValue();
33675 getYear : function()
33677 return this.yearField.getValue();
33680 getValue : function()
33682 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33684 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33694 this.inputEl.dom.value = '';
33699 validate : function()
33701 var d = this.dayField.validate();
33702 var m = this.monthField.validate();
33703 var y = this.yearField.validate();
33708 (!this.dayAllowBlank && !d) ||
33709 (!this.monthAllowBlank && !m) ||
33710 (!this.yearAllowBlank && !y)
33715 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33724 this.markInvalid();
33729 markValid : function()
33732 var label = this.el.select('label', true).first();
33733 var icon = this.el.select('i.fa-star', true).first();
33739 this.fireEvent('valid', this);
33743 * Mark this field as invalid
33744 * @param {String} msg The validation message
33746 markInvalid : function(msg)
33749 var label = this.el.select('label', true).first();
33750 var icon = this.el.select('i.fa-star', true).first();
33752 if(label && !icon){
33753 this.el.select('.roo-date-split-field-label', true).createChild({
33755 cls : 'text-danger fa fa-lg fa-star',
33756 tooltip : 'This field is required',
33757 style : 'margin-right:5px;'
33761 this.fireEvent('invalid', this, msg);
33764 clearInvalid : function()
33766 var label = this.el.select('label', true).first();
33767 var icon = this.el.select('i.fa-star', true).first();
33773 this.fireEvent('valid', this);
33776 getName: function()
33786 * http://masonry.desandro.com
33788 * The idea is to render all the bricks based on vertical width...
33790 * The original code extends 'outlayer' - we might need to use that....
33796 * @class Roo.bootstrap.LayoutMasonry
33797 * @extends Roo.bootstrap.Component
33798 * Bootstrap Layout Masonry class
33801 * Create a new Element
33802 * @param {Object} config The config object
33805 Roo.bootstrap.LayoutMasonry = function(config){
33807 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33811 Roo.bootstrap.LayoutMasonry.register(this);
33817 * Fire after layout the items
33818 * @param {Roo.bootstrap.LayoutMasonry} this
33819 * @param {Roo.EventObject} e
33826 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33829 * @cfg {Boolean} isLayoutInstant = no animation?
33831 isLayoutInstant : false, // needed?
33834 * @cfg {Number} boxWidth width of the columns
33839 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33844 * @cfg {Number} padWidth padding below box..
33849 * @cfg {Number} gutter gutter width..
33854 * @cfg {Number} maxCols maximum number of columns
33860 * @cfg {Boolean} isAutoInitial defalut true
33862 isAutoInitial : true,
33867 * @cfg {Boolean} isHorizontal defalut false
33869 isHorizontal : false,
33871 currentSize : null,
33877 bricks: null, //CompositeElement
33881 _isLayoutInited : false,
33883 // isAlternative : false, // only use for vertical layout...
33886 * @cfg {Number} alternativePadWidth padding below box..
33888 alternativePadWidth : 50,
33890 selectedBrick : [],
33892 getAutoCreate : function(){
33894 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33898 cls: 'blog-masonary-wrapper ' + this.cls,
33900 cls : 'mas-boxes masonary'
33907 getChildContainer: function( )
33909 if (this.boxesEl) {
33910 return this.boxesEl;
33913 this.boxesEl = this.el.select('.mas-boxes').first();
33915 return this.boxesEl;
33919 initEvents : function()
33923 if(this.isAutoInitial){
33924 Roo.log('hook children rendered');
33925 this.on('childrenrendered', function() {
33926 Roo.log('children rendered');
33932 initial : function()
33934 this.selectedBrick = [];
33936 this.currentSize = this.el.getBox(true);
33938 Roo.EventManager.onWindowResize(this.resize, this);
33940 if(!this.isAutoInitial){
33948 //this.layout.defer(500,this);
33952 resize : function()
33954 var cs = this.el.getBox(true);
33957 this.currentSize.width == cs.width &&
33958 this.currentSize.x == cs.x &&
33959 this.currentSize.height == cs.height &&
33960 this.currentSize.y == cs.y
33962 Roo.log("no change in with or X or Y");
33966 this.currentSize = cs;
33972 layout : function()
33974 this._resetLayout();
33976 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33978 this.layoutItems( isInstant );
33980 this._isLayoutInited = true;
33982 this.fireEvent('layout', this);
33986 _resetLayout : function()
33988 if(this.isHorizontal){
33989 this.horizontalMeasureColumns();
33993 this.verticalMeasureColumns();
33997 verticalMeasureColumns : function()
33999 this.getContainerWidth();
34001 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34002 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34006 var boxWidth = this.boxWidth + this.padWidth;
34008 if(this.containerWidth < this.boxWidth){
34009 boxWidth = this.containerWidth
34012 var containerWidth = this.containerWidth;
34014 var cols = Math.floor(containerWidth / boxWidth);
34016 this.cols = Math.max( cols, 1 );
34018 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34020 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34022 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34024 this.colWidth = boxWidth + avail - this.padWidth;
34026 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34027 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34030 horizontalMeasureColumns : function()
34032 this.getContainerWidth();
34034 var boxWidth = this.boxWidth;
34036 if(this.containerWidth < boxWidth){
34037 boxWidth = this.containerWidth;
34040 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34042 this.el.setHeight(boxWidth);
34046 getContainerWidth : function()
34048 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34051 layoutItems : function( isInstant )
34053 Roo.log(this.bricks);
34055 var items = Roo.apply([], this.bricks);
34057 if(this.isHorizontal){
34058 this._horizontalLayoutItems( items , isInstant );
34062 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34063 // this._verticalAlternativeLayoutItems( items , isInstant );
34067 this._verticalLayoutItems( items , isInstant );
34071 _verticalLayoutItems : function ( items , isInstant)
34073 if ( !items || !items.length ) {
34078 ['xs', 'xs', 'xs', 'tall'],
34079 ['xs', 'xs', 'tall'],
34080 ['xs', 'xs', 'sm'],
34081 ['xs', 'xs', 'xs'],
34087 ['sm', 'xs', 'xs'],
34091 ['tall', 'xs', 'xs', 'xs'],
34092 ['tall', 'xs', 'xs'],
34104 Roo.each(items, function(item, k){
34106 switch (item.size) {
34107 // these layouts take up a full box,
34118 boxes.push([item]);
34141 var filterPattern = function(box, length)
34149 var pattern = box.slice(0, length);
34153 Roo.each(pattern, function(i){
34154 format.push(i.size);
34157 Roo.each(standard, function(s){
34159 if(String(s) != String(format)){
34168 if(!match && length == 1){
34173 filterPattern(box, length - 1);
34177 queue.push(pattern);
34179 box = box.slice(length, box.length);
34181 filterPattern(box, 4);
34187 Roo.each(boxes, function(box, k){
34193 if(box.length == 1){
34198 filterPattern(box, 4);
34202 this._processVerticalLayoutQueue( queue, isInstant );
34206 // _verticalAlternativeLayoutItems : function( items , isInstant )
34208 // if ( !items || !items.length ) {
34212 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34216 _horizontalLayoutItems : function ( items , isInstant)
34218 if ( !items || !items.length || items.length < 3) {
34224 var eItems = items.slice(0, 3);
34226 items = items.slice(3, items.length);
34229 ['xs', 'xs', 'xs', 'wide'],
34230 ['xs', 'xs', 'wide'],
34231 ['xs', 'xs', 'sm'],
34232 ['xs', 'xs', 'xs'],
34238 ['sm', 'xs', 'xs'],
34242 ['wide', 'xs', 'xs', 'xs'],
34243 ['wide', 'xs', 'xs'],
34256 Roo.each(items, function(item, k){
34258 switch (item.size) {
34269 boxes.push([item]);
34293 var filterPattern = function(box, length)
34301 var pattern = box.slice(0, length);
34305 Roo.each(pattern, function(i){
34306 format.push(i.size);
34309 Roo.each(standard, function(s){
34311 if(String(s) != String(format)){
34320 if(!match && length == 1){
34325 filterPattern(box, length - 1);
34329 queue.push(pattern);
34331 box = box.slice(length, box.length);
34333 filterPattern(box, 4);
34339 Roo.each(boxes, function(box, k){
34345 if(box.length == 1){
34350 filterPattern(box, 4);
34357 var pos = this.el.getBox(true);
34361 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34363 var hit_end = false;
34365 Roo.each(queue, function(box){
34369 Roo.each(box, function(b){
34371 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34381 Roo.each(box, function(b){
34383 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34386 mx = Math.max(mx, b.x);
34390 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34394 Roo.each(box, function(b){
34396 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34410 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34413 /** Sets position of item in DOM
34414 * @param {Element} item
34415 * @param {Number} x - horizontal position
34416 * @param {Number} y - vertical position
34417 * @param {Boolean} isInstant - disables transitions
34419 _processVerticalLayoutQueue : function( queue, isInstant )
34421 var pos = this.el.getBox(true);
34426 for (var i = 0; i < this.cols; i++){
34430 Roo.each(queue, function(box, k){
34432 var col = k % this.cols;
34434 Roo.each(box, function(b,kk){
34436 b.el.position('absolute');
34438 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34439 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34441 if(b.size == 'md-left' || b.size == 'md-right'){
34442 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34443 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34446 b.el.setWidth(width);
34447 b.el.setHeight(height);
34449 b.el.select('iframe',true).setSize(width,height);
34453 for (var i = 0; i < this.cols; i++){
34455 if(maxY[i] < maxY[col]){
34460 col = Math.min(col, i);
34464 x = pos.x + col * (this.colWidth + this.padWidth);
34468 var positions = [];
34470 switch (box.length){
34472 positions = this.getVerticalOneBoxColPositions(x, y, box);
34475 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34478 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34481 positions = this.getVerticalFourBoxColPositions(x, y, box);
34487 Roo.each(box, function(b,kk){
34489 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34491 var sz = b.el.getSize();
34493 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34501 for (var i = 0; i < this.cols; i++){
34502 mY = Math.max(mY, maxY[i]);
34505 this.el.setHeight(mY - pos.y);
34509 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34511 // var pos = this.el.getBox(true);
34514 // var maxX = pos.right;
34516 // var maxHeight = 0;
34518 // Roo.each(items, function(item, k){
34522 // item.el.position('absolute');
34524 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34526 // item.el.setWidth(width);
34528 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34530 // item.el.setHeight(height);
34533 // item.el.setXY([x, y], isInstant ? false : true);
34535 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34538 // y = y + height + this.alternativePadWidth;
34540 // maxHeight = maxHeight + height + this.alternativePadWidth;
34544 // this.el.setHeight(maxHeight);
34548 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34550 var pos = this.el.getBox(true);
34555 var maxX = pos.right;
34557 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34559 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34561 Roo.each(queue, function(box, k){
34563 Roo.each(box, function(b, kk){
34565 b.el.position('absolute');
34567 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34568 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34570 if(b.size == 'md-left' || b.size == 'md-right'){
34571 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34572 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34575 b.el.setWidth(width);
34576 b.el.setHeight(height);
34584 var positions = [];
34586 switch (box.length){
34588 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34591 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34594 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34597 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34603 Roo.each(box, function(b,kk){
34605 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34607 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34615 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34617 Roo.each(eItems, function(b,k){
34619 b.size = (k == 0) ? 'sm' : 'xs';
34620 b.x = (k == 0) ? 2 : 1;
34621 b.y = (k == 0) ? 2 : 1;
34623 b.el.position('absolute');
34625 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34627 b.el.setWidth(width);
34629 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34631 b.el.setHeight(height);
34635 var positions = [];
34638 x : maxX - this.unitWidth * 2 - this.gutter,
34643 x : maxX - this.unitWidth,
34644 y : minY + (this.unitWidth + this.gutter) * 2
34648 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34652 Roo.each(eItems, function(b,k){
34654 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34660 getVerticalOneBoxColPositions : function(x, y, box)
34664 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34666 if(box[0].size == 'md-left'){
34670 if(box[0].size == 'md-right'){
34675 x : x + (this.unitWidth + this.gutter) * rand,
34682 getVerticalTwoBoxColPositions : function(x, y, box)
34686 if(box[0].size == 'xs'){
34690 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34694 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34708 x : x + (this.unitWidth + this.gutter) * 2,
34709 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34716 getVerticalThreeBoxColPositions : function(x, y, box)
34720 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34728 x : x + (this.unitWidth + this.gutter) * 1,
34733 x : x + (this.unitWidth + this.gutter) * 2,
34741 if(box[0].size == 'xs' && box[1].size == 'xs'){
34750 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34754 x : x + (this.unitWidth + this.gutter) * 1,
34768 x : x + (this.unitWidth + this.gutter) * 2,
34773 x : x + (this.unitWidth + this.gutter) * 2,
34774 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34781 getVerticalFourBoxColPositions : function(x, y, box)
34785 if(box[0].size == 'xs'){
34794 y : y + (this.unitHeight + this.gutter) * 1
34799 y : y + (this.unitHeight + this.gutter) * 2
34803 x : x + (this.unitWidth + this.gutter) * 1,
34817 x : x + (this.unitWidth + this.gutter) * 2,
34822 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34823 y : y + (this.unitHeight + this.gutter) * 1
34827 x : x + (this.unitWidth + this.gutter) * 2,
34828 y : y + (this.unitWidth + this.gutter) * 2
34835 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34839 if(box[0].size == 'md-left'){
34841 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34848 if(box[0].size == 'md-right'){
34850 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34851 y : minY + (this.unitWidth + this.gutter) * 1
34857 var rand = Math.floor(Math.random() * (4 - box[0].y));
34860 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34861 y : minY + (this.unitWidth + this.gutter) * rand
34868 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34872 if(box[0].size == 'xs'){
34875 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34880 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34881 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34889 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34894 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34895 y : minY + (this.unitWidth + this.gutter) * 2
34902 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34906 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34909 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34914 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34915 y : minY + (this.unitWidth + this.gutter) * 1
34919 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34920 y : minY + (this.unitWidth + this.gutter) * 2
34927 if(box[0].size == 'xs' && box[1].size == 'xs'){
34930 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34935 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34940 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34941 y : minY + (this.unitWidth + this.gutter) * 1
34949 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34954 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34955 y : minY + (this.unitWidth + this.gutter) * 2
34959 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34960 y : minY + (this.unitWidth + this.gutter) * 2
34967 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34971 if(box[0].size == 'xs'){
34974 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34979 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34984 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),
34989 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34990 y : minY + (this.unitWidth + this.gutter) * 1
34998 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35003 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35004 y : minY + (this.unitWidth + this.gutter) * 2
35008 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35009 y : minY + (this.unitWidth + this.gutter) * 2
35013 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),
35014 y : minY + (this.unitWidth + this.gutter) * 2
35022 * remove a Masonry Brick
35023 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35025 removeBrick : function(brick_id)
35031 for (var i = 0; i<this.bricks.length; i++) {
35032 if (this.bricks[i].id == brick_id) {
35033 this.bricks.splice(i,1);
35034 this.el.dom.removeChild(Roo.get(brick_id).dom);
35041 * adds a Masonry Brick
35042 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35044 addBrick : function(cfg)
35046 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35047 //this.register(cn);
35048 cn.parentId = this.id;
35049 cn.render(this.el);
35054 * register a Masonry Brick
35055 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35058 register : function(brick)
35060 this.bricks.push(brick);
35061 brick.masonryId = this.id;
35065 * clear all the Masonry Brick
35067 clearAll : function()
35070 //this.getChildContainer().dom.innerHTML = "";
35071 this.el.dom.innerHTML = '';
35074 getSelected : function()
35076 if (!this.selectedBrick) {
35080 return this.selectedBrick;
35084 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35088 * register a Masonry Layout
35089 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35092 register : function(layout)
35094 this.groups[layout.id] = layout;
35097 * fetch a Masonry Layout based on the masonry layout ID
35098 * @param {string} the masonry layout to add
35099 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35102 get: function(layout_id) {
35103 if (typeof(this.groups[layout_id]) == 'undefined') {
35106 return this.groups[layout_id] ;
35118 * http://masonry.desandro.com
35120 * The idea is to render all the bricks based on vertical width...
35122 * The original code extends 'outlayer' - we might need to use that....
35128 * @class Roo.bootstrap.LayoutMasonryAuto
35129 * @extends Roo.bootstrap.Component
35130 * Bootstrap Layout Masonry class
35133 * Create a new Element
35134 * @param {Object} config The config object
35137 Roo.bootstrap.LayoutMasonryAuto = function(config){
35138 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35141 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35144 * @cfg {Boolean} isFitWidth - resize the width..
35146 isFitWidth : false, // options..
35148 * @cfg {Boolean} isOriginLeft = left align?
35150 isOriginLeft : true,
35152 * @cfg {Boolean} isOriginTop = top align?
35154 isOriginTop : false,
35156 * @cfg {Boolean} isLayoutInstant = no animation?
35158 isLayoutInstant : false, // needed?
35160 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35162 isResizingContainer : true,
35164 * @cfg {Number} columnWidth width of the columns
35170 * @cfg {Number} maxCols maximum number of columns
35175 * @cfg {Number} padHeight padding below box..
35181 * @cfg {Boolean} isAutoInitial defalut true
35184 isAutoInitial : true,
35190 initialColumnWidth : 0,
35191 currentSize : null,
35193 colYs : null, // array.
35200 bricks: null, //CompositeElement
35201 cols : 0, // array?
35202 // element : null, // wrapped now this.el
35203 _isLayoutInited : null,
35206 getAutoCreate : function(){
35210 cls: 'blog-masonary-wrapper ' + this.cls,
35212 cls : 'mas-boxes masonary'
35219 getChildContainer: function( )
35221 if (this.boxesEl) {
35222 return this.boxesEl;
35225 this.boxesEl = this.el.select('.mas-boxes').first();
35227 return this.boxesEl;
35231 initEvents : function()
35235 if(this.isAutoInitial){
35236 Roo.log('hook children rendered');
35237 this.on('childrenrendered', function() {
35238 Roo.log('children rendered');
35245 initial : function()
35247 this.reloadItems();
35249 this.currentSize = this.el.getBox(true);
35251 /// was window resize... - let's see if this works..
35252 Roo.EventManager.onWindowResize(this.resize, this);
35254 if(!this.isAutoInitial){
35259 this.layout.defer(500,this);
35262 reloadItems: function()
35264 this.bricks = this.el.select('.masonry-brick', true);
35266 this.bricks.each(function(b) {
35267 //Roo.log(b.getSize());
35268 if (!b.attr('originalwidth')) {
35269 b.attr('originalwidth', b.getSize().width);
35274 Roo.log(this.bricks.elements.length);
35277 resize : function()
35280 var cs = this.el.getBox(true);
35282 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35283 Roo.log("no change in with or X");
35286 this.currentSize = cs;
35290 layout : function()
35293 this._resetLayout();
35294 //this._manageStamps();
35296 // don't animate first layout
35297 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35298 this.layoutItems( isInstant );
35300 // flag for initalized
35301 this._isLayoutInited = true;
35304 layoutItems : function( isInstant )
35306 //var items = this._getItemsForLayout( this.items );
35307 // original code supports filtering layout items.. we just ignore it..
35309 this._layoutItems( this.bricks , isInstant );
35311 this._postLayout();
35313 _layoutItems : function ( items , isInstant)
35315 //this.fireEvent( 'layout', this, items );
35318 if ( !items || !items.elements.length ) {
35319 // no items, emit event with empty array
35324 items.each(function(item) {
35325 Roo.log("layout item");
35327 // get x/y object from method
35328 var position = this._getItemLayoutPosition( item );
35330 position.item = item;
35331 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35332 queue.push( position );
35335 this._processLayoutQueue( queue );
35337 /** Sets position of item in DOM
35338 * @param {Element} item
35339 * @param {Number} x - horizontal position
35340 * @param {Number} y - vertical position
35341 * @param {Boolean} isInstant - disables transitions
35343 _processLayoutQueue : function( queue )
35345 for ( var i=0, len = queue.length; i < len; i++ ) {
35346 var obj = queue[i];
35347 obj.item.position('absolute');
35348 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35354 * Any logic you want to do after each layout,
35355 * i.e. size the container
35357 _postLayout : function()
35359 this.resizeContainer();
35362 resizeContainer : function()
35364 if ( !this.isResizingContainer ) {
35367 var size = this._getContainerSize();
35369 this.el.setSize(size.width,size.height);
35370 this.boxesEl.setSize(size.width,size.height);
35376 _resetLayout : function()
35378 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35379 this.colWidth = this.el.getWidth();
35380 //this.gutter = this.el.getWidth();
35382 this.measureColumns();
35388 this.colYs.push( 0 );
35394 measureColumns : function()
35396 this.getContainerWidth();
35397 // if columnWidth is 0, default to outerWidth of first item
35398 if ( !this.columnWidth ) {
35399 var firstItem = this.bricks.first();
35400 Roo.log(firstItem);
35401 this.columnWidth = this.containerWidth;
35402 if (firstItem && firstItem.attr('originalwidth') ) {
35403 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35405 // columnWidth fall back to item of first element
35406 Roo.log("set column width?");
35407 this.initialColumnWidth = this.columnWidth ;
35409 // if first elem has no width, default to size of container
35414 if (this.initialColumnWidth) {
35415 this.columnWidth = this.initialColumnWidth;
35420 // column width is fixed at the top - however if container width get's smaller we should
35423 // this bit calcs how man columns..
35425 var columnWidth = this.columnWidth += this.gutter;
35427 // calculate columns
35428 var containerWidth = this.containerWidth + this.gutter;
35430 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35431 // fix rounding errors, typically with gutters
35432 var excess = columnWidth - containerWidth % columnWidth;
35435 // if overshoot is less than a pixel, round up, otherwise floor it
35436 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35437 cols = Math[ mathMethod ]( cols );
35438 this.cols = Math.max( cols, 1 );
35439 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35441 // padding positioning..
35442 var totalColWidth = this.cols * this.columnWidth;
35443 var padavail = this.containerWidth - totalColWidth;
35444 // so for 2 columns - we need 3 'pads'
35446 var padNeeded = (1+this.cols) * this.padWidth;
35448 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35450 this.columnWidth += padExtra
35451 //this.padWidth = Math.floor(padavail / ( this.cols));
35453 // adjust colum width so that padding is fixed??
35455 // we have 3 columns ... total = width * 3
35456 // we have X left over... that should be used by
35458 //if (this.expandC) {
35466 getContainerWidth : function()
35468 /* // container is parent if fit width
35469 var container = this.isFitWidth ? this.element.parentNode : this.element;
35470 // check that this.size and size are there
35471 // IE8 triggers resize on body size change, so they might not be
35473 var size = getSize( container ); //FIXME
35474 this.containerWidth = size && size.innerWidth; //FIXME
35477 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35481 _getItemLayoutPosition : function( item ) // what is item?
35483 // we resize the item to our columnWidth..
35485 item.setWidth(this.columnWidth);
35486 item.autoBoxAdjust = false;
35488 var sz = item.getSize();
35490 // how many columns does this brick span
35491 var remainder = this.containerWidth % this.columnWidth;
35493 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35494 // round if off by 1 pixel, otherwise use ceil
35495 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35496 colSpan = Math.min( colSpan, this.cols );
35498 // normally this should be '1' as we dont' currently allow multi width columns..
35500 var colGroup = this._getColGroup( colSpan );
35501 // get the minimum Y value from the columns
35502 var minimumY = Math.min.apply( Math, colGroup );
35503 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35505 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35507 // position the brick
35509 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35510 y: this.currentSize.y + minimumY + this.padHeight
35514 // apply setHeight to necessary columns
35515 var setHeight = minimumY + sz.height + this.padHeight;
35516 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35518 var setSpan = this.cols + 1 - colGroup.length;
35519 for ( var i = 0; i < setSpan; i++ ) {
35520 this.colYs[ shortColIndex + i ] = setHeight ;
35527 * @param {Number} colSpan - number of columns the element spans
35528 * @returns {Array} colGroup
35530 _getColGroup : function( colSpan )
35532 if ( colSpan < 2 ) {
35533 // if brick spans only one column, use all the column Ys
35538 // how many different places could this brick fit horizontally
35539 var groupCount = this.cols + 1 - colSpan;
35540 // for each group potential horizontal position
35541 for ( var i = 0; i < groupCount; i++ ) {
35542 // make an array of colY values for that one group
35543 var groupColYs = this.colYs.slice( i, i + colSpan );
35544 // and get the max value of the array
35545 colGroup[i] = Math.max.apply( Math, groupColYs );
35550 _manageStamp : function( stamp )
35552 var stampSize = stamp.getSize();
35553 var offset = stamp.getBox();
35554 // get the columns that this stamp affects
35555 var firstX = this.isOriginLeft ? offset.x : offset.right;
35556 var lastX = firstX + stampSize.width;
35557 var firstCol = Math.floor( firstX / this.columnWidth );
35558 firstCol = Math.max( 0, firstCol );
35560 var lastCol = Math.floor( lastX / this.columnWidth );
35561 // lastCol should not go over if multiple of columnWidth #425
35562 lastCol -= lastX % this.columnWidth ? 0 : 1;
35563 lastCol = Math.min( this.cols - 1, lastCol );
35565 // set colYs to bottom of the stamp
35566 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35569 for ( var i = firstCol; i <= lastCol; i++ ) {
35570 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35575 _getContainerSize : function()
35577 this.maxY = Math.max.apply( Math, this.colYs );
35582 if ( this.isFitWidth ) {
35583 size.width = this._getContainerFitWidth();
35589 _getContainerFitWidth : function()
35591 var unusedCols = 0;
35592 // count unused columns
35595 if ( this.colYs[i] !== 0 ) {
35600 // fit container to columns that have been used
35601 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35604 needsResizeLayout : function()
35606 var previousWidth = this.containerWidth;
35607 this.getContainerWidth();
35608 return previousWidth !== this.containerWidth;
35623 * @class Roo.bootstrap.MasonryBrick
35624 * @extends Roo.bootstrap.Component
35625 * Bootstrap MasonryBrick class
35628 * Create a new MasonryBrick
35629 * @param {Object} config The config object
35632 Roo.bootstrap.MasonryBrick = function(config){
35634 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35636 Roo.bootstrap.MasonryBrick.register(this);
35642 * When a MasonryBrick is clcik
35643 * @param {Roo.bootstrap.MasonryBrick} this
35644 * @param {Roo.EventObject} e
35650 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35653 * @cfg {String} title
35657 * @cfg {String} html
35661 * @cfg {String} bgimage
35665 * @cfg {String} videourl
35669 * @cfg {String} cls
35673 * @cfg {String} href
35677 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35682 * @cfg {String} placetitle (center|bottom)
35687 * @cfg {Boolean} isFitContainer defalut true
35689 isFitContainer : true,
35692 * @cfg {Boolean} preventDefault defalut false
35694 preventDefault : false,
35697 * @cfg {Boolean} inverse defalut false
35699 maskInverse : false,
35701 getAutoCreate : function()
35703 if(!this.isFitContainer){
35704 return this.getSplitAutoCreate();
35707 var cls = 'masonry-brick masonry-brick-full';
35709 if(this.href.length){
35710 cls += ' masonry-brick-link';
35713 if(this.bgimage.length){
35714 cls += ' masonry-brick-image';
35717 if(this.maskInverse){
35718 cls += ' mask-inverse';
35721 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35722 cls += ' enable-mask';
35726 cls += ' masonry-' + this.size + '-brick';
35729 if(this.placetitle.length){
35731 switch (this.placetitle) {
35733 cls += ' masonry-center-title';
35736 cls += ' masonry-bottom-title';
35743 if(!this.html.length && !this.bgimage.length){
35744 cls += ' masonry-center-title';
35747 if(!this.html.length && this.bgimage.length){
35748 cls += ' masonry-bottom-title';
35753 cls += ' ' + this.cls;
35757 tag: (this.href.length) ? 'a' : 'div',
35762 cls: 'masonry-brick-mask'
35766 cls: 'masonry-brick-paragraph',
35772 if(this.href.length){
35773 cfg.href = this.href;
35776 var cn = cfg.cn[1].cn;
35778 if(this.title.length){
35781 cls: 'masonry-brick-title',
35786 if(this.html.length){
35789 cls: 'masonry-brick-text',
35794 if (!this.title.length && !this.html.length) {
35795 cfg.cn[1].cls += ' hide';
35798 if(this.bgimage.length){
35801 cls: 'masonry-brick-image-view',
35806 if(this.videourl.length){
35807 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35808 // youtube support only?
35811 cls: 'masonry-brick-image-view',
35814 allowfullscreen : true
35822 getSplitAutoCreate : function()
35824 var cls = 'masonry-brick masonry-brick-split';
35826 if(this.href.length){
35827 cls += ' masonry-brick-link';
35830 if(this.bgimage.length){
35831 cls += ' masonry-brick-image';
35835 cls += ' masonry-' + this.size + '-brick';
35838 switch (this.placetitle) {
35840 cls += ' masonry-center-title';
35843 cls += ' masonry-bottom-title';
35846 if(!this.bgimage.length){
35847 cls += ' masonry-center-title';
35850 if(this.bgimage.length){
35851 cls += ' masonry-bottom-title';
35857 cls += ' ' + this.cls;
35861 tag: (this.href.length) ? 'a' : 'div',
35866 cls: 'masonry-brick-split-head',
35870 cls: 'masonry-brick-paragraph',
35877 cls: 'masonry-brick-split-body',
35883 if(this.href.length){
35884 cfg.href = this.href;
35887 if(this.title.length){
35888 cfg.cn[0].cn[0].cn.push({
35890 cls: 'masonry-brick-title',
35895 if(this.html.length){
35896 cfg.cn[1].cn.push({
35898 cls: 'masonry-brick-text',
35903 if(this.bgimage.length){
35904 cfg.cn[0].cn.push({
35906 cls: 'masonry-brick-image-view',
35911 if(this.videourl.length){
35912 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35913 // youtube support only?
35914 cfg.cn[0].cn.cn.push({
35916 cls: 'masonry-brick-image-view',
35919 allowfullscreen : true
35926 initEvents: function()
35928 switch (this.size) {
35961 this.el.on('touchstart', this.onTouchStart, this);
35962 this.el.on('touchmove', this.onTouchMove, this);
35963 this.el.on('touchend', this.onTouchEnd, this);
35964 this.el.on('contextmenu', this.onContextMenu, this);
35966 this.el.on('mouseenter' ,this.enter, this);
35967 this.el.on('mouseleave', this.leave, this);
35968 this.el.on('click', this.onClick, this);
35971 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35972 this.parent().bricks.push(this);
35977 onClick: function(e, el)
35979 var time = this.endTimer - this.startTimer;
35980 // Roo.log(e.preventDefault());
35983 e.preventDefault();
35988 if(!this.preventDefault){
35992 e.preventDefault();
35994 if (this.activeClass != '') {
35995 this.selectBrick();
35998 this.fireEvent('click', this, e);
36001 enter: function(e, el)
36003 e.preventDefault();
36005 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36009 if(this.bgimage.length && this.html.length){
36010 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36014 leave: function(e, el)
36016 e.preventDefault();
36018 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36022 if(this.bgimage.length && this.html.length){
36023 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36027 onTouchStart: function(e, el)
36029 // e.preventDefault();
36031 this.touchmoved = false;
36033 if(!this.isFitContainer){
36037 if(!this.bgimage.length || !this.html.length){
36041 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36043 this.timer = new Date().getTime();
36047 onTouchMove: function(e, el)
36049 this.touchmoved = true;
36052 onContextMenu : function(e,el)
36054 e.preventDefault();
36055 e.stopPropagation();
36059 onTouchEnd: function(e, el)
36061 // e.preventDefault();
36063 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36070 if(!this.bgimage.length || !this.html.length){
36072 if(this.href.length){
36073 window.location.href = this.href;
36079 if(!this.isFitContainer){
36083 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36085 window.location.href = this.href;
36088 //selection on single brick only
36089 selectBrick : function() {
36091 if (!this.parentId) {
36095 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36096 var index = m.selectedBrick.indexOf(this.id);
36099 m.selectedBrick.splice(index,1);
36100 this.el.removeClass(this.activeClass);
36104 for(var i = 0; i < m.selectedBrick.length; i++) {
36105 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36106 b.el.removeClass(b.activeClass);
36109 m.selectedBrick = [];
36111 m.selectedBrick.push(this.id);
36112 this.el.addClass(this.activeClass);
36116 isSelected : function(){
36117 return this.el.hasClass(this.activeClass);
36122 Roo.apply(Roo.bootstrap.MasonryBrick, {
36125 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36127 * register a Masonry Brick
36128 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36131 register : function(brick)
36133 //this.groups[brick.id] = brick;
36134 this.groups.add(brick.id, brick);
36137 * fetch a masonry brick based on the masonry brick ID
36138 * @param {string} the masonry brick to add
36139 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36142 get: function(brick_id)
36144 // if (typeof(this.groups[brick_id]) == 'undefined') {
36147 // return this.groups[brick_id] ;
36149 if(this.groups.key(brick_id)) {
36150 return this.groups.key(brick_id);
36168 * @class Roo.bootstrap.Brick
36169 * @extends Roo.bootstrap.Component
36170 * Bootstrap Brick class
36173 * Create a new Brick
36174 * @param {Object} config The config object
36177 Roo.bootstrap.Brick = function(config){
36178 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36184 * When a Brick is click
36185 * @param {Roo.bootstrap.Brick} this
36186 * @param {Roo.EventObject} e
36192 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36195 * @cfg {String} title
36199 * @cfg {String} html
36203 * @cfg {String} bgimage
36207 * @cfg {String} cls
36211 * @cfg {String} href
36215 * @cfg {String} video
36219 * @cfg {Boolean} square
36223 getAutoCreate : function()
36225 var cls = 'roo-brick';
36227 if(this.href.length){
36228 cls += ' roo-brick-link';
36231 if(this.bgimage.length){
36232 cls += ' roo-brick-image';
36235 if(!this.html.length && !this.bgimage.length){
36236 cls += ' roo-brick-center-title';
36239 if(!this.html.length && this.bgimage.length){
36240 cls += ' roo-brick-bottom-title';
36244 cls += ' ' + this.cls;
36248 tag: (this.href.length) ? 'a' : 'div',
36253 cls: 'roo-brick-paragraph',
36259 if(this.href.length){
36260 cfg.href = this.href;
36263 var cn = cfg.cn[0].cn;
36265 if(this.title.length){
36268 cls: 'roo-brick-title',
36273 if(this.html.length){
36276 cls: 'roo-brick-text',
36283 if(this.bgimage.length){
36286 cls: 'roo-brick-image-view',
36294 initEvents: function()
36296 if(this.title.length || this.html.length){
36297 this.el.on('mouseenter' ,this.enter, this);
36298 this.el.on('mouseleave', this.leave, this);
36301 Roo.EventManager.onWindowResize(this.resize, this);
36303 if(this.bgimage.length){
36304 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36305 this.imageEl.on('load', this.onImageLoad, this);
36312 onImageLoad : function()
36317 resize : function()
36319 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36321 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36323 if(this.bgimage.length){
36324 var image = this.el.select('.roo-brick-image-view', true).first();
36326 image.setWidth(paragraph.getWidth());
36329 image.setHeight(paragraph.getWidth());
36332 this.el.setHeight(image.getHeight());
36333 paragraph.setHeight(image.getHeight());
36339 enter: function(e, el)
36341 e.preventDefault();
36343 if(this.bgimage.length){
36344 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36345 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36349 leave: function(e, el)
36351 e.preventDefault();
36353 if(this.bgimage.length){
36354 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36355 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36370 * @class Roo.bootstrap.NumberField
36371 * @extends Roo.bootstrap.Input
36372 * Bootstrap NumberField class
36378 * Create a new NumberField
36379 * @param {Object} config The config object
36382 Roo.bootstrap.NumberField = function(config){
36383 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36386 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36389 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36391 allowDecimals : true,
36393 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36395 decimalSeparator : ".",
36397 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36399 decimalPrecision : 2,
36401 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36403 allowNegative : true,
36406 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36410 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36412 minValue : Number.NEGATIVE_INFINITY,
36414 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36416 maxValue : Number.MAX_VALUE,
36418 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36420 minText : "The minimum value for this field is {0}",
36422 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36424 maxText : "The maximum value for this field is {0}",
36426 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36427 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36429 nanText : "{0} is not a valid number",
36431 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36433 thousandsDelimiter : false,
36435 * @cfg {String} valueAlign alignment of value
36437 valueAlign : "left",
36439 getAutoCreate : function()
36441 var hiddenInput = {
36445 cls: 'hidden-number-input'
36449 hiddenInput.name = this.name;
36454 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36456 this.name = hiddenInput.name;
36458 if(cfg.cn.length > 0) {
36459 cfg.cn.push(hiddenInput);
36466 initEvents : function()
36468 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36470 var allowed = "0123456789";
36472 if(this.allowDecimals){
36473 allowed += this.decimalSeparator;
36476 if(this.allowNegative){
36480 if(this.thousandsDelimiter) {
36484 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36486 var keyPress = function(e){
36488 var k = e.getKey();
36490 var c = e.getCharCode();
36493 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36494 allowed.indexOf(String.fromCharCode(c)) === -1
36500 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36504 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36509 this.el.on("keypress", keyPress, this);
36512 validateValue : function(value)
36515 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36519 var num = this.parseValue(value);
36522 this.markInvalid(String.format(this.nanText, value));
36526 if(num < this.minValue){
36527 this.markInvalid(String.format(this.minText, this.minValue));
36531 if(num > this.maxValue){
36532 this.markInvalid(String.format(this.maxText, this.maxValue));
36539 getValue : function()
36541 var v = this.hiddenEl().getValue();
36543 return this.fixPrecision(this.parseValue(v));
36546 parseValue : function(value)
36548 if(this.thousandsDelimiter) {
36550 r = new RegExp(",", "g");
36551 value = value.replace(r, "");
36554 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36555 return isNaN(value) ? '' : value;
36558 fixPrecision : function(value)
36560 if(this.thousandsDelimiter) {
36562 r = new RegExp(",", "g");
36563 value = value.replace(r, "");
36566 var nan = isNaN(value);
36568 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36569 return nan ? '' : value;
36571 return parseFloat(value).toFixed(this.decimalPrecision);
36574 setValue : function(v)
36576 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36582 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36584 this.inputEl().dom.value = (v == '') ? '' :
36585 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36587 if(!this.allowZero && v === '0') {
36588 this.hiddenEl().dom.value = '';
36589 this.inputEl().dom.value = '';
36596 decimalPrecisionFcn : function(v)
36598 return Math.floor(v);
36601 beforeBlur : function()
36603 var v = this.parseValue(this.getRawValue());
36605 if(v || v === 0 || v === ''){
36610 hiddenEl : function()
36612 return this.el.select('input.hidden-number-input',true).first();
36624 * @class Roo.bootstrap.DocumentSlider
36625 * @extends Roo.bootstrap.Component
36626 * Bootstrap DocumentSlider class
36629 * Create a new DocumentViewer
36630 * @param {Object} config The config object
36633 Roo.bootstrap.DocumentSlider = function(config){
36634 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36641 * Fire after initEvent
36642 * @param {Roo.bootstrap.DocumentSlider} this
36647 * Fire after update
36648 * @param {Roo.bootstrap.DocumentSlider} this
36654 * @param {Roo.bootstrap.DocumentSlider} this
36660 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36666 getAutoCreate : function()
36670 cls : 'roo-document-slider',
36674 cls : 'roo-document-slider-header',
36678 cls : 'roo-document-slider-header-title'
36684 cls : 'roo-document-slider-body',
36688 cls : 'roo-document-slider-prev',
36692 cls : 'fa fa-chevron-left'
36698 cls : 'roo-document-slider-thumb',
36702 cls : 'roo-document-slider-image'
36708 cls : 'roo-document-slider-next',
36712 cls : 'fa fa-chevron-right'
36724 initEvents : function()
36726 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36727 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36729 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36730 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36732 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36733 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36735 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36736 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36738 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36739 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36741 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36742 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36744 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36745 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36747 this.thumbEl.on('click', this.onClick, this);
36749 this.prevIndicator.on('click', this.prev, this);
36751 this.nextIndicator.on('click', this.next, this);
36755 initial : function()
36757 if(this.files.length){
36758 this.indicator = 1;
36762 this.fireEvent('initial', this);
36765 update : function()
36767 this.imageEl.attr('src', this.files[this.indicator - 1]);
36769 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36771 this.prevIndicator.show();
36773 if(this.indicator == 1){
36774 this.prevIndicator.hide();
36777 this.nextIndicator.show();
36779 if(this.indicator == this.files.length){
36780 this.nextIndicator.hide();
36783 this.thumbEl.scrollTo('top');
36785 this.fireEvent('update', this);
36788 onClick : function(e)
36790 e.preventDefault();
36792 this.fireEvent('click', this);
36797 e.preventDefault();
36799 this.indicator = Math.max(1, this.indicator - 1);
36806 e.preventDefault();
36808 this.indicator = Math.min(this.files.length, this.indicator + 1);
36822 * @class Roo.bootstrap.RadioSet
36823 * @extends Roo.bootstrap.Input
36824 * Bootstrap RadioSet class
36825 * @cfg {String} indicatorpos (left|right) default left
36826 * @cfg {Boolean} inline (true|false) inline the element (default true)
36827 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36829 * Create a new RadioSet
36830 * @param {Object} config The config object
36833 Roo.bootstrap.RadioSet = function(config){
36835 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36839 Roo.bootstrap.RadioSet.register(this);
36844 * Fires when the element is checked or unchecked.
36845 * @param {Roo.bootstrap.RadioSet} this This radio
36846 * @param {Roo.bootstrap.Radio} item The checked item
36851 * Fires when the element is click.
36852 * @param {Roo.bootstrap.RadioSet} this This radio set
36853 * @param {Roo.bootstrap.Radio} item The checked item
36854 * @param {Roo.EventObject} e The event object
36861 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36869 indicatorpos : 'left',
36871 getAutoCreate : function()
36875 cls : 'roo-radio-set-label',
36879 html : this.fieldLabel
36883 if (Roo.bootstrap.version == 3) {
36886 if(this.indicatorpos == 'left'){
36889 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36890 tooltip : 'This field is required'
36895 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36896 tooltip : 'This field is required'
36902 cls : 'roo-radio-set-items'
36905 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36907 if (align === 'left' && this.fieldLabel.length) {
36910 cls : "roo-radio-set-right",
36916 if(this.labelWidth > 12){
36917 label.style = "width: " + this.labelWidth + 'px';
36920 if(this.labelWidth < 13 && this.labelmd == 0){
36921 this.labelmd = this.labelWidth;
36924 if(this.labellg > 0){
36925 label.cls += ' col-lg-' + this.labellg;
36926 items.cls += ' col-lg-' + (12 - this.labellg);
36929 if(this.labelmd > 0){
36930 label.cls += ' col-md-' + this.labelmd;
36931 items.cls += ' col-md-' + (12 - this.labelmd);
36934 if(this.labelsm > 0){
36935 label.cls += ' col-sm-' + this.labelsm;
36936 items.cls += ' col-sm-' + (12 - this.labelsm);
36939 if(this.labelxs > 0){
36940 label.cls += ' col-xs-' + this.labelxs;
36941 items.cls += ' col-xs-' + (12 - this.labelxs);
36947 cls : 'roo-radio-set',
36951 cls : 'roo-radio-set-input',
36954 value : this.value ? this.value : ''
36961 if(this.weight.length){
36962 cfg.cls += ' roo-radio-' + this.weight;
36966 cfg.cls += ' roo-radio-set-inline';
36970 ['xs','sm','md','lg'].map(function(size){
36971 if (settings[size]) {
36972 cfg.cls += ' col-' + size + '-' + settings[size];
36980 initEvents : function()
36982 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36983 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36985 if(!this.fieldLabel.length){
36986 this.labelEl.hide();
36989 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36990 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36992 this.indicator = this.indicatorEl();
36994 if(this.indicator){
36995 this.indicator.addClass('invisible');
36998 this.originalValue = this.getValue();
37002 inputEl: function ()
37004 return this.el.select('.roo-radio-set-input', true).first();
37007 getChildContainer : function()
37009 return this.itemsEl;
37012 register : function(item)
37014 this.radioes.push(item);
37018 validate : function()
37020 if(this.getVisibilityEl().hasClass('hidden')){
37026 Roo.each(this.radioes, function(i){
37035 if(this.allowBlank) {
37039 if(this.disabled || valid){
37044 this.markInvalid();
37049 markValid : function()
37051 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37052 this.indicatorEl().removeClass('visible');
37053 this.indicatorEl().addClass('invisible');
37057 if (Roo.bootstrap.version == 3) {
37058 this.el.removeClass([this.invalidClass, this.validClass]);
37059 this.el.addClass(this.validClass);
37061 this.el.removeClass(['is-invalid','is-valid']);
37062 this.el.addClass(['is-valid']);
37064 this.fireEvent('valid', this);
37067 markInvalid : function(msg)
37069 if(this.allowBlank || this.disabled){
37073 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37074 this.indicatorEl().removeClass('invisible');
37075 this.indicatorEl().addClass('visible');
37077 if (Roo.bootstrap.version == 3) {
37078 this.el.removeClass([this.invalidClass, this.validClass]);
37079 this.el.addClass(this.invalidClass);
37081 this.el.removeClass(['is-invalid','is-valid']);
37082 this.el.addClass(['is-invalid']);
37085 this.fireEvent('invalid', this, msg);
37089 setValue : function(v, suppressEvent)
37091 if(this.value === v){
37098 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37101 Roo.each(this.radioes, function(i){
37103 i.el.removeClass('checked');
37106 Roo.each(this.radioes, function(i){
37108 if(i.value === v || i.value.toString() === v.toString()){
37110 i.el.addClass('checked');
37112 if(suppressEvent !== true){
37113 this.fireEvent('check', this, i);
37124 clearInvalid : function(){
37126 if(!this.el || this.preventMark){
37130 this.el.removeClass([this.invalidClass]);
37132 this.fireEvent('valid', this);
37137 Roo.apply(Roo.bootstrap.RadioSet, {
37141 register : function(set)
37143 this.groups[set.name] = set;
37146 get: function(name)
37148 if (typeof(this.groups[name]) == 'undefined') {
37152 return this.groups[name] ;
37158 * Ext JS Library 1.1.1
37159 * Copyright(c) 2006-2007, Ext JS, LLC.
37161 * Originally Released Under LGPL - original licence link has changed is not relivant.
37164 * <script type="text/javascript">
37169 * @class Roo.bootstrap.SplitBar
37170 * @extends Roo.util.Observable
37171 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37175 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37176 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37177 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37178 split.minSize = 100;
37179 split.maxSize = 600;
37180 split.animate = true;
37181 split.on('moved', splitterMoved);
37184 * Create a new SplitBar
37185 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37186 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37187 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37188 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37189 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37190 position of the SplitBar).
37192 Roo.bootstrap.SplitBar = function(cfg){
37197 // dragElement : elm
37198 // resizingElement: el,
37200 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37201 // placement : Roo.bootstrap.SplitBar.LEFT ,
37202 // existingProxy ???
37205 this.el = Roo.get(cfg.dragElement, true);
37206 this.el.dom.unselectable = "on";
37208 this.resizingEl = Roo.get(cfg.resizingElement, true);
37212 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37213 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37216 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37219 * The minimum size of the resizing element. (Defaults to 0)
37225 * The maximum size of the resizing element. (Defaults to 2000)
37228 this.maxSize = 2000;
37231 * Whether to animate the transition to the new size
37234 this.animate = false;
37237 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37240 this.useShim = false;
37245 if(!cfg.existingProxy){
37247 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37249 this.proxy = Roo.get(cfg.existingProxy).dom;
37252 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37255 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37258 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37261 this.dragSpecs = {};
37264 * @private The adapter to use to positon and resize elements
37266 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37267 this.adapter.init(this);
37269 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37271 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37272 this.el.addClass("roo-splitbar-h");
37275 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37276 this.el.addClass("roo-splitbar-v");
37282 * Fires when the splitter is moved (alias for {@link #event-moved})
37283 * @param {Roo.bootstrap.SplitBar} this
37284 * @param {Number} newSize the new width or height
37289 * Fires when the splitter is moved
37290 * @param {Roo.bootstrap.SplitBar} this
37291 * @param {Number} newSize the new width or height
37295 * @event beforeresize
37296 * Fires before the splitter is dragged
37297 * @param {Roo.bootstrap.SplitBar} this
37299 "beforeresize" : true,
37301 "beforeapply" : true
37304 Roo.util.Observable.call(this);
37307 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37308 onStartProxyDrag : function(x, y){
37309 this.fireEvent("beforeresize", this);
37311 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37313 o.enableDisplayMode("block");
37314 // all splitbars share the same overlay
37315 Roo.bootstrap.SplitBar.prototype.overlay = o;
37317 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37318 this.overlay.show();
37319 Roo.get(this.proxy).setDisplayed("block");
37320 var size = this.adapter.getElementSize(this);
37321 this.activeMinSize = this.getMinimumSize();;
37322 this.activeMaxSize = this.getMaximumSize();;
37323 var c1 = size - this.activeMinSize;
37324 var c2 = Math.max(this.activeMaxSize - size, 0);
37325 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37326 this.dd.resetConstraints();
37327 this.dd.setXConstraint(
37328 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37329 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37331 this.dd.setYConstraint(0, 0);
37333 this.dd.resetConstraints();
37334 this.dd.setXConstraint(0, 0);
37335 this.dd.setYConstraint(
37336 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37337 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37340 this.dragSpecs.startSize = size;
37341 this.dragSpecs.startPoint = [x, y];
37342 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37346 * @private Called after the drag operation by the DDProxy
37348 onEndProxyDrag : function(e){
37349 Roo.get(this.proxy).setDisplayed(false);
37350 var endPoint = Roo.lib.Event.getXY(e);
37352 this.overlay.hide();
37355 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37356 newSize = this.dragSpecs.startSize +
37357 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37358 endPoint[0] - this.dragSpecs.startPoint[0] :
37359 this.dragSpecs.startPoint[0] - endPoint[0]
37362 newSize = this.dragSpecs.startSize +
37363 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37364 endPoint[1] - this.dragSpecs.startPoint[1] :
37365 this.dragSpecs.startPoint[1] - endPoint[1]
37368 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37369 if(newSize != this.dragSpecs.startSize){
37370 if(this.fireEvent('beforeapply', this, newSize) !== false){
37371 this.adapter.setElementSize(this, newSize);
37372 this.fireEvent("moved", this, newSize);
37373 this.fireEvent("resize", this, newSize);
37379 * Get the adapter this SplitBar uses
37380 * @return The adapter object
37382 getAdapter : function(){
37383 return this.adapter;
37387 * Set the adapter this SplitBar uses
37388 * @param {Object} adapter A SplitBar adapter object
37390 setAdapter : function(adapter){
37391 this.adapter = adapter;
37392 this.adapter.init(this);
37396 * Gets the minimum size for the resizing element
37397 * @return {Number} The minimum size
37399 getMinimumSize : function(){
37400 return this.minSize;
37404 * Sets the minimum size for the resizing element
37405 * @param {Number} minSize The minimum size
37407 setMinimumSize : function(minSize){
37408 this.minSize = minSize;
37412 * Gets the maximum size for the resizing element
37413 * @return {Number} The maximum size
37415 getMaximumSize : function(){
37416 return this.maxSize;
37420 * Sets the maximum size for the resizing element
37421 * @param {Number} maxSize The maximum size
37423 setMaximumSize : function(maxSize){
37424 this.maxSize = maxSize;
37428 * Sets the initialize size for the resizing element
37429 * @param {Number} size The initial size
37431 setCurrentSize : function(size){
37432 var oldAnimate = this.animate;
37433 this.animate = false;
37434 this.adapter.setElementSize(this, size);
37435 this.animate = oldAnimate;
37439 * Destroy this splitbar.
37440 * @param {Boolean} removeEl True to remove the element
37442 destroy : function(removeEl){
37444 this.shim.remove();
37447 this.proxy.parentNode.removeChild(this.proxy);
37455 * @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.
37457 Roo.bootstrap.SplitBar.createProxy = function(dir){
37458 var proxy = new Roo.Element(document.createElement("div"));
37459 proxy.unselectable();
37460 var cls = 'roo-splitbar-proxy';
37461 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37462 document.body.appendChild(proxy.dom);
37467 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37468 * Default Adapter. It assumes the splitter and resizing element are not positioned
37469 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37471 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37474 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37475 // do nothing for now
37476 init : function(s){
37480 * Called before drag operations to get the current size of the resizing element.
37481 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37483 getElementSize : function(s){
37484 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37485 return s.resizingEl.getWidth();
37487 return s.resizingEl.getHeight();
37492 * Called after drag operations to set the size of the resizing element.
37493 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37494 * @param {Number} newSize The new size to set
37495 * @param {Function} onComplete A function to be invoked when resizing is complete
37497 setElementSize : function(s, newSize, onComplete){
37498 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37500 s.resizingEl.setWidth(newSize);
37502 onComplete(s, newSize);
37505 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37510 s.resizingEl.setHeight(newSize);
37512 onComplete(s, newSize);
37515 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37522 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37523 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37524 * Adapter that moves the splitter element to align with the resized sizing element.
37525 * Used with an absolute positioned SplitBar.
37526 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37527 * document.body, make sure you assign an id to the body element.
37529 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37530 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37531 this.container = Roo.get(container);
37534 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37535 init : function(s){
37536 this.basic.init(s);
37539 getElementSize : function(s){
37540 return this.basic.getElementSize(s);
37543 setElementSize : function(s, newSize, onComplete){
37544 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37547 moveSplitter : function(s){
37548 var yes = Roo.bootstrap.SplitBar;
37549 switch(s.placement){
37551 s.el.setX(s.resizingEl.getRight());
37554 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37557 s.el.setY(s.resizingEl.getBottom());
37560 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37567 * Orientation constant - Create a vertical SplitBar
37571 Roo.bootstrap.SplitBar.VERTICAL = 1;
37574 * Orientation constant - Create a horizontal SplitBar
37578 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37581 * Placement constant - The resizing element is to the left of the splitter element
37585 Roo.bootstrap.SplitBar.LEFT = 1;
37588 * Placement constant - The resizing element is to the right of the splitter element
37592 Roo.bootstrap.SplitBar.RIGHT = 2;
37595 * Placement constant - The resizing element is positioned above the splitter element
37599 Roo.bootstrap.SplitBar.TOP = 3;
37602 * Placement constant - The resizing element is positioned under splitter element
37606 Roo.bootstrap.SplitBar.BOTTOM = 4;
37607 Roo.namespace("Roo.bootstrap.layout");/*
37609 * Ext JS Library 1.1.1
37610 * Copyright(c) 2006-2007, Ext JS, LLC.
37612 * Originally Released Under LGPL - original licence link has changed is not relivant.
37615 * <script type="text/javascript">
37619 * @class Roo.bootstrap.layout.Manager
37620 * @extends Roo.bootstrap.Component
37621 * Base class for layout managers.
37623 Roo.bootstrap.layout.Manager = function(config)
37625 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37631 /** false to disable window resize monitoring @type Boolean */
37632 this.monitorWindowResize = true;
37637 * Fires when a layout is performed.
37638 * @param {Roo.LayoutManager} this
37642 * @event regionresized
37643 * Fires when the user resizes a region.
37644 * @param {Roo.LayoutRegion} region The resized region
37645 * @param {Number} newSize The new size (width for east/west, height for north/south)
37647 "regionresized" : true,
37649 * @event regioncollapsed
37650 * Fires when a region is collapsed.
37651 * @param {Roo.LayoutRegion} region The collapsed region
37653 "regioncollapsed" : true,
37655 * @event regionexpanded
37656 * Fires when a region is expanded.
37657 * @param {Roo.LayoutRegion} region The expanded region
37659 "regionexpanded" : true
37661 this.updating = false;
37664 this.el = Roo.get(config.el);
37670 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37675 monitorWindowResize : true,
37681 onRender : function(ct, position)
37684 this.el = Roo.get(ct);
37687 //this.fireEvent('render',this);
37691 initEvents: function()
37695 // ie scrollbar fix
37696 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37697 document.body.scroll = "no";
37698 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37699 this.el.position('relative');
37701 this.id = this.el.id;
37702 this.el.addClass("roo-layout-container");
37703 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37704 if(this.el.dom != document.body ) {
37705 this.el.on('resize', this.layout,this);
37706 this.el.on('show', this.layout,this);
37712 * Returns true if this layout is currently being updated
37713 * @return {Boolean}
37715 isUpdating : function(){
37716 return this.updating;
37720 * Suspend the LayoutManager from doing auto-layouts while
37721 * making multiple add or remove calls
37723 beginUpdate : function(){
37724 this.updating = true;
37728 * Restore auto-layouts and optionally disable the manager from performing a layout
37729 * @param {Boolean} noLayout true to disable a layout update
37731 endUpdate : function(noLayout){
37732 this.updating = false;
37738 layout: function(){
37742 onRegionResized : function(region, newSize){
37743 this.fireEvent("regionresized", region, newSize);
37747 onRegionCollapsed : function(region){
37748 this.fireEvent("regioncollapsed", region);
37751 onRegionExpanded : function(region){
37752 this.fireEvent("regionexpanded", region);
37756 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37757 * performs box-model adjustments.
37758 * @return {Object} The size as an object {width: (the width), height: (the height)}
37760 getViewSize : function()
37763 if(this.el.dom != document.body){
37764 size = this.el.getSize();
37766 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37768 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37769 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37774 * Returns the Element this layout is bound to.
37775 * @return {Roo.Element}
37777 getEl : function(){
37782 * Returns the specified region.
37783 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37784 * @return {Roo.LayoutRegion}
37786 getRegion : function(target){
37787 return this.regions[target.toLowerCase()];
37790 onWindowResize : function(){
37791 if(this.monitorWindowResize){
37798 * Ext JS Library 1.1.1
37799 * Copyright(c) 2006-2007, Ext JS, LLC.
37801 * Originally Released Under LGPL - original licence link has changed is not relivant.
37804 * <script type="text/javascript">
37807 * @class Roo.bootstrap.layout.Border
37808 * @extends Roo.bootstrap.layout.Manager
37809 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37810 * please see: examples/bootstrap/nested.html<br><br>
37812 <b>The container the layout is rendered into can be either the body element or any other element.
37813 If it is not the body element, the container needs to either be an absolute positioned element,
37814 or you will need to add "position:relative" to the css of the container. You will also need to specify
37815 the container size if it is not the body element.</b>
37818 * Create a new Border
37819 * @param {Object} config Configuration options
37821 Roo.bootstrap.layout.Border = function(config){
37822 config = config || {};
37823 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37827 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37828 if(config[region]){
37829 config[region].region = region;
37830 this.addRegion(config[region]);
37836 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37838 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37840 parent : false, // this might point to a 'nest' or a ???
37843 * Creates and adds a new region if it doesn't already exist.
37844 * @param {String} target The target region key (north, south, east, west or center).
37845 * @param {Object} config The regions config object
37846 * @return {BorderLayoutRegion} The new region
37848 addRegion : function(config)
37850 if(!this.regions[config.region]){
37851 var r = this.factory(config);
37852 this.bindRegion(r);
37854 return this.regions[config.region];
37858 bindRegion : function(r){
37859 this.regions[r.config.region] = r;
37861 r.on("visibilitychange", this.layout, this);
37862 r.on("paneladded", this.layout, this);
37863 r.on("panelremoved", this.layout, this);
37864 r.on("invalidated", this.layout, this);
37865 r.on("resized", this.onRegionResized, this);
37866 r.on("collapsed", this.onRegionCollapsed, this);
37867 r.on("expanded", this.onRegionExpanded, this);
37871 * Performs a layout update.
37873 layout : function()
37875 if(this.updating) {
37879 // render all the rebions if they have not been done alreayd?
37880 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37881 if(this.regions[region] && !this.regions[region].bodyEl){
37882 this.regions[region].onRender(this.el)
37886 var size = this.getViewSize();
37887 var w = size.width;
37888 var h = size.height;
37893 //var x = 0, y = 0;
37895 var rs = this.regions;
37896 var north = rs["north"];
37897 var south = rs["south"];
37898 var west = rs["west"];
37899 var east = rs["east"];
37900 var center = rs["center"];
37901 //if(this.hideOnLayout){ // not supported anymore
37902 //c.el.setStyle("display", "none");
37904 if(north && north.isVisible()){
37905 var b = north.getBox();
37906 var m = north.getMargins();
37907 b.width = w - (m.left+m.right);
37910 centerY = b.height + b.y + m.bottom;
37911 centerH -= centerY;
37912 north.updateBox(this.safeBox(b));
37914 if(south && south.isVisible()){
37915 var b = south.getBox();
37916 var m = south.getMargins();
37917 b.width = w - (m.left+m.right);
37919 var totalHeight = (b.height + m.top + m.bottom);
37920 b.y = h - totalHeight + m.top;
37921 centerH -= totalHeight;
37922 south.updateBox(this.safeBox(b));
37924 if(west && west.isVisible()){
37925 var b = west.getBox();
37926 var m = west.getMargins();
37927 b.height = centerH - (m.top+m.bottom);
37929 b.y = centerY + m.top;
37930 var totalWidth = (b.width + m.left + m.right);
37931 centerX += totalWidth;
37932 centerW -= totalWidth;
37933 west.updateBox(this.safeBox(b));
37935 if(east && east.isVisible()){
37936 var b = east.getBox();
37937 var m = east.getMargins();
37938 b.height = centerH - (m.top+m.bottom);
37939 var totalWidth = (b.width + m.left + m.right);
37940 b.x = w - totalWidth + m.left;
37941 b.y = centerY + m.top;
37942 centerW -= totalWidth;
37943 east.updateBox(this.safeBox(b));
37946 var m = center.getMargins();
37948 x: centerX + m.left,
37949 y: centerY + m.top,
37950 width: centerW - (m.left+m.right),
37951 height: centerH - (m.top+m.bottom)
37953 //if(this.hideOnLayout){
37954 //center.el.setStyle("display", "block");
37956 center.updateBox(this.safeBox(centerBox));
37959 this.fireEvent("layout", this);
37963 safeBox : function(box){
37964 box.width = Math.max(0, box.width);
37965 box.height = Math.max(0, box.height);
37970 * Adds a ContentPanel (or subclass) to this layout.
37971 * @param {String} target The target region key (north, south, east, west or center).
37972 * @param {Roo.ContentPanel} panel The panel to add
37973 * @return {Roo.ContentPanel} The added panel
37975 add : function(target, panel){
37977 target = target.toLowerCase();
37978 return this.regions[target].add(panel);
37982 * Remove a ContentPanel (or subclass) to this layout.
37983 * @param {String} target The target region key (north, south, east, west or center).
37984 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37985 * @return {Roo.ContentPanel} The removed panel
37987 remove : function(target, panel){
37988 target = target.toLowerCase();
37989 return this.regions[target].remove(panel);
37993 * Searches all regions for a panel with the specified id
37994 * @param {String} panelId
37995 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37997 findPanel : function(panelId){
37998 var rs = this.regions;
37999 for(var target in rs){
38000 if(typeof rs[target] != "function"){
38001 var p = rs[target].getPanel(panelId);
38011 * Searches all regions for a panel with the specified id and activates (shows) it.
38012 * @param {String/ContentPanel} panelId The panels id or the panel itself
38013 * @return {Roo.ContentPanel} The shown panel or null
38015 showPanel : function(panelId) {
38016 var rs = this.regions;
38017 for(var target in rs){
38018 var r = rs[target];
38019 if(typeof r != "function"){
38020 if(r.hasPanel(panelId)){
38021 return r.showPanel(panelId);
38029 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38030 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38033 restoreState : function(provider){
38035 provider = Roo.state.Manager;
38037 var sm = new Roo.LayoutStateManager();
38038 sm.init(this, provider);
38044 * Adds a xtype elements to the layout.
38048 xtype : 'ContentPanel',
38055 xtype : 'NestedLayoutPanel',
38061 items : [ ... list of content panels or nested layout panels.. ]
38065 * @param {Object} cfg Xtype definition of item to add.
38067 addxtype : function(cfg)
38069 // basically accepts a pannel...
38070 // can accept a layout region..!?!?
38071 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38074 // theory? children can only be panels??
38076 //if (!cfg.xtype.match(/Panel$/)) {
38081 if (typeof(cfg.region) == 'undefined') {
38082 Roo.log("Failed to add Panel, region was not set");
38086 var region = cfg.region;
38092 xitems = cfg.items;
38097 if ( region == 'center') {
38098 Roo.log("Center: " + cfg.title);
38104 case 'Content': // ContentPanel (el, cfg)
38105 case 'Scroll': // ContentPanel (el, cfg)
38107 cfg.autoCreate = cfg.autoCreate || true;
38108 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38110 // var el = this.el.createChild();
38111 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38114 this.add(region, ret);
38118 case 'TreePanel': // our new panel!
38119 cfg.el = this.el.createChild();
38120 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38121 this.add(region, ret);
38126 // create a new Layout (which is a Border Layout...
38128 var clayout = cfg.layout;
38129 clayout.el = this.el.createChild();
38130 clayout.items = clayout.items || [];
38134 // replace this exitems with the clayout ones..
38135 xitems = clayout.items;
38137 // force background off if it's in center...
38138 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38139 cfg.background = false;
38141 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38144 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38145 //console.log('adding nested layout panel ' + cfg.toSource());
38146 this.add(region, ret);
38147 nb = {}; /// find first...
38152 // needs grid and region
38154 //var el = this.getRegion(region).el.createChild();
38156 *var el = this.el.createChild();
38157 // create the grid first...
38158 cfg.grid.container = el;
38159 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38162 if (region == 'center' && this.active ) {
38163 cfg.background = false;
38166 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38168 this.add(region, ret);
38170 if (cfg.background) {
38171 // render grid on panel activation (if panel background)
38172 ret.on('activate', function(gp) {
38173 if (!gp.grid.rendered) {
38174 // gp.grid.render(el);
38178 // cfg.grid.render(el);
38184 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38185 // it was the old xcomponent building that caused this before.
38186 // espeically if border is the top element in the tree.
38196 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38198 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38199 this.add(region, ret);
38203 throw "Can not add '" + cfg.xtype + "' to Border";
38209 this.beginUpdate();
38213 Roo.each(xitems, function(i) {
38214 region = nb && i.region ? i.region : false;
38216 var add = ret.addxtype(i);
38219 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38220 if (!i.background) {
38221 abn[region] = nb[region] ;
38228 // make the last non-background panel active..
38229 //if (nb) { Roo.log(abn); }
38232 for(var r in abn) {
38233 region = this.getRegion(r);
38235 // tried using nb[r], but it does not work..
38237 region.showPanel(abn[r]);
38248 factory : function(cfg)
38251 var validRegions = Roo.bootstrap.layout.Border.regions;
38253 var target = cfg.region;
38256 var r = Roo.bootstrap.layout;
38260 return new r.North(cfg);
38262 return new r.South(cfg);
38264 return new r.East(cfg);
38266 return new r.West(cfg);
38268 return new r.Center(cfg);
38270 throw 'Layout region "'+target+'" not supported.';
38277 * Ext JS Library 1.1.1
38278 * Copyright(c) 2006-2007, Ext JS, LLC.
38280 * Originally Released Under LGPL - original licence link has changed is not relivant.
38283 * <script type="text/javascript">
38287 * @class Roo.bootstrap.layout.Basic
38288 * @extends Roo.util.Observable
38289 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38290 * and does not have a titlebar, tabs or any other features. All it does is size and position
38291 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38292 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38293 * @cfg {string} region the region that it inhabits..
38294 * @cfg {bool} skipConfig skip config?
38298 Roo.bootstrap.layout.Basic = function(config){
38300 this.mgr = config.mgr;
38302 this.position = config.region;
38304 var skipConfig = config.skipConfig;
38308 * @scope Roo.BasicLayoutRegion
38312 * @event beforeremove
38313 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38314 * @param {Roo.LayoutRegion} this
38315 * @param {Roo.ContentPanel} panel The panel
38316 * @param {Object} e The cancel event object
38318 "beforeremove" : true,
38320 * @event invalidated
38321 * Fires when the layout for this region is changed.
38322 * @param {Roo.LayoutRegion} this
38324 "invalidated" : true,
38326 * @event visibilitychange
38327 * Fires when this region is shown or hidden
38328 * @param {Roo.LayoutRegion} this
38329 * @param {Boolean} visibility true or false
38331 "visibilitychange" : true,
38333 * @event paneladded
38334 * Fires when a panel is added.
38335 * @param {Roo.LayoutRegion} this
38336 * @param {Roo.ContentPanel} panel The panel
38338 "paneladded" : true,
38340 * @event panelremoved
38341 * Fires when a panel is removed.
38342 * @param {Roo.LayoutRegion} this
38343 * @param {Roo.ContentPanel} panel The panel
38345 "panelremoved" : true,
38347 * @event beforecollapse
38348 * Fires when this region before collapse.
38349 * @param {Roo.LayoutRegion} this
38351 "beforecollapse" : true,
38354 * Fires when this region is collapsed.
38355 * @param {Roo.LayoutRegion} this
38357 "collapsed" : true,
38360 * Fires when this region is expanded.
38361 * @param {Roo.LayoutRegion} this
38366 * Fires when this region is slid into view.
38367 * @param {Roo.LayoutRegion} this
38369 "slideshow" : true,
38372 * Fires when this region slides out of view.
38373 * @param {Roo.LayoutRegion} this
38375 "slidehide" : true,
38377 * @event panelactivated
38378 * Fires when a panel is activated.
38379 * @param {Roo.LayoutRegion} this
38380 * @param {Roo.ContentPanel} panel The activated panel
38382 "panelactivated" : true,
38385 * Fires when the user resizes this region.
38386 * @param {Roo.LayoutRegion} this
38387 * @param {Number} newSize The new size (width for east/west, height for north/south)
38391 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38392 this.panels = new Roo.util.MixedCollection();
38393 this.panels.getKey = this.getPanelId.createDelegate(this);
38395 this.activePanel = null;
38396 // ensure listeners are added...
38398 if (config.listeners || config.events) {
38399 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38400 listeners : config.listeners || {},
38401 events : config.events || {}
38405 if(skipConfig !== true){
38406 this.applyConfig(config);
38410 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38412 getPanelId : function(p){
38416 applyConfig : function(config){
38417 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38418 this.config = config;
38423 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38424 * the width, for horizontal (north, south) the height.
38425 * @param {Number} newSize The new width or height
38427 resizeTo : function(newSize){
38428 var el = this.el ? this.el :
38429 (this.activePanel ? this.activePanel.getEl() : null);
38431 switch(this.position){
38434 el.setWidth(newSize);
38435 this.fireEvent("resized", this, newSize);
38439 el.setHeight(newSize);
38440 this.fireEvent("resized", this, newSize);
38446 getBox : function(){
38447 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38450 getMargins : function(){
38451 return this.margins;
38454 updateBox : function(box){
38456 var el = this.activePanel.getEl();
38457 el.dom.style.left = box.x + "px";
38458 el.dom.style.top = box.y + "px";
38459 this.activePanel.setSize(box.width, box.height);
38463 * Returns the container element for this region.
38464 * @return {Roo.Element}
38466 getEl : function(){
38467 return this.activePanel;
38471 * Returns true if this region is currently visible.
38472 * @return {Boolean}
38474 isVisible : function(){
38475 return this.activePanel ? true : false;
38478 setActivePanel : function(panel){
38479 panel = this.getPanel(panel);
38480 if(this.activePanel && this.activePanel != panel){
38481 this.activePanel.setActiveState(false);
38482 this.activePanel.getEl().setLeftTop(-10000,-10000);
38484 this.activePanel = panel;
38485 panel.setActiveState(true);
38487 panel.setSize(this.box.width, this.box.height);
38489 this.fireEvent("panelactivated", this, panel);
38490 this.fireEvent("invalidated");
38494 * Show the specified panel.
38495 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38496 * @return {Roo.ContentPanel} The shown panel or null
38498 showPanel : function(panel){
38499 panel = this.getPanel(panel);
38501 this.setActivePanel(panel);
38507 * Get the active panel for this region.
38508 * @return {Roo.ContentPanel} The active panel or null
38510 getActivePanel : function(){
38511 return this.activePanel;
38515 * Add the passed ContentPanel(s)
38516 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38517 * @return {Roo.ContentPanel} The panel added (if only one was added)
38519 add : function(panel){
38520 if(arguments.length > 1){
38521 for(var i = 0, len = arguments.length; i < len; i++) {
38522 this.add(arguments[i]);
38526 if(this.hasPanel(panel)){
38527 this.showPanel(panel);
38530 var el = panel.getEl();
38531 if(el.dom.parentNode != this.mgr.el.dom){
38532 this.mgr.el.dom.appendChild(el.dom);
38534 if(panel.setRegion){
38535 panel.setRegion(this);
38537 this.panels.add(panel);
38538 el.setStyle("position", "absolute");
38539 if(!panel.background){
38540 this.setActivePanel(panel);
38541 if(this.config.initialSize && this.panels.getCount()==1){
38542 this.resizeTo(this.config.initialSize);
38545 this.fireEvent("paneladded", this, panel);
38550 * Returns true if the panel is in this region.
38551 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38552 * @return {Boolean}
38554 hasPanel : function(panel){
38555 if(typeof panel == "object"){ // must be panel obj
38556 panel = panel.getId();
38558 return this.getPanel(panel) ? true : false;
38562 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38563 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38564 * @param {Boolean} preservePanel Overrides the config preservePanel option
38565 * @return {Roo.ContentPanel} The panel that was removed
38567 remove : function(panel, preservePanel){
38568 panel = this.getPanel(panel);
38573 this.fireEvent("beforeremove", this, panel, e);
38574 if(e.cancel === true){
38577 var panelId = panel.getId();
38578 this.panels.removeKey(panelId);
38583 * Returns the panel specified or null if it's not in this region.
38584 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38585 * @return {Roo.ContentPanel}
38587 getPanel : function(id){
38588 if(typeof id == "object"){ // must be panel obj
38591 return this.panels.get(id);
38595 * Returns this regions position (north/south/east/west/center).
38598 getPosition: function(){
38599 return this.position;
38603 * Ext JS Library 1.1.1
38604 * Copyright(c) 2006-2007, Ext JS, LLC.
38606 * Originally Released Under LGPL - original licence link has changed is not relivant.
38609 * <script type="text/javascript">
38613 * @class Roo.bootstrap.layout.Region
38614 * @extends Roo.bootstrap.layout.Basic
38615 * This class represents a region in a layout manager.
38617 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38618 * @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})
38619 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38620 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38621 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38622 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38623 * @cfg {String} title The title for the region (overrides panel titles)
38624 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38625 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38626 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38627 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38628 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38629 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38630 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38631 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38632 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38633 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38635 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38636 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38637 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38638 * @cfg {Number} width For East/West panels
38639 * @cfg {Number} height For North/South panels
38640 * @cfg {Boolean} split To show the splitter
38641 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38643 * @cfg {string} cls Extra CSS classes to add to region
38645 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38646 * @cfg {string} region the region that it inhabits..
38649 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38650 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38652 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38653 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38654 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38656 Roo.bootstrap.layout.Region = function(config)
38658 this.applyConfig(config);
38660 var mgr = config.mgr;
38661 var pos = config.region;
38662 config.skipConfig = true;
38663 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38666 this.onRender(mgr.el);
38669 this.visible = true;
38670 this.collapsed = false;
38671 this.unrendered_panels = [];
38674 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38676 position: '', // set by wrapper (eg. north/south etc..)
38677 unrendered_panels : null, // unrendered panels.
38679 tabPosition : false,
38681 mgr: false, // points to 'Border'
38684 createBody : function(){
38685 /** This region's body element
38686 * @type Roo.Element */
38687 this.bodyEl = this.el.createChild({
38689 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38693 onRender: function(ctr, pos)
38695 var dh = Roo.DomHelper;
38696 /** This region's container element
38697 * @type Roo.Element */
38698 this.el = dh.append(ctr.dom, {
38700 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38702 /** This region's title element
38703 * @type Roo.Element */
38705 this.titleEl = dh.append(this.el.dom, {
38707 unselectable: "on",
38708 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38710 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38711 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38715 this.titleEl.enableDisplayMode();
38716 /** This region's title text element
38717 * @type HTMLElement */
38718 this.titleTextEl = this.titleEl.dom.firstChild;
38719 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38721 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38722 this.closeBtn.enableDisplayMode();
38723 this.closeBtn.on("click", this.closeClicked, this);
38724 this.closeBtn.hide();
38726 this.createBody(this.config);
38727 if(this.config.hideWhenEmpty){
38729 this.on("paneladded", this.validateVisibility, this);
38730 this.on("panelremoved", this.validateVisibility, this);
38732 if(this.autoScroll){
38733 this.bodyEl.setStyle("overflow", "auto");
38735 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38737 //if(c.titlebar !== false){
38738 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38739 this.titleEl.hide();
38741 this.titleEl.show();
38742 if(this.config.title){
38743 this.titleTextEl.innerHTML = this.config.title;
38747 if(this.config.collapsed){
38748 this.collapse(true);
38750 if(this.config.hidden){
38754 if (this.unrendered_panels && this.unrendered_panels.length) {
38755 for (var i =0;i< this.unrendered_panels.length; i++) {
38756 this.add(this.unrendered_panels[i]);
38758 this.unrendered_panels = null;
38764 applyConfig : function(c)
38767 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38768 var dh = Roo.DomHelper;
38769 if(c.titlebar !== false){
38770 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38771 this.collapseBtn.on("click", this.collapse, this);
38772 this.collapseBtn.enableDisplayMode();
38774 if(c.showPin === true || this.showPin){
38775 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38776 this.stickBtn.enableDisplayMode();
38777 this.stickBtn.on("click", this.expand, this);
38778 this.stickBtn.hide();
38783 /** This region's collapsed element
38784 * @type Roo.Element */
38787 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38788 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38791 if(c.floatable !== false){
38792 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38793 this.collapsedEl.on("click", this.collapseClick, this);
38796 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38797 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38798 id: "message", unselectable: "on", style:{"float":"left"}});
38799 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38801 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38802 this.expandBtn.on("click", this.expand, this);
38806 if(this.collapseBtn){
38807 this.collapseBtn.setVisible(c.collapsible == true);
38810 this.cmargins = c.cmargins || this.cmargins ||
38811 (this.position == "west" || this.position == "east" ?
38812 {top: 0, left: 2, right:2, bottom: 0} :
38813 {top: 2, left: 0, right:0, bottom: 2});
38815 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38818 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38820 this.autoScroll = c.autoScroll || false;
38825 this.duration = c.duration || .30;
38826 this.slideDuration = c.slideDuration || .45;
38831 * Returns true if this region is currently visible.
38832 * @return {Boolean}
38834 isVisible : function(){
38835 return this.visible;
38839 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38840 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38842 //setCollapsedTitle : function(title){
38843 // title = title || " ";
38844 // if(this.collapsedTitleTextEl){
38845 // this.collapsedTitleTextEl.innerHTML = title;
38849 getBox : function(){
38851 // if(!this.collapsed){
38852 b = this.el.getBox(false, true);
38854 // b = this.collapsedEl.getBox(false, true);
38859 getMargins : function(){
38860 return this.margins;
38861 //return this.collapsed ? this.cmargins : this.margins;
38864 highlight : function(){
38865 this.el.addClass("x-layout-panel-dragover");
38868 unhighlight : function(){
38869 this.el.removeClass("x-layout-panel-dragover");
38872 updateBox : function(box)
38874 if (!this.bodyEl) {
38875 return; // not rendered yet..
38879 if(!this.collapsed){
38880 this.el.dom.style.left = box.x + "px";
38881 this.el.dom.style.top = box.y + "px";
38882 this.updateBody(box.width, box.height);
38884 this.collapsedEl.dom.style.left = box.x + "px";
38885 this.collapsedEl.dom.style.top = box.y + "px";
38886 this.collapsedEl.setSize(box.width, box.height);
38889 this.tabs.autoSizeTabs();
38893 updateBody : function(w, h)
38896 this.el.setWidth(w);
38897 w -= this.el.getBorderWidth("rl");
38898 if(this.config.adjustments){
38899 w += this.config.adjustments[0];
38902 if(h !== null && h > 0){
38903 this.el.setHeight(h);
38904 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38905 h -= this.el.getBorderWidth("tb");
38906 if(this.config.adjustments){
38907 h += this.config.adjustments[1];
38909 this.bodyEl.setHeight(h);
38911 h = this.tabs.syncHeight(h);
38914 if(this.panelSize){
38915 w = w !== null ? w : this.panelSize.width;
38916 h = h !== null ? h : this.panelSize.height;
38918 if(this.activePanel){
38919 var el = this.activePanel.getEl();
38920 w = w !== null ? w : el.getWidth();
38921 h = h !== null ? h : el.getHeight();
38922 this.panelSize = {width: w, height: h};
38923 this.activePanel.setSize(w, h);
38925 if(Roo.isIE && this.tabs){
38926 this.tabs.el.repaint();
38931 * Returns the container element for this region.
38932 * @return {Roo.Element}
38934 getEl : function(){
38939 * Hides this region.
38942 //if(!this.collapsed){
38943 this.el.dom.style.left = "-2000px";
38946 // this.collapsedEl.dom.style.left = "-2000px";
38947 // this.collapsedEl.hide();
38949 this.visible = false;
38950 this.fireEvent("visibilitychange", this, false);
38954 * Shows this region if it was previously hidden.
38957 //if(!this.collapsed){
38960 // this.collapsedEl.show();
38962 this.visible = true;
38963 this.fireEvent("visibilitychange", this, true);
38966 closeClicked : function(){
38967 if(this.activePanel){
38968 this.remove(this.activePanel);
38972 collapseClick : function(e){
38974 e.stopPropagation();
38977 e.stopPropagation();
38983 * Collapses this region.
38984 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38987 collapse : function(skipAnim, skipCheck = false){
38988 if(this.collapsed) {
38992 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38994 this.collapsed = true;
38996 this.split.el.hide();
38998 if(this.config.animate && skipAnim !== true){
38999 this.fireEvent("invalidated", this);
39000 this.animateCollapse();
39002 this.el.setLocation(-20000,-20000);
39004 this.collapsedEl.show();
39005 this.fireEvent("collapsed", this);
39006 this.fireEvent("invalidated", this);
39012 animateCollapse : function(){
39017 * Expands this region if it was previously collapsed.
39018 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39019 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39022 expand : function(e, skipAnim){
39024 e.stopPropagation();
39026 if(!this.collapsed || this.el.hasActiveFx()) {
39030 this.afterSlideIn();
39033 this.collapsed = false;
39034 if(this.config.animate && skipAnim !== true){
39035 this.animateExpand();
39039 this.split.el.show();
39041 this.collapsedEl.setLocation(-2000,-2000);
39042 this.collapsedEl.hide();
39043 this.fireEvent("invalidated", this);
39044 this.fireEvent("expanded", this);
39048 animateExpand : function(){
39052 initTabs : function()
39054 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39056 var ts = new Roo.bootstrap.panel.Tabs({
39057 el: this.bodyEl.dom,
39059 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39060 disableTooltips: this.config.disableTabTips,
39061 toolbar : this.config.toolbar
39064 if(this.config.hideTabs){
39065 ts.stripWrap.setDisplayed(false);
39068 ts.resizeTabs = this.config.resizeTabs === true;
39069 ts.minTabWidth = this.config.minTabWidth || 40;
39070 ts.maxTabWidth = this.config.maxTabWidth || 250;
39071 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39072 ts.monitorResize = false;
39073 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39074 ts.bodyEl.addClass('roo-layout-tabs-body');
39075 this.panels.each(this.initPanelAsTab, this);
39078 initPanelAsTab : function(panel){
39079 var ti = this.tabs.addTab(
39083 this.config.closeOnTab && panel.isClosable(),
39086 if(panel.tabTip !== undefined){
39087 ti.setTooltip(panel.tabTip);
39089 ti.on("activate", function(){
39090 this.setActivePanel(panel);
39093 if(this.config.closeOnTab){
39094 ti.on("beforeclose", function(t, e){
39096 this.remove(panel);
39100 panel.tabItem = ti;
39105 updatePanelTitle : function(panel, title)
39107 if(this.activePanel == panel){
39108 this.updateTitle(title);
39111 var ti = this.tabs.getTab(panel.getEl().id);
39113 if(panel.tabTip !== undefined){
39114 ti.setTooltip(panel.tabTip);
39119 updateTitle : function(title){
39120 if(this.titleTextEl && !this.config.title){
39121 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39125 setActivePanel : function(panel)
39127 panel = this.getPanel(panel);
39128 if(this.activePanel && this.activePanel != panel){
39129 if(this.activePanel.setActiveState(false) === false){
39133 this.activePanel = panel;
39134 panel.setActiveState(true);
39135 if(this.panelSize){
39136 panel.setSize(this.panelSize.width, this.panelSize.height);
39139 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39141 this.updateTitle(panel.getTitle());
39143 this.fireEvent("invalidated", this);
39145 this.fireEvent("panelactivated", this, panel);
39149 * Shows the specified panel.
39150 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39151 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39153 showPanel : function(panel)
39155 panel = this.getPanel(panel);
39158 var tab = this.tabs.getTab(panel.getEl().id);
39159 if(tab.isHidden()){
39160 this.tabs.unhideTab(tab.id);
39164 this.setActivePanel(panel);
39171 * Get the active panel for this region.
39172 * @return {Roo.ContentPanel} The active panel or null
39174 getActivePanel : function(){
39175 return this.activePanel;
39178 validateVisibility : function(){
39179 if(this.panels.getCount() < 1){
39180 this.updateTitle(" ");
39181 this.closeBtn.hide();
39184 if(!this.isVisible()){
39191 * Adds the passed ContentPanel(s) to this region.
39192 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39193 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39195 add : function(panel)
39197 if(arguments.length > 1){
39198 for(var i = 0, len = arguments.length; i < len; i++) {
39199 this.add(arguments[i]);
39204 // if we have not been rendered yet, then we can not really do much of this..
39205 if (!this.bodyEl) {
39206 this.unrendered_panels.push(panel);
39213 if(this.hasPanel(panel)){
39214 this.showPanel(panel);
39217 panel.setRegion(this);
39218 this.panels.add(panel);
39219 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39220 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39221 // and hide them... ???
39222 this.bodyEl.dom.appendChild(panel.getEl().dom);
39223 if(panel.background !== true){
39224 this.setActivePanel(panel);
39226 this.fireEvent("paneladded", this, panel);
39233 this.initPanelAsTab(panel);
39237 if(panel.background !== true){
39238 this.tabs.activate(panel.getEl().id);
39240 this.fireEvent("paneladded", this, panel);
39245 * Hides the tab for the specified panel.
39246 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39248 hidePanel : function(panel){
39249 if(this.tabs && (panel = this.getPanel(panel))){
39250 this.tabs.hideTab(panel.getEl().id);
39255 * Unhides the tab for a previously hidden panel.
39256 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39258 unhidePanel : function(panel){
39259 if(this.tabs && (panel = this.getPanel(panel))){
39260 this.tabs.unhideTab(panel.getEl().id);
39264 clearPanels : function(){
39265 while(this.panels.getCount() > 0){
39266 this.remove(this.panels.first());
39271 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39272 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39273 * @param {Boolean} preservePanel Overrides the config preservePanel option
39274 * @return {Roo.ContentPanel} The panel that was removed
39276 remove : function(panel, preservePanel)
39278 panel = this.getPanel(panel);
39283 this.fireEvent("beforeremove", this, panel, e);
39284 if(e.cancel === true){
39287 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39288 var panelId = panel.getId();
39289 this.panels.removeKey(panelId);
39291 document.body.appendChild(panel.getEl().dom);
39294 this.tabs.removeTab(panel.getEl().id);
39295 }else if (!preservePanel){
39296 this.bodyEl.dom.removeChild(panel.getEl().dom);
39298 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39299 var p = this.panels.first();
39300 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39301 tempEl.appendChild(p.getEl().dom);
39302 this.bodyEl.update("");
39303 this.bodyEl.dom.appendChild(p.getEl().dom);
39305 this.updateTitle(p.getTitle());
39307 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39308 this.setActivePanel(p);
39310 panel.setRegion(null);
39311 if(this.activePanel == panel){
39312 this.activePanel = null;
39314 if(this.config.autoDestroy !== false && preservePanel !== true){
39315 try{panel.destroy();}catch(e){}
39317 this.fireEvent("panelremoved", this, panel);
39322 * Returns the TabPanel component used by this region
39323 * @return {Roo.TabPanel}
39325 getTabs : function(){
39329 createTool : function(parentEl, className){
39330 var btn = Roo.DomHelper.append(parentEl, {
39332 cls: "x-layout-tools-button",
39335 cls: "roo-layout-tools-button-inner " + className,
39339 btn.addClassOnOver("roo-layout-tools-button-over");
39344 * Ext JS Library 1.1.1
39345 * Copyright(c) 2006-2007, Ext JS, LLC.
39347 * Originally Released Under LGPL - original licence link has changed is not relivant.
39350 * <script type="text/javascript">
39356 * @class Roo.SplitLayoutRegion
39357 * @extends Roo.LayoutRegion
39358 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39360 Roo.bootstrap.layout.Split = function(config){
39361 this.cursor = config.cursor;
39362 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39365 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39367 splitTip : "Drag to resize.",
39368 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39369 useSplitTips : false,
39371 applyConfig : function(config){
39372 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39375 onRender : function(ctr,pos) {
39377 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39378 if(!this.config.split){
39383 var splitEl = Roo.DomHelper.append(ctr.dom, {
39385 id: this.el.id + "-split",
39386 cls: "roo-layout-split roo-layout-split-"+this.position,
39389 /** The SplitBar for this region
39390 * @type Roo.SplitBar */
39391 // does not exist yet...
39392 Roo.log([this.position, this.orientation]);
39394 this.split = new Roo.bootstrap.SplitBar({
39395 dragElement : splitEl,
39396 resizingElement: this.el,
39397 orientation : this.orientation
39400 this.split.on("moved", this.onSplitMove, this);
39401 this.split.useShim = this.config.useShim === true;
39402 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39403 if(this.useSplitTips){
39404 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39406 //if(config.collapsible){
39407 // this.split.el.on("dblclick", this.collapse, this);
39410 if(typeof this.config.minSize != "undefined"){
39411 this.split.minSize = this.config.minSize;
39413 if(typeof this.config.maxSize != "undefined"){
39414 this.split.maxSize = this.config.maxSize;
39416 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39417 this.hideSplitter();
39422 getHMaxSize : function(){
39423 var cmax = this.config.maxSize || 10000;
39424 var center = this.mgr.getRegion("center");
39425 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39428 getVMaxSize : function(){
39429 var cmax = this.config.maxSize || 10000;
39430 var center = this.mgr.getRegion("center");
39431 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39434 onSplitMove : function(split, newSize){
39435 this.fireEvent("resized", this, newSize);
39439 * Returns the {@link Roo.SplitBar} for this region.
39440 * @return {Roo.SplitBar}
39442 getSplitBar : function(){
39447 this.hideSplitter();
39448 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39451 hideSplitter : function(){
39453 this.split.el.setLocation(-2000,-2000);
39454 this.split.el.hide();
39460 this.split.el.show();
39462 Roo.bootstrap.layout.Split.superclass.show.call(this);
39465 beforeSlide: function(){
39466 if(Roo.isGecko){// firefox overflow auto bug workaround
39467 this.bodyEl.clip();
39469 this.tabs.bodyEl.clip();
39471 if(this.activePanel){
39472 this.activePanel.getEl().clip();
39474 if(this.activePanel.beforeSlide){
39475 this.activePanel.beforeSlide();
39481 afterSlide : function(){
39482 if(Roo.isGecko){// firefox overflow auto bug workaround
39483 this.bodyEl.unclip();
39485 this.tabs.bodyEl.unclip();
39487 if(this.activePanel){
39488 this.activePanel.getEl().unclip();
39489 if(this.activePanel.afterSlide){
39490 this.activePanel.afterSlide();
39496 initAutoHide : function(){
39497 if(this.autoHide !== false){
39498 if(!this.autoHideHd){
39499 var st = new Roo.util.DelayedTask(this.slideIn, this);
39500 this.autoHideHd = {
39501 "mouseout": function(e){
39502 if(!e.within(this.el, true)){
39506 "mouseover" : function(e){
39512 this.el.on(this.autoHideHd);
39516 clearAutoHide : function(){
39517 if(this.autoHide !== false){
39518 this.el.un("mouseout", this.autoHideHd.mouseout);
39519 this.el.un("mouseover", this.autoHideHd.mouseover);
39523 clearMonitor : function(){
39524 Roo.get(document).un("click", this.slideInIf, this);
39527 // these names are backwards but not changed for compat
39528 slideOut : function(){
39529 if(this.isSlid || this.el.hasActiveFx()){
39532 this.isSlid = true;
39533 if(this.collapseBtn){
39534 this.collapseBtn.hide();
39536 this.closeBtnState = this.closeBtn.getStyle('display');
39537 this.closeBtn.hide();
39539 this.stickBtn.show();
39542 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39543 this.beforeSlide();
39544 this.el.setStyle("z-index", 10001);
39545 this.el.slideIn(this.getSlideAnchor(), {
39546 callback: function(){
39548 this.initAutoHide();
39549 Roo.get(document).on("click", this.slideInIf, this);
39550 this.fireEvent("slideshow", this);
39557 afterSlideIn : function(){
39558 this.clearAutoHide();
39559 this.isSlid = false;
39560 this.clearMonitor();
39561 this.el.setStyle("z-index", "");
39562 if(this.collapseBtn){
39563 this.collapseBtn.show();
39565 this.closeBtn.setStyle('display', this.closeBtnState);
39567 this.stickBtn.hide();
39569 this.fireEvent("slidehide", this);
39572 slideIn : function(cb){
39573 if(!this.isSlid || this.el.hasActiveFx()){
39577 this.isSlid = false;
39578 this.beforeSlide();
39579 this.el.slideOut(this.getSlideAnchor(), {
39580 callback: function(){
39581 this.el.setLeftTop(-10000, -10000);
39583 this.afterSlideIn();
39591 slideInIf : function(e){
39592 if(!e.within(this.el)){
39597 animateCollapse : function(){
39598 this.beforeSlide();
39599 this.el.setStyle("z-index", 20000);
39600 var anchor = this.getSlideAnchor();
39601 this.el.slideOut(anchor, {
39602 callback : function(){
39603 this.el.setStyle("z-index", "");
39604 this.collapsedEl.slideIn(anchor, {duration:.3});
39606 this.el.setLocation(-10000,-10000);
39608 this.fireEvent("collapsed", this);
39615 animateExpand : function(){
39616 this.beforeSlide();
39617 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39618 this.el.setStyle("z-index", 20000);
39619 this.collapsedEl.hide({
39622 this.el.slideIn(this.getSlideAnchor(), {
39623 callback : function(){
39624 this.el.setStyle("z-index", "");
39627 this.split.el.show();
39629 this.fireEvent("invalidated", this);
39630 this.fireEvent("expanded", this);
39658 getAnchor : function(){
39659 return this.anchors[this.position];
39662 getCollapseAnchor : function(){
39663 return this.canchors[this.position];
39666 getSlideAnchor : function(){
39667 return this.sanchors[this.position];
39670 getAlignAdj : function(){
39671 var cm = this.cmargins;
39672 switch(this.position){
39688 getExpandAdj : function(){
39689 var c = this.collapsedEl, cm = this.cmargins;
39690 switch(this.position){
39692 return [-(cm.right+c.getWidth()+cm.left), 0];
39695 return [cm.right+c.getWidth()+cm.left, 0];
39698 return [0, -(cm.top+cm.bottom+c.getHeight())];
39701 return [0, cm.top+cm.bottom+c.getHeight()];
39707 * Ext JS Library 1.1.1
39708 * Copyright(c) 2006-2007, Ext JS, LLC.
39710 * Originally Released Under LGPL - original licence link has changed is not relivant.
39713 * <script type="text/javascript">
39716 * These classes are private internal classes
39718 Roo.bootstrap.layout.Center = function(config){
39719 config.region = "center";
39720 Roo.bootstrap.layout.Region.call(this, config);
39721 this.visible = true;
39722 this.minWidth = config.minWidth || 20;
39723 this.minHeight = config.minHeight || 20;
39726 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39728 // center panel can't be hidden
39732 // center panel can't be hidden
39735 getMinWidth: function(){
39736 return this.minWidth;
39739 getMinHeight: function(){
39740 return this.minHeight;
39754 Roo.bootstrap.layout.North = function(config)
39756 config.region = 'north';
39757 config.cursor = 'n-resize';
39759 Roo.bootstrap.layout.Split.call(this, config);
39763 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39764 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39765 this.split.el.addClass("roo-layout-split-v");
39767 //var size = config.initialSize || config.height;
39768 //if(this.el && typeof size != "undefined"){
39769 // this.el.setHeight(size);
39772 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39774 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39777 onRender : function(ctr, pos)
39779 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39780 var size = this.config.initialSize || this.config.height;
39781 if(this.el && typeof size != "undefined"){
39782 this.el.setHeight(size);
39787 getBox : function(){
39788 if(this.collapsed){
39789 return this.collapsedEl.getBox();
39791 var box = this.el.getBox();
39793 box.height += this.split.el.getHeight();
39798 updateBox : function(box){
39799 if(this.split && !this.collapsed){
39800 box.height -= this.split.el.getHeight();
39801 this.split.el.setLeft(box.x);
39802 this.split.el.setTop(box.y+box.height);
39803 this.split.el.setWidth(box.width);
39805 if(this.collapsed){
39806 this.updateBody(box.width, null);
39808 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39816 Roo.bootstrap.layout.South = function(config){
39817 config.region = 'south';
39818 config.cursor = 's-resize';
39819 Roo.bootstrap.layout.Split.call(this, config);
39821 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39822 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39823 this.split.el.addClass("roo-layout-split-v");
39828 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39829 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39831 onRender : function(ctr, pos)
39833 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39834 var size = this.config.initialSize || this.config.height;
39835 if(this.el && typeof size != "undefined"){
39836 this.el.setHeight(size);
39841 getBox : function(){
39842 if(this.collapsed){
39843 return this.collapsedEl.getBox();
39845 var box = this.el.getBox();
39847 var sh = this.split.el.getHeight();
39854 updateBox : function(box){
39855 if(this.split && !this.collapsed){
39856 var sh = this.split.el.getHeight();
39859 this.split.el.setLeft(box.x);
39860 this.split.el.setTop(box.y-sh);
39861 this.split.el.setWidth(box.width);
39863 if(this.collapsed){
39864 this.updateBody(box.width, null);
39866 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39870 Roo.bootstrap.layout.East = function(config){
39871 config.region = "east";
39872 config.cursor = "e-resize";
39873 Roo.bootstrap.layout.Split.call(this, config);
39875 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39876 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39877 this.split.el.addClass("roo-layout-split-h");
39881 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39882 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39884 onRender : function(ctr, pos)
39886 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39887 var size = this.config.initialSize || this.config.width;
39888 if(this.el && typeof size != "undefined"){
39889 this.el.setWidth(size);
39894 getBox : function(){
39895 if(this.collapsed){
39896 return this.collapsedEl.getBox();
39898 var box = this.el.getBox();
39900 var sw = this.split.el.getWidth();
39907 updateBox : function(box){
39908 if(this.split && !this.collapsed){
39909 var sw = this.split.el.getWidth();
39911 this.split.el.setLeft(box.x);
39912 this.split.el.setTop(box.y);
39913 this.split.el.setHeight(box.height);
39916 if(this.collapsed){
39917 this.updateBody(null, box.height);
39919 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39923 Roo.bootstrap.layout.West = function(config){
39924 config.region = "west";
39925 config.cursor = "w-resize";
39927 Roo.bootstrap.layout.Split.call(this, config);
39929 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39930 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39931 this.split.el.addClass("roo-layout-split-h");
39935 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39936 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39938 onRender: function(ctr, pos)
39940 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39941 var size = this.config.initialSize || this.config.width;
39942 if(typeof size != "undefined"){
39943 this.el.setWidth(size);
39947 getBox : function(){
39948 if(this.collapsed){
39949 return this.collapsedEl.getBox();
39951 var box = this.el.getBox();
39952 if (box.width == 0) {
39953 box.width = this.config.width; // kludge?
39956 box.width += this.split.el.getWidth();
39961 updateBox : function(box){
39962 if(this.split && !this.collapsed){
39963 var sw = this.split.el.getWidth();
39965 this.split.el.setLeft(box.x+box.width);
39966 this.split.el.setTop(box.y);
39967 this.split.el.setHeight(box.height);
39969 if(this.collapsed){
39970 this.updateBody(null, box.height);
39972 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39974 });Roo.namespace("Roo.bootstrap.panel");/*
39976 * Ext JS Library 1.1.1
39977 * Copyright(c) 2006-2007, Ext JS, LLC.
39979 * Originally Released Under LGPL - original licence link has changed is not relivant.
39982 * <script type="text/javascript">
39985 * @class Roo.ContentPanel
39986 * @extends Roo.util.Observable
39987 * A basic ContentPanel element.
39988 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39989 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39990 * @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
39991 * @cfg {Boolean} closable True if the panel can be closed/removed
39992 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39993 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39994 * @cfg {Toolbar} toolbar A toolbar for this panel
39995 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39996 * @cfg {String} title The title for this panel
39997 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39998 * @cfg {String} url Calls {@link #setUrl} with this value
39999 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40000 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40001 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40002 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40003 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40004 * @cfg {Boolean} badges render the badges
40005 * @cfg {String} cls extra classes to use
40006 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40009 * Create a new ContentPanel.
40010 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40011 * @param {String/Object} config A string to set only the title or a config object
40012 * @param {String} content (optional) Set the HTML content for this panel
40013 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40015 Roo.bootstrap.panel.Content = function( config){
40017 this.tpl = config.tpl || false;
40019 var el = config.el;
40020 var content = config.content;
40022 if(config.autoCreate){ // xtype is available if this is called from factory
40025 this.el = Roo.get(el);
40026 if(!this.el && config && config.autoCreate){
40027 if(typeof config.autoCreate == "object"){
40028 if(!config.autoCreate.id){
40029 config.autoCreate.id = config.id||el;
40031 this.el = Roo.DomHelper.append(document.body,
40032 config.autoCreate, true);
40036 cls: (config.cls || '') +
40037 (config.background ? ' bg-' + config.background : '') +
40038 " roo-layout-inactive-content",
40041 if (config.iframe) {
40045 style : 'border: 0px',
40046 src : 'about:blank'
40052 elcfg.html = config.html;
40056 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40057 if (config.iframe) {
40058 this.iframeEl = this.el.select('iframe',true).first();
40063 this.closable = false;
40064 this.loaded = false;
40065 this.active = false;
40068 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40070 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40072 this.wrapEl = this.el; //this.el.wrap();
40074 if (config.toolbar.items) {
40075 ti = config.toolbar.items ;
40076 delete config.toolbar.items ;
40080 this.toolbar.render(this.wrapEl, 'before');
40081 for(var i =0;i < ti.length;i++) {
40082 // Roo.log(['add child', items[i]]);
40083 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40085 this.toolbar.items = nitems;
40086 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40087 delete config.toolbar;
40091 // xtype created footer. - not sure if will work as we normally have to render first..
40092 if (this.footer && !this.footer.el && this.footer.xtype) {
40093 if (!this.wrapEl) {
40094 this.wrapEl = this.el.wrap();
40097 this.footer.container = this.wrapEl.createChild();
40099 this.footer = Roo.factory(this.footer, Roo);
40104 if(typeof config == "string"){
40105 this.title = config;
40107 Roo.apply(this, config);
40111 this.resizeEl = Roo.get(this.resizeEl, true);
40113 this.resizeEl = this.el;
40115 // handle view.xtype
40123 * Fires when this panel is activated.
40124 * @param {Roo.ContentPanel} this
40128 * @event deactivate
40129 * Fires when this panel is activated.
40130 * @param {Roo.ContentPanel} this
40132 "deactivate" : true,
40136 * Fires when this panel is resized if fitToFrame is true.
40137 * @param {Roo.ContentPanel} this
40138 * @param {Number} width The width after any component adjustments
40139 * @param {Number} height The height after any component adjustments
40145 * Fires when this tab is created
40146 * @param {Roo.ContentPanel} this
40157 if(this.autoScroll && !this.iframe){
40158 this.resizeEl.setStyle("overflow", "auto");
40160 // fix randome scrolling
40161 //this.el.on('scroll', function() {
40162 // Roo.log('fix random scolling');
40163 // this.scrollTo('top',0);
40166 content = content || this.content;
40168 this.setContent(content);
40170 if(config && config.url){
40171 this.setUrl(this.url, this.params, this.loadOnce);
40176 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40178 if (this.view && typeof(this.view.xtype) != 'undefined') {
40179 this.view.el = this.el.appendChild(document.createElement("div"));
40180 this.view = Roo.factory(this.view);
40181 this.view.render && this.view.render(false, '');
40185 this.fireEvent('render', this);
40188 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40198 setRegion : function(region){
40199 this.region = region;
40200 this.setActiveClass(region && !this.background);
40204 setActiveClass: function(state)
40207 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40208 this.el.setStyle('position','relative');
40210 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40211 this.el.setStyle('position', 'absolute');
40216 * Returns the toolbar for this Panel if one was configured.
40217 * @return {Roo.Toolbar}
40219 getToolbar : function(){
40220 return this.toolbar;
40223 setActiveState : function(active)
40225 this.active = active;
40226 this.setActiveClass(active);
40228 if(this.fireEvent("deactivate", this) === false){
40233 this.fireEvent("activate", this);
40237 * Updates this panel's element (not for iframe)
40238 * @param {String} content The new content
40239 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40241 setContent : function(content, loadScripts){
40246 this.el.update(content, loadScripts);
40249 ignoreResize : function(w, h){
40250 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40253 this.lastSize = {width: w, height: h};
40258 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40259 * @return {Roo.UpdateManager} The UpdateManager
40261 getUpdateManager : function(){
40265 return this.el.getUpdateManager();
40268 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40269 * Does not work with IFRAME contents
40270 * @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:
40273 url: "your-url.php",
40274 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40275 callback: yourFunction,
40276 scope: yourObject, //(optional scope)
40279 text: "Loading...",
40285 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40286 * 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.
40287 * @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}
40288 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40289 * @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.
40290 * @return {Roo.ContentPanel} this
40298 var um = this.el.getUpdateManager();
40299 um.update.apply(um, arguments);
40305 * 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.
40306 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40307 * @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)
40308 * @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)
40309 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40311 setUrl : function(url, params, loadOnce){
40313 this.iframeEl.dom.src = url;
40317 if(this.refreshDelegate){
40318 this.removeListener("activate", this.refreshDelegate);
40320 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40321 this.on("activate", this.refreshDelegate);
40322 return this.el.getUpdateManager();
40325 _handleRefresh : function(url, params, loadOnce){
40326 if(!loadOnce || !this.loaded){
40327 var updater = this.el.getUpdateManager();
40328 updater.update(url, params, this._setLoaded.createDelegate(this));
40332 _setLoaded : function(){
40333 this.loaded = true;
40337 * Returns this panel's id
40340 getId : function(){
40345 * Returns this panel's element - used by regiosn to add.
40346 * @return {Roo.Element}
40348 getEl : function(){
40349 return this.wrapEl || this.el;
40354 adjustForComponents : function(width, height)
40356 //Roo.log('adjustForComponents ');
40357 if(this.resizeEl != this.el){
40358 width -= this.el.getFrameWidth('lr');
40359 height -= this.el.getFrameWidth('tb');
40362 var te = this.toolbar.getEl();
40363 te.setWidth(width);
40364 height -= te.getHeight();
40367 var te = this.footer.getEl();
40368 te.setWidth(width);
40369 height -= te.getHeight();
40373 if(this.adjustments){
40374 width += this.adjustments[0];
40375 height += this.adjustments[1];
40377 return {"width": width, "height": height};
40380 setSize : function(width, height){
40381 if(this.fitToFrame && !this.ignoreResize(width, height)){
40382 if(this.fitContainer && this.resizeEl != this.el){
40383 this.el.setSize(width, height);
40385 var size = this.adjustForComponents(width, height);
40387 this.iframeEl.setSize(width,height);
40390 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40391 this.fireEvent('resize', this, size.width, size.height);
40398 * Returns this panel's title
40401 getTitle : function(){
40403 if (typeof(this.title) != 'object') {
40408 for (var k in this.title) {
40409 if (!this.title.hasOwnProperty(k)) {
40413 if (k.indexOf('-') >= 0) {
40414 var s = k.split('-');
40415 for (var i = 0; i<s.length; i++) {
40416 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40419 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40426 * Set this panel's title
40427 * @param {String} title
40429 setTitle : function(title){
40430 this.title = title;
40432 this.region.updatePanelTitle(this, title);
40437 * Returns true is this panel was configured to be closable
40438 * @return {Boolean}
40440 isClosable : function(){
40441 return this.closable;
40444 beforeSlide : function(){
40446 this.resizeEl.clip();
40449 afterSlide : function(){
40451 this.resizeEl.unclip();
40455 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40456 * Will fail silently if the {@link #setUrl} method has not been called.
40457 * This does not activate the panel, just updates its content.
40459 refresh : function(){
40460 if(this.refreshDelegate){
40461 this.loaded = false;
40462 this.refreshDelegate();
40467 * Destroys this panel
40469 destroy : function(){
40470 this.el.removeAllListeners();
40471 var tempEl = document.createElement("span");
40472 tempEl.appendChild(this.el.dom);
40473 tempEl.innerHTML = "";
40479 * form - if the content panel contains a form - this is a reference to it.
40480 * @type {Roo.form.Form}
40484 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40485 * This contains a reference to it.
40491 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40501 * @param {Object} cfg Xtype definition of item to add.
40505 getChildContainer: function () {
40506 return this.getEl();
40511 var ret = new Roo.factory(cfg);
40516 if (cfg.xtype.match(/^Form$/)) {
40519 //if (this.footer) {
40520 // el = this.footer.container.insertSibling(false, 'before');
40522 el = this.el.createChild();
40525 this.form = new Roo.form.Form(cfg);
40528 if ( this.form.allItems.length) {
40529 this.form.render(el.dom);
40533 // should only have one of theses..
40534 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40535 // views.. should not be just added - used named prop 'view''
40537 cfg.el = this.el.appendChild(document.createElement("div"));
40540 var ret = new Roo.factory(cfg);
40542 ret.render && ret.render(false, ''); // render blank..
40552 * @class Roo.bootstrap.panel.Grid
40553 * @extends Roo.bootstrap.panel.Content
40555 * Create a new GridPanel.
40556 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40557 * @param {Object} config A the config object
40563 Roo.bootstrap.panel.Grid = function(config)
40567 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40568 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40570 config.el = this.wrapper;
40571 //this.el = this.wrapper;
40573 if (config.container) {
40574 // ctor'ed from a Border/panel.grid
40577 this.wrapper.setStyle("overflow", "hidden");
40578 this.wrapper.addClass('roo-grid-container');
40583 if(config.toolbar){
40584 var tool_el = this.wrapper.createChild();
40585 this.toolbar = Roo.factory(config.toolbar);
40587 if (config.toolbar.items) {
40588 ti = config.toolbar.items ;
40589 delete config.toolbar.items ;
40593 this.toolbar.render(tool_el);
40594 for(var i =0;i < ti.length;i++) {
40595 // Roo.log(['add child', items[i]]);
40596 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40598 this.toolbar.items = nitems;
40600 delete config.toolbar;
40603 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40604 config.grid.scrollBody = true;;
40605 config.grid.monitorWindowResize = false; // turn off autosizing
40606 config.grid.autoHeight = false;
40607 config.grid.autoWidth = false;
40609 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40611 if (config.background) {
40612 // render grid on panel activation (if panel background)
40613 this.on('activate', function(gp) {
40614 if (!gp.grid.rendered) {
40615 gp.grid.render(this.wrapper);
40616 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40621 this.grid.render(this.wrapper);
40622 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40625 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40626 // ??? needed ??? config.el = this.wrapper;
40631 // xtype created footer. - not sure if will work as we normally have to render first..
40632 if (this.footer && !this.footer.el && this.footer.xtype) {
40634 var ctr = this.grid.getView().getFooterPanel(true);
40635 this.footer.dataSource = this.grid.dataSource;
40636 this.footer = Roo.factory(this.footer, Roo);
40637 this.footer.render(ctr);
40647 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40648 getId : function(){
40649 return this.grid.id;
40653 * Returns the grid for this panel
40654 * @return {Roo.bootstrap.Table}
40656 getGrid : function(){
40660 setSize : function(width, height){
40661 if(!this.ignoreResize(width, height)){
40662 var grid = this.grid;
40663 var size = this.adjustForComponents(width, height);
40664 // tfoot is not a footer?
40667 var gridel = grid.getGridEl();
40668 gridel.setSize(size.width, size.height);
40670 var tbd = grid.getGridEl().select('tbody', true).first();
40671 var thd = grid.getGridEl().select('thead',true).first();
40672 var tbf= grid.getGridEl().select('tfoot', true).first();
40675 size.height -= tbf.getHeight();
40678 size.height -= thd.getHeight();
40681 tbd.setSize(size.width, size.height );
40682 // this is for the account management tab -seems to work there.
40683 var thd = grid.getGridEl().select('thead',true).first();
40685 // tbd.setSize(size.width, size.height - thd.getHeight());
40694 beforeSlide : function(){
40695 this.grid.getView().scroller.clip();
40698 afterSlide : function(){
40699 this.grid.getView().scroller.unclip();
40702 destroy : function(){
40703 this.grid.destroy();
40705 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40710 * @class Roo.bootstrap.panel.Nest
40711 * @extends Roo.bootstrap.panel.Content
40713 * Create a new Panel, that can contain a layout.Border.
40716 * @param {Roo.BorderLayout} layout The layout for this panel
40717 * @param {String/Object} config A string to set only the title or a config object
40719 Roo.bootstrap.panel.Nest = function(config)
40721 // construct with only one argument..
40722 /* FIXME - implement nicer consturctors
40723 if (layout.layout) {
40725 layout = config.layout;
40726 delete config.layout;
40728 if (layout.xtype && !layout.getEl) {
40729 // then layout needs constructing..
40730 layout = Roo.factory(layout, Roo);
40734 config.el = config.layout.getEl();
40736 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40738 config.layout.monitorWindowResize = false; // turn off autosizing
40739 this.layout = config.layout;
40740 this.layout.getEl().addClass("roo-layout-nested-layout");
40741 this.layout.parent = this;
40748 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40750 setSize : function(width, height){
40751 if(!this.ignoreResize(width, height)){
40752 var size = this.adjustForComponents(width, height);
40753 var el = this.layout.getEl();
40754 if (size.height < 1) {
40755 el.setWidth(size.width);
40757 el.setSize(size.width, size.height);
40759 var touch = el.dom.offsetWidth;
40760 this.layout.layout();
40761 // ie requires a double layout on the first pass
40762 if(Roo.isIE && !this.initialized){
40763 this.initialized = true;
40764 this.layout.layout();
40769 // activate all subpanels if not currently active..
40771 setActiveState : function(active){
40772 this.active = active;
40773 this.setActiveClass(active);
40776 this.fireEvent("deactivate", this);
40780 this.fireEvent("activate", this);
40781 // not sure if this should happen before or after..
40782 if (!this.layout) {
40783 return; // should not happen..
40786 for (var r in this.layout.regions) {
40787 reg = this.layout.getRegion(r);
40788 if (reg.getActivePanel()) {
40789 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40790 reg.setActivePanel(reg.getActivePanel());
40793 if (!reg.panels.length) {
40796 reg.showPanel(reg.getPanel(0));
40805 * Returns the nested BorderLayout for this panel
40806 * @return {Roo.BorderLayout}
40808 getLayout : function(){
40809 return this.layout;
40813 * Adds a xtype elements to the layout of the nested panel
40817 xtype : 'ContentPanel',
40824 xtype : 'NestedLayoutPanel',
40830 items : [ ... list of content panels or nested layout panels.. ]
40834 * @param {Object} cfg Xtype definition of item to add.
40836 addxtype : function(cfg) {
40837 return this.layout.addxtype(cfg);
40842 * Ext JS Library 1.1.1
40843 * Copyright(c) 2006-2007, Ext JS, LLC.
40845 * Originally Released Under LGPL - original licence link has changed is not relivant.
40848 * <script type="text/javascript">
40851 * @class Roo.TabPanel
40852 * @extends Roo.util.Observable
40853 * A lightweight tab container.
40857 // basic tabs 1, built from existing content
40858 var tabs = new Roo.TabPanel("tabs1");
40859 tabs.addTab("script", "View Script");
40860 tabs.addTab("markup", "View Markup");
40861 tabs.activate("script");
40863 // more advanced tabs, built from javascript
40864 var jtabs = new Roo.TabPanel("jtabs");
40865 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40867 // set up the UpdateManager
40868 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40869 var updater = tab2.getUpdateManager();
40870 updater.setDefaultUrl("ajax1.htm");
40871 tab2.on('activate', updater.refresh, updater, true);
40873 // Use setUrl for Ajax loading
40874 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40875 tab3.setUrl("ajax2.htm", null, true);
40878 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40881 jtabs.activate("jtabs-1");
40884 * Create a new TabPanel.
40885 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40886 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40888 Roo.bootstrap.panel.Tabs = function(config){
40890 * The container element for this TabPanel.
40891 * @type Roo.Element
40893 this.el = Roo.get(config.el);
40896 if(typeof config == "boolean"){
40897 this.tabPosition = config ? "bottom" : "top";
40899 Roo.apply(this, config);
40903 if(this.tabPosition == "bottom"){
40904 // if tabs are at the bottom = create the body first.
40905 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40906 this.el.addClass("roo-tabs-bottom");
40908 // next create the tabs holders
40910 if (this.tabPosition == "west"){
40912 var reg = this.region; // fake it..
40914 if (!reg.mgr.parent) {
40917 reg = reg.mgr.parent.region;
40919 Roo.log("got nest?");
40921 if (reg.mgr.getRegion('west')) {
40922 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40923 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40924 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40925 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40926 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40934 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40935 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40936 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40937 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40942 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40945 // finally - if tabs are at the top, then create the body last..
40946 if(this.tabPosition != "bottom"){
40947 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40948 * @type Roo.Element
40950 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40951 this.el.addClass("roo-tabs-top");
40955 this.bodyEl.setStyle("position", "relative");
40957 this.active = null;
40958 this.activateDelegate = this.activate.createDelegate(this);
40963 * Fires when the active tab changes
40964 * @param {Roo.TabPanel} this
40965 * @param {Roo.TabPanelItem} activePanel The new active tab
40969 * @event beforetabchange
40970 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40971 * @param {Roo.TabPanel} this
40972 * @param {Object} e Set cancel to true on this object to cancel the tab change
40973 * @param {Roo.TabPanelItem} tab The tab being changed to
40975 "beforetabchange" : true
40978 Roo.EventManager.onWindowResize(this.onResize, this);
40979 this.cpad = this.el.getPadding("lr");
40980 this.hiddenCount = 0;
40983 // toolbar on the tabbar support...
40984 if (this.toolbar) {
40985 alert("no toolbar support yet");
40986 this.toolbar = false;
40988 var tcfg = this.toolbar;
40989 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40990 this.toolbar = new Roo.Toolbar(tcfg);
40991 if (Roo.isSafari) {
40992 var tbl = tcfg.container.child('table', true);
40993 tbl.setAttribute('width', '100%');
41001 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41004 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41006 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41008 tabPosition : "top",
41010 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41012 currentTabWidth : 0,
41014 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41018 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41022 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41024 preferredTabWidth : 175,
41026 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41028 resizeTabs : false,
41030 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41032 monitorResize : true,
41034 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41036 toolbar : false, // set by caller..
41038 region : false, /// set by caller
41040 disableTooltips : true, // not used yet...
41043 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41044 * @param {String} id The id of the div to use <b>or create</b>
41045 * @param {String} text The text for the tab
41046 * @param {String} content (optional) Content to put in the TabPanelItem body
41047 * @param {Boolean} closable (optional) True to create a close icon on the tab
41048 * @return {Roo.TabPanelItem} The created TabPanelItem
41050 addTab : function(id, text, content, closable, tpl)
41052 var item = new Roo.bootstrap.panel.TabItem({
41056 closable : closable,
41059 this.addTabItem(item);
41061 item.setContent(content);
41067 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41068 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41069 * @return {Roo.TabPanelItem}
41071 getTab : function(id){
41072 return this.items[id];
41076 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41077 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41079 hideTab : function(id){
41080 var t = this.items[id];
41083 this.hiddenCount++;
41084 this.autoSizeTabs();
41089 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41090 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41092 unhideTab : function(id){
41093 var t = this.items[id];
41095 t.setHidden(false);
41096 this.hiddenCount--;
41097 this.autoSizeTabs();
41102 * Adds an existing {@link Roo.TabPanelItem}.
41103 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41105 addTabItem : function(item)
41107 this.items[item.id] = item;
41108 this.items.push(item);
41109 this.autoSizeTabs();
41110 // if(this.resizeTabs){
41111 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41112 // this.autoSizeTabs();
41114 // item.autoSize();
41119 * Removes a {@link Roo.TabPanelItem}.
41120 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41122 removeTab : function(id){
41123 var items = this.items;
41124 var tab = items[id];
41125 if(!tab) { return; }
41126 var index = items.indexOf(tab);
41127 if(this.active == tab && items.length > 1){
41128 var newTab = this.getNextAvailable(index);
41133 this.stripEl.dom.removeChild(tab.pnode.dom);
41134 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41135 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41137 items.splice(index, 1);
41138 delete this.items[tab.id];
41139 tab.fireEvent("close", tab);
41140 tab.purgeListeners();
41141 this.autoSizeTabs();
41144 getNextAvailable : function(start){
41145 var items = this.items;
41147 // look for a next tab that will slide over to
41148 // replace the one being removed
41149 while(index < items.length){
41150 var item = items[++index];
41151 if(item && !item.isHidden()){
41155 // if one isn't found select the previous tab (on the left)
41158 var item = items[--index];
41159 if(item && !item.isHidden()){
41167 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41168 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41170 disableTab : function(id){
41171 var tab = this.items[id];
41172 if(tab && this.active != tab){
41178 * Enables a {@link Roo.TabPanelItem} that is disabled.
41179 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41181 enableTab : function(id){
41182 var tab = this.items[id];
41187 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41188 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41189 * @return {Roo.TabPanelItem} The TabPanelItem.
41191 activate : function(id)
41193 //Roo.log('activite:' + id);
41195 var tab = this.items[id];
41199 if(tab == this.active || tab.disabled){
41203 this.fireEvent("beforetabchange", this, e, tab);
41204 if(e.cancel !== true && !tab.disabled){
41206 this.active.hide();
41208 this.active = this.items[id];
41209 this.active.show();
41210 this.fireEvent("tabchange", this, this.active);
41216 * Gets the active {@link Roo.TabPanelItem}.
41217 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41219 getActiveTab : function(){
41220 return this.active;
41224 * Updates the tab body element to fit the height of the container element
41225 * for overflow scrolling
41226 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41228 syncHeight : function(targetHeight){
41229 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41230 var bm = this.bodyEl.getMargins();
41231 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41232 this.bodyEl.setHeight(newHeight);
41236 onResize : function(){
41237 if(this.monitorResize){
41238 this.autoSizeTabs();
41243 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41245 beginUpdate : function(){
41246 this.updating = true;
41250 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41252 endUpdate : function(){
41253 this.updating = false;
41254 this.autoSizeTabs();
41258 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41260 autoSizeTabs : function()
41262 var count = this.items.length;
41263 var vcount = count - this.hiddenCount;
41266 this.stripEl.hide();
41268 this.stripEl.show();
41271 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41276 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41277 var availWidth = Math.floor(w / vcount);
41278 var b = this.stripBody;
41279 if(b.getWidth() > w){
41280 var tabs = this.items;
41281 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41282 if(availWidth < this.minTabWidth){
41283 /*if(!this.sleft){ // incomplete scrolling code
41284 this.createScrollButtons();
41287 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41290 if(this.currentTabWidth < this.preferredTabWidth){
41291 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41297 * Returns the number of tabs in this TabPanel.
41300 getCount : function(){
41301 return this.items.length;
41305 * Resizes all the tabs to the passed width
41306 * @param {Number} The new width
41308 setTabWidth : function(width){
41309 this.currentTabWidth = width;
41310 for(var i = 0, len = this.items.length; i < len; i++) {
41311 if(!this.items[i].isHidden()) {
41312 this.items[i].setWidth(width);
41318 * Destroys this TabPanel
41319 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41321 destroy : function(removeEl){
41322 Roo.EventManager.removeResizeListener(this.onResize, this);
41323 for(var i = 0, len = this.items.length; i < len; i++){
41324 this.items[i].purgeListeners();
41326 if(removeEl === true){
41327 this.el.update("");
41332 createStrip : function(container)
41334 var strip = document.createElement("nav");
41335 strip.className = Roo.bootstrap.version == 4 ?
41336 "navbar-light bg-light" :
41337 "navbar navbar-default"; //"x-tabs-wrap";
41338 container.appendChild(strip);
41342 createStripList : function(strip)
41344 // div wrapper for retard IE
41345 // returns the "tr" element.
41346 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41347 //'<div class="x-tabs-strip-wrap">'+
41348 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41349 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41350 return strip.firstChild; //.firstChild.firstChild.firstChild;
41352 createBody : function(container)
41354 var body = document.createElement("div");
41355 Roo.id(body, "tab-body");
41356 //Roo.fly(body).addClass("x-tabs-body");
41357 Roo.fly(body).addClass("tab-content");
41358 container.appendChild(body);
41361 createItemBody :function(bodyEl, id){
41362 var body = Roo.getDom(id);
41364 body = document.createElement("div");
41367 //Roo.fly(body).addClass("x-tabs-item-body");
41368 Roo.fly(body).addClass("tab-pane");
41369 bodyEl.insertBefore(body, bodyEl.firstChild);
41373 createStripElements : function(stripEl, text, closable, tpl)
41375 var td = document.createElement("li"); // was td..
41376 td.className = 'nav-item';
41378 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41381 stripEl.appendChild(td);
41383 td.className = "x-tabs-closable";
41384 if(!this.closeTpl){
41385 this.closeTpl = new Roo.Template(
41386 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41387 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41388 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41391 var el = this.closeTpl.overwrite(td, {"text": text});
41392 var close = el.getElementsByTagName("div")[0];
41393 var inner = el.getElementsByTagName("em")[0];
41394 return {"el": el, "close": close, "inner": inner};
41397 // not sure what this is..
41398 // if(!this.tabTpl){
41399 //this.tabTpl = new Roo.Template(
41400 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41401 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41403 // this.tabTpl = new Roo.Template(
41404 // '<a href="#">' +
41405 // '<span unselectable="on"' +
41406 // (this.disableTooltips ? '' : ' title="{text}"') +
41407 // ' >{text}</span></a>'
41413 var template = tpl || this.tabTpl || false;
41416 template = new Roo.Template(
41417 Roo.bootstrap.version == 4 ?
41419 '<a class="nav-link" href="#" unselectable="on"' +
41420 (this.disableTooltips ? '' : ' title="{text}"') +
41423 '<a class="nav-link" href="#">' +
41424 '<span unselectable="on"' +
41425 (this.disableTooltips ? '' : ' title="{text}"') +
41426 ' >{text}</span></a>'
41431 switch (typeof(template)) {
41435 template = new Roo.Template(template);
41441 var el = template.overwrite(td, {"text": text});
41443 var inner = el.getElementsByTagName("span")[0];
41445 return {"el": el, "inner": inner};
41453 * @class Roo.TabPanelItem
41454 * @extends Roo.util.Observable
41455 * Represents an individual item (tab plus body) in a TabPanel.
41456 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41457 * @param {String} id The id of this TabPanelItem
41458 * @param {String} text The text for the tab of this TabPanelItem
41459 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41461 Roo.bootstrap.panel.TabItem = function(config){
41463 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41464 * @type Roo.TabPanel
41466 this.tabPanel = config.panel;
41468 * The id for this TabPanelItem
41471 this.id = config.id;
41473 this.disabled = false;
41475 this.text = config.text;
41477 this.loaded = false;
41478 this.closable = config.closable;
41481 * The body element for this TabPanelItem.
41482 * @type Roo.Element
41484 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41485 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41486 this.bodyEl.setStyle("display", "block");
41487 this.bodyEl.setStyle("zoom", "1");
41488 //this.hideAction();
41490 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41492 this.el = Roo.get(els.el);
41493 this.inner = Roo.get(els.inner, true);
41494 this.textEl = Roo.bootstrap.version == 4 ?
41495 this.el : Roo.get(this.el.dom.firstChild, true);
41497 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41498 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41501 // this.el.on("mousedown", this.onTabMouseDown, this);
41502 this.el.on("click", this.onTabClick, this);
41504 if(config.closable){
41505 var c = Roo.get(els.close, true);
41506 c.dom.title = this.closeText;
41507 c.addClassOnOver("close-over");
41508 c.on("click", this.closeClick, this);
41514 * Fires when this tab becomes the active tab.
41515 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41516 * @param {Roo.TabPanelItem} this
41520 * @event beforeclose
41521 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41522 * @param {Roo.TabPanelItem} this
41523 * @param {Object} e Set cancel to true on this object to cancel the close.
41525 "beforeclose": true,
41528 * Fires when this tab is closed.
41529 * @param {Roo.TabPanelItem} this
41533 * @event deactivate
41534 * Fires when this tab is no longer the active tab.
41535 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41536 * @param {Roo.TabPanelItem} this
41538 "deactivate" : true
41540 this.hidden = false;
41542 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41545 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41547 purgeListeners : function(){
41548 Roo.util.Observable.prototype.purgeListeners.call(this);
41549 this.el.removeAllListeners();
41552 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41555 this.status_node.addClass("active");
41558 this.tabPanel.stripWrap.repaint();
41560 this.fireEvent("activate", this.tabPanel, this);
41564 * Returns true if this tab is the active tab.
41565 * @return {Boolean}
41567 isActive : function(){
41568 return this.tabPanel.getActiveTab() == this;
41572 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41575 this.status_node.removeClass("active");
41577 this.fireEvent("deactivate", this.tabPanel, this);
41580 hideAction : function(){
41581 this.bodyEl.hide();
41582 this.bodyEl.setStyle("position", "absolute");
41583 this.bodyEl.setLeft("-20000px");
41584 this.bodyEl.setTop("-20000px");
41587 showAction : function(){
41588 this.bodyEl.setStyle("position", "relative");
41589 this.bodyEl.setTop("");
41590 this.bodyEl.setLeft("");
41591 this.bodyEl.show();
41595 * Set the tooltip for the tab.
41596 * @param {String} tooltip The tab's tooltip
41598 setTooltip : function(text){
41599 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41600 this.textEl.dom.qtip = text;
41601 this.textEl.dom.removeAttribute('title');
41603 this.textEl.dom.title = text;
41607 onTabClick : function(e){
41608 e.preventDefault();
41609 this.tabPanel.activate(this.id);
41612 onTabMouseDown : function(e){
41613 e.preventDefault();
41614 this.tabPanel.activate(this.id);
41617 getWidth : function(){
41618 return this.inner.getWidth();
41621 setWidth : function(width){
41622 var iwidth = width - this.linode.getPadding("lr");
41623 this.inner.setWidth(iwidth);
41624 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41625 this.linode.setWidth(width);
41629 * Show or hide the tab
41630 * @param {Boolean} hidden True to hide or false to show.
41632 setHidden : function(hidden){
41633 this.hidden = hidden;
41634 this.linode.setStyle("display", hidden ? "none" : "");
41638 * Returns true if this tab is "hidden"
41639 * @return {Boolean}
41641 isHidden : function(){
41642 return this.hidden;
41646 * Returns the text for this tab
41649 getText : function(){
41653 autoSize : function(){
41654 //this.el.beginMeasure();
41655 this.textEl.setWidth(1);
41657 * #2804 [new] Tabs in Roojs
41658 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41660 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41661 //this.el.endMeasure();
41665 * Sets the text for the tab (Note: this also sets the tooltip text)
41666 * @param {String} text The tab's text and tooltip
41668 setText : function(text){
41670 this.textEl.update(text);
41671 this.setTooltip(text);
41672 //if(!this.tabPanel.resizeTabs){
41673 // this.autoSize();
41677 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41679 activate : function(){
41680 this.tabPanel.activate(this.id);
41684 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41686 disable : function(){
41687 if(this.tabPanel.active != this){
41688 this.disabled = true;
41689 this.status_node.addClass("disabled");
41694 * Enables this TabPanelItem if it was previously disabled.
41696 enable : function(){
41697 this.disabled = false;
41698 this.status_node.removeClass("disabled");
41702 * Sets the content for this TabPanelItem.
41703 * @param {String} content The content
41704 * @param {Boolean} loadScripts true to look for and load scripts
41706 setContent : function(content, loadScripts){
41707 this.bodyEl.update(content, loadScripts);
41711 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41712 * @return {Roo.UpdateManager} The UpdateManager
41714 getUpdateManager : function(){
41715 return this.bodyEl.getUpdateManager();
41719 * Set a URL to be used to load the content for this TabPanelItem.
41720 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41721 * @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)
41722 * @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)
41723 * @return {Roo.UpdateManager} The UpdateManager
41725 setUrl : function(url, params, loadOnce){
41726 if(this.refreshDelegate){
41727 this.un('activate', this.refreshDelegate);
41729 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41730 this.on("activate", this.refreshDelegate);
41731 return this.bodyEl.getUpdateManager();
41735 _handleRefresh : function(url, params, loadOnce){
41736 if(!loadOnce || !this.loaded){
41737 var updater = this.bodyEl.getUpdateManager();
41738 updater.update(url, params, this._setLoaded.createDelegate(this));
41743 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41744 * Will fail silently if the setUrl method has not been called.
41745 * This does not activate the panel, just updates its content.
41747 refresh : function(){
41748 if(this.refreshDelegate){
41749 this.loaded = false;
41750 this.refreshDelegate();
41755 _setLoaded : function(){
41756 this.loaded = true;
41760 closeClick : function(e){
41763 this.fireEvent("beforeclose", this, o);
41764 if(o.cancel !== true){
41765 this.tabPanel.removeTab(this.id);
41769 * The text displayed in the tooltip for the close icon.
41772 closeText : "Close this tab"
41775 * This script refer to:
41776 * Title: International Telephone Input
41777 * Author: Jack O'Connor
41778 * Code version: v12.1.12
41779 * Availability: https://github.com/jackocnr/intl-tel-input.git
41782 Roo.bootstrap.PhoneInputData = function() {
41785 "Afghanistan (افغانستان)",
41790 "Albania (Shqipëri)",
41795 "Algeria (الجزائر)",
41820 "Antigua and Barbuda",
41830 "Armenia (Հայաստան)",
41846 "Austria (Österreich)",
41851 "Azerbaijan (Azərbaycan)",
41861 "Bahrain (البحرين)",
41866 "Bangladesh (বাংলাদেশ)",
41876 "Belarus (Беларусь)",
41881 "Belgium (België)",
41911 "Bosnia and Herzegovina (Босна и Херцеговина)",
41926 "British Indian Ocean Territory",
41931 "British Virgin Islands",
41941 "Bulgaria (България)",
41951 "Burundi (Uburundi)",
41956 "Cambodia (កម្ពុជា)",
41961 "Cameroon (Cameroun)",
41970 ["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"]
41973 "Cape Verde (Kabu Verdi)",
41978 "Caribbean Netherlands",
41989 "Central African Republic (République centrafricaine)",
42009 "Christmas Island",
42015 "Cocos (Keeling) Islands",
42026 "Comoros (جزر القمر)",
42031 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42036 "Congo (Republic) (Congo-Brazzaville)",
42056 "Croatia (Hrvatska)",
42077 "Czech Republic (Česká republika)",
42082 "Denmark (Danmark)",
42097 "Dominican Republic (República Dominicana)",
42101 ["809", "829", "849"]
42119 "Equatorial Guinea (Guinea Ecuatorial)",
42139 "Falkland Islands (Islas Malvinas)",
42144 "Faroe Islands (Føroyar)",
42165 "French Guiana (Guyane française)",
42170 "French Polynesia (Polynésie française)",
42185 "Georgia (საქართველო)",
42190 "Germany (Deutschland)",
42210 "Greenland (Kalaallit Nunaat)",
42247 "Guinea-Bissau (Guiné Bissau)",
42272 "Hungary (Magyarország)",
42277 "Iceland (Ísland)",
42297 "Iraq (العراق)",
42313 "Israel (ישראל)",
42340 "Jordan (الأردن)",
42345 "Kazakhstan (Казахстан)",
42366 "Kuwait (الكويت)",
42371 "Kyrgyzstan (Кыргызстан)",
42381 "Latvia (Latvija)",
42386 "Lebanon (لبنان)",
42401 "Libya (ليبيا)",
42411 "Lithuania (Lietuva)",
42426 "Macedonia (FYROM) (Македонија)",
42431 "Madagascar (Madagasikara)",
42461 "Marshall Islands",
42471 "Mauritania (موريتانيا)",
42476 "Mauritius (Moris)",
42497 "Moldova (Republica Moldova)",
42507 "Mongolia (Монгол)",
42512 "Montenegro (Crna Gora)",
42522 "Morocco (المغرب)",
42528 "Mozambique (Moçambique)",
42533 "Myanmar (Burma) (မြန်မာ)",
42538 "Namibia (Namibië)",
42553 "Netherlands (Nederland)",
42558 "New Caledonia (Nouvelle-Calédonie)",
42593 "North Korea (조선 민주주의 인민 공화국)",
42598 "Northern Mariana Islands",
42614 "Pakistan (پاکستان)",
42624 "Palestine (فلسطين)",
42634 "Papua New Guinea",
42676 "Réunion (La Réunion)",
42682 "Romania (România)",
42698 "Saint Barthélemy",
42709 "Saint Kitts and Nevis",
42719 "Saint Martin (Saint-Martin (partie française))",
42725 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42730 "Saint Vincent and the Grenadines",
42745 "São Tomé and Príncipe (São Tomé e Príncipe)",
42750 "Saudi Arabia (المملكة العربية السعودية)",
42755 "Senegal (Sénégal)",
42785 "Slovakia (Slovensko)",
42790 "Slovenia (Slovenija)",
42800 "Somalia (Soomaaliya)",
42810 "South Korea (대한민국)",
42815 "South Sudan (جنوب السودان)",
42825 "Sri Lanka (ශ්රී ලංකාව)",
42830 "Sudan (السودان)",
42840 "Svalbard and Jan Mayen",
42851 "Sweden (Sverige)",
42856 "Switzerland (Schweiz)",
42861 "Syria (سوريا)",
42906 "Trinidad and Tobago",
42911 "Tunisia (تونس)",
42916 "Turkey (Türkiye)",
42926 "Turks and Caicos Islands",
42936 "U.S. Virgin Islands",
42946 "Ukraine (Україна)",
42951 "United Arab Emirates (الإمارات العربية المتحدة)",
42973 "Uzbekistan (Oʻzbekiston)",
42983 "Vatican City (Città del Vaticano)",
42994 "Vietnam (Việt Nam)",
42999 "Wallis and Futuna (Wallis-et-Futuna)",
43004 "Western Sahara (الصحراء الغربية)",
43010 "Yemen (اليمن)",
43034 * This script refer to:
43035 * Title: International Telephone Input
43036 * Author: Jack O'Connor
43037 * Code version: v12.1.12
43038 * Availability: https://github.com/jackocnr/intl-tel-input.git
43042 * @class Roo.bootstrap.PhoneInput
43043 * @extends Roo.bootstrap.TriggerField
43044 * An input with International dial-code selection
43046 * @cfg {String} defaultDialCode default '+852'
43047 * @cfg {Array} preferedCountries default []
43050 * Create a new PhoneInput.
43051 * @param {Object} config Configuration options
43054 Roo.bootstrap.PhoneInput = function(config) {
43055 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43058 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43060 listWidth: undefined,
43062 selectedClass: 'active',
43064 invalidClass : "has-warning",
43066 validClass: 'has-success',
43068 allowed: '0123456789',
43073 * @cfg {String} defaultDialCode The default dial code when initializing the input
43075 defaultDialCode: '+852',
43078 * @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
43080 preferedCountries: false,
43082 getAutoCreate : function()
43084 var data = Roo.bootstrap.PhoneInputData();
43085 var align = this.labelAlign || this.parentLabelAlign();
43088 this.allCountries = [];
43089 this.dialCodeMapping = [];
43091 for (var i = 0; i < data.length; i++) {
43093 this.allCountries[i] = {
43097 priority: c[3] || 0,
43098 areaCodes: c[4] || null
43100 this.dialCodeMapping[c[2]] = {
43103 priority: c[3] || 0,
43104 areaCodes: c[4] || null
43116 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43117 maxlength: this.max_length,
43118 cls : 'form-control tel-input',
43119 autocomplete: 'new-password'
43122 var hiddenInput = {
43125 cls: 'hidden-tel-input'
43129 hiddenInput.name = this.name;
43132 if (this.disabled) {
43133 input.disabled = true;
43136 var flag_container = {
43153 cls: this.hasFeedback ? 'has-feedback' : '',
43159 cls: 'dial-code-holder',
43166 cls: 'roo-select2-container input-group',
43173 if (this.fieldLabel.length) {
43176 tooltip: 'This field is required'
43182 cls: 'control-label',
43188 html: this.fieldLabel
43191 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43197 if(this.indicatorpos == 'right') {
43198 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43205 if(align == 'left') {
43213 if(this.labelWidth > 12){
43214 label.style = "width: " + this.labelWidth + 'px';
43216 if(this.labelWidth < 13 && this.labelmd == 0){
43217 this.labelmd = this.labelWidth;
43219 if(this.labellg > 0){
43220 label.cls += ' col-lg-' + this.labellg;
43221 input.cls += ' col-lg-' + (12 - this.labellg);
43223 if(this.labelmd > 0){
43224 label.cls += ' col-md-' + this.labelmd;
43225 container.cls += ' col-md-' + (12 - this.labelmd);
43227 if(this.labelsm > 0){
43228 label.cls += ' col-sm-' + this.labelsm;
43229 container.cls += ' col-sm-' + (12 - this.labelsm);
43231 if(this.labelxs > 0){
43232 label.cls += ' col-xs-' + this.labelxs;
43233 container.cls += ' col-xs-' + (12 - this.labelxs);
43243 var settings = this;
43245 ['xs','sm','md','lg'].map(function(size){
43246 if (settings[size]) {
43247 cfg.cls += ' col-' + size + '-' + settings[size];
43251 this.store = new Roo.data.Store({
43252 proxy : new Roo.data.MemoryProxy({}),
43253 reader : new Roo.data.JsonReader({
43264 'name' : 'dialCode',
43268 'name' : 'priority',
43272 'name' : 'areaCodes',
43279 if(!this.preferedCountries) {
43280 this.preferedCountries = [
43287 var p = this.preferedCountries.reverse();
43290 for (var i = 0; i < p.length; i++) {
43291 for (var j = 0; j < this.allCountries.length; j++) {
43292 if(this.allCountries[j].iso2 == p[i]) {
43293 var t = this.allCountries[j];
43294 this.allCountries.splice(j,1);
43295 this.allCountries.unshift(t);
43301 this.store.proxy.data = {
43303 data: this.allCountries
43309 initEvents : function()
43312 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43314 this.indicator = this.indicatorEl();
43315 this.flag = this.flagEl();
43316 this.dialCodeHolder = this.dialCodeHolderEl();
43318 this.trigger = this.el.select('div.flag-box',true).first();
43319 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43324 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43325 _this.list.setWidth(lw);
43328 this.list.on('mouseover', this.onViewOver, this);
43329 this.list.on('mousemove', this.onViewMove, this);
43330 this.inputEl().on("keyup", this.onKeyUp, this);
43331 this.inputEl().on("keypress", this.onKeyPress, this);
43333 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43335 this.view = new Roo.View(this.list, this.tpl, {
43336 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43339 this.view.on('click', this.onViewClick, this);
43340 this.setValue(this.defaultDialCode);
43343 onTriggerClick : function(e)
43345 Roo.log('trigger click');
43350 if(this.isExpanded()){
43352 this.hasFocus = false;
43354 this.store.load({});
43355 this.hasFocus = true;
43360 isExpanded : function()
43362 return this.list.isVisible();
43365 collapse : function()
43367 if(!this.isExpanded()){
43371 Roo.get(document).un('mousedown', this.collapseIf, this);
43372 Roo.get(document).un('mousewheel', this.collapseIf, this);
43373 this.fireEvent('collapse', this);
43377 expand : function()
43381 if(this.isExpanded() || !this.hasFocus){
43385 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43386 this.list.setWidth(lw);
43389 this.restrictHeight();
43391 Roo.get(document).on('mousedown', this.collapseIf, this);
43392 Roo.get(document).on('mousewheel', this.collapseIf, this);
43394 this.fireEvent('expand', this);
43397 restrictHeight : function()
43399 this.list.alignTo(this.inputEl(), this.listAlign);
43400 this.list.alignTo(this.inputEl(), this.listAlign);
43403 onViewOver : function(e, t)
43405 if(this.inKeyMode){
43408 var item = this.view.findItemFromChild(t);
43411 var index = this.view.indexOf(item);
43412 this.select(index, false);
43417 onViewClick : function(view, doFocus, el, e)
43419 var index = this.view.getSelectedIndexes()[0];
43421 var r = this.store.getAt(index);
43424 this.onSelect(r, index);
43426 if(doFocus !== false && !this.blockFocus){
43427 this.inputEl().focus();
43431 onViewMove : function(e, t)
43433 this.inKeyMode = false;
43436 select : function(index, scrollIntoView)
43438 this.selectedIndex = index;
43439 this.view.select(index);
43440 if(scrollIntoView !== false){
43441 var el = this.view.getNode(index);
43443 this.list.scrollChildIntoView(el, false);
43448 createList : function()
43450 this.list = Roo.get(document.body).createChild({
43452 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43453 style: 'display:none'
43456 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43459 collapseIf : function(e)
43461 var in_combo = e.within(this.el);
43462 var in_list = e.within(this.list);
43463 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43465 if (in_combo || in_list || is_list) {
43471 onSelect : function(record, index)
43473 if(this.fireEvent('beforeselect', this, record, index) !== false){
43475 this.setFlagClass(record.data.iso2);
43476 this.setDialCode(record.data.dialCode);
43477 this.hasFocus = false;
43479 this.fireEvent('select', this, record, index);
43483 flagEl : function()
43485 var flag = this.el.select('div.flag',true).first();
43492 dialCodeHolderEl : function()
43494 var d = this.el.select('input.dial-code-holder',true).first();
43501 setDialCode : function(v)
43503 this.dialCodeHolder.dom.value = '+'+v;
43506 setFlagClass : function(n)
43508 this.flag.dom.className = 'flag '+n;
43511 getValue : function()
43513 var v = this.inputEl().getValue();
43514 if(this.dialCodeHolder) {
43515 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43520 setValue : function(v)
43522 var d = this.getDialCode(v);
43524 //invalid dial code
43525 if(v.length == 0 || !d || d.length == 0) {
43527 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43528 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43534 this.setFlagClass(this.dialCodeMapping[d].iso2);
43535 this.setDialCode(d);
43536 this.inputEl().dom.value = v.replace('+'+d,'');
43537 this.hiddenEl().dom.value = this.getValue();
43542 getDialCode : function(v)
43546 if (v.length == 0) {
43547 return this.dialCodeHolder.dom.value;
43551 if (v.charAt(0) != "+") {
43554 var numericChars = "";
43555 for (var i = 1; i < v.length; i++) {
43556 var c = v.charAt(i);
43559 if (this.dialCodeMapping[numericChars]) {
43560 dialCode = v.substr(1, i);
43562 if (numericChars.length == 4) {
43572 this.setValue(this.defaultDialCode);
43576 hiddenEl : function()
43578 return this.el.select('input.hidden-tel-input',true).first();
43581 // after setting val
43582 onKeyUp : function(e){
43583 this.setValue(this.getValue());
43586 onKeyPress : function(e){
43587 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43594 * @class Roo.bootstrap.MoneyField
43595 * @extends Roo.bootstrap.ComboBox
43596 * Bootstrap MoneyField class
43599 * Create a new MoneyField.
43600 * @param {Object} config Configuration options
43603 Roo.bootstrap.MoneyField = function(config) {
43605 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43609 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43612 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43614 allowDecimals : true,
43616 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43618 decimalSeparator : ".",
43620 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43622 decimalPrecision : 0,
43624 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43626 allowNegative : true,
43628 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43632 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43634 minValue : Number.NEGATIVE_INFINITY,
43636 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43638 maxValue : Number.MAX_VALUE,
43640 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43642 minText : "The minimum value for this field is {0}",
43644 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43646 maxText : "The maximum value for this field is {0}",
43648 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43649 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43651 nanText : "{0} is not a valid number",
43653 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43657 * @cfg {String} defaults currency of the MoneyField
43658 * value should be in lkey
43660 defaultCurrency : false,
43662 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43664 thousandsDelimiter : false,
43666 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43677 getAutoCreate : function()
43679 var align = this.labelAlign || this.parentLabelAlign();
43691 cls : 'form-control roo-money-amount-input',
43692 autocomplete: 'new-password'
43695 var hiddenInput = {
43699 cls: 'hidden-number-input'
43702 if(this.max_length) {
43703 input.maxlength = this.max_length;
43707 hiddenInput.name = this.name;
43710 if (this.disabled) {
43711 input.disabled = true;
43714 var clg = 12 - this.inputlg;
43715 var cmd = 12 - this.inputmd;
43716 var csm = 12 - this.inputsm;
43717 var cxs = 12 - this.inputxs;
43721 cls : 'row roo-money-field',
43725 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43729 cls: 'roo-select2-container input-group',
43733 cls : 'form-control roo-money-currency-input',
43734 autocomplete: 'new-password',
43736 name : this.currencyName
43740 cls : 'input-group-addon',
43754 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43758 cls: this.hasFeedback ? 'has-feedback' : '',
43769 if (this.fieldLabel.length) {
43772 tooltip: 'This field is required'
43778 cls: 'control-label',
43784 html: this.fieldLabel
43787 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43793 if(this.indicatorpos == 'right') {
43794 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43801 if(align == 'left') {
43809 if(this.labelWidth > 12){
43810 label.style = "width: " + this.labelWidth + 'px';
43812 if(this.labelWidth < 13 && this.labelmd == 0){
43813 this.labelmd = this.labelWidth;
43815 if(this.labellg > 0){
43816 label.cls += ' col-lg-' + this.labellg;
43817 input.cls += ' col-lg-' + (12 - this.labellg);
43819 if(this.labelmd > 0){
43820 label.cls += ' col-md-' + this.labelmd;
43821 container.cls += ' col-md-' + (12 - this.labelmd);
43823 if(this.labelsm > 0){
43824 label.cls += ' col-sm-' + this.labelsm;
43825 container.cls += ' col-sm-' + (12 - this.labelsm);
43827 if(this.labelxs > 0){
43828 label.cls += ' col-xs-' + this.labelxs;
43829 container.cls += ' col-xs-' + (12 - this.labelxs);
43840 var settings = this;
43842 ['xs','sm','md','lg'].map(function(size){
43843 if (settings[size]) {
43844 cfg.cls += ' col-' + size + '-' + settings[size];
43851 initEvents : function()
43853 this.indicator = this.indicatorEl();
43855 this.initCurrencyEvent();
43857 this.initNumberEvent();
43860 initCurrencyEvent : function()
43863 throw "can not find store for combo";
43866 this.store = Roo.factory(this.store, Roo.data);
43867 this.store.parent = this;
43871 this.triggerEl = this.el.select('.input-group-addon', true).first();
43873 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43878 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43879 _this.list.setWidth(lw);
43882 this.list.on('mouseover', this.onViewOver, this);
43883 this.list.on('mousemove', this.onViewMove, this);
43884 this.list.on('scroll', this.onViewScroll, this);
43887 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43890 this.view = new Roo.View(this.list, this.tpl, {
43891 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43894 this.view.on('click', this.onViewClick, this);
43896 this.store.on('beforeload', this.onBeforeLoad, this);
43897 this.store.on('load', this.onLoad, this);
43898 this.store.on('loadexception', this.onLoadException, this);
43900 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43901 "up" : function(e){
43902 this.inKeyMode = true;
43906 "down" : function(e){
43907 if(!this.isExpanded()){
43908 this.onTriggerClick();
43910 this.inKeyMode = true;
43915 "enter" : function(e){
43918 if(this.fireEvent("specialkey", this, e)){
43919 this.onViewClick(false);
43925 "esc" : function(e){
43929 "tab" : function(e){
43932 if(this.fireEvent("specialkey", this, e)){
43933 this.onViewClick(false);
43941 doRelay : function(foo, bar, hname){
43942 if(hname == 'down' || this.scope.isExpanded()){
43943 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43951 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43955 initNumberEvent : function(e)
43957 this.inputEl().on("keydown" , this.fireKey, this);
43958 this.inputEl().on("focus", this.onFocus, this);
43959 this.inputEl().on("blur", this.onBlur, this);
43961 this.inputEl().relayEvent('keyup', this);
43963 if(this.indicator){
43964 this.indicator.addClass('invisible');
43967 this.originalValue = this.getValue();
43969 if(this.validationEvent == 'keyup'){
43970 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43971 this.inputEl().on('keyup', this.filterValidation, this);
43973 else if(this.validationEvent !== false){
43974 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43977 if(this.selectOnFocus){
43978 this.on("focus", this.preFocus, this);
43981 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43982 this.inputEl().on("keypress", this.filterKeys, this);
43984 this.inputEl().relayEvent('keypress', this);
43987 var allowed = "0123456789";
43989 if(this.allowDecimals){
43990 allowed += this.decimalSeparator;
43993 if(this.allowNegative){
43997 if(this.thousandsDelimiter) {
44001 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44003 var keyPress = function(e){
44005 var k = e.getKey();
44007 var c = e.getCharCode();
44010 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44011 allowed.indexOf(String.fromCharCode(c)) === -1
44017 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44021 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44026 this.inputEl().on("keypress", keyPress, this);
44030 onTriggerClick : function(e)
44037 this.loadNext = false;
44039 if(this.isExpanded()){
44044 this.hasFocus = true;
44046 if(this.triggerAction == 'all') {
44047 this.doQuery(this.allQuery, true);
44051 this.doQuery(this.getRawValue());
44054 getCurrency : function()
44056 var v = this.currencyEl().getValue();
44061 restrictHeight : function()
44063 this.list.alignTo(this.currencyEl(), this.listAlign);
44064 this.list.alignTo(this.currencyEl(), this.listAlign);
44067 onViewClick : function(view, doFocus, el, e)
44069 var index = this.view.getSelectedIndexes()[0];
44071 var r = this.store.getAt(index);
44074 this.onSelect(r, index);
44078 onSelect : function(record, index){
44080 if(this.fireEvent('beforeselect', this, record, index) !== false){
44082 this.setFromCurrencyData(index > -1 ? record.data : false);
44086 this.fireEvent('select', this, record, index);
44090 setFromCurrencyData : function(o)
44094 this.lastCurrency = o;
44096 if (this.currencyField) {
44097 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44099 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44102 this.lastSelectionText = currency;
44104 //setting default currency
44105 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44106 this.setCurrency(this.defaultCurrency);
44110 this.setCurrency(currency);
44113 setFromData : function(o)
44117 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44119 this.setFromCurrencyData(c);
44124 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44126 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44129 this.setValue(value);
44133 setCurrency : function(v)
44135 this.currencyValue = v;
44138 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44143 setValue : function(v)
44145 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44151 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44153 this.inputEl().dom.value = (v == '') ? '' :
44154 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44156 if(!this.allowZero && v === '0') {
44157 this.hiddenEl().dom.value = '';
44158 this.inputEl().dom.value = '';
44165 getRawValue : function()
44167 var v = this.inputEl().getValue();
44172 getValue : function()
44174 return this.fixPrecision(this.parseValue(this.getRawValue()));
44177 parseValue : function(value)
44179 if(this.thousandsDelimiter) {
44181 r = new RegExp(",", "g");
44182 value = value.replace(r, "");
44185 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44186 return isNaN(value) ? '' : value;
44190 fixPrecision : function(value)
44192 if(this.thousandsDelimiter) {
44194 r = new RegExp(",", "g");
44195 value = value.replace(r, "");
44198 var nan = isNaN(value);
44200 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44201 return nan ? '' : value;
44203 return parseFloat(value).toFixed(this.decimalPrecision);
44206 decimalPrecisionFcn : function(v)
44208 return Math.floor(v);
44211 validateValue : function(value)
44213 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44217 var num = this.parseValue(value);
44220 this.markInvalid(String.format(this.nanText, value));
44224 if(num < this.minValue){
44225 this.markInvalid(String.format(this.minText, this.minValue));
44229 if(num > this.maxValue){
44230 this.markInvalid(String.format(this.maxText, this.maxValue));
44237 validate : function()
44239 if(this.disabled || this.allowBlank){
44244 var currency = this.getCurrency();
44246 if(this.validateValue(this.getRawValue()) && currency.length){
44251 this.markInvalid();
44255 getName: function()
44260 beforeBlur : function()
44266 var v = this.parseValue(this.getRawValue());
44273 onBlur : function()
44277 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44278 //this.el.removeClass(this.focusClass);
44281 this.hasFocus = false;
44283 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44287 var v = this.getValue();
44289 if(String(v) !== String(this.startValue)){
44290 this.fireEvent('change', this, v, this.startValue);
44293 this.fireEvent("blur", this);
44296 inputEl : function()
44298 return this.el.select('.roo-money-amount-input', true).first();
44301 currencyEl : function()
44303 return this.el.select('.roo-money-currency-input', true).first();
44306 hiddenEl : function()
44308 return this.el.select('input.hidden-number-input',true).first();
44312 * @class Roo.bootstrap.BezierSignature
44313 * @extends Roo.bootstrap.Component
44314 * Bootstrap BezierSignature class
44315 * This script refer to:
44316 * Title: Signature Pad
44318 * Availability: https://github.com/szimek/signature_pad
44321 * Create a new BezierSignature
44322 * @param {Object} config The config object
44325 Roo.bootstrap.BezierSignature = function(config){
44326 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44332 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44339 mouse_btn_down: true,
44342 * @cfg {int} canvas height
44344 canvas_height: '200px',
44347 * @cfg {float|function} Radius of a single dot.
44352 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44357 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44362 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44367 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44372 * @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.
44374 bg_color: 'rgba(0, 0, 0, 0)',
44377 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44379 dot_color: 'black',
44382 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44384 velocity_filter_weight: 0.7,
44387 * @cfg {function} Callback when stroke begin.
44392 * @cfg {function} Callback when stroke end.
44396 getAutoCreate : function()
44398 var cls = 'roo-signature column';
44401 cls += ' ' + this.cls;
44411 for(var i = 0; i < col_sizes.length; i++) {
44412 if(this[col_sizes[i]]) {
44413 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44423 cls: 'roo-signature-body',
44427 cls: 'roo-signature-body-canvas',
44428 height: this.canvas_height,
44429 width: this.canvas_width
44436 style: 'display: none'
44444 initEvents: function()
44446 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44448 var canvas = this.canvasEl();
44450 // mouse && touch event swapping...
44451 canvas.dom.style.touchAction = 'none';
44452 canvas.dom.style.msTouchAction = 'none';
44454 this.mouse_btn_down = false;
44455 canvas.on('mousedown', this._handleMouseDown, this);
44456 canvas.on('mousemove', this._handleMouseMove, this);
44457 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44459 if (window.PointerEvent) {
44460 canvas.on('pointerdown', this._handleMouseDown, this);
44461 canvas.on('pointermove', this._handleMouseMove, this);
44462 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44465 if ('ontouchstart' in window) {
44466 canvas.on('touchstart', this._handleTouchStart, this);
44467 canvas.on('touchmove', this._handleTouchMove, this);
44468 canvas.on('touchend', this._handleTouchEnd, this);
44471 Roo.EventManager.onWindowResize(this.resize, this, true);
44473 // file input event
44474 this.fileEl().on('change', this.uploadImage, this);
44481 resize: function(){
44483 var canvas = this.canvasEl().dom;
44484 var ctx = this.canvasElCtx();
44485 var img_data = false;
44487 if(canvas.width > 0) {
44488 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44490 // setting canvas width will clean img data
44493 var style = window.getComputedStyle ?
44494 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44496 var padding_left = parseInt(style.paddingLeft) || 0;
44497 var padding_right = parseInt(style.paddingRight) || 0;
44499 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44502 ctx.putImageData(img_data, 0, 0);
44506 _handleMouseDown: function(e)
44508 if (e.browserEvent.which === 1) {
44509 this.mouse_btn_down = true;
44510 this.strokeBegin(e);
44514 _handleMouseMove: function (e)
44516 if (this.mouse_btn_down) {
44517 this.strokeMoveUpdate(e);
44521 _handleMouseUp: function (e)
44523 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44524 this.mouse_btn_down = false;
44529 _handleTouchStart: function (e) {
44531 e.preventDefault();
44532 if (e.browserEvent.targetTouches.length === 1) {
44533 // var touch = e.browserEvent.changedTouches[0];
44534 // this.strokeBegin(touch);
44536 this.strokeBegin(e); // assume e catching the correct xy...
44540 _handleTouchMove: function (e) {
44541 e.preventDefault();
44542 // var touch = event.targetTouches[0];
44543 // _this._strokeMoveUpdate(touch);
44544 this.strokeMoveUpdate(e);
44547 _handleTouchEnd: function (e) {
44548 var wasCanvasTouched = e.target === this.canvasEl().dom;
44549 if (wasCanvasTouched) {
44550 e.preventDefault();
44551 // var touch = event.changedTouches[0];
44552 // _this._strokeEnd(touch);
44557 reset: function () {
44558 this._lastPoints = [];
44559 this._lastVelocity = 0;
44560 this._lastWidth = (this.min_width + this.max_width) / 2;
44561 this.canvasElCtx().fillStyle = this.dot_color;
44564 strokeMoveUpdate: function(e)
44566 this.strokeUpdate(e);
44568 if (this.throttle) {
44569 this.throttleStroke(this.strokeUpdate, this.throttle);
44572 this.strokeUpdate(e);
44576 strokeBegin: function(e)
44578 var newPointGroup = {
44579 color: this.dot_color,
44583 if (typeof this.onBegin === 'function') {
44587 this.curve_data.push(newPointGroup);
44589 this.strokeUpdate(e);
44592 strokeUpdate: function(e)
44594 var rect = this.canvasEl().dom.getBoundingClientRect();
44595 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44596 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44597 var lastPoints = lastPointGroup.points;
44598 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44599 var isLastPointTooClose = lastPoint
44600 ? point.distanceTo(lastPoint) <= this.min_distance
44602 var color = lastPointGroup.color;
44603 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44604 var curve = this.addPoint(point);
44606 this.drawDot({color: color, point: point});
44609 this.drawCurve({color: color, curve: curve});
44619 strokeEnd: function(e)
44621 this.strokeUpdate(e);
44622 if (typeof this.onEnd === 'function') {
44627 addPoint: function (point) {
44628 var _lastPoints = this._lastPoints;
44629 _lastPoints.push(point);
44630 if (_lastPoints.length > 2) {
44631 if (_lastPoints.length === 3) {
44632 _lastPoints.unshift(_lastPoints[0]);
44634 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44635 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44636 _lastPoints.shift();
44642 calculateCurveWidths: function (startPoint, endPoint) {
44643 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44644 (1 - this.velocity_filter_weight) * this._lastVelocity;
44646 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44649 start: this._lastWidth
44652 this._lastVelocity = velocity;
44653 this._lastWidth = newWidth;
44657 drawDot: function (_a) {
44658 var color = _a.color, point = _a.point;
44659 var ctx = this.canvasElCtx();
44660 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44662 this.drawCurveSegment(point.x, point.y, width);
44664 ctx.fillStyle = color;
44668 drawCurve: function (_a) {
44669 var color = _a.color, curve = _a.curve;
44670 var ctx = this.canvasElCtx();
44671 var widthDelta = curve.endWidth - curve.startWidth;
44672 var drawSteps = Math.floor(curve.length()) * 2;
44674 ctx.fillStyle = color;
44675 for (var i = 0; i < drawSteps; i += 1) {
44676 var t = i / drawSteps;
44682 var x = uuu * curve.startPoint.x;
44683 x += 3 * uu * t * curve.control1.x;
44684 x += 3 * u * tt * curve.control2.x;
44685 x += ttt * curve.endPoint.x;
44686 var y = uuu * curve.startPoint.y;
44687 y += 3 * uu * t * curve.control1.y;
44688 y += 3 * u * tt * curve.control2.y;
44689 y += ttt * curve.endPoint.y;
44690 var width = curve.startWidth + ttt * widthDelta;
44691 this.drawCurveSegment(x, y, width);
44697 drawCurveSegment: function (x, y, width) {
44698 var ctx = this.canvasElCtx();
44700 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44701 this.is_empty = false;
44706 var ctx = this.canvasElCtx();
44707 var canvas = this.canvasEl().dom;
44708 ctx.fillStyle = this.bg_color;
44709 ctx.clearRect(0, 0, canvas.width, canvas.height);
44710 ctx.fillRect(0, 0, canvas.width, canvas.height);
44711 this.curve_data = [];
44713 this.is_empty = true;
44718 return this.el.select('input',true).first();
44721 canvasEl: function()
44723 return this.el.select('canvas',true).first();
44726 canvasElCtx: function()
44728 return this.el.select('canvas',true).first().dom.getContext('2d');
44731 getImage: function(type)
44733 if(this.is_empty) {
44738 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44741 drawFromImage: function(img_src)
44743 var img = new Image();
44745 img.onload = function(){
44746 this.canvasElCtx().drawImage(img, 0, 0);
44751 this.is_empty = false;
44754 selectImage: function()
44756 this.fileEl().dom.click();
44759 uploadImage: function(e)
44761 var reader = new FileReader();
44763 reader.onload = function(e){
44764 var img = new Image();
44765 img.onload = function(){
44767 this.canvasElCtx().drawImage(img, 0, 0);
44769 img.src = e.target.result;
44772 reader.readAsDataURL(e.target.files[0]);
44775 // Bezier Point Constructor
44776 Point: (function () {
44777 function Point(x, y, time) {
44780 this.time = time || Date.now();
44782 Point.prototype.distanceTo = function (start) {
44783 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44785 Point.prototype.equals = function (other) {
44786 return this.x === other.x && this.y === other.y && this.time === other.time;
44788 Point.prototype.velocityFrom = function (start) {
44789 return this.time !== start.time
44790 ? this.distanceTo(start) / (this.time - start.time)
44797 // Bezier Constructor
44798 Bezier: (function () {
44799 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44800 this.startPoint = startPoint;
44801 this.control2 = control2;
44802 this.control1 = control1;
44803 this.endPoint = endPoint;
44804 this.startWidth = startWidth;
44805 this.endWidth = endWidth;
44807 Bezier.fromPoints = function (points, widths, scope) {
44808 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44809 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44810 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44812 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44813 var dx1 = s1.x - s2.x;
44814 var dy1 = s1.y - s2.y;
44815 var dx2 = s2.x - s3.x;
44816 var dy2 = s2.y - s3.y;
44817 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44818 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44819 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44820 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44821 var dxm = m1.x - m2.x;
44822 var dym = m1.y - m2.y;
44823 var k = l2 / (l1 + l2);
44824 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44825 var tx = s2.x - cm.x;
44826 var ty = s2.y - cm.y;
44828 c1: new scope.Point(m1.x + tx, m1.y + ty),
44829 c2: new scope.Point(m2.x + tx, m2.y + ty)
44832 Bezier.prototype.length = function () {
44837 for (var i = 0; i <= steps; i += 1) {
44839 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44840 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44842 var xdiff = cx - px;
44843 var ydiff = cy - py;
44844 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44851 Bezier.prototype.point = function (t, start, c1, c2, end) {
44852 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44853 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44854 + (3.0 * c2 * (1.0 - t) * t * t)
44855 + (end * t * t * t);
44860 throttleStroke: function(fn, wait) {
44861 if (wait === void 0) { wait = 250; }
44863 var timeout = null;
44867 var later = function () {
44868 previous = Date.now();
44870 result = fn.apply(storedContext, storedArgs);
44872 storedContext = null;
44876 return function wrapper() {
44878 for (var _i = 0; _i < arguments.length; _i++) {
44879 args[_i] = arguments[_i];
44881 var now = Date.now();
44882 var remaining = wait - (now - previous);
44883 storedContext = this;
44885 if (remaining <= 0 || remaining > wait) {
44887 clearTimeout(timeout);
44891 result = fn.apply(storedContext, storedArgs);
44893 storedContext = null;
44897 else if (!timeout) {
44898 timeout = window.setTimeout(later, remaining);