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;
7793 if(typeof c.dataIndex == "undefined"){
7796 if(typeof c.renderer == "string"){
7797 c.renderer = Roo.util.Format[c.renderer];
7799 if(typeof c.id == "undefined"){
7802 if(c.editor && c.editor.xtype){
7803 c.editor = Roo.factory(c.editor, Roo.grid);
7805 if(c.editor && c.editor.isFormField){
7806 c.editor = new Roo.grid.GridEditor(c.editor);
7808 this.lookup[c.id] = c;
7813 Roo.grid.ColumnModel.defaultRenderer = function(value)
7815 if(typeof value == "object") {
7818 if(typeof value == "string" && value.length < 1){
7822 return String.format("{0}", value);
7825 // Alias for backwards compatibility
7826 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7829 * Ext JS Library 1.1.1
7830 * Copyright(c) 2006-2007, Ext JS, LLC.
7832 * Originally Released Under LGPL - original licence link has changed is not relivant.
7835 * <script type="text/javascript">
7839 * @class Roo.LoadMask
7840 * A simple utility class for generically masking elements while loading data. If the element being masked has
7841 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7842 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7843 * element's UpdateManager load indicator and will be destroyed after the initial load.
7845 * Create a new LoadMask
7846 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7847 * @param {Object} config The config object
7849 Roo.LoadMask = function(el, config){
7850 this.el = Roo.get(el);
7851 Roo.apply(this, config);
7853 this.store.on('beforeload', this.onBeforeLoad, this);
7854 this.store.on('load', this.onLoad, this);
7855 this.store.on('loadexception', this.onLoadException, this);
7856 this.removeMask = false;
7858 var um = this.el.getUpdateManager();
7859 um.showLoadIndicator = false; // disable the default indicator
7860 um.on('beforeupdate', this.onBeforeLoad, this);
7861 um.on('update', this.onLoad, this);
7862 um.on('failure', this.onLoad, this);
7863 this.removeMask = true;
7867 Roo.LoadMask.prototype = {
7869 * @cfg {Boolean} removeMask
7870 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7871 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7875 * The text to display in a centered loading message box (defaults to 'Loading...')
7879 * @cfg {String} msgCls
7880 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7882 msgCls : 'x-mask-loading',
7885 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7891 * Disables the mask to prevent it from being displayed
7893 disable : function(){
7894 this.disabled = true;
7898 * Enables the mask so that it can be displayed
7900 enable : function(){
7901 this.disabled = false;
7904 onLoadException : function()
7908 if (typeof(arguments[3]) != 'undefined') {
7909 Roo.MessageBox.alert("Error loading",arguments[3]);
7913 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7914 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7921 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7926 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7930 onBeforeLoad : function(){
7932 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7937 destroy : function(){
7939 this.store.un('beforeload', this.onBeforeLoad, this);
7940 this.store.un('load', this.onLoad, this);
7941 this.store.un('loadexception', this.onLoadException, this);
7943 var um = this.el.getUpdateManager();
7944 um.un('beforeupdate', this.onBeforeLoad, this);
7945 um.un('update', this.onLoad, this);
7946 um.un('failure', this.onLoad, this);
7957 * @class Roo.bootstrap.Table
7958 * @extends Roo.bootstrap.Component
7959 * Bootstrap Table class
7960 * @cfg {String} cls table class
7961 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7962 * @cfg {String} bgcolor Specifies the background color for a table
7963 * @cfg {Number} border Specifies whether the table cells should have borders or not
7964 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7965 * @cfg {Number} cellspacing Specifies the space between cells
7966 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7967 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7968 * @cfg {String} sortable Specifies that the table should be sortable
7969 * @cfg {String} summary Specifies a summary of the content of a table
7970 * @cfg {Number} width Specifies the width of a table
7971 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7973 * @cfg {boolean} striped Should the rows be alternative striped
7974 * @cfg {boolean} bordered Add borders to the table
7975 * @cfg {boolean} hover Add hover highlighting
7976 * @cfg {boolean} condensed Format condensed
7977 * @cfg {boolean} responsive Format condensed
7978 * @cfg {Boolean} loadMask (true|false) default false
7979 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7980 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7981 * @cfg {Boolean} rowSelection (true|false) default false
7982 * @cfg {Boolean} cellSelection (true|false) default false
7983 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7984 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7985 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7986 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7990 * Create a new Table
7991 * @param {Object} config The config object
7994 Roo.bootstrap.Table = function(config){
7995 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8000 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8001 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8002 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8003 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8005 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8007 this.sm.grid = this;
8008 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
8009 this.sm = this.selModel;
8010 this.sm.xmodule = this.xmodule || false;
8013 if (this.cm && typeof(this.cm.config) == 'undefined') {
8014 this.colModel = new Roo.grid.ColumnModel(this.cm);
8015 this.cm = this.colModel;
8016 this.cm.xmodule = this.xmodule || false;
8019 this.store= Roo.factory(this.store, Roo.data);
8020 this.ds = this.store;
8021 this.ds.xmodule = this.xmodule || false;
8024 if (this.footer && this.store) {
8025 this.footer.dataSource = this.ds;
8026 this.footer = Roo.factory(this.footer);
8033 * Fires when a cell is clicked
8034 * @param {Roo.bootstrap.Table} this
8035 * @param {Roo.Element} el
8036 * @param {Number} rowIndex
8037 * @param {Number} columnIndex
8038 * @param {Roo.EventObject} e
8042 * @event celldblclick
8043 * Fires when a cell is double clicked
8044 * @param {Roo.bootstrap.Table} this
8045 * @param {Roo.Element} el
8046 * @param {Number} rowIndex
8047 * @param {Number} columnIndex
8048 * @param {Roo.EventObject} e
8050 "celldblclick" : true,
8053 * Fires when a row is clicked
8054 * @param {Roo.bootstrap.Table} this
8055 * @param {Roo.Element} el
8056 * @param {Number} rowIndex
8057 * @param {Roo.EventObject} e
8061 * @event rowdblclick
8062 * Fires when a row is double clicked
8063 * @param {Roo.bootstrap.Table} this
8064 * @param {Roo.Element} el
8065 * @param {Number} rowIndex
8066 * @param {Roo.EventObject} e
8068 "rowdblclick" : true,
8071 * Fires when a mouseover occur
8072 * @param {Roo.bootstrap.Table} this
8073 * @param {Roo.Element} el
8074 * @param {Number} rowIndex
8075 * @param {Number} columnIndex
8076 * @param {Roo.EventObject} e
8081 * Fires when a mouseout occur
8082 * @param {Roo.bootstrap.Table} this
8083 * @param {Roo.Element} el
8084 * @param {Number} rowIndex
8085 * @param {Number} columnIndex
8086 * @param {Roo.EventObject} e
8091 * Fires when a row is rendered, so you can change add a style to it.
8092 * @param {Roo.bootstrap.Table} this
8093 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8097 * @event rowsrendered
8098 * Fires when all the rows have been rendered
8099 * @param {Roo.bootstrap.Table} this
8101 'rowsrendered' : true,
8103 * @event contextmenu
8104 * The raw contextmenu event for the entire grid.
8105 * @param {Roo.EventObject} e
8107 "contextmenu" : true,
8109 * @event rowcontextmenu
8110 * Fires when a row is right clicked
8111 * @param {Roo.bootstrap.Table} this
8112 * @param {Number} rowIndex
8113 * @param {Roo.EventObject} e
8115 "rowcontextmenu" : true,
8117 * @event cellcontextmenu
8118 * Fires when a cell is right clicked
8119 * @param {Roo.bootstrap.Table} this
8120 * @param {Number} rowIndex
8121 * @param {Number} cellIndex
8122 * @param {Roo.EventObject} e
8124 "cellcontextmenu" : true,
8126 * @event headercontextmenu
8127 * Fires when a header is right clicked
8128 * @param {Roo.bootstrap.Table} this
8129 * @param {Number} columnIndex
8130 * @param {Roo.EventObject} e
8132 "headercontextmenu" : true
8136 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8162 rowSelection : false,
8163 cellSelection : false,
8166 // Roo.Element - the tbody
8168 // Roo.Element - thead element
8171 container: false, // used by gridpanel...
8177 auto_hide_footer : false,
8179 getAutoCreate : function()
8181 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8188 if (this.scrollBody) {
8189 cfg.cls += ' table-body-fixed';
8192 cfg.cls += ' table-striped';
8196 cfg.cls += ' table-hover';
8198 if (this.bordered) {
8199 cfg.cls += ' table-bordered';
8201 if (this.condensed) {
8202 cfg.cls += ' table-condensed';
8204 if (this.responsive) {
8205 cfg.cls += ' table-responsive';
8209 cfg.cls+= ' ' +this.cls;
8212 // this lot should be simplifed...
8225 ].forEach(function(k) {
8233 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8236 if(this.store || this.cm){
8237 if(this.headerShow){
8238 cfg.cn.push(this.renderHeader());
8241 cfg.cn.push(this.renderBody());
8243 if(this.footerShow){
8244 cfg.cn.push(this.renderFooter());
8246 // where does this come from?
8247 //cfg.cls+= ' TableGrid';
8250 return { cn : [ cfg ] };
8253 initEvents : function()
8255 if(!this.store || !this.cm){
8258 if (this.selModel) {
8259 this.selModel.initEvents();
8263 //Roo.log('initEvents with ds!!!!');
8265 this.mainBody = this.el.select('tbody', true).first();
8266 this.mainHead = this.el.select('thead', true).first();
8267 this.mainFoot = this.el.select('tfoot', true).first();
8273 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8274 e.on('click', _this.sort, _this);
8277 this.mainBody.on("click", this.onClick, this);
8278 this.mainBody.on("dblclick", this.onDblClick, this);
8280 // why is this done????? = it breaks dialogs??
8281 //this.parent().el.setStyle('position', 'relative');
8285 this.footer.parentId = this.id;
8286 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8289 this.el.select('tfoot tr td').first().addClass('hide');
8294 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8297 this.store.on('load', this.onLoad, this);
8298 this.store.on('beforeload', this.onBeforeLoad, this);
8299 this.store.on('update', this.onUpdate, this);
8300 this.store.on('add', this.onAdd, this);
8301 this.store.on("clear", this.clear, this);
8303 this.el.on("contextmenu", this.onContextMenu, this);
8305 this.mainBody.on('scroll', this.onBodyScroll, this);
8307 this.cm.on("headerchange", this.onHeaderChange, this);
8309 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8313 onContextMenu : function(e, t)
8315 this.processEvent("contextmenu", e);
8318 processEvent : function(name, e)
8320 if (name != 'touchstart' ) {
8321 this.fireEvent(name, e);
8324 var t = e.getTarget();
8326 var cell = Roo.get(t);
8332 if(cell.findParent('tfoot', false, true)){
8336 if(cell.findParent('thead', false, true)){
8338 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8339 cell = Roo.get(t).findParent('th', false, true);
8341 Roo.log("failed to find th in thead?");
8342 Roo.log(e.getTarget());
8347 var cellIndex = cell.dom.cellIndex;
8349 var ename = name == 'touchstart' ? 'click' : name;
8350 this.fireEvent("header" + ename, this, cellIndex, e);
8355 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8356 cell = Roo.get(t).findParent('td', false, true);
8358 Roo.log("failed to find th in tbody?");
8359 Roo.log(e.getTarget());
8364 var row = cell.findParent('tr', false, true);
8365 var cellIndex = cell.dom.cellIndex;
8366 var rowIndex = row.dom.rowIndex - 1;
8370 this.fireEvent("row" + name, this, rowIndex, e);
8374 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8380 onMouseover : function(e, el)
8382 var cell = Roo.get(el);
8388 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8389 cell = cell.findParent('td', false, true);
8392 var row = cell.findParent('tr', false, true);
8393 var cellIndex = cell.dom.cellIndex;
8394 var rowIndex = row.dom.rowIndex - 1; // start from 0
8396 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8400 onMouseout : function(e, el)
8402 var cell = Roo.get(el);
8408 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8409 cell = cell.findParent('td', false, true);
8412 var row = cell.findParent('tr', false, true);
8413 var cellIndex = cell.dom.cellIndex;
8414 var rowIndex = row.dom.rowIndex - 1; // start from 0
8416 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8420 onClick : function(e, el)
8422 var cell = Roo.get(el);
8424 if(!cell || (!this.cellSelection && !this.rowSelection)){
8428 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8429 cell = cell.findParent('td', false, true);
8432 if(!cell || typeof(cell) == 'undefined'){
8436 var row = cell.findParent('tr', false, true);
8438 if(!row || typeof(row) == 'undefined'){
8442 var cellIndex = cell.dom.cellIndex;
8443 var rowIndex = this.getRowIndex(row);
8445 // why??? - should these not be based on SelectionModel?
8446 if(this.cellSelection){
8447 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8450 if(this.rowSelection){
8451 this.fireEvent('rowclick', this, row, rowIndex, e);
8457 onDblClick : function(e,el)
8459 var cell = Roo.get(el);
8461 if(!cell || (!this.cellSelection && !this.rowSelection)){
8465 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8466 cell = cell.findParent('td', false, true);
8469 if(!cell || typeof(cell) == 'undefined'){
8473 var row = cell.findParent('tr', false, true);
8475 if(!row || typeof(row) == 'undefined'){
8479 var cellIndex = cell.dom.cellIndex;
8480 var rowIndex = this.getRowIndex(row);
8482 if(this.cellSelection){
8483 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8486 if(this.rowSelection){
8487 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8491 sort : function(e,el)
8493 var col = Roo.get(el);
8495 if(!col.hasClass('sortable')){
8499 var sort = col.attr('sort');
8502 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8506 this.store.sortInfo = {field : sort, direction : dir};
8509 Roo.log("calling footer first");
8510 this.footer.onClick('first');
8513 this.store.load({ params : { start : 0 } });
8517 renderHeader : function()
8525 this.totalWidth = 0;
8527 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8529 var config = cm.config[i];
8533 cls : 'x-hcol-' + i,
8535 html: cm.getColumnHeader(i)
8540 if(typeof(config.sortable) != 'undefined' && config.sortable){
8542 c.html = '<i class="glyphicon"></i>' + c.html;
8545 // could use BS4 hidden-..-down
8547 if(typeof(config.lgHeader) != 'undefined'){
8548 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8551 if(typeof(config.mdHeader) != 'undefined'){
8552 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8555 if(typeof(config.smHeader) != 'undefined'){
8556 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8559 if(typeof(config.xsHeader) != 'undefined'){
8560 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8567 if(typeof(config.tooltip) != 'undefined'){
8568 c.tooltip = config.tooltip;
8571 if(typeof(config.colspan) != 'undefined'){
8572 c.colspan = config.colspan;
8575 if(typeof(config.hidden) != 'undefined' && config.hidden){
8576 c.style += ' display:none;';
8579 if(typeof(config.dataIndex) != 'undefined'){
8580 c.sort = config.dataIndex;
8585 if(typeof(config.align) != 'undefined' && config.align.length){
8586 c.style += ' text-align:' + config.align + ';';
8589 if(typeof(config.width) != 'undefined'){
8590 c.style += ' width:' + config.width + 'px;';
8591 this.totalWidth += config.width;
8593 this.totalWidth += 100; // assume minimum of 100 per column?
8596 if(typeof(config.cls) != 'undefined'){
8597 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8600 ['xs','sm','md','lg'].map(function(size){
8602 if(typeof(config[size]) == 'undefined'){
8606 if (!config[size]) { // 0 = hidden
8607 // BS 4 '0' is treated as hide that column and below.
8608 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8612 c.cls += ' col-' + size + '-' + config[size] + (
8613 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8625 renderBody : function()
8635 colspan : this.cm.getColumnCount()
8645 renderFooter : function()
8655 colspan : this.cm.getColumnCount()
8669 // Roo.log('ds onload');
8674 var ds = this.store;
8676 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8677 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8678 if (_this.store.sortInfo) {
8680 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8681 e.select('i', true).addClass(['glyphicon-arrow-up']);
8684 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8685 e.select('i', true).addClass(['glyphicon-arrow-down']);
8690 var tbody = this.mainBody;
8692 if(ds.getCount() > 0){
8693 ds.data.each(function(d,rowIndex){
8694 var row = this.renderRow(cm, ds, rowIndex);
8696 tbody.createChild(row);
8700 if(row.cellObjects.length){
8701 Roo.each(row.cellObjects, function(r){
8702 _this.renderCellObject(r);
8709 var tfoot = this.el.select('tfoot', true).first();
8711 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8713 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8715 var total = this.ds.getTotalCount();
8717 if(this.footer.pageSize < total){
8718 this.mainFoot.show();
8722 Roo.each(this.el.select('tbody td', true).elements, function(e){
8723 e.on('mouseover', _this.onMouseover, _this);
8726 Roo.each(this.el.select('tbody td', true).elements, function(e){
8727 e.on('mouseout', _this.onMouseout, _this);
8729 this.fireEvent('rowsrendered', this);
8735 onUpdate : function(ds,record)
8737 this.refreshRow(record);
8741 onRemove : function(ds, record, index, isUpdate){
8742 if(isUpdate !== true){
8743 this.fireEvent("beforerowremoved", this, index, record);
8745 var bt = this.mainBody.dom;
8747 var rows = this.el.select('tbody > tr', true).elements;
8749 if(typeof(rows[index]) != 'undefined'){
8750 bt.removeChild(rows[index].dom);
8753 // if(bt.rows[index]){
8754 // bt.removeChild(bt.rows[index]);
8757 if(isUpdate !== true){
8758 //this.stripeRows(index);
8759 //this.syncRowHeights(index, index);
8761 this.fireEvent("rowremoved", this, index, record);
8765 onAdd : function(ds, records, rowIndex)
8767 //Roo.log('on Add called');
8768 // - note this does not handle multiple adding very well..
8769 var bt = this.mainBody.dom;
8770 for (var i =0 ; i < records.length;i++) {
8771 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8772 //Roo.log(records[i]);
8773 //Roo.log(this.store.getAt(rowIndex+i));
8774 this.insertRow(this.store, rowIndex + i, false);
8781 refreshRow : function(record){
8782 var ds = this.store, index;
8783 if(typeof record == 'number'){
8785 record = ds.getAt(index);
8787 index = ds.indexOf(record);
8789 return; // should not happen - but seems to
8792 this.insertRow(ds, index, true);
8794 this.onRemove(ds, record, index+1, true);
8796 //this.syncRowHeights(index, index);
8798 this.fireEvent("rowupdated", this, index, record);
8801 insertRow : function(dm, rowIndex, isUpdate){
8804 this.fireEvent("beforerowsinserted", this, rowIndex);
8806 //var s = this.getScrollState();
8807 var row = this.renderRow(this.cm, this.store, rowIndex);
8808 // insert before rowIndex..
8809 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8813 if(row.cellObjects.length){
8814 Roo.each(row.cellObjects, function(r){
8815 _this.renderCellObject(r);
8820 this.fireEvent("rowsinserted", this, rowIndex);
8821 //this.syncRowHeights(firstRow, lastRow);
8822 //this.stripeRows(firstRow);
8829 getRowDom : function(rowIndex)
8831 var rows = this.el.select('tbody > tr', true).elements;
8833 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8836 // returns the object tree for a tr..
8839 renderRow : function(cm, ds, rowIndex)
8841 var d = ds.getAt(rowIndex);
8845 cls : 'x-row-' + rowIndex,
8849 var cellObjects = [];
8851 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8852 var config = cm.config[i];
8854 var renderer = cm.getRenderer(i);
8858 if(typeof(renderer) !== 'undefined'){
8859 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8861 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8862 // and are rendered into the cells after the row is rendered - using the id for the element.
8864 if(typeof(value) === 'object'){
8874 rowIndex : rowIndex,
8879 this.fireEvent('rowclass', this, rowcfg);
8883 cls : rowcfg.rowClass + ' x-col-' + i,
8885 html: (typeof(value) === 'object') ? '' : value
8892 if(typeof(config.colspan) != 'undefined'){
8893 td.colspan = config.colspan;
8896 if(typeof(config.hidden) != 'undefined' && config.hidden){
8897 td.style += ' display:none;';
8900 if(typeof(config.align) != 'undefined' && config.align.length){
8901 td.style += ' text-align:' + config.align + ';';
8903 if(typeof(config.valign) != 'undefined' && config.valign.length){
8904 td.style += ' vertical-align:' + config.valign + ';';
8907 if(typeof(config.width) != 'undefined'){
8908 td.style += ' width:' + config.width + 'px;';
8911 if(typeof(config.cursor) != 'undefined'){
8912 td.style += ' cursor:' + config.cursor + ';';
8915 if(typeof(config.cls) != 'undefined'){
8916 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8919 ['xs','sm','md','lg'].map(function(size){
8921 if(typeof(config[size]) == 'undefined'){
8927 if (!config[size]) { // 0 = hidden
8928 // BS 4 '0' is treated as hide that column and below.
8929 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8933 td.cls += ' col-' + size + '-' + config[size] + (
8934 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8944 row.cellObjects = cellObjects;
8952 onBeforeLoad : function()
8961 this.el.select('tbody', true).first().dom.innerHTML = '';
8964 * Show or hide a row.
8965 * @param {Number} rowIndex to show or hide
8966 * @param {Boolean} state hide
8968 setRowVisibility : function(rowIndex, state)
8970 var bt = this.mainBody.dom;
8972 var rows = this.el.select('tbody > tr', true).elements;
8974 if(typeof(rows[rowIndex]) == 'undefined'){
8977 rows[rowIndex].dom.style.display = state ? '' : 'none';
8981 getSelectionModel : function(){
8983 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8985 return this.selModel;
8988 * Render the Roo.bootstrap object from renderder
8990 renderCellObject : function(r)
8994 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8996 var t = r.cfg.render(r.container);
8999 Roo.each(r.cfg.cn, function(c){
9001 container: t.getChildContainer(),
9004 _this.renderCellObject(child);
9009 getRowIndex : function(row)
9013 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9024 * Returns the grid's underlying element = used by panel.Grid
9025 * @return {Element} The element
9027 getGridEl : function(){
9031 * Forces a resize - used by panel.Grid
9032 * @return {Element} The element
9034 autoSize : function()
9036 //var ctr = Roo.get(this.container.dom.parentElement);
9037 var ctr = Roo.get(this.el.dom);
9039 var thd = this.getGridEl().select('thead',true).first();
9040 var tbd = this.getGridEl().select('tbody', true).first();
9041 var tfd = this.getGridEl().select('tfoot', true).first();
9043 var cw = ctr.getWidth();
9044 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9048 tbd.setWidth(ctr.getWidth());
9049 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9050 // this needs fixing for various usage - currently only hydra job advers I think..
9052 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9054 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9057 cw = Math.max(cw, this.totalWidth);
9058 this.getGridEl().select('tbody tr',true).setWidth(cw);
9060 // resize 'expandable coloumn?
9062 return; // we doe not have a view in this design..
9065 onBodyScroll: function()
9067 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9069 this.mainHead.setStyle({
9070 'position' : 'relative',
9071 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9077 var scrollHeight = this.mainBody.dom.scrollHeight;
9079 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9081 var height = this.mainBody.getHeight();
9083 if(scrollHeight - height == scrollTop) {
9085 var total = this.ds.getTotalCount();
9087 if(this.footer.cursor + this.footer.pageSize < total){
9089 this.footer.ds.load({
9091 start : this.footer.cursor + this.footer.pageSize,
9092 limit : this.footer.pageSize
9102 onHeaderChange : function()
9104 var header = this.renderHeader();
9105 var table = this.el.select('table', true).first();
9107 this.mainHead.remove();
9108 this.mainHead = table.createChild(header, this.mainBody, false);
9111 onHiddenChange : function(colModel, colIndex, hidden)
9113 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9114 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9116 this.CSS.updateRule(thSelector, "display", "");
9117 this.CSS.updateRule(tdSelector, "display", "");
9120 this.CSS.updateRule(thSelector, "display", "none");
9121 this.CSS.updateRule(tdSelector, "display", "none");
9124 this.onHeaderChange();
9128 setColumnWidth: function(col_index, width)
9130 // width = "md-2 xs-2..."
9131 if(!this.colModel.config[col_index]) {
9135 var w = width.split(" ");
9137 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9139 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9142 for(var j = 0; j < w.length; j++) {
9148 var size_cls = w[j].split("-");
9150 if(!Number.isInteger(size_cls[1] * 1)) {
9154 if(!this.colModel.config[col_index][size_cls[0]]) {
9158 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9162 h_row[0].classList.replace(
9163 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9164 "col-"+size_cls[0]+"-"+size_cls[1]
9167 for(var i = 0; i < rows.length; i++) {
9169 var size_cls = w[j].split("-");
9171 if(!Number.isInteger(size_cls[1] * 1)) {
9175 if(!this.colModel.config[col_index][size_cls[0]]) {
9179 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9183 rows[i].classList.replace(
9184 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9185 "col-"+size_cls[0]+"-"+size_cls[1]
9189 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9204 * @class Roo.bootstrap.TableCell
9205 * @extends Roo.bootstrap.Component
9206 * Bootstrap TableCell class
9207 * @cfg {String} html cell contain text
9208 * @cfg {String} cls cell class
9209 * @cfg {String} tag cell tag (td|th) default td
9210 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9211 * @cfg {String} align Aligns the content in a cell
9212 * @cfg {String} axis Categorizes cells
9213 * @cfg {String} bgcolor Specifies the background color of a cell
9214 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9215 * @cfg {Number} colspan Specifies the number of columns a cell should span
9216 * @cfg {String} headers Specifies one or more header cells a cell is related to
9217 * @cfg {Number} height Sets the height of a cell
9218 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9219 * @cfg {Number} rowspan Sets the number of rows a cell should span
9220 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9221 * @cfg {String} valign Vertical aligns the content in a cell
9222 * @cfg {Number} width Specifies the width of a cell
9225 * Create a new TableCell
9226 * @param {Object} config The config object
9229 Roo.bootstrap.TableCell = function(config){
9230 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9233 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9253 getAutoCreate : function(){
9254 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9274 cfg.align=this.align
9280 cfg.bgcolor=this.bgcolor
9283 cfg.charoff=this.charoff
9286 cfg.colspan=this.colspan
9289 cfg.headers=this.headers
9292 cfg.height=this.height
9295 cfg.nowrap=this.nowrap
9298 cfg.rowspan=this.rowspan
9301 cfg.scope=this.scope
9304 cfg.valign=this.valign
9307 cfg.width=this.width
9326 * @class Roo.bootstrap.TableRow
9327 * @extends Roo.bootstrap.Component
9328 * Bootstrap TableRow class
9329 * @cfg {String} cls row class
9330 * @cfg {String} align Aligns the content in a table row
9331 * @cfg {String} bgcolor Specifies a background color for a table row
9332 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9333 * @cfg {String} valign Vertical aligns the content in a table row
9336 * Create a new TableRow
9337 * @param {Object} config The config object
9340 Roo.bootstrap.TableRow = function(config){
9341 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9344 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9352 getAutoCreate : function(){
9353 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9363 cfg.align = this.align;
9366 cfg.bgcolor = this.bgcolor;
9369 cfg.charoff = this.charoff;
9372 cfg.valign = this.valign;
9390 * @class Roo.bootstrap.TableBody
9391 * @extends Roo.bootstrap.Component
9392 * Bootstrap TableBody class
9393 * @cfg {String} cls element class
9394 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9395 * @cfg {String} align Aligns the content inside the element
9396 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9397 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9400 * Create a new TableBody
9401 * @param {Object} config The config object
9404 Roo.bootstrap.TableBody = function(config){
9405 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9408 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9416 getAutoCreate : function(){
9417 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9431 cfg.align = this.align;
9434 cfg.charoff = this.charoff;
9437 cfg.valign = this.valign;
9444 // initEvents : function()
9451 // this.store = Roo.factory(this.store, Roo.data);
9452 // this.store.on('load', this.onLoad, this);
9454 // this.store.load();
9458 // onLoad: function ()
9460 // this.fireEvent('load', this);
9470 * Ext JS Library 1.1.1
9471 * Copyright(c) 2006-2007, Ext JS, LLC.
9473 * Originally Released Under LGPL - original licence link has changed is not relivant.
9476 * <script type="text/javascript">
9479 // as we use this in bootstrap.
9480 Roo.namespace('Roo.form');
9482 * @class Roo.form.Action
9483 * Internal Class used to handle form actions
9485 * @param {Roo.form.BasicForm} el The form element or its id
9486 * @param {Object} config Configuration options
9491 // define the action interface
9492 Roo.form.Action = function(form, options){
9494 this.options = options || {};
9497 * Client Validation Failed
9500 Roo.form.Action.CLIENT_INVALID = 'client';
9502 * Server Validation Failed
9505 Roo.form.Action.SERVER_INVALID = 'server';
9507 * Connect to Server Failed
9510 Roo.form.Action.CONNECT_FAILURE = 'connect';
9512 * Reading Data from Server Failed
9515 Roo.form.Action.LOAD_FAILURE = 'load';
9517 Roo.form.Action.prototype = {
9519 failureType : undefined,
9520 response : undefined,
9524 run : function(options){
9529 success : function(response){
9534 handleResponse : function(response){
9538 // default connection failure
9539 failure : function(response){
9541 this.response = response;
9542 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9543 this.form.afterAction(this, false);
9546 processResponse : function(response){
9547 this.response = response;
9548 if(!response.responseText){
9551 this.result = this.handleResponse(response);
9555 // utility functions used internally
9556 getUrl : function(appendParams){
9557 var url = this.options.url || this.form.url || this.form.el.dom.action;
9559 var p = this.getParams();
9561 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9567 getMethod : function(){
9568 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9571 getParams : function(){
9572 var bp = this.form.baseParams;
9573 var p = this.options.params;
9575 if(typeof p == "object"){
9576 p = Roo.urlEncode(Roo.applyIf(p, bp));
9577 }else if(typeof p == 'string' && bp){
9578 p += '&' + Roo.urlEncode(bp);
9581 p = Roo.urlEncode(bp);
9586 createCallback : function(){
9588 success: this.success,
9589 failure: this.failure,
9591 timeout: (this.form.timeout*1000),
9592 upload: this.form.fileUpload ? this.success : undefined
9597 Roo.form.Action.Submit = function(form, options){
9598 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9601 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9604 haveProgress : false,
9605 uploadComplete : false,
9607 // uploadProgress indicator.
9608 uploadProgress : function()
9610 if (!this.form.progressUrl) {
9614 if (!this.haveProgress) {
9615 Roo.MessageBox.progress("Uploading", "Uploading");
9617 if (this.uploadComplete) {
9618 Roo.MessageBox.hide();
9622 this.haveProgress = true;
9624 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9626 var c = new Roo.data.Connection();
9628 url : this.form.progressUrl,
9633 success : function(req){
9634 //console.log(data);
9638 rdata = Roo.decode(req.responseText)
9640 Roo.log("Invalid data from server..");
9644 if (!rdata || !rdata.success) {
9646 Roo.MessageBox.alert(Roo.encode(rdata));
9649 var data = rdata.data;
9651 if (this.uploadComplete) {
9652 Roo.MessageBox.hide();
9657 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9658 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9661 this.uploadProgress.defer(2000,this);
9664 failure: function(data) {
9665 Roo.log('progress url failed ');
9676 // run get Values on the form, so it syncs any secondary forms.
9677 this.form.getValues();
9679 var o = this.options;
9680 var method = this.getMethod();
9681 var isPost = method == 'POST';
9682 if(o.clientValidation === false || this.form.isValid()){
9684 if (this.form.progressUrl) {
9685 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9686 (new Date() * 1) + '' + Math.random());
9691 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9692 form:this.form.el.dom,
9693 url:this.getUrl(!isPost),
9695 params:isPost ? this.getParams() : null,
9696 isUpload: this.form.fileUpload,
9697 formData : this.form.formData
9700 this.uploadProgress();
9702 }else if (o.clientValidation !== false){ // client validation failed
9703 this.failureType = Roo.form.Action.CLIENT_INVALID;
9704 this.form.afterAction(this, false);
9708 success : function(response)
9710 this.uploadComplete= true;
9711 if (this.haveProgress) {
9712 Roo.MessageBox.hide();
9716 var result = this.processResponse(response);
9717 if(result === true || result.success){
9718 this.form.afterAction(this, true);
9722 this.form.markInvalid(result.errors);
9723 this.failureType = Roo.form.Action.SERVER_INVALID;
9725 this.form.afterAction(this, false);
9727 failure : function(response)
9729 this.uploadComplete= true;
9730 if (this.haveProgress) {
9731 Roo.MessageBox.hide();
9734 this.response = response;
9735 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9736 this.form.afterAction(this, false);
9739 handleResponse : function(response){
9740 if(this.form.errorReader){
9741 var rs = this.form.errorReader.read(response);
9744 for(var i = 0, len = rs.records.length; i < len; i++) {
9745 var r = rs.records[i];
9749 if(errors.length < 1){
9753 success : rs.success,
9759 ret = Roo.decode(response.responseText);
9763 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9773 Roo.form.Action.Load = function(form, options){
9774 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9775 this.reader = this.form.reader;
9778 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9783 Roo.Ajax.request(Roo.apply(
9784 this.createCallback(), {
9785 method:this.getMethod(),
9786 url:this.getUrl(false),
9787 params:this.getParams()
9791 success : function(response){
9793 var result = this.processResponse(response);
9794 if(result === true || !result.success || !result.data){
9795 this.failureType = Roo.form.Action.LOAD_FAILURE;
9796 this.form.afterAction(this, false);
9799 this.form.clearInvalid();
9800 this.form.setValues(result.data);
9801 this.form.afterAction(this, true);
9804 handleResponse : function(response){
9805 if(this.form.reader){
9806 var rs = this.form.reader.read(response);
9807 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9809 success : rs.success,
9813 return Roo.decode(response.responseText);
9817 Roo.form.Action.ACTION_TYPES = {
9818 'load' : Roo.form.Action.Load,
9819 'submit' : Roo.form.Action.Submit
9828 * @class Roo.bootstrap.Form
9829 * @extends Roo.bootstrap.Component
9830 * Bootstrap Form class
9831 * @cfg {String} method GET | POST (default POST)
9832 * @cfg {String} labelAlign top | left (default top)
9833 * @cfg {String} align left | right - for navbars
9834 * @cfg {Boolean} loadMask load mask when submit (default true)
9839 * @param {Object} config The config object
9843 Roo.bootstrap.Form = function(config){
9845 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9847 Roo.bootstrap.Form.popover.apply();
9851 * @event clientvalidation
9852 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9853 * @param {Form} this
9854 * @param {Boolean} valid true if the form has passed client-side validation
9856 clientvalidation: true,
9858 * @event beforeaction
9859 * Fires before any action is performed. Return false to cancel the action.
9860 * @param {Form} this
9861 * @param {Action} action The action to be performed
9865 * @event actionfailed
9866 * Fires when an action fails.
9867 * @param {Form} this
9868 * @param {Action} action The action that failed
9870 actionfailed : true,
9872 * @event actioncomplete
9873 * Fires when an action is completed.
9874 * @param {Form} this
9875 * @param {Action} action The action that completed
9877 actioncomplete : true
9881 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9884 * @cfg {String} method
9885 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9890 * The URL to use for form actions if one isn't supplied in the action options.
9893 * @cfg {Boolean} fileUpload
9894 * Set to true if this form is a file upload.
9898 * @cfg {Object} baseParams
9899 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9903 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9907 * @cfg {Sting} align (left|right) for navbar forms
9912 activeAction : null,
9915 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9916 * element by passing it or its id or mask the form itself by passing in true.
9919 waitMsgTarget : false,
9924 * @cfg {Boolean} errorMask (true|false) default false
9929 * @cfg {Number} maskOffset Default 100
9934 * @cfg {Boolean} maskBody
9938 getAutoCreate : function(){
9942 method : this.method || 'POST',
9943 id : this.id || Roo.id(),
9946 if (this.parent().xtype.match(/^Nav/)) {
9947 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9951 if (this.labelAlign == 'left' ) {
9952 cfg.cls += ' form-horizontal';
9958 initEvents : function()
9960 this.el.on('submit', this.onSubmit, this);
9961 // this was added as random key presses on the form where triggering form submit.
9962 this.el.on('keypress', function(e) {
9963 if (e.getCharCode() != 13) {
9966 // we might need to allow it for textareas.. and some other items.
9967 // check e.getTarget().
9969 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9973 Roo.log("keypress blocked");
9981 onSubmit : function(e){
9986 * Returns true if client-side validation on the form is successful.
9989 isValid : function(){
9990 var items = this.getItems();
9994 items.each(function(f){
10000 Roo.log('invalid field: ' + f.name);
10004 if(!target && f.el.isVisible(true)){
10010 if(this.errorMask && !valid){
10011 Roo.bootstrap.Form.popover.mask(this, target);
10018 * Returns true if any fields in this form have changed since their original load.
10021 isDirty : function(){
10023 var items = this.getItems();
10024 items.each(function(f){
10034 * Performs a predefined action (submit or load) or custom actions you define on this form.
10035 * @param {String} actionName The name of the action type
10036 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10037 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10038 * accept other config options):
10040 Property Type Description
10041 ---------------- --------------- ----------------------------------------------------------------------------------
10042 url String The url for the action (defaults to the form's url)
10043 method String The form method to use (defaults to the form's method, or POST if not defined)
10044 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10045 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10046 validate the form on the client (defaults to false)
10048 * @return {BasicForm} this
10050 doAction : function(action, options){
10051 if(typeof action == 'string'){
10052 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10054 if(this.fireEvent('beforeaction', this, action) !== false){
10055 this.beforeAction(action);
10056 action.run.defer(100, action);
10062 beforeAction : function(action){
10063 var o = action.options;
10068 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10070 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10073 // not really supported yet.. ??
10075 //if(this.waitMsgTarget === true){
10076 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10077 //}else if(this.waitMsgTarget){
10078 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10079 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10081 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10087 afterAction : function(action, success){
10088 this.activeAction = null;
10089 var o = action.options;
10094 Roo.get(document.body).unmask();
10100 //if(this.waitMsgTarget === true){
10101 // this.el.unmask();
10102 //}else if(this.waitMsgTarget){
10103 // this.waitMsgTarget.unmask();
10105 // Roo.MessageBox.updateProgress(1);
10106 // Roo.MessageBox.hide();
10113 Roo.callback(o.success, o.scope, [this, action]);
10114 this.fireEvent('actioncomplete', this, action);
10118 // failure condition..
10119 // we have a scenario where updates need confirming.
10120 // eg. if a locking scenario exists..
10121 // we look for { errors : { needs_confirm : true }} in the response.
10123 (typeof(action.result) != 'undefined') &&
10124 (typeof(action.result.errors) != 'undefined') &&
10125 (typeof(action.result.errors.needs_confirm) != 'undefined')
10128 Roo.log("not supported yet");
10131 Roo.MessageBox.confirm(
10132 "Change requires confirmation",
10133 action.result.errorMsg,
10138 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10148 Roo.callback(o.failure, o.scope, [this, action]);
10149 // show an error message if no failed handler is set..
10150 if (!this.hasListener('actionfailed')) {
10151 Roo.log("need to add dialog support");
10153 Roo.MessageBox.alert("Error",
10154 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10155 action.result.errorMsg :
10156 "Saving Failed, please check your entries or try again"
10161 this.fireEvent('actionfailed', this, action);
10166 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10167 * @param {String} id The value to search for
10170 findField : function(id){
10171 var items = this.getItems();
10172 var field = items.get(id);
10174 items.each(function(f){
10175 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10182 return field || null;
10185 * Mark fields in this form invalid in bulk.
10186 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10187 * @return {BasicForm} this
10189 markInvalid : function(errors){
10190 if(errors instanceof Array){
10191 for(var i = 0, len = errors.length; i < len; i++){
10192 var fieldError = errors[i];
10193 var f = this.findField(fieldError.id);
10195 f.markInvalid(fieldError.msg);
10201 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10202 field.markInvalid(errors[id]);
10206 //Roo.each(this.childForms || [], function (f) {
10207 // f.markInvalid(errors);
10214 * Set values for fields in this form in bulk.
10215 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10216 * @return {BasicForm} this
10218 setValues : function(values){
10219 if(values instanceof Array){ // array of objects
10220 for(var i = 0, len = values.length; i < len; i++){
10222 var f = this.findField(v.id);
10224 f.setValue(v.value);
10225 if(this.trackResetOnLoad){
10226 f.originalValue = f.getValue();
10230 }else{ // object hash
10233 if(typeof values[id] != 'function' && (field = this.findField(id))){
10235 if (field.setFromData &&
10236 field.valueField &&
10237 field.displayField &&
10238 // combos' with local stores can
10239 // be queried via setValue()
10240 // to set their value..
10241 (field.store && !field.store.isLocal)
10245 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10246 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10247 field.setFromData(sd);
10249 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10251 field.setFromData(values);
10254 field.setValue(values[id]);
10258 if(this.trackResetOnLoad){
10259 field.originalValue = field.getValue();
10265 //Roo.each(this.childForms || [], function (f) {
10266 // f.setValues(values);
10273 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10274 * they are returned as an array.
10275 * @param {Boolean} asString
10278 getValues : function(asString){
10279 //if (this.childForms) {
10280 // copy values from the child forms
10281 // Roo.each(this.childForms, function (f) {
10282 // this.setValues(f.getValues());
10288 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10289 if(asString === true){
10292 return Roo.urlDecode(fs);
10296 * Returns the fields in this form as an object with key/value pairs.
10297 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10300 getFieldValues : function(with_hidden)
10302 var items = this.getItems();
10304 items.each(function(f){
10306 if (!f.getName()) {
10310 var v = f.getValue();
10312 if (f.inputType =='radio') {
10313 if (typeof(ret[f.getName()]) == 'undefined') {
10314 ret[f.getName()] = ''; // empty..
10317 if (!f.el.dom.checked) {
10321 v = f.el.dom.value;
10325 if(f.xtype == 'MoneyField'){
10326 ret[f.currencyName] = f.getCurrency();
10329 // not sure if this supported any more..
10330 if ((typeof(v) == 'object') && f.getRawValue) {
10331 v = f.getRawValue() ; // dates..
10333 // combo boxes where name != hiddenName...
10334 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10335 ret[f.name] = f.getRawValue();
10337 ret[f.getName()] = v;
10344 * Clears all invalid messages in this form.
10345 * @return {BasicForm} this
10347 clearInvalid : function(){
10348 var items = this.getItems();
10350 items.each(function(f){
10358 * Resets this form.
10359 * @return {BasicForm} this
10361 reset : function(){
10362 var items = this.getItems();
10363 items.each(function(f){
10367 Roo.each(this.childForms || [], function (f) {
10375 getItems : function()
10377 var r=new Roo.util.MixedCollection(false, function(o){
10378 return o.id || (o.id = Roo.id());
10380 var iter = function(el) {
10387 Roo.each(el.items,function(e) {
10396 hideFields : function(items)
10398 Roo.each(items, function(i){
10400 var f = this.findField(i);
10411 showFields : function(items)
10413 Roo.each(items, function(i){
10415 var f = this.findField(i);
10428 Roo.apply(Roo.bootstrap.Form, {
10444 intervalID : false,
10450 if(this.isApplied){
10455 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10456 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10457 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10458 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10461 this.maskEl.top.enableDisplayMode("block");
10462 this.maskEl.left.enableDisplayMode("block");
10463 this.maskEl.bottom.enableDisplayMode("block");
10464 this.maskEl.right.enableDisplayMode("block");
10466 this.toolTip = new Roo.bootstrap.Tooltip({
10467 cls : 'roo-form-error-popover',
10469 'left' : ['r-l', [-2,0], 'right'],
10470 'right' : ['l-r', [2,0], 'left'],
10471 'bottom' : ['tl-bl', [0,2], 'top'],
10472 'top' : [ 'bl-tl', [0,-2], 'bottom']
10476 this.toolTip.render(Roo.get(document.body));
10478 this.toolTip.el.enableDisplayMode("block");
10480 Roo.get(document.body).on('click', function(){
10484 Roo.get(document.body).on('touchstart', function(){
10488 this.isApplied = true
10491 mask : function(form, target)
10495 this.target = target;
10497 if(!this.form.errorMask || !target.el){
10501 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10503 Roo.log(scrollable);
10505 var ot = this.target.el.calcOffsetsTo(scrollable);
10507 var scrollTo = ot[1] - this.form.maskOffset;
10509 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10511 scrollable.scrollTo('top', scrollTo);
10513 var box = this.target.el.getBox();
10515 var zIndex = Roo.bootstrap.Modal.zIndex++;
10518 this.maskEl.top.setStyle('position', 'absolute');
10519 this.maskEl.top.setStyle('z-index', zIndex);
10520 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10521 this.maskEl.top.setLeft(0);
10522 this.maskEl.top.setTop(0);
10523 this.maskEl.top.show();
10525 this.maskEl.left.setStyle('position', 'absolute');
10526 this.maskEl.left.setStyle('z-index', zIndex);
10527 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10528 this.maskEl.left.setLeft(0);
10529 this.maskEl.left.setTop(box.y - this.padding);
10530 this.maskEl.left.show();
10532 this.maskEl.bottom.setStyle('position', 'absolute');
10533 this.maskEl.bottom.setStyle('z-index', zIndex);
10534 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10535 this.maskEl.bottom.setLeft(0);
10536 this.maskEl.bottom.setTop(box.bottom + this.padding);
10537 this.maskEl.bottom.show();
10539 this.maskEl.right.setStyle('position', 'absolute');
10540 this.maskEl.right.setStyle('z-index', zIndex);
10541 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10542 this.maskEl.right.setLeft(box.right + this.padding);
10543 this.maskEl.right.setTop(box.y - this.padding);
10544 this.maskEl.right.show();
10546 this.toolTip.bindEl = this.target.el;
10548 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10550 var tip = this.target.blankText;
10552 if(this.target.getValue() !== '' ) {
10554 if (this.target.invalidText.length) {
10555 tip = this.target.invalidText;
10556 } else if (this.target.regexText.length){
10557 tip = this.target.regexText;
10561 this.toolTip.show(tip);
10563 this.intervalID = window.setInterval(function() {
10564 Roo.bootstrap.Form.popover.unmask();
10567 window.onwheel = function(){ return false;};
10569 (function(){ this.isMasked = true; }).defer(500, this);
10573 unmask : function()
10575 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10579 this.maskEl.top.setStyle('position', 'absolute');
10580 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10581 this.maskEl.top.hide();
10583 this.maskEl.left.setStyle('position', 'absolute');
10584 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10585 this.maskEl.left.hide();
10587 this.maskEl.bottom.setStyle('position', 'absolute');
10588 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10589 this.maskEl.bottom.hide();
10591 this.maskEl.right.setStyle('position', 'absolute');
10592 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10593 this.maskEl.right.hide();
10595 this.toolTip.hide();
10597 this.toolTip.el.hide();
10599 window.onwheel = function(){ return true;};
10601 if(this.intervalID){
10602 window.clearInterval(this.intervalID);
10603 this.intervalID = false;
10606 this.isMasked = false;
10616 * Ext JS Library 1.1.1
10617 * Copyright(c) 2006-2007, Ext JS, LLC.
10619 * Originally Released Under LGPL - original licence link has changed is not relivant.
10622 * <script type="text/javascript">
10625 * @class Roo.form.VTypes
10626 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10629 Roo.form.VTypes = function(){
10630 // closure these in so they are only created once.
10631 var alpha = /^[a-zA-Z_]+$/;
10632 var alphanum = /^[a-zA-Z0-9_]+$/;
10633 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10634 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10636 // All these messages and functions are configurable
10639 * The function used to validate email addresses
10640 * @param {String} value The email address
10642 'email' : function(v){
10643 return email.test(v);
10646 * The error text to display when the email validation function returns false
10649 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10651 * The keystroke filter mask to be applied on email input
10654 'emailMask' : /[a-z0-9_\.\-@]/i,
10657 * The function used to validate URLs
10658 * @param {String} value The URL
10660 'url' : function(v){
10661 return url.test(v);
10664 * The error text to display when the url validation function returns false
10667 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10670 * The function used to validate alpha values
10671 * @param {String} value The value
10673 'alpha' : function(v){
10674 return alpha.test(v);
10677 * The error text to display when the alpha validation function returns false
10680 'alphaText' : 'This field should only contain letters and _',
10682 * The keystroke filter mask to be applied on alpha input
10685 'alphaMask' : /[a-z_]/i,
10688 * The function used to validate alphanumeric values
10689 * @param {String} value The value
10691 'alphanum' : function(v){
10692 return alphanum.test(v);
10695 * The error text to display when the alphanumeric validation function returns false
10698 'alphanumText' : 'This field should only contain letters, numbers and _',
10700 * The keystroke filter mask to be applied on alphanumeric input
10703 'alphanumMask' : /[a-z0-9_]/i
10713 * @class Roo.bootstrap.Input
10714 * @extends Roo.bootstrap.Component
10715 * Bootstrap Input class
10716 * @cfg {Boolean} disabled is it disabled
10717 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10718 * @cfg {String} name name of the input
10719 * @cfg {string} fieldLabel - the label associated
10720 * @cfg {string} placeholder - placeholder to put in text.
10721 * @cfg {string} before - input group add on before
10722 * @cfg {string} after - input group add on after
10723 * @cfg {string} size - (lg|sm) or leave empty..
10724 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10725 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10726 * @cfg {Number} md colspan out of 12 for computer-sized screens
10727 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10728 * @cfg {string} value default value of the input
10729 * @cfg {Number} labelWidth set the width of label
10730 * @cfg {Number} labellg set the width of label (1-12)
10731 * @cfg {Number} labelmd set the width of label (1-12)
10732 * @cfg {Number} labelsm set the width of label (1-12)
10733 * @cfg {Number} labelxs set the width of label (1-12)
10734 * @cfg {String} labelAlign (top|left)
10735 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10736 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10737 * @cfg {String} indicatorpos (left|right) default left
10738 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10739 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10740 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10742 * @cfg {String} align (left|center|right) Default left
10743 * @cfg {Boolean} forceFeedback (true|false) Default false
10746 * Create a new Input
10747 * @param {Object} config The config object
10750 Roo.bootstrap.Input = function(config){
10752 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10757 * Fires when this field receives input focus.
10758 * @param {Roo.form.Field} this
10763 * Fires when this field loses input focus.
10764 * @param {Roo.form.Field} this
10768 * @event specialkey
10769 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10770 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10771 * @param {Roo.form.Field} this
10772 * @param {Roo.EventObject} e The event object
10777 * Fires just before the field blurs if the field value has changed.
10778 * @param {Roo.form.Field} this
10779 * @param {Mixed} newValue The new value
10780 * @param {Mixed} oldValue The original value
10785 * Fires after the field has been marked as invalid.
10786 * @param {Roo.form.Field} this
10787 * @param {String} msg The validation message
10792 * Fires after the field has been validated with no errors.
10793 * @param {Roo.form.Field} this
10798 * Fires after the key up
10799 * @param {Roo.form.Field} this
10800 * @param {Roo.EventObject} e The event Object
10805 * Fires after the user pastes into input
10806 * @param {Roo.form.Field} this
10807 * @param {Roo.EventObject} e The event Object
10813 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10815 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10816 automatic validation (defaults to "keyup").
10818 validationEvent : "keyup",
10820 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10822 validateOnBlur : true,
10824 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10826 validationDelay : 250,
10828 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10830 focusClass : "x-form-focus", // not needed???
10834 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10836 invalidClass : "has-warning",
10839 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10841 validClass : "has-success",
10844 * @cfg {Boolean} hasFeedback (true|false) default true
10846 hasFeedback : true,
10849 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10851 invalidFeedbackClass : "glyphicon-warning-sign",
10854 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10856 validFeedbackClass : "glyphicon-ok",
10859 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10861 selectOnFocus : false,
10864 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10868 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10873 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10875 disableKeyFilter : false,
10878 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10882 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10886 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10888 blankText : "Please complete this mandatory field",
10891 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10895 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10897 maxLength : Number.MAX_VALUE,
10899 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10901 minLengthText : "The minimum length for this field is {0}",
10903 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10905 maxLengthText : "The maximum length for this field is {0}",
10909 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10910 * If available, this function will be called only after the basic validators all return true, and will be passed the
10911 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10915 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10916 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10917 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10921 * @cfg {String} regexText -- Depricated - use Invalid Text
10926 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10932 autocomplete: false,
10936 inputType : 'text',
10939 placeholder: false,
10944 preventMark: false,
10945 isFormField : true,
10948 labelAlign : false,
10951 formatedValue : false,
10952 forceFeedback : false,
10954 indicatorpos : 'left',
10964 parentLabelAlign : function()
10967 while (parent.parent()) {
10968 parent = parent.parent();
10969 if (typeof(parent.labelAlign) !='undefined') {
10970 return parent.labelAlign;
10977 getAutoCreate : function()
10979 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10985 if(this.inputType != 'hidden'){
10986 cfg.cls = 'form-group' //input-group
10992 type : this.inputType,
10993 value : this.value,
10994 cls : 'form-control',
10995 placeholder : this.placeholder || '',
10996 autocomplete : this.autocomplete || 'new-password'
10998 if (this.inputType == 'file') {
10999 input.style = 'overflow:hidden'; // why not in CSS?
11002 if(this.capture.length){
11003 input.capture = this.capture;
11006 if(this.accept.length){
11007 input.accept = this.accept + "/*";
11011 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11014 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11015 input.maxLength = this.maxLength;
11018 if (this.disabled) {
11019 input.disabled=true;
11022 if (this.readOnly) {
11023 input.readonly=true;
11027 input.name = this.name;
11031 input.cls += ' input-' + this.size;
11035 ['xs','sm','md','lg'].map(function(size){
11036 if (settings[size]) {
11037 cfg.cls += ' col-' + size + '-' + settings[size];
11041 var inputblock = input;
11045 cls: 'glyphicon form-control-feedback'
11048 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11051 cls : 'has-feedback',
11059 if (this.before || this.after) {
11062 cls : 'input-group',
11066 if (this.before && typeof(this.before) == 'string') {
11068 inputblock.cn.push({
11070 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11074 if (this.before && typeof(this.before) == 'object') {
11075 this.before = Roo.factory(this.before);
11077 inputblock.cn.push({
11079 cls : 'roo-input-before input-group-prepend input-group-' +
11080 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11084 inputblock.cn.push(input);
11086 if (this.after && typeof(this.after) == 'string') {
11087 inputblock.cn.push({
11089 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11093 if (this.after && typeof(this.after) == 'object') {
11094 this.after = Roo.factory(this.after);
11096 inputblock.cn.push({
11098 cls : 'roo-input-after input-group-append input-group-' +
11099 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11103 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11104 inputblock.cls += ' has-feedback';
11105 inputblock.cn.push(feedback);
11110 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11111 tooltip : 'This field is required'
11113 if (this.allowBlank ) {
11114 indicator.style = this.allowBlank ? ' display:none' : '';
11116 if (align ==='left' && this.fieldLabel.length) {
11118 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11125 cls : 'control-label col-form-label',
11126 html : this.fieldLabel
11137 var labelCfg = cfg.cn[1];
11138 var contentCfg = cfg.cn[2];
11140 if(this.indicatorpos == 'right'){
11145 cls : 'control-label col-form-label',
11149 html : this.fieldLabel
11163 labelCfg = cfg.cn[0];
11164 contentCfg = cfg.cn[1];
11168 if(this.labelWidth > 12){
11169 labelCfg.style = "width: " + this.labelWidth + 'px';
11172 if(this.labelWidth < 13 && this.labelmd == 0){
11173 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11176 if(this.labellg > 0){
11177 labelCfg.cls += ' col-lg-' + this.labellg;
11178 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11181 if(this.labelmd > 0){
11182 labelCfg.cls += ' col-md-' + this.labelmd;
11183 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11186 if(this.labelsm > 0){
11187 labelCfg.cls += ' col-sm-' + this.labelsm;
11188 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11191 if(this.labelxs > 0){
11192 labelCfg.cls += ' col-xs-' + this.labelxs;
11193 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11197 } else if ( this.fieldLabel.length) {
11204 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11205 tooltip : 'This field is required',
11206 style : this.allowBlank ? ' display:none' : ''
11210 //cls : 'input-group-addon',
11211 html : this.fieldLabel
11219 if(this.indicatorpos == 'right'){
11224 //cls : 'input-group-addon',
11225 html : this.fieldLabel
11230 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11231 tooltip : 'This field is required',
11232 style : this.allowBlank ? ' display:none' : ''
11252 if (this.parentType === 'Navbar' && this.parent().bar) {
11253 cfg.cls += ' navbar-form';
11256 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11257 // on BS4 we do this only if not form
11258 cfg.cls += ' navbar-form';
11266 * return the real input element.
11268 inputEl: function ()
11270 return this.el.select('input.form-control',true).first();
11273 tooltipEl : function()
11275 return this.inputEl();
11278 indicatorEl : function()
11280 if (Roo.bootstrap.version == 4) {
11281 return false; // not enabled in v4 yet.
11284 var indicator = this.el.select('i.roo-required-indicator',true).first();
11294 setDisabled : function(v)
11296 var i = this.inputEl().dom;
11298 i.removeAttribute('disabled');
11302 i.setAttribute('disabled','true');
11304 initEvents : function()
11307 this.inputEl().on("keydown" , this.fireKey, this);
11308 this.inputEl().on("focus", this.onFocus, this);
11309 this.inputEl().on("blur", this.onBlur, this);
11311 this.inputEl().relayEvent('keyup', this);
11312 this.inputEl().relayEvent('paste', this);
11314 this.indicator = this.indicatorEl();
11316 if(this.indicator){
11317 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11320 // reference to original value for reset
11321 this.originalValue = this.getValue();
11322 //Roo.form.TextField.superclass.initEvents.call(this);
11323 if(this.validationEvent == 'keyup'){
11324 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11325 this.inputEl().on('keyup', this.filterValidation, this);
11327 else if(this.validationEvent !== false){
11328 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11331 if(this.selectOnFocus){
11332 this.on("focus", this.preFocus, this);
11335 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11336 this.inputEl().on("keypress", this.filterKeys, this);
11338 this.inputEl().relayEvent('keypress', this);
11341 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11342 this.el.on("click", this.autoSize, this);
11345 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11346 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11349 if (typeof(this.before) == 'object') {
11350 this.before.render(this.el.select('.roo-input-before',true).first());
11352 if (typeof(this.after) == 'object') {
11353 this.after.render(this.el.select('.roo-input-after',true).first());
11356 this.inputEl().on('change', this.onChange, this);
11359 filterValidation : function(e){
11360 if(!e.isNavKeyPress()){
11361 this.validationTask.delay(this.validationDelay);
11365 * Validates the field value
11366 * @return {Boolean} True if the value is valid, else false
11368 validate : function(){
11369 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11370 if(this.disabled || this.validateValue(this.getRawValue())){
11375 this.markInvalid();
11381 * Validates a value according to the field's validation rules and marks the field as invalid
11382 * if the validation fails
11383 * @param {Mixed} value The value to validate
11384 * @return {Boolean} True if the value is valid, else false
11386 validateValue : function(value)
11388 if(this.getVisibilityEl().hasClass('hidden')){
11392 if(value.length < 1) { // if it's blank
11393 if(this.allowBlank){
11399 if(value.length < this.minLength){
11402 if(value.length > this.maxLength){
11406 var vt = Roo.form.VTypes;
11407 if(!vt[this.vtype](value, this)){
11411 if(typeof this.validator == "function"){
11412 var msg = this.validator(value);
11416 if (typeof(msg) == 'string') {
11417 this.invalidText = msg;
11421 if(this.regex && !this.regex.test(value)){
11429 fireKey : function(e){
11430 //Roo.log('field ' + e.getKey());
11431 if(e.isNavKeyPress()){
11432 this.fireEvent("specialkey", this, e);
11435 focus : function (selectText){
11437 this.inputEl().focus();
11438 if(selectText === true){
11439 this.inputEl().dom.select();
11445 onFocus : function(){
11446 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11447 // this.el.addClass(this.focusClass);
11449 if(!this.hasFocus){
11450 this.hasFocus = true;
11451 this.startValue = this.getValue();
11452 this.fireEvent("focus", this);
11456 beforeBlur : Roo.emptyFn,
11460 onBlur : function(){
11462 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11463 //this.el.removeClass(this.focusClass);
11465 this.hasFocus = false;
11466 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11469 var v = this.getValue();
11470 if(String(v) !== String(this.startValue)){
11471 this.fireEvent('change', this, v, this.startValue);
11473 this.fireEvent("blur", this);
11476 onChange : function(e)
11478 var v = this.getValue();
11479 if(String(v) !== String(this.startValue)){
11480 this.fireEvent('change', this, v, this.startValue);
11486 * Resets the current field value to the originally loaded value and clears any validation messages
11488 reset : function(){
11489 this.setValue(this.originalValue);
11493 * Returns the name of the field
11494 * @return {Mixed} name The name field
11496 getName: function(){
11500 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11501 * @return {Mixed} value The field value
11503 getValue : function(){
11505 var v = this.inputEl().getValue();
11510 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11511 * @return {Mixed} value The field value
11513 getRawValue : function(){
11514 var v = this.inputEl().getValue();
11520 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11521 * @param {Mixed} value The value to set
11523 setRawValue : function(v){
11524 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11527 selectText : function(start, end){
11528 var v = this.getRawValue();
11530 start = start === undefined ? 0 : start;
11531 end = end === undefined ? v.length : end;
11532 var d = this.inputEl().dom;
11533 if(d.setSelectionRange){
11534 d.setSelectionRange(start, end);
11535 }else if(d.createTextRange){
11536 var range = d.createTextRange();
11537 range.moveStart("character", start);
11538 range.moveEnd("character", v.length-end);
11545 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11546 * @param {Mixed} value The value to set
11548 setValue : function(v){
11551 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11557 processValue : function(value){
11558 if(this.stripCharsRe){
11559 var newValue = value.replace(this.stripCharsRe, '');
11560 if(newValue !== value){
11561 this.setRawValue(newValue);
11568 preFocus : function(){
11570 if(this.selectOnFocus){
11571 this.inputEl().dom.select();
11574 filterKeys : function(e){
11575 var k = e.getKey();
11576 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11579 var c = e.getCharCode(), cc = String.fromCharCode(c);
11580 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11583 if(!this.maskRe.test(cc)){
11588 * Clear any invalid styles/messages for this field
11590 clearInvalid : function(){
11592 if(!this.el || this.preventMark){ // not rendered
11597 this.el.removeClass([this.invalidClass, 'is-invalid']);
11599 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11601 var feedback = this.el.select('.form-control-feedback', true).first();
11604 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11609 if(this.indicator){
11610 this.indicator.removeClass('visible');
11611 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11614 this.fireEvent('valid', this);
11618 * Mark this field as valid
11620 markValid : function()
11622 if(!this.el || this.preventMark){ // not rendered...
11626 this.el.removeClass([this.invalidClass, this.validClass]);
11627 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11629 var feedback = this.el.select('.form-control-feedback', true).first();
11632 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11635 if(this.indicator){
11636 this.indicator.removeClass('visible');
11637 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11645 if(this.allowBlank && !this.getRawValue().length){
11648 if (Roo.bootstrap.version == 3) {
11649 this.el.addClass(this.validClass);
11651 this.inputEl().addClass('is-valid');
11654 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11656 var feedback = this.el.select('.form-control-feedback', true).first();
11659 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11660 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11665 this.fireEvent('valid', this);
11669 * Mark this field as invalid
11670 * @param {String} msg The validation message
11672 markInvalid : function(msg)
11674 if(!this.el || this.preventMark){ // not rendered
11678 this.el.removeClass([this.invalidClass, this.validClass]);
11679 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11681 var feedback = this.el.select('.form-control-feedback', true).first();
11684 this.el.select('.form-control-feedback', true).first().removeClass(
11685 [this.invalidFeedbackClass, this.validFeedbackClass]);
11692 if(this.allowBlank && !this.getRawValue().length){
11696 if(this.indicator){
11697 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11698 this.indicator.addClass('visible');
11700 if (Roo.bootstrap.version == 3) {
11701 this.el.addClass(this.invalidClass);
11703 this.inputEl().addClass('is-invalid');
11708 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11710 var feedback = this.el.select('.form-control-feedback', true).first();
11713 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11715 if(this.getValue().length || this.forceFeedback){
11716 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11723 this.fireEvent('invalid', this, msg);
11726 SafariOnKeyDown : function(event)
11728 // this is a workaround for a password hang bug on chrome/ webkit.
11729 if (this.inputEl().dom.type != 'password') {
11733 var isSelectAll = false;
11735 if(this.inputEl().dom.selectionEnd > 0){
11736 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11738 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11739 event.preventDefault();
11744 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11746 event.preventDefault();
11747 // this is very hacky as keydown always get's upper case.
11749 var cc = String.fromCharCode(event.getCharCode());
11750 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11754 adjustWidth : function(tag, w){
11755 tag = tag.toLowerCase();
11756 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11757 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11758 if(tag == 'input'){
11761 if(tag == 'textarea'){
11764 }else if(Roo.isOpera){
11765 if(tag == 'input'){
11768 if(tag == 'textarea'){
11776 setFieldLabel : function(v)
11778 if(!this.rendered){
11782 if(this.indicatorEl()){
11783 var ar = this.el.select('label > span',true);
11785 if (ar.elements.length) {
11786 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11787 this.fieldLabel = v;
11791 var br = this.el.select('label',true);
11793 if(br.elements.length) {
11794 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11795 this.fieldLabel = v;
11799 Roo.log('Cannot Found any of label > span || label in input');
11803 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11804 this.fieldLabel = v;
11819 * @class Roo.bootstrap.TextArea
11820 * @extends Roo.bootstrap.Input
11821 * Bootstrap TextArea class
11822 * @cfg {Number} cols Specifies the visible width of a text area
11823 * @cfg {Number} rows Specifies the visible number of lines in a text area
11824 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11825 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11826 * @cfg {string} html text
11829 * Create a new TextArea
11830 * @param {Object} config The config object
11833 Roo.bootstrap.TextArea = function(config){
11834 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11838 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11848 getAutoCreate : function(){
11850 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11856 if(this.inputType != 'hidden'){
11857 cfg.cls = 'form-group' //input-group
11865 value : this.value || '',
11866 html: this.html || '',
11867 cls : 'form-control',
11868 placeholder : this.placeholder || ''
11872 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11873 input.maxLength = this.maxLength;
11877 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11881 input.cols = this.cols;
11884 if (this.readOnly) {
11885 input.readonly = true;
11889 input.name = this.name;
11893 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11897 ['xs','sm','md','lg'].map(function(size){
11898 if (settings[size]) {
11899 cfg.cls += ' col-' + size + '-' + settings[size];
11903 var inputblock = input;
11905 if(this.hasFeedback && !this.allowBlank){
11909 cls: 'glyphicon form-control-feedback'
11913 cls : 'has-feedback',
11922 if (this.before || this.after) {
11925 cls : 'input-group',
11929 inputblock.cn.push({
11931 cls : 'input-group-addon',
11936 inputblock.cn.push(input);
11938 if(this.hasFeedback && !this.allowBlank){
11939 inputblock.cls += ' has-feedback';
11940 inputblock.cn.push(feedback);
11944 inputblock.cn.push({
11946 cls : 'input-group-addon',
11953 if (align ==='left' && this.fieldLabel.length) {
11958 cls : 'control-label',
11959 html : this.fieldLabel
11970 if(this.labelWidth > 12){
11971 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11974 if(this.labelWidth < 13 && this.labelmd == 0){
11975 this.labelmd = this.labelWidth;
11978 if(this.labellg > 0){
11979 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11980 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11983 if(this.labelmd > 0){
11984 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11985 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11988 if(this.labelsm > 0){
11989 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11990 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11993 if(this.labelxs > 0){
11994 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11995 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11998 } else if ( this.fieldLabel.length) {
12003 //cls : 'input-group-addon',
12004 html : this.fieldLabel
12022 if (this.disabled) {
12023 input.disabled=true;
12030 * return the real textarea element.
12032 inputEl: function ()
12034 return this.el.select('textarea.form-control',true).first();
12038 * Clear any invalid styles/messages for this field
12040 clearInvalid : function()
12043 if(!this.el || this.preventMark){ // not rendered
12047 var label = this.el.select('label', true).first();
12048 var icon = this.el.select('i.fa-star', true).first();
12053 this.el.removeClass( this.validClass);
12054 this.inputEl().removeClass('is-invalid');
12056 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12058 var feedback = this.el.select('.form-control-feedback', true).first();
12061 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12066 this.fireEvent('valid', this);
12070 * Mark this field as valid
12072 markValid : function()
12074 if(!this.el || this.preventMark){ // not rendered
12078 this.el.removeClass([this.invalidClass, this.validClass]);
12079 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12081 var feedback = this.el.select('.form-control-feedback', true).first();
12084 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12087 if(this.disabled || this.allowBlank){
12091 var label = this.el.select('label', true).first();
12092 var icon = this.el.select('i.fa-star', true).first();
12097 if (Roo.bootstrap.version == 3) {
12098 this.el.addClass(this.validClass);
12100 this.inputEl().addClass('is-valid');
12104 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12106 var feedback = this.el.select('.form-control-feedback', true).first();
12109 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12110 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12115 this.fireEvent('valid', this);
12119 * Mark this field as invalid
12120 * @param {String} msg The validation message
12122 markInvalid : function(msg)
12124 if(!this.el || this.preventMark){ // not rendered
12128 this.el.removeClass([this.invalidClass, this.validClass]);
12129 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12131 var feedback = this.el.select('.form-control-feedback', true).first();
12134 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12137 if(this.disabled || this.allowBlank){
12141 var label = this.el.select('label', true).first();
12142 var icon = this.el.select('i.fa-star', true).first();
12144 if(!this.getValue().length && label && !icon){
12145 this.el.createChild({
12147 cls : 'text-danger fa fa-lg fa-star',
12148 tooltip : 'This field is required',
12149 style : 'margin-right:5px;'
12153 if (Roo.bootstrap.version == 3) {
12154 this.el.addClass(this.invalidClass);
12156 this.inputEl().addClass('is-invalid');
12159 // fixme ... this may be depricated need to test..
12160 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12162 var feedback = this.el.select('.form-control-feedback', true).first();
12165 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12167 if(this.getValue().length || this.forceFeedback){
12168 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12175 this.fireEvent('invalid', this, msg);
12183 * trigger field - base class for combo..
12188 * @class Roo.bootstrap.TriggerField
12189 * @extends Roo.bootstrap.Input
12190 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12191 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12192 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12193 * for which you can provide a custom implementation. For example:
12195 var trigger = new Roo.bootstrap.TriggerField();
12196 trigger.onTriggerClick = myTriggerFn;
12197 trigger.applyTo('my-field');
12200 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12201 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12202 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12203 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12204 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12207 * Create a new TriggerField.
12208 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12209 * to the base TextField)
12211 Roo.bootstrap.TriggerField = function(config){
12212 this.mimicing = false;
12213 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12216 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12218 * @cfg {String} triggerClass A CSS class to apply to the trigger
12221 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12226 * @cfg {Boolean} removable (true|false) special filter default false
12230 /** @cfg {Boolean} grow @hide */
12231 /** @cfg {Number} growMin @hide */
12232 /** @cfg {Number} growMax @hide */
12238 autoSize: Roo.emptyFn,
12242 deferHeight : true,
12245 actionMode : 'wrap',
12250 getAutoCreate : function(){
12252 var align = this.labelAlign || this.parentLabelAlign();
12257 cls: 'form-group' //input-group
12264 type : this.inputType,
12265 cls : 'form-control',
12266 autocomplete: 'new-password',
12267 placeholder : this.placeholder || ''
12271 input.name = this.name;
12274 input.cls += ' input-' + this.size;
12277 if (this.disabled) {
12278 input.disabled=true;
12281 var inputblock = input;
12283 if(this.hasFeedback && !this.allowBlank){
12287 cls: 'glyphicon form-control-feedback'
12290 if(this.removable && !this.editable ){
12292 cls : 'has-feedback',
12298 cls : 'roo-combo-removable-btn close'
12305 cls : 'has-feedback',
12314 if(this.removable && !this.editable ){
12316 cls : 'roo-removable',
12322 cls : 'roo-combo-removable-btn close'
12329 if (this.before || this.after) {
12332 cls : 'input-group',
12336 inputblock.cn.push({
12338 cls : 'input-group-addon input-group-prepend input-group-text',
12343 inputblock.cn.push(input);
12345 if(this.hasFeedback && !this.allowBlank){
12346 inputblock.cls += ' has-feedback';
12347 inputblock.cn.push(feedback);
12351 inputblock.cn.push({
12353 cls : 'input-group-addon input-group-append input-group-text',
12362 var ibwrap = inputblock;
12367 cls: 'roo-select2-choices',
12371 cls: 'roo-select2-search-field',
12383 cls: 'roo-select2-container input-group',
12388 cls: 'form-hidden-field'
12394 if(!this.multiple && this.showToggleBtn){
12400 if (this.caret != false) {
12403 cls: 'fa fa-' + this.caret
12410 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12412 Roo.bootstrap.version == 3 ? caret : '',
12415 cls: 'combobox-clear',
12429 combobox.cls += ' roo-select2-container-multi';
12433 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12434 tooltip : 'This field is required'
12436 if (Roo.bootstrap.version == 4) {
12439 style : 'display:none'
12444 if (align ==='left' && this.fieldLabel.length) {
12446 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12453 cls : 'control-label',
12454 html : this.fieldLabel
12466 var labelCfg = cfg.cn[1];
12467 var contentCfg = cfg.cn[2];
12469 if(this.indicatorpos == 'right'){
12474 cls : 'control-label',
12478 html : this.fieldLabel
12492 labelCfg = cfg.cn[0];
12493 contentCfg = cfg.cn[1];
12496 if(this.labelWidth > 12){
12497 labelCfg.style = "width: " + this.labelWidth + 'px';
12500 if(this.labelWidth < 13 && this.labelmd == 0){
12501 this.labelmd = this.labelWidth;
12504 if(this.labellg > 0){
12505 labelCfg.cls += ' col-lg-' + this.labellg;
12506 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12509 if(this.labelmd > 0){
12510 labelCfg.cls += ' col-md-' + this.labelmd;
12511 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12514 if(this.labelsm > 0){
12515 labelCfg.cls += ' col-sm-' + this.labelsm;
12516 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12519 if(this.labelxs > 0){
12520 labelCfg.cls += ' col-xs-' + this.labelxs;
12521 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12524 } else if ( this.fieldLabel.length) {
12525 // Roo.log(" label");
12530 //cls : 'input-group-addon',
12531 html : this.fieldLabel
12539 if(this.indicatorpos == 'right'){
12547 html : this.fieldLabel
12561 // Roo.log(" no label && no align");
12568 ['xs','sm','md','lg'].map(function(size){
12569 if (settings[size]) {
12570 cfg.cls += ' col-' + size + '-' + settings[size];
12581 onResize : function(w, h){
12582 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12583 // if(typeof w == 'number'){
12584 // var x = w - this.trigger.getWidth();
12585 // this.inputEl().setWidth(this.adjustWidth('input', x));
12586 // this.trigger.setStyle('left', x+'px');
12591 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12594 getResizeEl : function(){
12595 return this.inputEl();
12599 getPositionEl : function(){
12600 return this.inputEl();
12604 alignErrorIcon : function(){
12605 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12609 initEvents : function(){
12613 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12614 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12615 if(!this.multiple && this.showToggleBtn){
12616 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12617 if(this.hideTrigger){
12618 this.trigger.setDisplayed(false);
12620 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12624 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12627 if(this.removable && !this.editable && !this.tickable){
12628 var close = this.closeTriggerEl();
12631 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12632 close.on('click', this.removeBtnClick, this, close);
12636 //this.trigger.addClassOnOver('x-form-trigger-over');
12637 //this.trigger.addClassOnClick('x-form-trigger-click');
12640 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12644 closeTriggerEl : function()
12646 var close = this.el.select('.roo-combo-removable-btn', true).first();
12647 return close ? close : false;
12650 removeBtnClick : function(e, h, el)
12652 e.preventDefault();
12654 if(this.fireEvent("remove", this) !== false){
12656 this.fireEvent("afterremove", this)
12660 createList : function()
12662 this.list = Roo.get(document.body).createChild({
12663 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12664 cls: 'typeahead typeahead-long dropdown-menu shadow',
12665 style: 'display:none'
12668 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12673 initTrigger : function(){
12678 onDestroy : function(){
12680 this.trigger.removeAllListeners();
12681 // this.trigger.remove();
12684 // this.wrap.remove();
12686 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12690 onFocus : function(){
12691 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12693 if(!this.mimicing){
12694 this.wrap.addClass('x-trigger-wrap-focus');
12695 this.mimicing = true;
12696 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12697 if(this.monitorTab){
12698 this.el.on("keydown", this.checkTab, this);
12705 checkTab : function(e){
12706 if(e.getKey() == e.TAB){
12707 this.triggerBlur();
12712 onBlur : function(){
12717 mimicBlur : function(e, t){
12719 if(!this.wrap.contains(t) && this.validateBlur()){
12720 this.triggerBlur();
12726 triggerBlur : function(){
12727 this.mimicing = false;
12728 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12729 if(this.monitorTab){
12730 this.el.un("keydown", this.checkTab, this);
12732 //this.wrap.removeClass('x-trigger-wrap-focus');
12733 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12737 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12738 validateBlur : function(e, t){
12743 onDisable : function(){
12744 this.inputEl().dom.disabled = true;
12745 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12747 // this.wrap.addClass('x-item-disabled');
12752 onEnable : function(){
12753 this.inputEl().dom.disabled = false;
12754 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12756 // this.el.removeClass('x-item-disabled');
12761 onShow : function(){
12762 var ae = this.getActionEl();
12765 ae.dom.style.display = '';
12766 ae.dom.style.visibility = 'visible';
12772 onHide : function(){
12773 var ae = this.getActionEl();
12774 ae.dom.style.display = 'none';
12778 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12779 * by an implementing function.
12781 * @param {EventObject} e
12783 onTriggerClick : Roo.emptyFn
12791 * @class Roo.bootstrap.CardUploader
12792 * @extends Roo.bootstrap.Button
12793 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12794 * @cfg {Number} errorTimeout default 3000
12795 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12796 * @cfg {Array} html The button text.
12800 * Create a new CardUploader
12801 * @param {Object} config The config object
12804 Roo.bootstrap.CardUploader = function(config){
12808 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12811 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12819 * When a image is clicked on - and needs to display a slideshow or similar..
12820 * @param {Roo.bootstrap.Card} this
12821 * @param {Object} The image information data
12827 * When a the download link is clicked
12828 * @param {Roo.bootstrap.Card} this
12829 * @param {Object} The image information data contains
12836 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12839 errorTimeout : 3000,
12843 fileCollection : false,
12846 getAutoCreate : function()
12850 cls :'form-group' ,
12855 //cls : 'input-group-addon',
12856 html : this.fieldLabel
12864 value : this.value,
12865 cls : 'd-none form-control'
12870 multiple : 'multiple',
12872 cls : 'd-none roo-card-upload-selector'
12876 cls : 'roo-card-uploader-button-container w-100 mb-2'
12879 cls : 'card-columns roo-card-uploader-container'
12889 getChildContainer : function() /// what children are added to.
12891 return this.containerEl;
12894 getButtonContainer : function() /// what children are added to.
12896 return this.el.select(".roo-card-uploader-button-container").first();
12899 initEvents : function()
12902 Roo.bootstrap.Input.prototype.initEvents.call(this);
12906 xns: Roo.bootstrap,
12909 container_method : 'getButtonContainer' ,
12910 html : this.html, // fix changable?
12913 'click' : function(btn, e) {
12922 this.urlAPI = (window.createObjectURL && window) ||
12923 (window.URL && URL.revokeObjectURL && URL) ||
12924 (window.webkitURL && webkitURL);
12929 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12931 this.selectorEl.on('change', this.onFileSelected, this);
12934 this.images.forEach(function(img) {
12937 this.images = false;
12939 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12945 onClick : function(e)
12947 e.preventDefault();
12949 this.selectorEl.dom.click();
12953 onFileSelected : function(e)
12955 e.preventDefault();
12957 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12961 Roo.each(this.selectorEl.dom.files, function(file){
12962 this.addFile(file);
12971 addFile : function(file)
12974 if(typeof(file) === 'string'){
12975 throw "Add file by name?"; // should not happen
12979 if(!file || !this.urlAPI){
12989 var url = _this.urlAPI.createObjectURL( file);
12992 id : Roo.bootstrap.CardUploader.ID--,
12993 is_uploaded : false,
12997 mimetype : file.type,
13005 * addCard - add an Attachment to the uploader
13006 * @param data - the data about the image to upload
13010 title : "Title of file",
13011 is_uploaded : false,
13012 src : "http://.....",
13013 srcfile : { the File upload object },
13014 mimetype : file.type,
13017 .. any other data...
13023 addCard : function (data)
13025 // hidden input element?
13026 // if the file is not an image...
13027 //then we need to use something other that and header_image
13032 xns : Roo.bootstrap,
13033 xtype : 'CardFooter',
13036 xns : Roo.bootstrap,
13042 xns : Roo.bootstrap,
13044 html : String.format("<small>{0}</small>", data.title),
13045 cls : 'col-10 text-left',
13050 click : function() {
13052 t.fireEvent( "download", t, data );
13058 xns : Roo.bootstrap,
13060 style: 'max-height: 28px; ',
13066 click : function() {
13067 t.removeCard(data.id)
13079 var cn = this.addxtype(
13082 xns : Roo.bootstrap,
13085 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13086 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13087 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13092 initEvents : function() {
13093 Roo.bootstrap.Card.prototype.initEvents.call(this);
13095 this.imgEl = this.el.select('.card-img-top').first();
13097 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13098 this.imgEl.set({ 'pointer' : 'cursor' });
13101 this.getCardFooter().addClass('p-1');
13108 // dont' really need ot update items.
13109 // this.items.push(cn);
13110 this.fileCollection.add(cn);
13112 if (!data.srcfile) {
13113 this.updateInput();
13118 var reader = new FileReader();
13119 reader.addEventListener("load", function() {
13120 data.srcdata = reader.result;
13123 reader.readAsDataURL(data.srcfile);
13128 removeCard : function(id)
13131 var card = this.fileCollection.get(id);
13132 card.data.is_deleted = 1;
13133 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13134 //this.fileCollection.remove(card);
13135 //this.items = this.items.filter(function(e) { return e != card });
13136 // dont' really need ot update items.
13137 card.el.dom.parentNode.removeChild(card.el.dom);
13138 this.updateInput();
13144 this.fileCollection.each(function(card) {
13145 if (card.el.dom && card.el.dom.parentNode) {
13146 card.el.dom.parentNode.removeChild(card.el.dom);
13149 this.fileCollection.clear();
13150 this.updateInput();
13153 updateInput : function()
13156 this.fileCollection.each(function(e) {
13160 this.inputEl().dom.value = JSON.stringify(data);
13170 Roo.bootstrap.CardUploader.ID = -1;/*
13172 * Ext JS Library 1.1.1
13173 * Copyright(c) 2006-2007, Ext JS, LLC.
13175 * Originally Released Under LGPL - original licence link has changed is not relivant.
13178 * <script type="text/javascript">
13183 * @class Roo.data.SortTypes
13185 * Defines the default sorting (casting?) comparison functions used when sorting data.
13187 Roo.data.SortTypes = {
13189 * Default sort that does nothing
13190 * @param {Mixed} s The value being converted
13191 * @return {Mixed} The comparison value
13193 none : function(s){
13198 * The regular expression used to strip tags
13202 stripTagsRE : /<\/?[^>]+>/gi,
13205 * Strips all HTML tags to sort on text only
13206 * @param {Mixed} s The value being converted
13207 * @return {String} The comparison value
13209 asText : function(s){
13210 return String(s).replace(this.stripTagsRE, "");
13214 * Strips all HTML tags to sort on text only - Case insensitive
13215 * @param {Mixed} s The value being converted
13216 * @return {String} The comparison value
13218 asUCText : function(s){
13219 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13223 * Case insensitive string
13224 * @param {Mixed} s The value being converted
13225 * @return {String} The comparison value
13227 asUCString : function(s) {
13228 return String(s).toUpperCase();
13233 * @param {Mixed} s The value being converted
13234 * @return {Number} The comparison value
13236 asDate : function(s) {
13240 if(s instanceof Date){
13241 return s.getTime();
13243 return Date.parse(String(s));
13248 * @param {Mixed} s The value being converted
13249 * @return {Float} The comparison value
13251 asFloat : function(s) {
13252 var val = parseFloat(String(s).replace(/,/g, ""));
13261 * @param {Mixed} s The value being converted
13262 * @return {Number} The comparison value
13264 asInt : function(s) {
13265 var val = parseInt(String(s).replace(/,/g, ""));
13273 * Ext JS Library 1.1.1
13274 * Copyright(c) 2006-2007, Ext JS, LLC.
13276 * Originally Released Under LGPL - original licence link has changed is not relivant.
13279 * <script type="text/javascript">
13283 * @class Roo.data.Record
13284 * Instances of this class encapsulate both record <em>definition</em> information, and record
13285 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13286 * to access Records cached in an {@link Roo.data.Store} object.<br>
13288 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13289 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13292 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13294 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13295 * {@link #create}. The parameters are the same.
13296 * @param {Array} data An associative Array of data values keyed by the field name.
13297 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13298 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13299 * not specified an integer id is generated.
13301 Roo.data.Record = function(data, id){
13302 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13307 * Generate a constructor for a specific record layout.
13308 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13309 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13310 * Each field definition object may contain the following properties: <ul>
13311 * <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,
13312 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13313 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13314 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13315 * is being used, then this is a string containing the javascript expression to reference the data relative to
13316 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13317 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13318 * this may be omitted.</p></li>
13319 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13320 * <ul><li>auto (Default, implies no conversion)</li>
13325 * <li>date</li></ul></p></li>
13326 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13327 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13328 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13329 * by the Reader into an object that will be stored in the Record. It is passed the
13330 * following parameters:<ul>
13331 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13333 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13335 * <br>usage:<br><pre><code>
13336 var TopicRecord = Roo.data.Record.create(
13337 {name: 'title', mapping: 'topic_title'},
13338 {name: 'author', mapping: 'username'},
13339 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13340 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13341 {name: 'lastPoster', mapping: 'user2'},
13342 {name: 'excerpt', mapping: 'post_text'}
13345 var myNewRecord = new TopicRecord({
13346 title: 'Do my job please',
13349 lastPost: new Date(),
13350 lastPoster: 'Animal',
13351 excerpt: 'No way dude!'
13353 myStore.add(myNewRecord);
13358 Roo.data.Record.create = function(o){
13359 var f = function(){
13360 f.superclass.constructor.apply(this, arguments);
13362 Roo.extend(f, Roo.data.Record);
13363 var p = f.prototype;
13364 p.fields = new Roo.util.MixedCollection(false, function(field){
13367 for(var i = 0, len = o.length; i < len; i++){
13368 p.fields.add(new Roo.data.Field(o[i]));
13370 f.getField = function(name){
13371 return p.fields.get(name);
13376 Roo.data.Record.AUTO_ID = 1000;
13377 Roo.data.Record.EDIT = 'edit';
13378 Roo.data.Record.REJECT = 'reject';
13379 Roo.data.Record.COMMIT = 'commit';
13381 Roo.data.Record.prototype = {
13383 * Readonly flag - true if this record has been modified.
13392 join : function(store){
13393 this.store = store;
13397 * Set the named field to the specified value.
13398 * @param {String} name The name of the field to set.
13399 * @param {Object} value The value to set the field to.
13401 set : function(name, value){
13402 if(this.data[name] == value){
13406 if(!this.modified){
13407 this.modified = {};
13409 if(typeof this.modified[name] == 'undefined'){
13410 this.modified[name] = this.data[name];
13412 this.data[name] = value;
13413 if(!this.editing && this.store){
13414 this.store.afterEdit(this);
13419 * Get the value of the named field.
13420 * @param {String} name The name of the field to get the value of.
13421 * @return {Object} The value of the field.
13423 get : function(name){
13424 return this.data[name];
13428 beginEdit : function(){
13429 this.editing = true;
13430 this.modified = {};
13434 cancelEdit : function(){
13435 this.editing = false;
13436 delete this.modified;
13440 endEdit : function(){
13441 this.editing = false;
13442 if(this.dirty && this.store){
13443 this.store.afterEdit(this);
13448 * Usually called by the {@link Roo.data.Store} which owns the Record.
13449 * Rejects all changes made to the Record since either creation, or the last commit operation.
13450 * Modified fields are reverted to their original values.
13452 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13453 * of reject operations.
13455 reject : function(){
13456 var m = this.modified;
13458 if(typeof m[n] != "function"){
13459 this.data[n] = m[n];
13462 this.dirty = false;
13463 delete this.modified;
13464 this.editing = false;
13466 this.store.afterReject(this);
13471 * Usually called by the {@link Roo.data.Store} which owns the Record.
13472 * Commits all changes made to the Record since either creation, or the last commit operation.
13474 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13475 * of commit operations.
13477 commit : function(){
13478 this.dirty = false;
13479 delete this.modified;
13480 this.editing = false;
13482 this.store.afterCommit(this);
13487 hasError : function(){
13488 return this.error != null;
13492 clearError : function(){
13497 * Creates a copy of this record.
13498 * @param {String} id (optional) A new record id if you don't want to use this record's id
13501 copy : function(newId) {
13502 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13506 * Ext JS Library 1.1.1
13507 * Copyright(c) 2006-2007, Ext JS, LLC.
13509 * Originally Released Under LGPL - original licence link has changed is not relivant.
13512 * <script type="text/javascript">
13518 * @class Roo.data.Store
13519 * @extends Roo.util.Observable
13520 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13521 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13523 * 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
13524 * has no knowledge of the format of the data returned by the Proxy.<br>
13526 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13527 * instances from the data object. These records are cached and made available through accessor functions.
13529 * Creates a new Store.
13530 * @param {Object} config A config object containing the objects needed for the Store to access data,
13531 * and read the data into Records.
13533 Roo.data.Store = function(config){
13534 this.data = new Roo.util.MixedCollection(false);
13535 this.data.getKey = function(o){
13538 this.baseParams = {};
13540 this.paramNames = {
13545 "multisort" : "_multisort"
13548 if(config && config.data){
13549 this.inlineData = config.data;
13550 delete config.data;
13553 Roo.apply(this, config);
13555 if(this.reader){ // reader passed
13556 this.reader = Roo.factory(this.reader, Roo.data);
13557 this.reader.xmodule = this.xmodule || false;
13558 if(!this.recordType){
13559 this.recordType = this.reader.recordType;
13561 if(this.reader.onMetaChange){
13562 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13566 if(this.recordType){
13567 this.fields = this.recordType.prototype.fields;
13569 this.modified = [];
13573 * @event datachanged
13574 * Fires when the data cache has changed, and a widget which is using this Store
13575 * as a Record cache should refresh its view.
13576 * @param {Store} this
13578 datachanged : true,
13580 * @event metachange
13581 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13582 * @param {Store} this
13583 * @param {Object} meta The JSON metadata
13588 * Fires when Records have been added to the Store
13589 * @param {Store} this
13590 * @param {Roo.data.Record[]} records The array of Records added
13591 * @param {Number} index The index at which the record(s) were added
13596 * Fires when a Record has been removed from the Store
13597 * @param {Store} this
13598 * @param {Roo.data.Record} record The Record that was removed
13599 * @param {Number} index The index at which the record was removed
13604 * Fires when a Record has been updated
13605 * @param {Store} this
13606 * @param {Roo.data.Record} record The Record that was updated
13607 * @param {String} operation The update operation being performed. Value may be one of:
13609 Roo.data.Record.EDIT
13610 Roo.data.Record.REJECT
13611 Roo.data.Record.COMMIT
13617 * Fires when the data cache has been cleared.
13618 * @param {Store} this
13622 * @event beforeload
13623 * Fires before a request is made for a new data object. If the beforeload handler returns false
13624 * the load action will be canceled.
13625 * @param {Store} this
13626 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13630 * @event beforeloadadd
13631 * Fires after a new set of Records has been loaded.
13632 * @param {Store} this
13633 * @param {Roo.data.Record[]} records The Records that were loaded
13634 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13636 beforeloadadd : true,
13639 * Fires after a new set of Records has been loaded, before they are added to the store.
13640 * @param {Store} this
13641 * @param {Roo.data.Record[]} records The Records that were loaded
13642 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13643 * @params {Object} return from reader
13647 * @event loadexception
13648 * Fires if an exception occurs in the Proxy during loading.
13649 * Called with the signature of the Proxy's "loadexception" event.
13650 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13653 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13654 * @param {Object} load options
13655 * @param {Object} jsonData from your request (normally this contains the Exception)
13657 loadexception : true
13661 this.proxy = Roo.factory(this.proxy, Roo.data);
13662 this.proxy.xmodule = this.xmodule || false;
13663 this.relayEvents(this.proxy, ["loadexception"]);
13665 this.sortToggle = {};
13666 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13668 Roo.data.Store.superclass.constructor.call(this);
13670 if(this.inlineData){
13671 this.loadData(this.inlineData);
13672 delete this.inlineData;
13676 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13678 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13679 * without a remote query - used by combo/forms at present.
13683 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13686 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13689 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13690 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13693 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13694 * on any HTTP request
13697 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13700 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13704 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13705 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13707 remoteSort : false,
13710 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13711 * loaded or when a record is removed. (defaults to false).
13713 pruneModifiedRecords : false,
13716 lastOptions : null,
13719 * Add Records to the Store and fires the add event.
13720 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13722 add : function(records){
13723 records = [].concat(records);
13724 for(var i = 0, len = records.length; i < len; i++){
13725 records[i].join(this);
13727 var index = this.data.length;
13728 this.data.addAll(records);
13729 this.fireEvent("add", this, records, index);
13733 * Remove a Record from the Store and fires the remove event.
13734 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13736 remove : function(record){
13737 var index = this.data.indexOf(record);
13738 this.data.removeAt(index);
13740 if(this.pruneModifiedRecords){
13741 this.modified.remove(record);
13743 this.fireEvent("remove", this, record, index);
13747 * Remove all Records from the Store and fires the clear event.
13749 removeAll : function(){
13751 if(this.pruneModifiedRecords){
13752 this.modified = [];
13754 this.fireEvent("clear", this);
13758 * Inserts Records to the Store at the given index and fires the add event.
13759 * @param {Number} index The start index at which to insert the passed Records.
13760 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13762 insert : function(index, records){
13763 records = [].concat(records);
13764 for(var i = 0, len = records.length; i < len; i++){
13765 this.data.insert(index, records[i]);
13766 records[i].join(this);
13768 this.fireEvent("add", this, records, index);
13772 * Get the index within the cache of the passed Record.
13773 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13774 * @return {Number} The index of the passed Record. Returns -1 if not found.
13776 indexOf : function(record){
13777 return this.data.indexOf(record);
13781 * Get the index within the cache of the Record with the passed id.
13782 * @param {String} id The id of the Record to find.
13783 * @return {Number} The index of the Record. Returns -1 if not found.
13785 indexOfId : function(id){
13786 return this.data.indexOfKey(id);
13790 * Get the Record with the specified id.
13791 * @param {String} id The id of the Record to find.
13792 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13794 getById : function(id){
13795 return this.data.key(id);
13799 * Get the Record at the specified index.
13800 * @param {Number} index The index of the Record to find.
13801 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13803 getAt : function(index){
13804 return this.data.itemAt(index);
13808 * Returns a range of Records between specified indices.
13809 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13810 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13811 * @return {Roo.data.Record[]} An array of Records
13813 getRange : function(start, end){
13814 return this.data.getRange(start, end);
13818 storeOptions : function(o){
13819 o = Roo.apply({}, o);
13822 this.lastOptions = o;
13826 * Loads the Record cache from the configured Proxy using the configured Reader.
13828 * If using remote paging, then the first load call must specify the <em>start</em>
13829 * and <em>limit</em> properties in the options.params property to establish the initial
13830 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13832 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13833 * and this call will return before the new data has been loaded. Perform any post-processing
13834 * in a callback function, or in a "load" event handler.</strong>
13836 * @param {Object} options An object containing properties which control loading options:<ul>
13837 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13838 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13839 * passed the following arguments:<ul>
13840 * <li>r : Roo.data.Record[]</li>
13841 * <li>options: Options object from the load call</li>
13842 * <li>success: Boolean success indicator</li></ul></li>
13843 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13844 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13847 load : function(options){
13848 options = options || {};
13849 if(this.fireEvent("beforeload", this, options) !== false){
13850 this.storeOptions(options);
13851 var p = Roo.apply(options.params || {}, this.baseParams);
13852 // if meta was not loaded from remote source.. try requesting it.
13853 if (!this.reader.metaFromRemote) {
13854 p._requestMeta = 1;
13856 if(this.sortInfo && this.remoteSort){
13857 var pn = this.paramNames;
13858 p[pn["sort"]] = this.sortInfo.field;
13859 p[pn["dir"]] = this.sortInfo.direction;
13861 if (this.multiSort) {
13862 var pn = this.paramNames;
13863 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13866 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13871 * Reloads the Record cache from the configured Proxy using the configured Reader and
13872 * the options from the last load operation performed.
13873 * @param {Object} options (optional) An object containing properties which may override the options
13874 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13875 * the most recently used options are reused).
13877 reload : function(options){
13878 this.load(Roo.applyIf(options||{}, this.lastOptions));
13882 // Called as a callback by the Reader during a load operation.
13883 loadRecords : function(o, options, success){
13884 if(!o || success === false){
13885 if(success !== false){
13886 this.fireEvent("load", this, [], options, o);
13888 if(options.callback){
13889 options.callback.call(options.scope || this, [], options, false);
13893 // if data returned failure - throw an exception.
13894 if (o.success === false) {
13895 // show a message if no listener is registered.
13896 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13897 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13899 // loadmask wil be hooked into this..
13900 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13903 var r = o.records, t = o.totalRecords || r.length;
13905 this.fireEvent("beforeloadadd", this, r, options, o);
13907 if(!options || options.add !== true){
13908 if(this.pruneModifiedRecords){
13909 this.modified = [];
13911 for(var i = 0, len = r.length; i < len; i++){
13915 this.data = this.snapshot;
13916 delete this.snapshot;
13919 this.data.addAll(r);
13920 this.totalLength = t;
13922 this.fireEvent("datachanged", this);
13924 this.totalLength = Math.max(t, this.data.length+r.length);
13928 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13930 var e = new Roo.data.Record({});
13932 e.set(this.parent.displayField, this.parent.emptyTitle);
13933 e.set(this.parent.valueField, '');
13938 this.fireEvent("load", this, r, options, o);
13939 if(options.callback){
13940 options.callback.call(options.scope || this, r, options, true);
13946 * Loads data from a passed data block. A Reader which understands the format of the data
13947 * must have been configured in the constructor.
13948 * @param {Object} data The data block from which to read the Records. The format of the data expected
13949 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13950 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13952 loadData : function(o, append){
13953 var r = this.reader.readRecords(o);
13954 this.loadRecords(r, {add: append}, true);
13958 * using 'cn' the nested child reader read the child array into it's child stores.
13959 * @param {Object} rec The record with a 'children array
13961 loadDataFromChildren : function(rec)
13963 this.loadData(this.reader.toLoadData(rec));
13968 * Gets the number of cached records.
13970 * <em>If using paging, this may not be the total size of the dataset. If the data object
13971 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13972 * the data set size</em>
13974 getCount : function(){
13975 return this.data.length || 0;
13979 * Gets the total number of records in the dataset as returned by the server.
13981 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13982 * the dataset size</em>
13984 getTotalCount : function(){
13985 return this.totalLength || 0;
13989 * Returns the sort state of the Store as an object with two properties:
13991 field {String} The name of the field by which the Records are sorted
13992 direction {String} The sort order, "ASC" or "DESC"
13995 getSortState : function(){
13996 return this.sortInfo;
14000 applySort : function(){
14001 if(this.sortInfo && !this.remoteSort){
14002 var s = this.sortInfo, f = s.field;
14003 var st = this.fields.get(f).sortType;
14004 var fn = function(r1, r2){
14005 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14006 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14008 this.data.sort(s.direction, fn);
14009 if(this.snapshot && this.snapshot != this.data){
14010 this.snapshot.sort(s.direction, fn);
14016 * Sets the default sort column and order to be used by the next load operation.
14017 * @param {String} fieldName The name of the field to sort by.
14018 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14020 setDefaultSort : function(field, dir){
14021 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14025 * Sort the Records.
14026 * If remote sorting is used, the sort is performed on the server, and the cache is
14027 * reloaded. If local sorting is used, the cache is sorted internally.
14028 * @param {String} fieldName The name of the field to sort by.
14029 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14031 sort : function(fieldName, dir){
14032 var f = this.fields.get(fieldName);
14034 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14036 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14037 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14042 this.sortToggle[f.name] = dir;
14043 this.sortInfo = {field: f.name, direction: dir};
14044 if(!this.remoteSort){
14046 this.fireEvent("datachanged", this);
14048 this.load(this.lastOptions);
14053 * Calls the specified function for each of the Records in the cache.
14054 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14055 * Returning <em>false</em> aborts and exits the iteration.
14056 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14058 each : function(fn, scope){
14059 this.data.each(fn, scope);
14063 * Gets all records modified since the last commit. Modified records are persisted across load operations
14064 * (e.g., during paging).
14065 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14067 getModifiedRecords : function(){
14068 return this.modified;
14072 createFilterFn : function(property, value, anyMatch){
14073 if(!value.exec){ // not a regex
14074 value = String(value);
14075 if(value.length == 0){
14078 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14080 return function(r){
14081 return value.test(r.data[property]);
14086 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14087 * @param {String} property A field on your records
14088 * @param {Number} start The record index to start at (defaults to 0)
14089 * @param {Number} end The last record index to include (defaults to length - 1)
14090 * @return {Number} The sum
14092 sum : function(property, start, end){
14093 var rs = this.data.items, v = 0;
14094 start = start || 0;
14095 end = (end || end === 0) ? end : rs.length-1;
14097 for(var i = start; i <= end; i++){
14098 v += (rs[i].data[property] || 0);
14104 * Filter the records by a specified property.
14105 * @param {String} field A field on your records
14106 * @param {String/RegExp} value Either a string that the field
14107 * should start with or a RegExp to test against the field
14108 * @param {Boolean} anyMatch True to match any part not just the beginning
14110 filter : function(property, value, anyMatch){
14111 var fn = this.createFilterFn(property, value, anyMatch);
14112 return fn ? this.filterBy(fn) : this.clearFilter();
14116 * Filter by a function. The specified function will be called with each
14117 * record in this data source. If the function returns true the record is included,
14118 * otherwise it is filtered.
14119 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14120 * @param {Object} scope (optional) The scope of the function (defaults to this)
14122 filterBy : function(fn, scope){
14123 this.snapshot = this.snapshot || this.data;
14124 this.data = this.queryBy(fn, scope||this);
14125 this.fireEvent("datachanged", this);
14129 * Query the records by a specified property.
14130 * @param {String} field A field on your records
14131 * @param {String/RegExp} value Either a string that the field
14132 * should start with or a RegExp to test against the field
14133 * @param {Boolean} anyMatch True to match any part not just the beginning
14134 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14136 query : function(property, value, anyMatch){
14137 var fn = this.createFilterFn(property, value, anyMatch);
14138 return fn ? this.queryBy(fn) : this.data.clone();
14142 * Query by a function. The specified function will be called with each
14143 * record in this data source. If the function returns true the record is included
14145 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14146 * @param {Object} scope (optional) The scope of the function (defaults to this)
14147 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14149 queryBy : function(fn, scope){
14150 var data = this.snapshot || this.data;
14151 return data.filterBy(fn, scope||this);
14155 * Collects unique values for a particular dataIndex from this store.
14156 * @param {String} dataIndex The property to collect
14157 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14158 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14159 * @return {Array} An array of the unique values
14161 collect : function(dataIndex, allowNull, bypassFilter){
14162 var d = (bypassFilter === true && this.snapshot) ?
14163 this.snapshot.items : this.data.items;
14164 var v, sv, r = [], l = {};
14165 for(var i = 0, len = d.length; i < len; i++){
14166 v = d[i].data[dataIndex];
14168 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14177 * Revert to a view of the Record cache with no filtering applied.
14178 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14180 clearFilter : function(suppressEvent){
14181 if(this.snapshot && this.snapshot != this.data){
14182 this.data = this.snapshot;
14183 delete this.snapshot;
14184 if(suppressEvent !== true){
14185 this.fireEvent("datachanged", this);
14191 afterEdit : function(record){
14192 if(this.modified.indexOf(record) == -1){
14193 this.modified.push(record);
14195 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14199 afterReject : function(record){
14200 this.modified.remove(record);
14201 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14205 afterCommit : function(record){
14206 this.modified.remove(record);
14207 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14211 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14212 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14214 commitChanges : function(){
14215 var m = this.modified.slice(0);
14216 this.modified = [];
14217 for(var i = 0, len = m.length; i < len; i++){
14223 * Cancel outstanding changes on all changed records.
14225 rejectChanges : function(){
14226 var m = this.modified.slice(0);
14227 this.modified = [];
14228 for(var i = 0, len = m.length; i < len; i++){
14233 onMetaChange : function(meta, rtype, o){
14234 this.recordType = rtype;
14235 this.fields = rtype.prototype.fields;
14236 delete this.snapshot;
14237 this.sortInfo = meta.sortInfo || this.sortInfo;
14238 this.modified = [];
14239 this.fireEvent('metachange', this, this.reader.meta);
14242 moveIndex : function(data, type)
14244 var index = this.indexOf(data);
14246 var newIndex = index + type;
14250 this.insert(newIndex, data);
14255 * Ext JS Library 1.1.1
14256 * Copyright(c) 2006-2007, Ext JS, LLC.
14258 * Originally Released Under LGPL - original licence link has changed is not relivant.
14261 * <script type="text/javascript">
14265 * @class Roo.data.SimpleStore
14266 * @extends Roo.data.Store
14267 * Small helper class to make creating Stores from Array data easier.
14268 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14269 * @cfg {Array} fields An array of field definition objects, or field name strings.
14270 * @cfg {Object} an existing reader (eg. copied from another store)
14271 * @cfg {Array} data The multi-dimensional array of data
14273 * @param {Object} config
14275 Roo.data.SimpleStore = function(config)
14277 Roo.data.SimpleStore.superclass.constructor.call(this, {
14279 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14282 Roo.data.Record.create(config.fields)
14284 proxy : new Roo.data.MemoryProxy(config.data)
14288 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14290 * Ext JS Library 1.1.1
14291 * Copyright(c) 2006-2007, Ext JS, LLC.
14293 * Originally Released Under LGPL - original licence link has changed is not relivant.
14296 * <script type="text/javascript">
14301 * @extends Roo.data.Store
14302 * @class Roo.data.JsonStore
14303 * Small helper class to make creating Stores for JSON data easier. <br/>
14305 var store = new Roo.data.JsonStore({
14306 url: 'get-images.php',
14308 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14311 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14312 * JsonReader and HttpProxy (unless inline data is provided).</b>
14313 * @cfg {Array} fields An array of field definition objects, or field name strings.
14315 * @param {Object} config
14317 Roo.data.JsonStore = function(c){
14318 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14319 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14320 reader: new Roo.data.JsonReader(c, c.fields)
14323 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14325 * Ext JS Library 1.1.1
14326 * Copyright(c) 2006-2007, Ext JS, LLC.
14328 * Originally Released Under LGPL - original licence link has changed is not relivant.
14331 * <script type="text/javascript">
14335 Roo.data.Field = function(config){
14336 if(typeof config == "string"){
14337 config = {name: config};
14339 Roo.apply(this, config);
14342 this.type = "auto";
14345 var st = Roo.data.SortTypes;
14346 // named sortTypes are supported, here we look them up
14347 if(typeof this.sortType == "string"){
14348 this.sortType = st[this.sortType];
14351 // set default sortType for strings and dates
14352 if(!this.sortType){
14355 this.sortType = st.asUCString;
14358 this.sortType = st.asDate;
14361 this.sortType = st.none;
14366 var stripRe = /[\$,%]/g;
14368 // prebuilt conversion function for this field, instead of
14369 // switching every time we're reading a value
14371 var cv, dateFormat = this.dateFormat;
14376 cv = function(v){ return v; };
14379 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14383 return v !== undefined && v !== null && v !== '' ?
14384 parseInt(String(v).replace(stripRe, ""), 10) : '';
14389 return v !== undefined && v !== null && v !== '' ?
14390 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14395 cv = function(v){ return v === true || v === "true" || v == 1; };
14402 if(v instanceof Date){
14406 if(dateFormat == "timestamp"){
14407 return new Date(v*1000);
14409 return Date.parseDate(v, dateFormat);
14411 var parsed = Date.parse(v);
14412 return parsed ? new Date(parsed) : null;
14421 Roo.data.Field.prototype = {
14429 * Ext JS Library 1.1.1
14430 * Copyright(c) 2006-2007, Ext JS, LLC.
14432 * Originally Released Under LGPL - original licence link has changed is not relivant.
14435 * <script type="text/javascript">
14438 // Base class for reading structured data from a data source. This class is intended to be
14439 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14442 * @class Roo.data.DataReader
14443 * Base class for reading structured data from a data source. This class is intended to be
14444 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14447 Roo.data.DataReader = function(meta, recordType){
14451 this.recordType = recordType instanceof Array ?
14452 Roo.data.Record.create(recordType) : recordType;
14455 Roo.data.DataReader.prototype = {
14458 readerType : 'Data',
14460 * Create an empty record
14461 * @param {Object} data (optional) - overlay some values
14462 * @return {Roo.data.Record} record created.
14464 newRow : function(d) {
14466 this.recordType.prototype.fields.each(function(c) {
14468 case 'int' : da[c.name] = 0; break;
14469 case 'date' : da[c.name] = new Date(); break;
14470 case 'float' : da[c.name] = 0.0; break;
14471 case 'boolean' : da[c.name] = false; break;
14472 default : da[c.name] = ""; break;
14476 return new this.recordType(Roo.apply(da, d));
14482 * Ext JS Library 1.1.1
14483 * Copyright(c) 2006-2007, Ext JS, LLC.
14485 * Originally Released Under LGPL - original licence link has changed is not relivant.
14488 * <script type="text/javascript">
14492 * @class Roo.data.DataProxy
14493 * @extends Roo.data.Observable
14494 * This class is an abstract base class for implementations which provide retrieval of
14495 * unformatted data objects.<br>
14497 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14498 * (of the appropriate type which knows how to parse the data object) to provide a block of
14499 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14501 * Custom implementations must implement the load method as described in
14502 * {@link Roo.data.HttpProxy#load}.
14504 Roo.data.DataProxy = function(){
14507 * @event beforeload
14508 * Fires before a network request is made to retrieve a data object.
14509 * @param {Object} This DataProxy object.
14510 * @param {Object} params The params parameter to the load function.
14515 * Fires before the load method's callback is called.
14516 * @param {Object} This DataProxy object.
14517 * @param {Object} o The data object.
14518 * @param {Object} arg The callback argument object passed to the load function.
14522 * @event loadexception
14523 * Fires if an Exception occurs during data retrieval.
14524 * @param {Object} This DataProxy object.
14525 * @param {Object} o The data object.
14526 * @param {Object} arg The callback argument object passed to the load function.
14527 * @param {Object} e The Exception.
14529 loadexception : true
14531 Roo.data.DataProxy.superclass.constructor.call(this);
14534 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14537 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14541 * Ext JS Library 1.1.1
14542 * Copyright(c) 2006-2007, Ext JS, LLC.
14544 * Originally Released Under LGPL - original licence link has changed is not relivant.
14547 * <script type="text/javascript">
14550 * @class Roo.data.MemoryProxy
14551 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14552 * to the Reader when its load method is called.
14554 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14556 Roo.data.MemoryProxy = function(data){
14560 Roo.data.MemoryProxy.superclass.constructor.call(this);
14564 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14567 * Load data from the requested source (in this case an in-memory
14568 * data object passed to the constructor), read the data object into
14569 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14570 * process that block using the passed callback.
14571 * @param {Object} params This parameter is not used by the MemoryProxy class.
14572 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14573 * object into a block of Roo.data.Records.
14574 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14575 * The function must be passed <ul>
14576 * <li>The Record block object</li>
14577 * <li>The "arg" argument from the load function</li>
14578 * <li>A boolean success indicator</li>
14580 * @param {Object} scope The scope in which to call the callback
14581 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14583 load : function(params, reader, callback, scope, arg){
14584 params = params || {};
14587 result = reader.readRecords(params.data ? params.data :this.data);
14589 this.fireEvent("loadexception", this, arg, null, e);
14590 callback.call(scope, null, arg, false);
14593 callback.call(scope, result, arg, true);
14597 update : function(params, records){
14602 * Ext JS Library 1.1.1
14603 * Copyright(c) 2006-2007, Ext JS, LLC.
14605 * Originally Released Under LGPL - original licence link has changed is not relivant.
14608 * <script type="text/javascript">
14611 * @class Roo.data.HttpProxy
14612 * @extends Roo.data.DataProxy
14613 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14614 * configured to reference a certain URL.<br><br>
14616 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14617 * from which the running page was served.<br><br>
14619 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14621 * Be aware that to enable the browser to parse an XML document, the server must set
14622 * the Content-Type header in the HTTP response to "text/xml".
14624 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14625 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14626 * will be used to make the request.
14628 Roo.data.HttpProxy = function(conn){
14629 Roo.data.HttpProxy.superclass.constructor.call(this);
14630 // is conn a conn config or a real conn?
14632 this.useAjax = !conn || !conn.events;
14636 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14637 // thse are take from connection...
14640 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14643 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14644 * extra parameters to each request made by this object. (defaults to undefined)
14647 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14648 * to each request made by this object. (defaults to undefined)
14651 * @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)
14654 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14657 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14663 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14667 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14668 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14669 * a finer-grained basis than the DataProxy events.
14671 getConnection : function(){
14672 return this.useAjax ? Roo.Ajax : this.conn;
14676 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14677 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14678 * process that block using the passed callback.
14679 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14680 * for the request to the remote server.
14681 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14682 * object into a block of Roo.data.Records.
14683 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14684 * The function must be passed <ul>
14685 * <li>The Record block object</li>
14686 * <li>The "arg" argument from the load function</li>
14687 * <li>A boolean success indicator</li>
14689 * @param {Object} scope The scope in which to call the callback
14690 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14692 load : function(params, reader, callback, scope, arg){
14693 if(this.fireEvent("beforeload", this, params) !== false){
14695 params : params || {},
14697 callback : callback,
14702 callback : this.loadResponse,
14706 Roo.applyIf(o, this.conn);
14707 if(this.activeRequest){
14708 Roo.Ajax.abort(this.activeRequest);
14710 this.activeRequest = Roo.Ajax.request(o);
14712 this.conn.request(o);
14715 callback.call(scope||this, null, arg, false);
14720 loadResponse : function(o, success, response){
14721 delete this.activeRequest;
14723 this.fireEvent("loadexception", this, o, response);
14724 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14729 result = o.reader.read(response);
14731 this.fireEvent("loadexception", this, o, response, e);
14732 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14736 this.fireEvent("load", this, o, o.request.arg);
14737 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14741 update : function(dataSet){
14746 updateResponse : function(dataSet){
14751 * Ext JS Library 1.1.1
14752 * Copyright(c) 2006-2007, Ext JS, LLC.
14754 * Originally Released Under LGPL - original licence link has changed is not relivant.
14757 * <script type="text/javascript">
14761 * @class Roo.data.ScriptTagProxy
14762 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14763 * other than the originating domain of the running page.<br><br>
14765 * <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
14766 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14768 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14769 * source code that is used as the source inside a <script> tag.<br><br>
14771 * In order for the browser to process the returned data, the server must wrap the data object
14772 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14773 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14774 * depending on whether the callback name was passed:
14777 boolean scriptTag = false;
14778 String cb = request.getParameter("callback");
14781 response.setContentType("text/javascript");
14783 response.setContentType("application/x-json");
14785 Writer out = response.getWriter();
14787 out.write(cb + "(");
14789 out.print(dataBlock.toJsonString());
14796 * @param {Object} config A configuration object.
14798 Roo.data.ScriptTagProxy = function(config){
14799 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14800 Roo.apply(this, config);
14801 this.head = document.getElementsByTagName("head")[0];
14804 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14806 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14808 * @cfg {String} url The URL from which to request the data object.
14811 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14815 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14816 * the server the name of the callback function set up by the load call to process the returned data object.
14817 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14818 * javascript output which calls this named function passing the data object as its only parameter.
14820 callbackParam : "callback",
14822 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14823 * name to the request.
14828 * Load data from the configured URL, read the data object into
14829 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14830 * process that block using the passed callback.
14831 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14832 * for the request to the remote server.
14833 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14834 * object into a block of Roo.data.Records.
14835 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14836 * The function must be passed <ul>
14837 * <li>The Record block object</li>
14838 * <li>The "arg" argument from the load function</li>
14839 * <li>A boolean success indicator</li>
14841 * @param {Object} scope The scope in which to call the callback
14842 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14844 load : function(params, reader, callback, scope, arg){
14845 if(this.fireEvent("beforeload", this, params) !== false){
14847 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14849 var url = this.url;
14850 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14852 url += "&_dc=" + (new Date().getTime());
14854 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14857 cb : "stcCallback"+transId,
14858 scriptId : "stcScript"+transId,
14862 callback : callback,
14868 window[trans.cb] = function(o){
14869 conn.handleResponse(o, trans);
14872 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14874 if(this.autoAbort !== false){
14878 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14880 var script = document.createElement("script");
14881 script.setAttribute("src", url);
14882 script.setAttribute("type", "text/javascript");
14883 script.setAttribute("id", trans.scriptId);
14884 this.head.appendChild(script);
14886 this.trans = trans;
14888 callback.call(scope||this, null, arg, false);
14893 isLoading : function(){
14894 return this.trans ? true : false;
14898 * Abort the current server request.
14900 abort : function(){
14901 if(this.isLoading()){
14902 this.destroyTrans(this.trans);
14907 destroyTrans : function(trans, isLoaded){
14908 this.head.removeChild(document.getElementById(trans.scriptId));
14909 clearTimeout(trans.timeoutId);
14911 window[trans.cb] = undefined;
14913 delete window[trans.cb];
14916 // if hasn't been loaded, wait for load to remove it to prevent script error
14917 window[trans.cb] = function(){
14918 window[trans.cb] = undefined;
14920 delete window[trans.cb];
14927 handleResponse : function(o, trans){
14928 this.trans = false;
14929 this.destroyTrans(trans, true);
14932 result = trans.reader.readRecords(o);
14934 this.fireEvent("loadexception", this, o, trans.arg, e);
14935 trans.callback.call(trans.scope||window, null, trans.arg, false);
14938 this.fireEvent("load", this, o, trans.arg);
14939 trans.callback.call(trans.scope||window, result, trans.arg, true);
14943 handleFailure : function(trans){
14944 this.trans = false;
14945 this.destroyTrans(trans, false);
14946 this.fireEvent("loadexception", this, null, trans.arg);
14947 trans.callback.call(trans.scope||window, null, trans.arg, false);
14951 * Ext JS Library 1.1.1
14952 * Copyright(c) 2006-2007, Ext JS, LLC.
14954 * Originally Released Under LGPL - original licence link has changed is not relivant.
14957 * <script type="text/javascript">
14961 * @class Roo.data.JsonReader
14962 * @extends Roo.data.DataReader
14963 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14964 * based on mappings in a provided Roo.data.Record constructor.
14966 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14967 * in the reply previously.
14972 var RecordDef = Roo.data.Record.create([
14973 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14974 {name: 'occupation'} // This field will use "occupation" as the mapping.
14976 var myReader = new Roo.data.JsonReader({
14977 totalProperty: "results", // The property which contains the total dataset size (optional)
14978 root: "rows", // The property which contains an Array of row objects
14979 id: "id" // The property within each row object that provides an ID for the record (optional)
14983 * This would consume a JSON file like this:
14985 { 'results': 2, 'rows': [
14986 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14987 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14990 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14991 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14992 * paged from the remote server.
14993 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14994 * @cfg {String} root name of the property which contains the Array of row objects.
14995 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14996 * @cfg {Array} fields Array of field definition objects
14998 * Create a new JsonReader
14999 * @param {Object} meta Metadata configuration options
15000 * @param {Object} recordType Either an Array of field definition objects,
15001 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15003 Roo.data.JsonReader = function(meta, recordType){
15006 // set some defaults:
15007 Roo.applyIf(meta, {
15008 totalProperty: 'total',
15009 successProperty : 'success',
15014 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15016 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15018 readerType : 'Json',
15021 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15022 * Used by Store query builder to append _requestMeta to params.
15025 metaFromRemote : false,
15027 * This method is only used by a DataProxy which has retrieved data from a remote server.
15028 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15029 * @return {Object} data A data block which is used by an Roo.data.Store object as
15030 * a cache of Roo.data.Records.
15032 read : function(response){
15033 var json = response.responseText;
15035 var o = /* eval:var:o */ eval("("+json+")");
15037 throw {message: "JsonReader.read: Json object not found"};
15043 this.metaFromRemote = true;
15044 this.meta = o.metaData;
15045 this.recordType = Roo.data.Record.create(o.metaData.fields);
15046 this.onMetaChange(this.meta, this.recordType, o);
15048 return this.readRecords(o);
15051 // private function a store will implement
15052 onMetaChange : function(meta, recordType, o){
15059 simpleAccess: function(obj, subsc) {
15066 getJsonAccessor: function(){
15068 return function(expr) {
15070 return(re.test(expr))
15071 ? new Function("obj", "return obj." + expr)
15076 return Roo.emptyFn;
15081 * Create a data block containing Roo.data.Records from an XML document.
15082 * @param {Object} o An object which contains an Array of row objects in the property specified
15083 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15084 * which contains the total size of the dataset.
15085 * @return {Object} data A data block which is used by an Roo.data.Store object as
15086 * a cache of Roo.data.Records.
15088 readRecords : function(o){
15090 * After any data loads, the raw JSON data is available for further custom processing.
15094 var s = this.meta, Record = this.recordType,
15095 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15097 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15099 if(s.totalProperty) {
15100 this.getTotal = this.getJsonAccessor(s.totalProperty);
15102 if(s.successProperty) {
15103 this.getSuccess = this.getJsonAccessor(s.successProperty);
15105 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15107 var g = this.getJsonAccessor(s.id);
15108 this.getId = function(rec) {
15110 return (r === undefined || r === "") ? null : r;
15113 this.getId = function(){return null;};
15116 for(var jj = 0; jj < fl; jj++){
15118 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15119 this.ef[jj] = this.getJsonAccessor(map);
15123 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15124 if(s.totalProperty){
15125 var vt = parseInt(this.getTotal(o), 10);
15130 if(s.successProperty){
15131 var vs = this.getSuccess(o);
15132 if(vs === false || vs === 'false'){
15137 for(var i = 0; i < c; i++){
15140 var id = this.getId(n);
15141 for(var j = 0; j < fl; j++){
15143 var v = this.ef[j](n);
15145 Roo.log('missing convert for ' + f.name);
15149 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15151 var record = new Record(values, id);
15153 records[i] = record;
15159 totalRecords : totalRecords
15162 // used when loading children.. @see loadDataFromChildren
15163 toLoadData: function(rec)
15165 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15166 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15167 return { data : data, total : data.length };
15172 * Ext JS Library 1.1.1
15173 * Copyright(c) 2006-2007, Ext JS, LLC.
15175 * Originally Released Under LGPL - original licence link has changed is not relivant.
15178 * <script type="text/javascript">
15182 * @class Roo.data.ArrayReader
15183 * @extends Roo.data.DataReader
15184 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15185 * Each element of that Array represents a row of data fields. The
15186 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15187 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15191 var RecordDef = Roo.data.Record.create([
15192 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15193 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15195 var myReader = new Roo.data.ArrayReader({
15196 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15200 * This would consume an Array like this:
15202 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15206 * Create a new JsonReader
15207 * @param {Object} meta Metadata configuration options.
15208 * @param {Object|Array} recordType Either an Array of field definition objects
15210 * @cfg {Array} fields Array of field definition objects
15211 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15212 * as specified to {@link Roo.data.Record#create},
15213 * or an {@link Roo.data.Record} object
15216 * created using {@link Roo.data.Record#create}.
15218 Roo.data.ArrayReader = function(meta, recordType)
15220 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15223 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15226 * Create a data block containing Roo.data.Records from an XML document.
15227 * @param {Object} o An Array of row objects which represents the dataset.
15228 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15229 * a cache of Roo.data.Records.
15231 readRecords : function(o)
15233 var sid = this.meta ? this.meta.id : null;
15234 var recordType = this.recordType, fields = recordType.prototype.fields;
15237 for(var i = 0; i < root.length; i++){
15240 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15241 for(var j = 0, jlen = fields.length; j < jlen; j++){
15242 var f = fields.items[j];
15243 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15244 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15246 values[f.name] = v;
15248 var record = new recordType(values, id);
15250 records[records.length] = record;
15254 totalRecords : records.length
15257 // used when loading children.. @see loadDataFromChildren
15258 toLoadData: function(rec)
15260 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15261 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15272 * @class Roo.bootstrap.ComboBox
15273 * @extends Roo.bootstrap.TriggerField
15274 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15275 * @cfg {Boolean} append (true|false) default false
15276 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15277 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15278 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15279 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15280 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15281 * @cfg {Boolean} animate default true
15282 * @cfg {Boolean} emptyResultText only for touch device
15283 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15284 * @cfg {String} emptyTitle default ''
15285 * @cfg {Number} width fixed with? experimental
15287 * Create a new ComboBox.
15288 * @param {Object} config Configuration options
15290 Roo.bootstrap.ComboBox = function(config){
15291 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15295 * Fires when the dropdown list is expanded
15296 * @param {Roo.bootstrap.ComboBox} combo This combo box
15301 * Fires when the dropdown list is collapsed
15302 * @param {Roo.bootstrap.ComboBox} combo This combo box
15306 * @event beforeselect
15307 * Fires before a list item is selected. Return false to cancel the selection.
15308 * @param {Roo.bootstrap.ComboBox} combo This combo box
15309 * @param {Roo.data.Record} record The data record returned from the underlying store
15310 * @param {Number} index The index of the selected item in the dropdown list
15312 'beforeselect' : true,
15315 * Fires when a list item is selected
15316 * @param {Roo.bootstrap.ComboBox} combo This combo box
15317 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15318 * @param {Number} index The index of the selected item in the dropdown list
15322 * @event beforequery
15323 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15324 * The event object passed has these properties:
15325 * @param {Roo.bootstrap.ComboBox} combo This combo box
15326 * @param {String} query The query
15327 * @param {Boolean} forceAll true to force "all" query
15328 * @param {Boolean} cancel true to cancel the query
15329 * @param {Object} e The query event object
15331 'beforequery': true,
15334 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15335 * @param {Roo.bootstrap.ComboBox} combo This combo box
15340 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15341 * @param {Roo.bootstrap.ComboBox} combo This combo box
15342 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15347 * Fires when the remove value from the combobox array
15348 * @param {Roo.bootstrap.ComboBox} combo This combo box
15352 * @event afterremove
15353 * Fires when the remove value from the combobox array
15354 * @param {Roo.bootstrap.ComboBox} combo This combo box
15356 'afterremove' : true,
15358 * @event specialfilter
15359 * Fires when specialfilter
15360 * @param {Roo.bootstrap.ComboBox} combo This combo box
15362 'specialfilter' : true,
15365 * Fires when tick the element
15366 * @param {Roo.bootstrap.ComboBox} combo This combo box
15370 * @event touchviewdisplay
15371 * Fires when touch view require special display (default is using displayField)
15372 * @param {Roo.bootstrap.ComboBox} combo This combo box
15373 * @param {Object} cfg set html .
15375 'touchviewdisplay' : true
15380 this.tickItems = [];
15382 this.selectedIndex = -1;
15383 if(this.mode == 'local'){
15384 if(config.queryDelay === undefined){
15385 this.queryDelay = 10;
15387 if(config.minChars === undefined){
15393 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15396 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15397 * rendering into an Roo.Editor, defaults to false)
15400 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15401 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15404 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15407 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15408 * the dropdown list (defaults to undefined, with no header element)
15412 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15416 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15418 listWidth: undefined,
15420 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15421 * mode = 'remote' or 'text' if mode = 'local')
15423 displayField: undefined,
15426 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15427 * mode = 'remote' or 'value' if mode = 'local').
15428 * Note: use of a valueField requires the user make a selection
15429 * in order for a value to be mapped.
15431 valueField: undefined,
15433 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15438 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15439 * field's data value (defaults to the underlying DOM element's name)
15441 hiddenName: undefined,
15443 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15447 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15449 selectedClass: 'active',
15452 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15456 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15457 * anchor positions (defaults to 'tl-bl')
15459 listAlign: 'tl-bl?',
15461 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15465 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15466 * query specified by the allQuery config option (defaults to 'query')
15468 triggerAction: 'query',
15470 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15471 * (defaults to 4, does not apply if editable = false)
15475 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15476 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15480 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15481 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15485 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15486 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15490 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15491 * when editable = true (defaults to false)
15493 selectOnFocus:false,
15495 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15497 queryParam: 'query',
15499 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15500 * when mode = 'remote' (defaults to 'Loading...')
15502 loadingText: 'Loading...',
15504 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15508 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15512 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15513 * traditional select (defaults to true)
15517 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15521 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15525 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15526 * listWidth has a higher value)
15530 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15531 * allow the user to set arbitrary text into the field (defaults to false)
15533 forceSelection:false,
15535 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15536 * if typeAhead = true (defaults to 250)
15538 typeAheadDelay : 250,
15540 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15541 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15543 valueNotFoundText : undefined,
15545 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15547 blockFocus : false,
15550 * @cfg {Boolean} disableClear Disable showing of clear button.
15552 disableClear : false,
15554 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15556 alwaysQuery : false,
15559 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15564 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15566 invalidClass : "has-warning",
15569 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15571 validClass : "has-success",
15574 * @cfg {Boolean} specialFilter (true|false) special filter default false
15576 specialFilter : false,
15579 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15581 mobileTouchView : true,
15584 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15586 useNativeIOS : false,
15589 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15591 mobile_restrict_height : false,
15593 ios_options : false,
15605 btnPosition : 'right',
15606 triggerList : true,
15607 showToggleBtn : true,
15609 emptyResultText: 'Empty',
15610 triggerText : 'Select',
15614 // element that contains real text value.. (when hidden is used..)
15616 getAutoCreate : function()
15621 * Render classic select for iso
15624 if(Roo.isIOS && this.useNativeIOS){
15625 cfg = this.getAutoCreateNativeIOS();
15633 if(Roo.isTouch && this.mobileTouchView){
15634 cfg = this.getAutoCreateTouchView();
15641 if(!this.tickable){
15642 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15647 * ComboBox with tickable selections
15650 var align = this.labelAlign || this.parentLabelAlign();
15653 cls : 'form-group roo-combobox-tickable' //input-group
15656 var btn_text_select = '';
15657 var btn_text_done = '';
15658 var btn_text_cancel = '';
15660 if (this.btn_text_show) {
15661 btn_text_select = 'Select';
15662 btn_text_done = 'Done';
15663 btn_text_cancel = 'Cancel';
15668 cls : 'tickable-buttons',
15673 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15674 //html : this.triggerText
15675 html: btn_text_select
15681 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15683 html: btn_text_done
15689 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15691 html: btn_text_cancel
15697 buttons.cn.unshift({
15699 cls: 'roo-select2-search-field-input'
15705 Roo.each(buttons.cn, function(c){
15707 c.cls += ' btn-' + _this.size;
15710 if (_this.disabled) {
15717 style : 'display: contents',
15722 cls: 'form-hidden-field'
15726 cls: 'roo-select2-choices',
15730 cls: 'roo-select2-search-field',
15741 cls: 'roo-select2-container input-group roo-select2-container-multi',
15747 // cls: 'typeahead typeahead-long dropdown-menu',
15748 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15753 if(this.hasFeedback && !this.allowBlank){
15757 cls: 'glyphicon form-control-feedback'
15760 combobox.cn.push(feedback);
15767 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15768 tooltip : 'This field is required'
15770 if (Roo.bootstrap.version == 4) {
15773 style : 'display:none'
15776 if (align ==='left' && this.fieldLabel.length) {
15778 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15785 cls : 'control-label col-form-label',
15786 html : this.fieldLabel
15798 var labelCfg = cfg.cn[1];
15799 var contentCfg = cfg.cn[2];
15802 if(this.indicatorpos == 'right'){
15808 cls : 'control-label col-form-label',
15812 html : this.fieldLabel
15828 labelCfg = cfg.cn[0];
15829 contentCfg = cfg.cn[1];
15833 if(this.labelWidth > 12){
15834 labelCfg.style = "width: " + this.labelWidth + 'px';
15836 if(this.width * 1 > 0){
15837 contentCfg.style = "width: " + this.width + 'px';
15839 if(this.labelWidth < 13 && this.labelmd == 0){
15840 this.labelmd = this.labelWidth;
15843 if(this.labellg > 0){
15844 labelCfg.cls += ' col-lg-' + this.labellg;
15845 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15848 if(this.labelmd > 0){
15849 labelCfg.cls += ' col-md-' + this.labelmd;
15850 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15853 if(this.labelsm > 0){
15854 labelCfg.cls += ' col-sm-' + this.labelsm;
15855 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15858 if(this.labelxs > 0){
15859 labelCfg.cls += ' col-xs-' + this.labelxs;
15860 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15864 } else if ( this.fieldLabel.length) {
15865 // Roo.log(" label");
15870 //cls : 'input-group-addon',
15871 html : this.fieldLabel
15876 if(this.indicatorpos == 'right'){
15880 //cls : 'input-group-addon',
15881 html : this.fieldLabel
15891 // Roo.log(" no label && no align");
15898 ['xs','sm','md','lg'].map(function(size){
15899 if (settings[size]) {
15900 cfg.cls += ' col-' + size + '-' + settings[size];
15908 _initEventsCalled : false,
15911 initEvents: function()
15913 if (this._initEventsCalled) { // as we call render... prevent looping...
15916 this._initEventsCalled = true;
15919 throw "can not find store for combo";
15922 this.indicator = this.indicatorEl();
15924 this.store = Roo.factory(this.store, Roo.data);
15925 this.store.parent = this;
15927 // if we are building from html. then this element is so complex, that we can not really
15928 // use the rendered HTML.
15929 // so we have to trash and replace the previous code.
15930 if (Roo.XComponent.build_from_html) {
15931 // remove this element....
15932 var e = this.el.dom, k=0;
15933 while (e ) { e = e.previousSibling; ++k;}
15938 this.rendered = false;
15940 this.render(this.parent().getChildContainer(true), k);
15943 if(Roo.isIOS && this.useNativeIOS){
15944 this.initIOSView();
15952 if(Roo.isTouch && this.mobileTouchView){
15953 this.initTouchView();
15958 this.initTickableEvents();
15962 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15964 if(this.hiddenName){
15966 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15968 this.hiddenField.dom.value =
15969 this.hiddenValue !== undefined ? this.hiddenValue :
15970 this.value !== undefined ? this.value : '';
15972 // prevent input submission
15973 this.el.dom.removeAttribute('name');
15974 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15979 // this.el.dom.setAttribute('autocomplete', 'off');
15982 var cls = 'x-combo-list';
15984 //this.list = new Roo.Layer({
15985 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15991 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15992 _this.list.setWidth(lw);
15995 this.list.on('mouseover', this.onViewOver, this);
15996 this.list.on('mousemove', this.onViewMove, this);
15997 this.list.on('scroll', this.onViewScroll, this);
16000 this.list.swallowEvent('mousewheel');
16001 this.assetHeight = 0;
16004 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16005 this.assetHeight += this.header.getHeight();
16008 this.innerList = this.list.createChild({cls:cls+'-inner'});
16009 this.innerList.on('mouseover', this.onViewOver, this);
16010 this.innerList.on('mousemove', this.onViewMove, this);
16011 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16013 if(this.allowBlank && !this.pageSize && !this.disableClear){
16014 this.footer = this.list.createChild({cls:cls+'-ft'});
16015 this.pageTb = new Roo.Toolbar(this.footer);
16019 this.footer = this.list.createChild({cls:cls+'-ft'});
16020 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16021 {pageSize: this.pageSize});
16025 if (this.pageTb && this.allowBlank && !this.disableClear) {
16027 this.pageTb.add(new Roo.Toolbar.Fill(), {
16028 cls: 'x-btn-icon x-btn-clear',
16030 handler: function()
16033 _this.clearValue();
16034 _this.onSelect(false, -1);
16039 this.assetHeight += this.footer.getHeight();
16044 this.tpl = Roo.bootstrap.version == 4 ?
16045 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16046 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16049 this.view = new Roo.View(this.list, this.tpl, {
16050 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16052 //this.view.wrapEl.setDisplayed(false);
16053 this.view.on('click', this.onViewClick, this);
16056 this.store.on('beforeload', this.onBeforeLoad, this);
16057 this.store.on('load', this.onLoad, this);
16058 this.store.on('loadexception', this.onLoadException, this);
16060 if(this.resizable){
16061 this.resizer = new Roo.Resizable(this.list, {
16062 pinned:true, handles:'se'
16064 this.resizer.on('resize', function(r, w, h){
16065 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16066 this.listWidth = w;
16067 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16068 this.restrictHeight();
16070 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16073 if(!this.editable){
16074 this.editable = true;
16075 this.setEditable(false);
16080 if (typeof(this.events.add.listeners) != 'undefined') {
16082 this.addicon = this.wrap.createChild(
16083 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16085 this.addicon.on('click', function(e) {
16086 this.fireEvent('add', this);
16089 if (typeof(this.events.edit.listeners) != 'undefined') {
16091 this.editicon = this.wrap.createChild(
16092 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16093 if (this.addicon) {
16094 this.editicon.setStyle('margin-left', '40px');
16096 this.editicon.on('click', function(e) {
16098 // we fire even if inothing is selected..
16099 this.fireEvent('edit', this, this.lastData );
16105 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16106 "up" : function(e){
16107 this.inKeyMode = true;
16111 "down" : function(e){
16112 if(!this.isExpanded()){
16113 this.onTriggerClick();
16115 this.inKeyMode = true;
16120 "enter" : function(e){
16121 // this.onViewClick();
16125 if(this.fireEvent("specialkey", this, e)){
16126 this.onViewClick(false);
16132 "esc" : function(e){
16136 "tab" : function(e){
16139 if(this.fireEvent("specialkey", this, e)){
16140 this.onViewClick(false);
16148 doRelay : function(foo, bar, hname){
16149 if(hname == 'down' || this.scope.isExpanded()){
16150 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16159 this.queryDelay = Math.max(this.queryDelay || 10,
16160 this.mode == 'local' ? 10 : 250);
16163 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16165 if(this.typeAhead){
16166 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16168 if(this.editable !== false){
16169 this.inputEl().on("keyup", this.onKeyUp, this);
16171 if(this.forceSelection){
16172 this.inputEl().on('blur', this.doForce, this);
16176 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16177 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16181 initTickableEvents: function()
16185 if(this.hiddenName){
16187 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16189 this.hiddenField.dom.value =
16190 this.hiddenValue !== undefined ? this.hiddenValue :
16191 this.value !== undefined ? this.value : '';
16193 // prevent input submission
16194 this.el.dom.removeAttribute('name');
16195 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16200 // this.list = this.el.select('ul.dropdown-menu',true).first();
16202 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16203 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16204 if(this.triggerList){
16205 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16208 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16209 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16211 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16212 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16214 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16215 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16217 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16218 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16219 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16222 this.cancelBtn.hide();
16227 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16228 _this.list.setWidth(lw);
16231 this.list.on('mouseover', this.onViewOver, this);
16232 this.list.on('mousemove', this.onViewMove, this);
16234 this.list.on('scroll', this.onViewScroll, this);
16237 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16238 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16241 this.view = new Roo.View(this.list, this.tpl, {
16246 selectedClass: this.selectedClass
16249 //this.view.wrapEl.setDisplayed(false);
16250 this.view.on('click', this.onViewClick, this);
16254 this.store.on('beforeload', this.onBeforeLoad, this);
16255 this.store.on('load', this.onLoad, this);
16256 this.store.on('loadexception', this.onLoadException, this);
16259 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16260 "up" : function(e){
16261 this.inKeyMode = true;
16265 "down" : function(e){
16266 this.inKeyMode = true;
16270 "enter" : function(e){
16271 if(this.fireEvent("specialkey", this, e)){
16272 this.onViewClick(false);
16278 "esc" : function(e){
16279 this.onTickableFooterButtonClick(e, false, false);
16282 "tab" : function(e){
16283 this.fireEvent("specialkey", this, e);
16285 this.onTickableFooterButtonClick(e, false, false);
16292 doRelay : function(e, fn, key){
16293 if(this.scope.isExpanded()){
16294 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16303 this.queryDelay = Math.max(this.queryDelay || 10,
16304 this.mode == 'local' ? 10 : 250);
16307 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16309 if(this.typeAhead){
16310 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16313 if(this.editable !== false){
16314 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16317 this.indicator = this.indicatorEl();
16319 if(this.indicator){
16320 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16321 this.indicator.hide();
16326 onDestroy : function(){
16328 this.view.setStore(null);
16329 this.view.el.removeAllListeners();
16330 this.view.el.remove();
16331 this.view.purgeListeners();
16334 this.list.dom.innerHTML = '';
16338 this.store.un('beforeload', this.onBeforeLoad, this);
16339 this.store.un('load', this.onLoad, this);
16340 this.store.un('loadexception', this.onLoadException, this);
16342 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16346 fireKey : function(e){
16347 if(e.isNavKeyPress() && !this.list.isVisible()){
16348 this.fireEvent("specialkey", this, e);
16353 onResize: function(w, h)
16357 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16359 // if(typeof w != 'number'){
16360 // // we do not handle it!?!?
16363 // var tw = this.trigger.getWidth();
16364 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16365 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16367 // this.inputEl().setWidth( this.adjustWidth('input', x));
16369 // //this.trigger.setStyle('left', x+'px');
16371 // if(this.list && this.listWidth === undefined){
16372 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16373 // this.list.setWidth(lw);
16374 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16382 * Allow or prevent the user from directly editing the field text. If false is passed,
16383 * the user will only be able to select from the items defined in the dropdown list. This method
16384 * is the runtime equivalent of setting the 'editable' config option at config time.
16385 * @param {Boolean} value True to allow the user to directly edit the field text
16387 setEditable : function(value){
16388 if(value == this.editable){
16391 this.editable = value;
16393 this.inputEl().dom.setAttribute('readOnly', true);
16394 this.inputEl().on('mousedown', this.onTriggerClick, this);
16395 this.inputEl().addClass('x-combo-noedit');
16397 this.inputEl().dom.removeAttribute('readOnly');
16398 this.inputEl().un('mousedown', this.onTriggerClick, this);
16399 this.inputEl().removeClass('x-combo-noedit');
16405 onBeforeLoad : function(combo,opts){
16406 if(!this.hasFocus){
16410 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16412 this.restrictHeight();
16413 this.selectedIndex = -1;
16417 onLoad : function(){
16419 this.hasQuery = false;
16421 if(!this.hasFocus){
16425 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16426 this.loading.hide();
16429 if(this.store.getCount() > 0){
16432 this.restrictHeight();
16433 if(this.lastQuery == this.allQuery){
16434 if(this.editable && !this.tickable){
16435 this.inputEl().dom.select();
16439 !this.selectByValue(this.value, true) &&
16442 !this.store.lastOptions ||
16443 typeof(this.store.lastOptions.add) == 'undefined' ||
16444 this.store.lastOptions.add != true
16447 this.select(0, true);
16450 if(this.autoFocus){
16453 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16454 this.taTask.delay(this.typeAheadDelay);
16458 this.onEmptyResults();
16464 onLoadException : function()
16466 this.hasQuery = false;
16468 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16469 this.loading.hide();
16472 if(this.tickable && this.editable){
16477 // only causes errors at present
16478 //Roo.log(this.store.reader.jsonData);
16479 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16481 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16487 onTypeAhead : function(){
16488 if(this.store.getCount() > 0){
16489 var r = this.store.getAt(0);
16490 var newValue = r.data[this.displayField];
16491 var len = newValue.length;
16492 var selStart = this.getRawValue().length;
16494 if(selStart != len){
16495 this.setRawValue(newValue);
16496 this.selectText(selStart, newValue.length);
16502 onSelect : function(record, index){
16504 if(this.fireEvent('beforeselect', this, record, index) !== false){
16506 this.setFromData(index > -1 ? record.data : false);
16509 this.fireEvent('select', this, record, index);
16514 * Returns the currently selected field value or empty string if no value is set.
16515 * @return {String} value The selected value
16517 getValue : function()
16519 if(Roo.isIOS && this.useNativeIOS){
16520 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16524 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16527 if(this.valueField){
16528 return typeof this.value != 'undefined' ? this.value : '';
16530 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16534 getRawValue : function()
16536 if(Roo.isIOS && this.useNativeIOS){
16537 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16540 var v = this.inputEl().getValue();
16546 * Clears any text/value currently set in the field
16548 clearValue : function(){
16550 if(this.hiddenField){
16551 this.hiddenField.dom.value = '';
16554 this.setRawValue('');
16555 this.lastSelectionText = '';
16556 this.lastData = false;
16558 var close = this.closeTriggerEl();
16569 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16570 * will be displayed in the field. If the value does not match the data value of an existing item,
16571 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16572 * Otherwise the field will be blank (although the value will still be set).
16573 * @param {String} value The value to match
16575 setValue : function(v)
16577 if(Roo.isIOS && this.useNativeIOS){
16578 this.setIOSValue(v);
16588 if(this.valueField){
16589 var r = this.findRecord(this.valueField, v);
16591 text = r.data[this.displayField];
16592 }else if(this.valueNotFoundText !== undefined){
16593 text = this.valueNotFoundText;
16596 this.lastSelectionText = text;
16597 if(this.hiddenField){
16598 this.hiddenField.dom.value = v;
16600 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16603 var close = this.closeTriggerEl();
16606 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16612 * @property {Object} the last set data for the element
16617 * Sets the value of the field based on a object which is related to the record format for the store.
16618 * @param {Object} value the value to set as. or false on reset?
16620 setFromData : function(o){
16627 var dv = ''; // display value
16628 var vv = ''; // value value..
16630 if (this.displayField) {
16631 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16633 // this is an error condition!!!
16634 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16637 if(this.valueField){
16638 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16641 var close = this.closeTriggerEl();
16644 if(dv.length || vv * 1 > 0){
16646 this.blockFocus=true;
16652 if(this.hiddenField){
16653 this.hiddenField.dom.value = vv;
16655 this.lastSelectionText = dv;
16656 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16660 // no hidden field.. - we store the value in 'value', but still display
16661 // display field!!!!
16662 this.lastSelectionText = dv;
16663 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16670 reset : function(){
16671 // overridden so that last data is reset..
16678 this.setValue(this.originalValue);
16679 //this.clearInvalid();
16680 this.lastData = false;
16682 this.view.clearSelections();
16688 findRecord : function(prop, value){
16690 if(this.store.getCount() > 0){
16691 this.store.each(function(r){
16692 if(r.data[prop] == value){
16702 getName: function()
16704 // returns hidden if it's set..
16705 if (!this.rendered) {return ''};
16706 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16710 onViewMove : function(e, t){
16711 this.inKeyMode = false;
16715 onViewOver : function(e, t){
16716 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16719 var item = this.view.findItemFromChild(t);
16722 var index = this.view.indexOf(item);
16723 this.select(index, false);
16728 onViewClick : function(view, doFocus, el, e)
16730 var index = this.view.getSelectedIndexes()[0];
16732 var r = this.store.getAt(index);
16736 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16743 Roo.each(this.tickItems, function(v,k){
16745 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16747 _this.tickItems.splice(k, 1);
16749 if(typeof(e) == 'undefined' && view == false){
16750 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16762 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16763 this.tickItems.push(r.data);
16766 if(typeof(e) == 'undefined' && view == false){
16767 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16774 this.onSelect(r, index);
16776 if(doFocus !== false && !this.blockFocus){
16777 this.inputEl().focus();
16782 restrictHeight : function(){
16783 //this.innerList.dom.style.height = '';
16784 //var inner = this.innerList.dom;
16785 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16786 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16787 //this.list.beginUpdate();
16788 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16789 this.list.alignTo(this.inputEl(), this.listAlign);
16790 this.list.alignTo(this.inputEl(), this.listAlign);
16791 //this.list.endUpdate();
16795 onEmptyResults : function(){
16797 if(this.tickable && this.editable){
16798 this.hasFocus = false;
16799 this.restrictHeight();
16807 * Returns true if the dropdown list is expanded, else false.
16809 isExpanded : function(){
16810 return this.list.isVisible();
16814 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16815 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16816 * @param {String} value The data value of the item to select
16817 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16818 * selected item if it is not currently in view (defaults to true)
16819 * @return {Boolean} True if the value matched an item in the list, else false
16821 selectByValue : function(v, scrollIntoView){
16822 if(v !== undefined && v !== null){
16823 var r = this.findRecord(this.valueField || this.displayField, v);
16825 this.select(this.store.indexOf(r), scrollIntoView);
16833 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16834 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16835 * @param {Number} index The zero-based index of the list item to select
16836 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16837 * selected item if it is not currently in view (defaults to true)
16839 select : function(index, scrollIntoView){
16840 this.selectedIndex = index;
16841 this.view.select(index);
16842 if(scrollIntoView !== false){
16843 var el = this.view.getNode(index);
16845 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16848 this.list.scrollChildIntoView(el, false);
16854 selectNext : function(){
16855 var ct = this.store.getCount();
16857 if(this.selectedIndex == -1){
16859 }else if(this.selectedIndex < ct-1){
16860 this.select(this.selectedIndex+1);
16866 selectPrev : function(){
16867 var ct = this.store.getCount();
16869 if(this.selectedIndex == -1){
16871 }else if(this.selectedIndex != 0){
16872 this.select(this.selectedIndex-1);
16878 onKeyUp : function(e){
16879 if(this.editable !== false && !e.isSpecialKey()){
16880 this.lastKey = e.getKey();
16881 this.dqTask.delay(this.queryDelay);
16886 validateBlur : function(){
16887 return !this.list || !this.list.isVisible();
16891 initQuery : function(){
16893 var v = this.getRawValue();
16895 if(this.tickable && this.editable){
16896 v = this.tickableInputEl().getValue();
16903 doForce : function(){
16904 if(this.inputEl().dom.value.length > 0){
16905 this.inputEl().dom.value =
16906 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16912 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16913 * query allowing the query action to be canceled if needed.
16914 * @param {String} query The SQL query to execute
16915 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16916 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16917 * saved in the current store (defaults to false)
16919 doQuery : function(q, forceAll){
16921 if(q === undefined || q === null){
16926 forceAll: forceAll,
16930 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16935 forceAll = qe.forceAll;
16936 if(forceAll === true || (q.length >= this.minChars)){
16938 this.hasQuery = true;
16940 if(this.lastQuery != q || this.alwaysQuery){
16941 this.lastQuery = q;
16942 if(this.mode == 'local'){
16943 this.selectedIndex = -1;
16945 this.store.clearFilter();
16948 if(this.specialFilter){
16949 this.fireEvent('specialfilter', this);
16954 this.store.filter(this.displayField, q);
16957 this.store.fireEvent("datachanged", this.store);
16964 this.store.baseParams[this.queryParam] = q;
16966 var options = {params : this.getParams(q)};
16969 options.add = true;
16970 options.params.start = this.page * this.pageSize;
16973 this.store.load(options);
16976 * this code will make the page width larger, at the beginning, the list not align correctly,
16977 * we should expand the list on onLoad
16978 * so command out it
16983 this.selectedIndex = -1;
16988 this.loadNext = false;
16992 getParams : function(q){
16994 //p[this.queryParam] = q;
16998 p.limit = this.pageSize;
17004 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17006 collapse : function(){
17007 if(!this.isExpanded()){
17013 this.hasFocus = false;
17017 this.cancelBtn.hide();
17018 this.trigger.show();
17021 this.tickableInputEl().dom.value = '';
17022 this.tickableInputEl().blur();
17027 Roo.get(document).un('mousedown', this.collapseIf, this);
17028 Roo.get(document).un('mousewheel', this.collapseIf, this);
17029 if (!this.editable) {
17030 Roo.get(document).un('keydown', this.listKeyPress, this);
17032 this.fireEvent('collapse', this);
17038 collapseIf : function(e){
17039 var in_combo = e.within(this.el);
17040 var in_list = e.within(this.list);
17041 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17043 if (in_combo || in_list || is_list) {
17044 //e.stopPropagation();
17049 this.onTickableFooterButtonClick(e, false, false);
17057 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17059 expand : function(){
17061 if(this.isExpanded() || !this.hasFocus){
17065 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17066 this.list.setWidth(lw);
17072 this.restrictHeight();
17076 this.tickItems = Roo.apply([], this.item);
17079 this.cancelBtn.show();
17080 this.trigger.hide();
17083 this.tickableInputEl().focus();
17088 Roo.get(document).on('mousedown', this.collapseIf, this);
17089 Roo.get(document).on('mousewheel', this.collapseIf, this);
17090 if (!this.editable) {
17091 Roo.get(document).on('keydown', this.listKeyPress, this);
17094 this.fireEvent('expand', this);
17098 // Implements the default empty TriggerField.onTriggerClick function
17099 onTriggerClick : function(e)
17101 Roo.log('trigger click');
17103 if(this.disabled || !this.triggerList){
17108 this.loadNext = false;
17110 if(this.isExpanded()){
17112 if (!this.blockFocus) {
17113 this.inputEl().focus();
17117 this.hasFocus = true;
17118 if(this.triggerAction == 'all') {
17119 this.doQuery(this.allQuery, true);
17121 this.doQuery(this.getRawValue());
17123 if (!this.blockFocus) {
17124 this.inputEl().focus();
17129 onTickableTriggerClick : function(e)
17136 this.loadNext = false;
17137 this.hasFocus = true;
17139 if(this.triggerAction == 'all') {
17140 this.doQuery(this.allQuery, true);
17142 this.doQuery(this.getRawValue());
17146 onSearchFieldClick : function(e)
17148 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17149 this.onTickableFooterButtonClick(e, false, false);
17153 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17158 this.loadNext = false;
17159 this.hasFocus = true;
17161 if(this.triggerAction == 'all') {
17162 this.doQuery(this.allQuery, true);
17164 this.doQuery(this.getRawValue());
17168 listKeyPress : function(e)
17170 //Roo.log('listkeypress');
17171 // scroll to first matching element based on key pres..
17172 if (e.isSpecialKey()) {
17175 var k = String.fromCharCode(e.getKey()).toUpperCase();
17178 var csel = this.view.getSelectedNodes();
17179 var cselitem = false;
17181 var ix = this.view.indexOf(csel[0]);
17182 cselitem = this.store.getAt(ix);
17183 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17189 this.store.each(function(v) {
17191 // start at existing selection.
17192 if (cselitem.id == v.id) {
17198 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17199 match = this.store.indexOf(v);
17205 if (match === false) {
17206 return true; // no more action?
17209 this.view.select(match);
17210 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17211 sn.scrollIntoView(sn.dom.parentNode, false);
17214 onViewScroll : function(e, t){
17216 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){
17220 this.hasQuery = true;
17222 this.loading = this.list.select('.loading', true).first();
17224 if(this.loading === null){
17225 this.list.createChild({
17227 cls: 'loading roo-select2-more-results roo-select2-active',
17228 html: 'Loading more results...'
17231 this.loading = this.list.select('.loading', true).first();
17233 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17235 this.loading.hide();
17238 this.loading.show();
17243 this.loadNext = true;
17245 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17250 addItem : function(o)
17252 var dv = ''; // display value
17254 if (this.displayField) {
17255 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17257 // this is an error condition!!!
17258 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17265 var choice = this.choices.createChild({
17267 cls: 'roo-select2-search-choice',
17276 cls: 'roo-select2-search-choice-close fa fa-times',
17281 }, this.searchField);
17283 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17285 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17293 this.inputEl().dom.value = '';
17298 onRemoveItem : function(e, _self, o)
17300 e.preventDefault();
17302 this.lastItem = Roo.apply([], this.item);
17304 var index = this.item.indexOf(o.data) * 1;
17307 Roo.log('not this item?!');
17311 this.item.splice(index, 1);
17316 this.fireEvent('remove', this, e);
17322 syncValue : function()
17324 if(!this.item.length){
17331 Roo.each(this.item, function(i){
17332 if(_this.valueField){
17333 value.push(i[_this.valueField]);
17340 this.value = value.join(',');
17342 if(this.hiddenField){
17343 this.hiddenField.dom.value = this.value;
17346 this.store.fireEvent("datachanged", this.store);
17351 clearItem : function()
17353 if(!this.multiple){
17359 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17367 if(this.tickable && !Roo.isTouch){
17368 this.view.refresh();
17372 inputEl: function ()
17374 if(Roo.isIOS && this.useNativeIOS){
17375 return this.el.select('select.roo-ios-select', true).first();
17378 if(Roo.isTouch && this.mobileTouchView){
17379 return this.el.select('input.form-control',true).first();
17383 return this.searchField;
17386 return this.el.select('input.form-control',true).first();
17389 onTickableFooterButtonClick : function(e, btn, el)
17391 e.preventDefault();
17393 this.lastItem = Roo.apply([], this.item);
17395 if(btn && btn.name == 'cancel'){
17396 this.tickItems = Roo.apply([], this.item);
17405 Roo.each(this.tickItems, function(o){
17413 validate : function()
17415 if(this.getVisibilityEl().hasClass('hidden')){
17419 var v = this.getRawValue();
17422 v = this.getValue();
17425 if(this.disabled || this.allowBlank || v.length){
17430 this.markInvalid();
17434 tickableInputEl : function()
17436 if(!this.tickable || !this.editable){
17437 return this.inputEl();
17440 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17444 getAutoCreateTouchView : function()
17449 cls: 'form-group' //input-group
17455 type : this.inputType,
17456 cls : 'form-control x-combo-noedit',
17457 autocomplete: 'new-password',
17458 placeholder : this.placeholder || '',
17463 input.name = this.name;
17467 input.cls += ' input-' + this.size;
17470 if (this.disabled) {
17471 input.disabled = true;
17475 cls : 'roo-combobox-wrap',
17482 inputblock.cls += ' input-group';
17484 inputblock.cn.unshift({
17486 cls : 'input-group-addon input-group-prepend input-group-text',
17491 if(this.removable && !this.multiple){
17492 inputblock.cls += ' roo-removable';
17494 inputblock.cn.push({
17497 cls : 'roo-combo-removable-btn close'
17501 if(this.hasFeedback && !this.allowBlank){
17503 inputblock.cls += ' has-feedback';
17505 inputblock.cn.push({
17507 cls: 'glyphicon form-control-feedback'
17514 inputblock.cls += (this.before) ? '' : ' input-group';
17516 inputblock.cn.push({
17518 cls : 'input-group-addon input-group-append input-group-text',
17524 var ibwrap = inputblock;
17529 cls: 'roo-select2-choices',
17533 cls: 'roo-select2-search-field',
17546 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17551 cls: 'form-hidden-field'
17557 if(!this.multiple && this.showToggleBtn){
17563 if (this.caret != false) {
17566 cls: 'fa fa-' + this.caret
17573 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17575 Roo.bootstrap.version == 3 ? caret : '',
17578 cls: 'combobox-clear',
17592 combobox.cls += ' roo-select2-container-multi';
17595 var required = this.allowBlank ? {
17597 style: 'display: none'
17600 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17601 tooltip : 'This field is required'
17604 var align = this.labelAlign || this.parentLabelAlign();
17606 if (align ==='left' && this.fieldLabel.length) {
17612 cls : 'control-label col-form-label',
17613 html : this.fieldLabel
17617 cls : 'roo-combobox-wrap ',
17624 var labelCfg = cfg.cn[1];
17625 var contentCfg = cfg.cn[2];
17628 if(this.indicatorpos == 'right'){
17633 cls : 'control-label col-form-label',
17637 html : this.fieldLabel
17643 cls : "roo-combobox-wrap ",
17651 labelCfg = cfg.cn[0];
17652 contentCfg = cfg.cn[1];
17657 if(this.labelWidth > 12){
17658 labelCfg.style = "width: " + this.labelWidth + 'px';
17661 if(this.labelWidth < 13 && this.labelmd == 0){
17662 this.labelmd = this.labelWidth;
17665 if(this.labellg > 0){
17666 labelCfg.cls += ' col-lg-' + this.labellg;
17667 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17670 if(this.labelmd > 0){
17671 labelCfg.cls += ' col-md-' + this.labelmd;
17672 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17675 if(this.labelsm > 0){
17676 labelCfg.cls += ' col-sm-' + this.labelsm;
17677 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17680 if(this.labelxs > 0){
17681 labelCfg.cls += ' col-xs-' + this.labelxs;
17682 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17686 } else if ( this.fieldLabel.length) {
17691 cls : 'control-label',
17692 html : this.fieldLabel
17703 if(this.indicatorpos == 'right'){
17707 cls : 'control-label',
17708 html : this.fieldLabel,
17726 var settings = this;
17728 ['xs','sm','md','lg'].map(function(size){
17729 if (settings[size]) {
17730 cfg.cls += ' col-' + size + '-' + settings[size];
17737 initTouchView : function()
17739 this.renderTouchView();
17741 this.touchViewEl.on('scroll', function(){
17742 this.el.dom.scrollTop = 0;
17745 this.originalValue = this.getValue();
17747 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17749 this.inputEl().on("click", this.showTouchView, this);
17750 if (this.triggerEl) {
17751 this.triggerEl.on("click", this.showTouchView, this);
17755 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17756 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17758 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17760 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17761 this.store.on('load', this.onTouchViewLoad, this);
17762 this.store.on('loadexception', this.onTouchViewLoadException, this);
17764 if(this.hiddenName){
17766 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17768 this.hiddenField.dom.value =
17769 this.hiddenValue !== undefined ? this.hiddenValue :
17770 this.value !== undefined ? this.value : '';
17772 this.el.dom.removeAttribute('name');
17773 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17777 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17778 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17781 if(this.removable && !this.multiple){
17782 var close = this.closeTriggerEl();
17784 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17785 close.on('click', this.removeBtnClick, this, close);
17789 * fix the bug in Safari iOS8
17791 this.inputEl().on("focus", function(e){
17792 document.activeElement.blur();
17795 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17802 renderTouchView : function()
17804 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17805 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17807 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17808 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17810 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17811 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17812 this.touchViewBodyEl.setStyle('overflow', 'auto');
17814 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17815 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17817 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17818 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17822 showTouchView : function()
17828 this.touchViewHeaderEl.hide();
17830 if(this.modalTitle.length){
17831 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17832 this.touchViewHeaderEl.show();
17835 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17836 this.touchViewEl.show();
17838 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17840 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17841 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17843 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17845 if(this.modalTitle.length){
17846 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17849 this.touchViewBodyEl.setHeight(bodyHeight);
17853 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17855 this.touchViewEl.addClass(['in','show']);
17858 if(this._touchViewMask){
17859 Roo.get(document.body).addClass("x-body-masked");
17860 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17861 this._touchViewMask.setStyle('z-index', 10000);
17862 this._touchViewMask.addClass('show');
17865 this.doTouchViewQuery();
17869 hideTouchView : function()
17871 this.touchViewEl.removeClass(['in','show']);
17875 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17877 this.touchViewEl.setStyle('display', 'none');
17880 if(this._touchViewMask){
17881 this._touchViewMask.removeClass('show');
17882 Roo.get(document.body).removeClass("x-body-masked");
17886 setTouchViewValue : function()
17893 Roo.each(this.tickItems, function(o){
17898 this.hideTouchView();
17901 doTouchViewQuery : function()
17910 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17914 if(!this.alwaysQuery || this.mode == 'local'){
17915 this.onTouchViewLoad();
17922 onTouchViewBeforeLoad : function(combo,opts)
17928 onTouchViewLoad : function()
17930 if(this.store.getCount() < 1){
17931 this.onTouchViewEmptyResults();
17935 this.clearTouchView();
17937 var rawValue = this.getRawValue();
17939 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17941 this.tickItems = [];
17943 this.store.data.each(function(d, rowIndex){
17944 var row = this.touchViewListGroup.createChild(template);
17946 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17947 row.addClass(d.data.cls);
17950 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17953 html : d.data[this.displayField]
17956 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17957 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17960 row.removeClass('selected');
17961 if(!this.multiple && this.valueField &&
17962 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17965 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17966 row.addClass('selected');
17969 if(this.multiple && this.valueField &&
17970 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17974 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17975 this.tickItems.push(d.data);
17978 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17982 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17984 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17986 if(this.modalTitle.length){
17987 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17990 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17992 if(this.mobile_restrict_height && listHeight < bodyHeight){
17993 this.touchViewBodyEl.setHeight(listHeight);
17998 if(firstChecked && listHeight > bodyHeight){
17999 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18004 onTouchViewLoadException : function()
18006 this.hideTouchView();
18009 onTouchViewEmptyResults : function()
18011 this.clearTouchView();
18013 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18015 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18019 clearTouchView : function()
18021 this.touchViewListGroup.dom.innerHTML = '';
18024 onTouchViewClick : function(e, el, o)
18026 e.preventDefault();
18029 var rowIndex = o.rowIndex;
18031 var r = this.store.getAt(rowIndex);
18033 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18035 if(!this.multiple){
18036 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18037 c.dom.removeAttribute('checked');
18040 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18042 this.setFromData(r.data);
18044 var close = this.closeTriggerEl();
18050 this.hideTouchView();
18052 this.fireEvent('select', this, r, rowIndex);
18057 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18058 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18059 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18063 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18064 this.addItem(r.data);
18065 this.tickItems.push(r.data);
18069 getAutoCreateNativeIOS : function()
18072 cls: 'form-group' //input-group,
18077 cls : 'roo-ios-select'
18081 combobox.name = this.name;
18084 if (this.disabled) {
18085 combobox.disabled = true;
18088 var settings = this;
18090 ['xs','sm','md','lg'].map(function(size){
18091 if (settings[size]) {
18092 cfg.cls += ' col-' + size + '-' + settings[size];
18102 initIOSView : function()
18104 this.store.on('load', this.onIOSViewLoad, this);
18109 onIOSViewLoad : function()
18111 if(this.store.getCount() < 1){
18115 this.clearIOSView();
18117 if(this.allowBlank) {
18119 var default_text = '-- SELECT --';
18121 if(this.placeholder.length){
18122 default_text = this.placeholder;
18125 if(this.emptyTitle.length){
18126 default_text += ' - ' + this.emptyTitle + ' -';
18129 var opt = this.inputEl().createChild({
18132 html : default_text
18136 o[this.valueField] = 0;
18137 o[this.displayField] = default_text;
18139 this.ios_options.push({
18146 this.store.data.each(function(d, rowIndex){
18150 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18151 html = d.data[this.displayField];
18156 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18157 value = d.data[this.valueField];
18166 if(this.value == d.data[this.valueField]){
18167 option['selected'] = true;
18170 var opt = this.inputEl().createChild(option);
18172 this.ios_options.push({
18179 this.inputEl().on('change', function(){
18180 this.fireEvent('select', this);
18185 clearIOSView: function()
18187 this.inputEl().dom.innerHTML = '';
18189 this.ios_options = [];
18192 setIOSValue: function(v)
18196 if(!this.ios_options){
18200 Roo.each(this.ios_options, function(opts){
18202 opts.el.dom.removeAttribute('selected');
18204 if(opts.data[this.valueField] != v){
18208 opts.el.dom.setAttribute('selected', true);
18214 * @cfg {Boolean} grow
18218 * @cfg {Number} growMin
18222 * @cfg {Number} growMax
18231 Roo.apply(Roo.bootstrap.ComboBox, {
18235 cls: 'modal-header',
18257 cls: 'list-group-item',
18261 cls: 'roo-combobox-list-group-item-value'
18265 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18279 listItemCheckbox : {
18281 cls: 'list-group-item',
18285 cls: 'roo-combobox-list-group-item-value'
18289 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18305 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18310 cls: 'modal-footer',
18318 cls: 'col-xs-6 text-left',
18321 cls: 'btn btn-danger roo-touch-view-cancel',
18327 cls: 'col-xs-6 text-right',
18330 cls: 'btn btn-success roo-touch-view-ok',
18341 Roo.apply(Roo.bootstrap.ComboBox, {
18343 touchViewTemplate : {
18345 cls: 'modal fade roo-combobox-touch-view',
18349 cls: 'modal-dialog',
18350 style : 'position:fixed', // we have to fix position....
18354 cls: 'modal-content',
18356 Roo.bootstrap.ComboBox.header,
18357 Roo.bootstrap.ComboBox.body,
18358 Roo.bootstrap.ComboBox.footer
18367 * Ext JS Library 1.1.1
18368 * Copyright(c) 2006-2007, Ext JS, LLC.
18370 * Originally Released Under LGPL - original licence link has changed is not relivant.
18373 * <script type="text/javascript">
18378 * @extends Roo.util.Observable
18379 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18380 * This class also supports single and multi selection modes. <br>
18381 * Create a data model bound view:
18383 var store = new Roo.data.Store(...);
18385 var view = new Roo.View({
18387 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18389 singleSelect: true,
18390 selectedClass: "ydataview-selected",
18394 // listen for node click?
18395 view.on("click", function(vw, index, node, e){
18396 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18400 dataModel.load("foobar.xml");
18402 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18404 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18405 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18407 * Note: old style constructor is still suported (container, template, config)
18410 * Create a new View
18411 * @param {Object} config The config object
18414 Roo.View = function(config, depreciated_tpl, depreciated_config){
18416 this.parent = false;
18418 if (typeof(depreciated_tpl) == 'undefined') {
18419 // new way.. - universal constructor.
18420 Roo.apply(this, config);
18421 this.el = Roo.get(this.el);
18424 this.el = Roo.get(config);
18425 this.tpl = depreciated_tpl;
18426 Roo.apply(this, depreciated_config);
18428 this.wrapEl = this.el.wrap().wrap();
18429 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18432 if(typeof(this.tpl) == "string"){
18433 this.tpl = new Roo.Template(this.tpl);
18435 // support xtype ctors..
18436 this.tpl = new Roo.factory(this.tpl, Roo);
18440 this.tpl.compile();
18445 * @event beforeclick
18446 * Fires before a click is processed. Returns false to cancel the default action.
18447 * @param {Roo.View} this
18448 * @param {Number} index The index of the target node
18449 * @param {HTMLElement} node The target node
18450 * @param {Roo.EventObject} e The raw event object
18452 "beforeclick" : true,
18455 * Fires when a template node is clicked.
18456 * @param {Roo.View} this
18457 * @param {Number} index The index of the target node
18458 * @param {HTMLElement} node The target node
18459 * @param {Roo.EventObject} e The raw event object
18464 * Fires when a template node is double clicked.
18465 * @param {Roo.View} this
18466 * @param {Number} index The index of the target node
18467 * @param {HTMLElement} node The target node
18468 * @param {Roo.EventObject} e The raw event object
18472 * @event contextmenu
18473 * Fires when a template node is right clicked.
18474 * @param {Roo.View} this
18475 * @param {Number} index The index of the target node
18476 * @param {HTMLElement} node The target node
18477 * @param {Roo.EventObject} e The raw event object
18479 "contextmenu" : true,
18481 * @event selectionchange
18482 * Fires when the selected nodes change.
18483 * @param {Roo.View} this
18484 * @param {Array} selections Array of the selected nodes
18486 "selectionchange" : true,
18489 * @event beforeselect
18490 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18491 * @param {Roo.View} this
18492 * @param {HTMLElement} node The node to be selected
18493 * @param {Array} selections Array of currently selected nodes
18495 "beforeselect" : true,
18497 * @event preparedata
18498 * Fires on every row to render, to allow you to change the data.
18499 * @param {Roo.View} this
18500 * @param {Object} data to be rendered (change this)
18502 "preparedata" : true
18510 "click": this.onClick,
18511 "dblclick": this.onDblClick,
18512 "contextmenu": this.onContextMenu,
18516 this.selections = [];
18518 this.cmp = new Roo.CompositeElementLite([]);
18520 this.store = Roo.factory(this.store, Roo.data);
18521 this.setStore(this.store, true);
18524 if ( this.footer && this.footer.xtype) {
18526 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18528 this.footer.dataSource = this.store;
18529 this.footer.container = fctr;
18530 this.footer = Roo.factory(this.footer, Roo);
18531 fctr.insertFirst(this.el);
18533 // this is a bit insane - as the paging toolbar seems to detach the el..
18534 // dom.parentNode.parentNode.parentNode
18535 // they get detached?
18539 Roo.View.superclass.constructor.call(this);
18544 Roo.extend(Roo.View, Roo.util.Observable, {
18547 * @cfg {Roo.data.Store} store Data store to load data from.
18552 * @cfg {String|Roo.Element} el The container element.
18557 * @cfg {String|Roo.Template} tpl The template used by this View
18561 * @cfg {String} dataName the named area of the template to use as the data area
18562 * Works with domtemplates roo-name="name"
18566 * @cfg {String} selectedClass The css class to add to selected nodes
18568 selectedClass : "x-view-selected",
18570 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18575 * @cfg {String} text to display on mask (default Loading)
18579 * @cfg {Boolean} multiSelect Allow multiple selection
18581 multiSelect : false,
18583 * @cfg {Boolean} singleSelect Allow single selection
18585 singleSelect: false,
18588 * @cfg {Boolean} toggleSelect - selecting
18590 toggleSelect : false,
18593 * @cfg {Boolean} tickable - selecting
18598 * Returns the element this view is bound to.
18599 * @return {Roo.Element}
18601 getEl : function(){
18602 return this.wrapEl;
18608 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18610 refresh : function(){
18611 //Roo.log('refresh');
18614 // if we are using something like 'domtemplate', then
18615 // the what gets used is:
18616 // t.applySubtemplate(NAME, data, wrapping data..)
18617 // the outer template then get' applied with
18618 // the store 'extra data'
18619 // and the body get's added to the
18620 // roo-name="data" node?
18621 // <span class='roo-tpl-{name}'></span> ?????
18625 this.clearSelections();
18626 this.el.update("");
18628 var records = this.store.getRange();
18629 if(records.length < 1) {
18631 // is this valid?? = should it render a template??
18633 this.el.update(this.emptyText);
18637 if (this.dataName) {
18638 this.el.update(t.apply(this.store.meta)); //????
18639 el = this.el.child('.roo-tpl-' + this.dataName);
18642 for(var i = 0, len = records.length; i < len; i++){
18643 var data = this.prepareData(records[i].data, i, records[i]);
18644 this.fireEvent("preparedata", this, data, i, records[i]);
18646 var d = Roo.apply({}, data);
18649 Roo.apply(d, {'roo-id' : Roo.id()});
18653 Roo.each(this.parent.item, function(item){
18654 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18657 Roo.apply(d, {'roo-data-checked' : 'checked'});
18661 html[html.length] = Roo.util.Format.trim(
18663 t.applySubtemplate(this.dataName, d, this.store.meta) :
18670 el.update(html.join(""));
18671 this.nodes = el.dom.childNodes;
18672 this.updateIndexes(0);
18677 * Function to override to reformat the data that is sent to
18678 * the template for each node.
18679 * DEPRICATED - use the preparedata event handler.
18680 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18681 * a JSON object for an UpdateManager bound view).
18683 prepareData : function(data, index, record)
18685 this.fireEvent("preparedata", this, data, index, record);
18689 onUpdate : function(ds, record){
18690 // Roo.log('on update');
18691 this.clearSelections();
18692 var index = this.store.indexOf(record);
18693 var n = this.nodes[index];
18694 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18695 n.parentNode.removeChild(n);
18696 this.updateIndexes(index, index);
18702 onAdd : function(ds, records, index)
18704 //Roo.log(['on Add', ds, records, index] );
18705 this.clearSelections();
18706 if(this.nodes.length == 0){
18710 var n = this.nodes[index];
18711 for(var i = 0, len = records.length; i < len; i++){
18712 var d = this.prepareData(records[i].data, i, records[i]);
18714 this.tpl.insertBefore(n, d);
18717 this.tpl.append(this.el, d);
18720 this.updateIndexes(index);
18723 onRemove : function(ds, record, index){
18724 // Roo.log('onRemove');
18725 this.clearSelections();
18726 var el = this.dataName ?
18727 this.el.child('.roo-tpl-' + this.dataName) :
18730 el.dom.removeChild(this.nodes[index]);
18731 this.updateIndexes(index);
18735 * Refresh an individual node.
18736 * @param {Number} index
18738 refreshNode : function(index){
18739 this.onUpdate(this.store, this.store.getAt(index));
18742 updateIndexes : function(startIndex, endIndex){
18743 var ns = this.nodes;
18744 startIndex = startIndex || 0;
18745 endIndex = endIndex || ns.length - 1;
18746 for(var i = startIndex; i <= endIndex; i++){
18747 ns[i].nodeIndex = i;
18752 * Changes the data store this view uses and refresh the view.
18753 * @param {Store} store
18755 setStore : function(store, initial){
18756 if(!initial && this.store){
18757 this.store.un("datachanged", this.refresh);
18758 this.store.un("add", this.onAdd);
18759 this.store.un("remove", this.onRemove);
18760 this.store.un("update", this.onUpdate);
18761 this.store.un("clear", this.refresh);
18762 this.store.un("beforeload", this.onBeforeLoad);
18763 this.store.un("load", this.onLoad);
18764 this.store.un("loadexception", this.onLoad);
18768 store.on("datachanged", this.refresh, this);
18769 store.on("add", this.onAdd, this);
18770 store.on("remove", this.onRemove, this);
18771 store.on("update", this.onUpdate, this);
18772 store.on("clear", this.refresh, this);
18773 store.on("beforeload", this.onBeforeLoad, this);
18774 store.on("load", this.onLoad, this);
18775 store.on("loadexception", this.onLoad, this);
18783 * onbeforeLoad - masks the loading area.
18786 onBeforeLoad : function(store,opts)
18788 //Roo.log('onBeforeLoad');
18790 this.el.update("");
18792 this.el.mask(this.mask ? this.mask : "Loading" );
18794 onLoad : function ()
18801 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18802 * @param {HTMLElement} node
18803 * @return {HTMLElement} The template node
18805 findItemFromChild : function(node){
18806 var el = this.dataName ?
18807 this.el.child('.roo-tpl-' + this.dataName,true) :
18810 if(!node || node.parentNode == el){
18813 var p = node.parentNode;
18814 while(p && p != el){
18815 if(p.parentNode == el){
18824 onClick : function(e){
18825 var item = this.findItemFromChild(e.getTarget());
18827 var index = this.indexOf(item);
18828 if(this.onItemClick(item, index, e) !== false){
18829 this.fireEvent("click", this, index, item, e);
18832 this.clearSelections();
18837 onContextMenu : function(e){
18838 var item = this.findItemFromChild(e.getTarget());
18840 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18845 onDblClick : function(e){
18846 var item = this.findItemFromChild(e.getTarget());
18848 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18852 onItemClick : function(item, index, e)
18854 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18857 if (this.toggleSelect) {
18858 var m = this.isSelected(item) ? 'unselect' : 'select';
18861 _t[m](item, true, false);
18864 if(this.multiSelect || this.singleSelect){
18865 if(this.multiSelect && e.shiftKey && this.lastSelection){
18866 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18868 this.select(item, this.multiSelect && e.ctrlKey);
18869 this.lastSelection = item;
18872 if(!this.tickable){
18873 e.preventDefault();
18881 * Get the number of selected nodes.
18884 getSelectionCount : function(){
18885 return this.selections.length;
18889 * Get the currently selected nodes.
18890 * @return {Array} An array of HTMLElements
18892 getSelectedNodes : function(){
18893 return this.selections;
18897 * Get the indexes of the selected nodes.
18900 getSelectedIndexes : function(){
18901 var indexes = [], s = this.selections;
18902 for(var i = 0, len = s.length; i < len; i++){
18903 indexes.push(s[i].nodeIndex);
18909 * Clear all selections
18910 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18912 clearSelections : function(suppressEvent){
18913 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18914 this.cmp.elements = this.selections;
18915 this.cmp.removeClass(this.selectedClass);
18916 this.selections = [];
18917 if(!suppressEvent){
18918 this.fireEvent("selectionchange", this, this.selections);
18924 * Returns true if the passed node is selected
18925 * @param {HTMLElement/Number} node The node or node index
18926 * @return {Boolean}
18928 isSelected : function(node){
18929 var s = this.selections;
18933 node = this.getNode(node);
18934 return s.indexOf(node) !== -1;
18939 * @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
18940 * @param {Boolean} keepExisting (optional) true to keep existing selections
18941 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18943 select : function(nodeInfo, keepExisting, suppressEvent){
18944 if(nodeInfo instanceof Array){
18946 this.clearSelections(true);
18948 for(var i = 0, len = nodeInfo.length; i < len; i++){
18949 this.select(nodeInfo[i], true, true);
18953 var node = this.getNode(nodeInfo);
18954 if(!node || this.isSelected(node)){
18955 return; // already selected.
18958 this.clearSelections(true);
18961 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18962 Roo.fly(node).addClass(this.selectedClass);
18963 this.selections.push(node);
18964 if(!suppressEvent){
18965 this.fireEvent("selectionchange", this, this.selections);
18973 * @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
18974 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18975 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18977 unselect : function(nodeInfo, keepExisting, suppressEvent)
18979 if(nodeInfo instanceof Array){
18980 Roo.each(this.selections, function(s) {
18981 this.unselect(s, nodeInfo);
18985 var node = this.getNode(nodeInfo);
18986 if(!node || !this.isSelected(node)){
18987 //Roo.log("not selected");
18988 return; // not selected.
18992 Roo.each(this.selections, function(s) {
18994 Roo.fly(node).removeClass(this.selectedClass);
19001 this.selections= ns;
19002 this.fireEvent("selectionchange", this, this.selections);
19006 * Gets a template node.
19007 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19008 * @return {HTMLElement} The node or null if it wasn't found
19010 getNode : function(nodeInfo){
19011 if(typeof nodeInfo == "string"){
19012 return document.getElementById(nodeInfo);
19013 }else if(typeof nodeInfo == "number"){
19014 return this.nodes[nodeInfo];
19020 * Gets a range template nodes.
19021 * @param {Number} startIndex
19022 * @param {Number} endIndex
19023 * @return {Array} An array of nodes
19025 getNodes : function(start, end){
19026 var ns = this.nodes;
19027 start = start || 0;
19028 end = typeof end == "undefined" ? ns.length - 1 : end;
19031 for(var i = start; i <= end; i++){
19035 for(var i = start; i >= end; i--){
19043 * Finds the index of the passed node
19044 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19045 * @return {Number} The index of the node or -1
19047 indexOf : function(node){
19048 node = this.getNode(node);
19049 if(typeof node.nodeIndex == "number"){
19050 return node.nodeIndex;
19052 var ns = this.nodes;
19053 for(var i = 0, len = ns.length; i < len; i++){
19064 * based on jquery fullcalendar
19068 Roo.bootstrap = Roo.bootstrap || {};
19070 * @class Roo.bootstrap.Calendar
19071 * @extends Roo.bootstrap.Component
19072 * Bootstrap Calendar class
19073 * @cfg {Boolean} loadMask (true|false) default false
19074 * @cfg {Object} header generate the user specific header of the calendar, default false
19077 * Create a new Container
19078 * @param {Object} config The config object
19083 Roo.bootstrap.Calendar = function(config){
19084 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19088 * Fires when a date is selected
19089 * @param {DatePicker} this
19090 * @param {Date} date The selected date
19094 * @event monthchange
19095 * Fires when the displayed month changes
19096 * @param {DatePicker} this
19097 * @param {Date} date The selected month
19099 'monthchange': true,
19101 * @event evententer
19102 * Fires when mouse over an event
19103 * @param {Calendar} this
19104 * @param {event} Event
19106 'evententer': true,
19108 * @event eventleave
19109 * Fires when the mouse leaves an
19110 * @param {Calendar} this
19113 'eventleave': true,
19115 * @event eventclick
19116 * Fires when the mouse click an
19117 * @param {Calendar} this
19126 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19129 * @cfg {Number} startDay
19130 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19138 getAutoCreate : function(){
19141 var fc_button = function(name, corner, style, content ) {
19142 return Roo.apply({},{
19144 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19146 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19149 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19160 style : 'width:100%',
19167 cls : 'fc-header-left',
19169 fc_button('prev', 'left', 'arrow', '‹' ),
19170 fc_button('next', 'right', 'arrow', '›' ),
19171 { tag: 'span', cls: 'fc-header-space' },
19172 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19180 cls : 'fc-header-center',
19184 cls: 'fc-header-title',
19187 html : 'month / year'
19195 cls : 'fc-header-right',
19197 /* fc_button('month', 'left', '', 'month' ),
19198 fc_button('week', '', '', 'week' ),
19199 fc_button('day', 'right', '', 'day' )
19211 header = this.header;
19214 var cal_heads = function() {
19216 // fixme - handle this.
19218 for (var i =0; i < Date.dayNames.length; i++) {
19219 var d = Date.dayNames[i];
19222 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19223 html : d.substring(0,3)
19227 ret[0].cls += ' fc-first';
19228 ret[6].cls += ' fc-last';
19231 var cal_cell = function(n) {
19234 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19239 cls: 'fc-day-number',
19243 cls: 'fc-day-content',
19247 style: 'position: relative;' // height: 17px;
19259 var cal_rows = function() {
19262 for (var r = 0; r < 6; r++) {
19269 for (var i =0; i < Date.dayNames.length; i++) {
19270 var d = Date.dayNames[i];
19271 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19274 row.cn[0].cls+=' fc-first';
19275 row.cn[0].cn[0].style = 'min-height:90px';
19276 row.cn[6].cls+=' fc-last';
19280 ret[0].cls += ' fc-first';
19281 ret[4].cls += ' fc-prev-last';
19282 ret[5].cls += ' fc-last';
19289 cls: 'fc-border-separate',
19290 style : 'width:100%',
19298 cls : 'fc-first fc-last',
19316 cls : 'fc-content',
19317 style : "position: relative;",
19320 cls : 'fc-view fc-view-month fc-grid',
19321 style : 'position: relative',
19322 unselectable : 'on',
19325 cls : 'fc-event-container',
19326 style : 'position:absolute;z-index:8;top:0;left:0;'
19344 initEvents : function()
19347 throw "can not find store for calendar";
19353 style: "text-align:center",
19357 style: "background-color:white;width:50%;margin:250 auto",
19361 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19372 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19374 var size = this.el.select('.fc-content', true).first().getSize();
19375 this.maskEl.setSize(size.width, size.height);
19376 this.maskEl.enableDisplayMode("block");
19377 if(!this.loadMask){
19378 this.maskEl.hide();
19381 this.store = Roo.factory(this.store, Roo.data);
19382 this.store.on('load', this.onLoad, this);
19383 this.store.on('beforeload', this.onBeforeLoad, this);
19387 this.cells = this.el.select('.fc-day',true);
19388 //Roo.log(this.cells);
19389 this.textNodes = this.el.query('.fc-day-number');
19390 this.cells.addClassOnOver('fc-state-hover');
19392 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19393 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19394 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19395 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19397 this.on('monthchange', this.onMonthChange, this);
19399 this.update(new Date().clearTime());
19402 resize : function() {
19403 var sz = this.el.getSize();
19405 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19406 this.el.select('.fc-day-content div',true).setHeight(34);
19411 showPrevMonth : function(e){
19412 this.update(this.activeDate.add("mo", -1));
19414 showToday : function(e){
19415 this.update(new Date().clearTime());
19418 showNextMonth : function(e){
19419 this.update(this.activeDate.add("mo", 1));
19423 showPrevYear : function(){
19424 this.update(this.activeDate.add("y", -1));
19428 showNextYear : function(){
19429 this.update(this.activeDate.add("y", 1));
19434 update : function(date)
19436 var vd = this.activeDate;
19437 this.activeDate = date;
19438 // if(vd && this.el){
19439 // var t = date.getTime();
19440 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19441 // Roo.log('using add remove');
19443 // this.fireEvent('monthchange', this, date);
19445 // this.cells.removeClass("fc-state-highlight");
19446 // this.cells.each(function(c){
19447 // if(c.dateValue == t){
19448 // c.addClass("fc-state-highlight");
19449 // setTimeout(function(){
19450 // try{c.dom.firstChild.focus();}catch(e){}
19460 var days = date.getDaysInMonth();
19462 var firstOfMonth = date.getFirstDateOfMonth();
19463 var startingPos = firstOfMonth.getDay()-this.startDay;
19465 if(startingPos < this.startDay){
19469 var pm = date.add(Date.MONTH, -1);
19470 var prevStart = pm.getDaysInMonth()-startingPos;
19472 this.cells = this.el.select('.fc-day',true);
19473 this.textNodes = this.el.query('.fc-day-number');
19474 this.cells.addClassOnOver('fc-state-hover');
19476 var cells = this.cells.elements;
19477 var textEls = this.textNodes;
19479 Roo.each(cells, function(cell){
19480 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19483 days += startingPos;
19485 // convert everything to numbers so it's fast
19486 var day = 86400000;
19487 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19490 //Roo.log(prevStart);
19492 var today = new Date().clearTime().getTime();
19493 var sel = date.clearTime().getTime();
19494 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19495 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19496 var ddMatch = this.disabledDatesRE;
19497 var ddText = this.disabledDatesText;
19498 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19499 var ddaysText = this.disabledDaysText;
19500 var format = this.format;
19502 var setCellClass = function(cal, cell){
19506 //Roo.log('set Cell Class');
19508 var t = d.getTime();
19512 cell.dateValue = t;
19514 cell.className += " fc-today";
19515 cell.className += " fc-state-highlight";
19516 cell.title = cal.todayText;
19519 // disable highlight in other month..
19520 //cell.className += " fc-state-highlight";
19525 cell.className = " fc-state-disabled";
19526 cell.title = cal.minText;
19530 cell.className = " fc-state-disabled";
19531 cell.title = cal.maxText;
19535 if(ddays.indexOf(d.getDay()) != -1){
19536 cell.title = ddaysText;
19537 cell.className = " fc-state-disabled";
19540 if(ddMatch && format){
19541 var fvalue = d.dateFormat(format);
19542 if(ddMatch.test(fvalue)){
19543 cell.title = ddText.replace("%0", fvalue);
19544 cell.className = " fc-state-disabled";
19548 if (!cell.initialClassName) {
19549 cell.initialClassName = cell.dom.className;
19552 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19557 for(; i < startingPos; i++) {
19558 textEls[i].innerHTML = (++prevStart);
19559 d.setDate(d.getDate()+1);
19561 cells[i].className = "fc-past fc-other-month";
19562 setCellClass(this, cells[i]);
19567 for(; i < days; i++){
19568 intDay = i - startingPos + 1;
19569 textEls[i].innerHTML = (intDay);
19570 d.setDate(d.getDate()+1);
19572 cells[i].className = ''; // "x-date-active";
19573 setCellClass(this, cells[i]);
19577 for(; i < 42; i++) {
19578 textEls[i].innerHTML = (++extraDays);
19579 d.setDate(d.getDate()+1);
19581 cells[i].className = "fc-future fc-other-month";
19582 setCellClass(this, cells[i]);
19585 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19587 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19589 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19590 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19592 if(totalRows != 6){
19593 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19594 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19597 this.fireEvent('monthchange', this, date);
19601 if(!this.internalRender){
19602 var main = this.el.dom.firstChild;
19603 var w = main.offsetWidth;
19604 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19605 Roo.fly(main).setWidth(w);
19606 this.internalRender = true;
19607 // opera does not respect the auto grow header center column
19608 // then, after it gets a width opera refuses to recalculate
19609 // without a second pass
19610 if(Roo.isOpera && !this.secondPass){
19611 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19612 this.secondPass = true;
19613 this.update.defer(10, this, [date]);
19620 findCell : function(dt) {
19621 dt = dt.clearTime().getTime();
19623 this.cells.each(function(c){
19624 //Roo.log("check " +c.dateValue + '?=' + dt);
19625 if(c.dateValue == dt){
19635 findCells : function(ev) {
19636 var s = ev.start.clone().clearTime().getTime();
19638 var e= ev.end.clone().clearTime().getTime();
19641 this.cells.each(function(c){
19642 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19644 if(c.dateValue > e){
19647 if(c.dateValue < s){
19656 // findBestRow: function(cells)
19660 // for (var i =0 ; i < cells.length;i++) {
19661 // ret = Math.max(cells[i].rows || 0,ret);
19668 addItem : function(ev)
19670 // look for vertical location slot in
19671 var cells = this.findCells(ev);
19673 // ev.row = this.findBestRow(cells);
19675 // work out the location.
19679 for(var i =0; i < cells.length; i++) {
19681 cells[i].row = cells[0].row;
19684 cells[i].row = cells[i].row + 1;
19694 if (crow.start.getY() == cells[i].getY()) {
19696 crow.end = cells[i];
19713 cells[0].events.push(ev);
19715 this.calevents.push(ev);
19718 clearEvents: function() {
19720 if(!this.calevents){
19724 Roo.each(this.cells.elements, function(c){
19730 Roo.each(this.calevents, function(e) {
19731 Roo.each(e.els, function(el) {
19732 el.un('mouseenter' ,this.onEventEnter, this);
19733 el.un('mouseleave' ,this.onEventLeave, this);
19738 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19744 renderEvents: function()
19748 this.cells.each(function(c) {
19757 if(c.row != c.events.length){
19758 r = 4 - (4 - (c.row - c.events.length));
19761 c.events = ev.slice(0, r);
19762 c.more = ev.slice(r);
19764 if(c.more.length && c.more.length == 1){
19765 c.events.push(c.more.pop());
19768 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19772 this.cells.each(function(c) {
19774 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19777 for (var e = 0; e < c.events.length; e++){
19778 var ev = c.events[e];
19779 var rows = ev.rows;
19781 for(var i = 0; i < rows.length; i++) {
19783 // how many rows should it span..
19786 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19787 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19789 unselectable : "on",
19792 cls: 'fc-event-inner',
19796 // cls: 'fc-event-time',
19797 // html : cells.length > 1 ? '' : ev.time
19801 cls: 'fc-event-title',
19802 html : String.format('{0}', ev.title)
19809 cls: 'ui-resizable-handle ui-resizable-e',
19810 html : '  '
19817 cfg.cls += ' fc-event-start';
19819 if ((i+1) == rows.length) {
19820 cfg.cls += ' fc-event-end';
19823 var ctr = _this.el.select('.fc-event-container',true).first();
19824 var cg = ctr.createChild(cfg);
19826 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19827 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19829 var r = (c.more.length) ? 1 : 0;
19830 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19831 cg.setWidth(ebox.right - sbox.x -2);
19833 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19834 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19835 cg.on('click', _this.onEventClick, _this, ev);
19846 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19847 style : 'position: absolute',
19848 unselectable : "on",
19851 cls: 'fc-event-inner',
19855 cls: 'fc-event-title',
19863 cls: 'ui-resizable-handle ui-resizable-e',
19864 html : '  '
19870 var ctr = _this.el.select('.fc-event-container',true).first();
19871 var cg = ctr.createChild(cfg);
19873 var sbox = c.select('.fc-day-content',true).first().getBox();
19874 var ebox = c.select('.fc-day-content',true).first().getBox();
19876 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19877 cg.setWidth(ebox.right - sbox.x -2);
19879 cg.on('click', _this.onMoreEventClick, _this, c.more);
19889 onEventEnter: function (e, el,event,d) {
19890 this.fireEvent('evententer', this, el, event);
19893 onEventLeave: function (e, el,event,d) {
19894 this.fireEvent('eventleave', this, el, event);
19897 onEventClick: function (e, el,event,d) {
19898 this.fireEvent('eventclick', this, el, event);
19901 onMonthChange: function () {
19905 onMoreEventClick: function(e, el, more)
19909 this.calpopover.placement = 'right';
19910 this.calpopover.setTitle('More');
19912 this.calpopover.setContent('');
19914 var ctr = this.calpopover.el.select('.popover-content', true).first();
19916 Roo.each(more, function(m){
19918 cls : 'fc-event-hori fc-event-draggable',
19921 var cg = ctr.createChild(cfg);
19923 cg.on('click', _this.onEventClick, _this, m);
19926 this.calpopover.show(el);
19931 onLoad: function ()
19933 this.calevents = [];
19936 if(this.store.getCount() > 0){
19937 this.store.data.each(function(d){
19940 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19941 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19942 time : d.data.start_time,
19943 title : d.data.title,
19944 description : d.data.description,
19945 venue : d.data.venue
19950 this.renderEvents();
19952 if(this.calevents.length && this.loadMask){
19953 this.maskEl.hide();
19957 onBeforeLoad: function()
19959 this.clearEvents();
19961 this.maskEl.show();
19975 * @class Roo.bootstrap.Popover
19976 * @extends Roo.bootstrap.Component
19977 * Bootstrap Popover class
19978 * @cfg {String} html contents of the popover (or false to use children..)
19979 * @cfg {String} title of popover (or false to hide)
19980 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19981 * @cfg {String} trigger click || hover (or false to trigger manually)
19982 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19983 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19984 * - if false and it has a 'parent' then it will be automatically added to that element
19985 * - if string - Roo.get will be called
19986 * @cfg {Number} delay - delay before showing
19989 * Create a new Popover
19990 * @param {Object} config The config object
19993 Roo.bootstrap.Popover = function(config){
19994 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20000 * After the popover show
20002 * @param {Roo.bootstrap.Popover} this
20007 * After the popover hide
20009 * @param {Roo.bootstrap.Popover} this
20015 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20020 placement : 'right',
20021 trigger : 'hover', // hover
20027 can_build_overlaid : false,
20029 maskEl : false, // the mask element
20032 alignEl : false, // when show is called with an element - this get's stored.
20034 getChildContainer : function()
20036 return this.contentEl;
20039 getPopoverHeader : function()
20041 this.title = true; // flag not to hide it..
20042 this.headerEl.addClass('p-0');
20043 return this.headerEl
20047 getAutoCreate : function(){
20050 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20051 style: 'display:block',
20057 cls : 'popover-inner ',
20061 cls: 'popover-title popover-header',
20062 html : this.title === false ? '' : this.title
20065 cls : 'popover-content popover-body ' + (this.cls || ''),
20066 html : this.html || ''
20077 * @param {string} the title
20079 setTitle: function(str)
20083 this.headerEl.dom.innerHTML = str;
20088 * @param {string} the body content
20090 setContent: function(str)
20093 if (this.contentEl) {
20094 this.contentEl.dom.innerHTML = str;
20098 // as it get's added to the bottom of the page.
20099 onRender : function(ct, position)
20101 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20106 var cfg = Roo.apply({}, this.getAutoCreate());
20110 cfg.cls += ' ' + this.cls;
20113 cfg.style = this.style;
20115 //Roo.log("adding to ");
20116 this.el = Roo.get(document.body).createChild(cfg, position);
20117 // Roo.log(this.el);
20120 this.contentEl = this.el.select('.popover-content',true).first();
20121 this.headerEl = this.el.select('.popover-title',true).first();
20124 if(typeof(this.items) != 'undefined'){
20125 var items = this.items;
20128 for(var i =0;i < items.length;i++) {
20129 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20133 this.items = nitems;
20135 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20136 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20143 resizeMask : function()
20145 this.maskEl.setSize(
20146 Roo.lib.Dom.getViewWidth(true),
20147 Roo.lib.Dom.getViewHeight(true)
20151 initEvents : function()
20155 Roo.bootstrap.Popover.register(this);
20158 this.arrowEl = this.el.select('.arrow',true).first();
20159 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20160 this.el.enableDisplayMode('block');
20164 if (this.over === false && !this.parent()) {
20167 if (this.triggers === false) {
20172 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20173 var triggers = this.trigger ? this.trigger.split(' ') : [];
20174 Roo.each(triggers, function(trigger) {
20176 if (trigger == 'click') {
20177 on_el.on('click', this.toggle, this);
20178 } else if (trigger != 'manual') {
20179 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20180 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20182 on_el.on(eventIn ,this.enter, this);
20183 on_el.on(eventOut, this.leave, this);
20193 toggle : function () {
20194 this.hoverState == 'in' ? this.leave() : this.enter();
20197 enter : function () {
20199 clearTimeout(this.timeout);
20201 this.hoverState = 'in';
20203 if (!this.delay || !this.delay.show) {
20208 this.timeout = setTimeout(function () {
20209 if (_t.hoverState == 'in') {
20212 }, this.delay.show)
20215 leave : function() {
20216 clearTimeout(this.timeout);
20218 this.hoverState = 'out';
20220 if (!this.delay || !this.delay.hide) {
20225 this.timeout = setTimeout(function () {
20226 if (_t.hoverState == 'out') {
20229 }, this.delay.hide)
20233 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20234 * @param {string} (left|right|top|bottom) position
20236 show : function (on_el, placement)
20238 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20239 on_el = on_el || false; // default to false
20242 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20243 on_el = this.parent().el;
20244 } else if (this.over) {
20245 on_el = Roo.get(this.over);
20250 this.alignEl = Roo.get( on_el );
20253 this.render(document.body);
20259 if (this.title === false) {
20260 this.headerEl.hide();
20265 this.el.dom.style.display = 'block';
20268 if (this.alignEl) {
20269 this.updatePosition(this.placement, true);
20272 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20273 var es = this.el.getSize();
20274 var x = Roo.lib.Dom.getViewWidth()/2;
20275 var y = Roo.lib.Dom.getViewHeight()/2;
20276 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20281 //var arrow = this.el.select('.arrow',true).first();
20282 //arrow.set(align[2],
20284 this.el.addClass('in');
20288 this.hoverState = 'in';
20291 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20292 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20293 this.maskEl.dom.style.display = 'block';
20294 this.maskEl.addClass('show');
20296 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20298 this.fireEvent('show', this);
20302 * fire this manually after loading a grid in the table for example
20303 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20304 * @param {Boolean} try and move it if we cant get right position.
20306 updatePosition : function(placement, try_move)
20308 // allow for calling with no parameters
20309 placement = placement ? placement : this.placement;
20310 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20312 this.el.removeClass([
20313 'fade','top','bottom', 'left', 'right','in',
20314 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20316 this.el.addClass(placement + ' bs-popover-' + placement);
20318 if (!this.alignEl ) {
20322 switch (placement) {
20324 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20325 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20326 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20327 //normal display... or moved up/down.
20328 this.el.setXY(offset);
20329 var xy = this.alignEl.getAnchorXY('tr', false);
20331 this.arrowEl.setXY(xy);
20334 // continue through...
20335 return this.updatePosition('left', false);
20339 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20340 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20341 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20342 //normal display... or moved up/down.
20343 this.el.setXY(offset);
20344 var xy = this.alignEl.getAnchorXY('tl', false);
20345 xy[0]-=10;xy[1]+=5; // << fix me
20346 this.arrowEl.setXY(xy);
20350 return this.updatePosition('right', false);
20353 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20354 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20355 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20356 //normal display... or moved up/down.
20357 this.el.setXY(offset);
20358 var xy = this.alignEl.getAnchorXY('t', false);
20359 xy[1]-=10; // << fix me
20360 this.arrowEl.setXY(xy);
20364 return this.updatePosition('bottom', false);
20367 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20368 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20369 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20370 //normal display... or moved up/down.
20371 this.el.setXY(offset);
20372 var xy = this.alignEl.getAnchorXY('b', false);
20373 xy[1]+=2; // << fix me
20374 this.arrowEl.setXY(xy);
20378 return this.updatePosition('top', false);
20389 this.el.setXY([0,0]);
20390 this.el.removeClass('in');
20392 this.hoverState = null;
20393 this.maskEl.hide(); // always..
20394 this.fireEvent('hide', this);
20400 Roo.apply(Roo.bootstrap.Popover, {
20403 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20404 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20405 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20406 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20411 clickHander : false,
20415 onMouseDown : function(e)
20417 if (this.popups.length && !e.getTarget(".roo-popover")) {
20418 /// what is nothing is showing..
20427 register : function(popup)
20429 if (!Roo.bootstrap.Popover.clickHandler) {
20430 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20432 // hide other popups.
20433 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
20434 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
20435 this.hideAll(); //<< why?
20436 //this.popups.push(popup);
20438 hideAll : function()
20440 this.popups.forEach(function(p) {
20444 onShow : function() {
20445 Roo.bootstrap.Popover.popups.push(this);
20447 onHide : function() {
20448 Roo.bootstrap.Popover.popups.remove(this);
20454 * Card header - holder for the card header elements.
20459 * @class Roo.bootstrap.PopoverNav
20460 * @extends Roo.bootstrap.NavGroup
20461 * Bootstrap Popover header navigation class
20463 * Create a new Popover Header Navigation
20464 * @param {Object} config The config object
20467 Roo.bootstrap.PopoverNav = function(config){
20468 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20471 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20474 container_method : 'getPopoverHeader'
20492 * @class Roo.bootstrap.Progress
20493 * @extends Roo.bootstrap.Component
20494 * Bootstrap Progress class
20495 * @cfg {Boolean} striped striped of the progress bar
20496 * @cfg {Boolean} active animated of the progress bar
20500 * Create a new Progress
20501 * @param {Object} config The config object
20504 Roo.bootstrap.Progress = function(config){
20505 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20508 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20513 getAutoCreate : function(){
20521 cfg.cls += ' progress-striped';
20525 cfg.cls += ' active';
20544 * @class Roo.bootstrap.ProgressBar
20545 * @extends Roo.bootstrap.Component
20546 * Bootstrap ProgressBar class
20547 * @cfg {Number} aria_valuenow aria-value now
20548 * @cfg {Number} aria_valuemin aria-value min
20549 * @cfg {Number} aria_valuemax aria-value max
20550 * @cfg {String} label label for the progress bar
20551 * @cfg {String} panel (success | info | warning | danger )
20552 * @cfg {String} role role of the progress bar
20553 * @cfg {String} sr_only text
20557 * Create a new ProgressBar
20558 * @param {Object} config The config object
20561 Roo.bootstrap.ProgressBar = function(config){
20562 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20565 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20569 aria_valuemax : 100,
20575 getAutoCreate : function()
20580 cls: 'progress-bar',
20581 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20593 cfg.role = this.role;
20596 if(this.aria_valuenow){
20597 cfg['aria-valuenow'] = this.aria_valuenow;
20600 if(this.aria_valuemin){
20601 cfg['aria-valuemin'] = this.aria_valuemin;
20604 if(this.aria_valuemax){
20605 cfg['aria-valuemax'] = this.aria_valuemax;
20608 if(this.label && !this.sr_only){
20609 cfg.html = this.label;
20613 cfg.cls += ' progress-bar-' + this.panel;
20619 update : function(aria_valuenow)
20621 this.aria_valuenow = aria_valuenow;
20623 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20638 * @class Roo.bootstrap.TabGroup
20639 * @extends Roo.bootstrap.Column
20640 * Bootstrap Column class
20641 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20642 * @cfg {Boolean} carousel true to make the group behave like a carousel
20643 * @cfg {Boolean} bullets show bullets for the panels
20644 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20645 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20646 * @cfg {Boolean} showarrow (true|false) show arrow default true
20649 * Create a new TabGroup
20650 * @param {Object} config The config object
20653 Roo.bootstrap.TabGroup = function(config){
20654 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20656 this.navId = Roo.id();
20659 Roo.bootstrap.TabGroup.register(this);
20663 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20666 transition : false,
20671 slideOnTouch : false,
20674 getAutoCreate : function()
20676 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20678 cfg.cls += ' tab-content';
20680 if (this.carousel) {
20681 cfg.cls += ' carousel slide';
20684 cls : 'carousel-inner',
20688 if(this.bullets && !Roo.isTouch){
20691 cls : 'carousel-bullets',
20695 if(this.bullets_cls){
20696 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20703 cfg.cn[0].cn.push(bullets);
20706 if(this.showarrow){
20707 cfg.cn[0].cn.push({
20709 class : 'carousel-arrow',
20713 class : 'carousel-prev',
20717 class : 'fa fa-chevron-left'
20723 class : 'carousel-next',
20727 class : 'fa fa-chevron-right'
20740 initEvents: function()
20742 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20743 // this.el.on("touchstart", this.onTouchStart, this);
20746 if(this.autoslide){
20749 this.slideFn = window.setInterval(function() {
20750 _this.showPanelNext();
20754 if(this.showarrow){
20755 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20756 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20762 // onTouchStart : function(e, el, o)
20764 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20768 // this.showPanelNext();
20772 getChildContainer : function()
20774 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20778 * register a Navigation item
20779 * @param {Roo.bootstrap.NavItem} the navitem to add
20781 register : function(item)
20783 this.tabs.push( item);
20784 item.navId = this.navId; // not really needed..
20789 getActivePanel : function()
20792 Roo.each(this.tabs, function(t) {
20802 getPanelByName : function(n)
20805 Roo.each(this.tabs, function(t) {
20806 if (t.tabId == n) {
20814 indexOfPanel : function(p)
20817 Roo.each(this.tabs, function(t,i) {
20818 if (t.tabId == p.tabId) {
20827 * show a specific panel
20828 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20829 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20831 showPanel : function (pan)
20833 if(this.transition || typeof(pan) == 'undefined'){
20834 Roo.log("waiting for the transitionend");
20838 if (typeof(pan) == 'number') {
20839 pan = this.tabs[pan];
20842 if (typeof(pan) == 'string') {
20843 pan = this.getPanelByName(pan);
20846 var cur = this.getActivePanel();
20849 Roo.log('pan or acitve pan is undefined');
20853 if (pan.tabId == this.getActivePanel().tabId) {
20857 if (false === cur.fireEvent('beforedeactivate')) {
20861 if(this.bullets > 0 && !Roo.isTouch){
20862 this.setActiveBullet(this.indexOfPanel(pan));
20865 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20867 //class="carousel-item carousel-item-next carousel-item-left"
20869 this.transition = true;
20870 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20871 var lr = dir == 'next' ? 'left' : 'right';
20872 pan.el.addClass(dir); // or prev
20873 pan.el.addClass('carousel-item-' + dir); // or prev
20874 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20875 cur.el.addClass(lr); // or right
20876 pan.el.addClass(lr);
20877 cur.el.addClass('carousel-item-' +lr); // or right
20878 pan.el.addClass('carousel-item-' +lr);
20882 cur.el.on('transitionend', function() {
20883 Roo.log("trans end?");
20885 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20886 pan.setActive(true);
20888 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20889 cur.setActive(false);
20891 _this.transition = false;
20893 }, this, { single: true } );
20898 cur.setActive(false);
20899 pan.setActive(true);
20904 showPanelNext : function()
20906 var i = this.indexOfPanel(this.getActivePanel());
20908 if (i >= this.tabs.length - 1 && !this.autoslide) {
20912 if (i >= this.tabs.length - 1 && this.autoslide) {
20916 this.showPanel(this.tabs[i+1]);
20919 showPanelPrev : function()
20921 var i = this.indexOfPanel(this.getActivePanel());
20923 if (i < 1 && !this.autoslide) {
20927 if (i < 1 && this.autoslide) {
20928 i = this.tabs.length;
20931 this.showPanel(this.tabs[i-1]);
20935 addBullet: function()
20937 if(!this.bullets || Roo.isTouch){
20940 var ctr = this.el.select('.carousel-bullets',true).first();
20941 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20942 var bullet = ctr.createChild({
20943 cls : 'bullet bullet-' + i
20944 },ctr.dom.lastChild);
20949 bullet.on('click', (function(e, el, o, ii, t){
20951 e.preventDefault();
20953 this.showPanel(ii);
20955 if(this.autoslide && this.slideFn){
20956 clearInterval(this.slideFn);
20957 this.slideFn = window.setInterval(function() {
20958 _this.showPanelNext();
20962 }).createDelegate(this, [i, bullet], true));
20967 setActiveBullet : function(i)
20973 Roo.each(this.el.select('.bullet', true).elements, function(el){
20974 el.removeClass('selected');
20977 var bullet = this.el.select('.bullet-' + i, true).first();
20983 bullet.addClass('selected');
20994 Roo.apply(Roo.bootstrap.TabGroup, {
20998 * register a Navigation Group
20999 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21001 register : function(navgrp)
21003 this.groups[navgrp.navId] = navgrp;
21007 * fetch a Navigation Group based on the navigation ID
21008 * if one does not exist , it will get created.
21009 * @param {string} the navgroup to add
21010 * @returns {Roo.bootstrap.NavGroup} the navgroup
21012 get: function(navId) {
21013 if (typeof(this.groups[navId]) == 'undefined') {
21014 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21016 return this.groups[navId] ;
21031 * @class Roo.bootstrap.TabPanel
21032 * @extends Roo.bootstrap.Component
21033 * Bootstrap TabPanel class
21034 * @cfg {Boolean} active panel active
21035 * @cfg {String} html panel content
21036 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21037 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21038 * @cfg {String} href click to link..
21039 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21043 * Create a new TabPanel
21044 * @param {Object} config The config object
21047 Roo.bootstrap.TabPanel = function(config){
21048 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21052 * Fires when the active status changes
21053 * @param {Roo.bootstrap.TabPanel} this
21054 * @param {Boolean} state the new state
21059 * @event beforedeactivate
21060 * Fires before a tab is de-activated - can be used to do validation on a form.
21061 * @param {Roo.bootstrap.TabPanel} this
21062 * @return {Boolean} false if there is an error
21065 'beforedeactivate': true
21068 this.tabId = this.tabId || Roo.id();
21072 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21079 touchSlide : false,
21080 getAutoCreate : function(){
21085 // item is needed for carousel - not sure if it has any effect otherwise
21086 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21087 html: this.html || ''
21091 cfg.cls += ' active';
21095 cfg.tabId = this.tabId;
21103 initEvents: function()
21105 var p = this.parent();
21107 this.navId = this.navId || p.navId;
21109 if (typeof(this.navId) != 'undefined') {
21110 // not really needed.. but just in case.. parent should be a NavGroup.
21111 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21115 var i = tg.tabs.length - 1;
21117 if(this.active && tg.bullets > 0 && i < tg.bullets){
21118 tg.setActiveBullet(i);
21122 this.el.on('click', this.onClick, this);
21124 if(Roo.isTouch && this.touchSlide){
21125 this.el.on("touchstart", this.onTouchStart, this);
21126 this.el.on("touchmove", this.onTouchMove, this);
21127 this.el.on("touchend", this.onTouchEnd, this);
21132 onRender : function(ct, position)
21134 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21137 setActive : function(state)
21139 Roo.log("panel - set active " + this.tabId + "=" + state);
21141 this.active = state;
21143 this.el.removeClass('active');
21145 } else if (!this.el.hasClass('active')) {
21146 this.el.addClass('active');
21149 this.fireEvent('changed', this, state);
21152 onClick : function(e)
21154 e.preventDefault();
21156 if(!this.href.length){
21160 window.location.href = this.href;
21169 onTouchStart : function(e)
21171 this.swiping = false;
21173 this.startX = e.browserEvent.touches[0].clientX;
21174 this.startY = e.browserEvent.touches[0].clientY;
21177 onTouchMove : function(e)
21179 this.swiping = true;
21181 this.endX = e.browserEvent.touches[0].clientX;
21182 this.endY = e.browserEvent.touches[0].clientY;
21185 onTouchEnd : function(e)
21192 var tabGroup = this.parent();
21194 if(this.endX > this.startX){ // swiping right
21195 tabGroup.showPanelPrev();
21199 if(this.startX > this.endX){ // swiping left
21200 tabGroup.showPanelNext();
21219 * @class Roo.bootstrap.DateField
21220 * @extends Roo.bootstrap.Input
21221 * Bootstrap DateField class
21222 * @cfg {Number} weekStart default 0
21223 * @cfg {String} viewMode default empty, (months|years)
21224 * @cfg {String} minViewMode default empty, (months|years)
21225 * @cfg {Number} startDate default -Infinity
21226 * @cfg {Number} endDate default Infinity
21227 * @cfg {Boolean} todayHighlight default false
21228 * @cfg {Boolean} todayBtn default false
21229 * @cfg {Boolean} calendarWeeks default false
21230 * @cfg {Object} daysOfWeekDisabled default empty
21231 * @cfg {Boolean} singleMode default false (true | false)
21233 * @cfg {Boolean} keyboardNavigation default true
21234 * @cfg {String} language default en
21237 * Create a new DateField
21238 * @param {Object} config The config object
21241 Roo.bootstrap.DateField = function(config){
21242 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21246 * Fires when this field show.
21247 * @param {Roo.bootstrap.DateField} this
21248 * @param {Mixed} date The date value
21253 * Fires when this field hide.
21254 * @param {Roo.bootstrap.DateField} this
21255 * @param {Mixed} date The date value
21260 * Fires when select a date.
21261 * @param {Roo.bootstrap.DateField} this
21262 * @param {Mixed} date The date value
21266 * @event beforeselect
21267 * Fires when before select a date.
21268 * @param {Roo.bootstrap.DateField} this
21269 * @param {Mixed} date The date value
21271 beforeselect : true
21275 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21278 * @cfg {String} format
21279 * The default date format string which can be overriden for localization support. The format must be
21280 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21284 * @cfg {String} altFormats
21285 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21286 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21288 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21296 todayHighlight : false,
21302 keyboardNavigation: true,
21304 calendarWeeks: false,
21306 startDate: -Infinity,
21310 daysOfWeekDisabled: [],
21314 singleMode : false,
21316 UTCDate: function()
21318 return new Date(Date.UTC.apply(Date, arguments));
21321 UTCToday: function()
21323 var today = new Date();
21324 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21327 getDate: function() {
21328 var d = this.getUTCDate();
21329 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21332 getUTCDate: function() {
21336 setDate: function(d) {
21337 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21340 setUTCDate: function(d) {
21342 this.setValue(this.formatDate(this.date));
21345 onRender: function(ct, position)
21348 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21350 this.language = this.language || 'en';
21351 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21352 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21354 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21355 this.format = this.format || 'm/d/y';
21356 this.isInline = false;
21357 this.isInput = true;
21358 this.component = this.el.select('.add-on', true).first() || false;
21359 this.component = (this.component && this.component.length === 0) ? false : this.component;
21360 this.hasInput = this.component && this.inputEl().length;
21362 if (typeof(this.minViewMode === 'string')) {
21363 switch (this.minViewMode) {
21365 this.minViewMode = 1;
21368 this.minViewMode = 2;
21371 this.minViewMode = 0;
21376 if (typeof(this.viewMode === 'string')) {
21377 switch (this.viewMode) {
21390 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21392 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21394 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21396 this.picker().on('mousedown', this.onMousedown, this);
21397 this.picker().on('click', this.onClick, this);
21399 this.picker().addClass('datepicker-dropdown');
21401 this.startViewMode = this.viewMode;
21403 if(this.singleMode){
21404 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21405 v.setVisibilityMode(Roo.Element.DISPLAY);
21409 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21410 v.setStyle('width', '189px');
21414 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21415 if(!this.calendarWeeks){
21420 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21421 v.attr('colspan', function(i, val){
21422 return parseInt(val) + 1;
21427 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21429 this.setStartDate(this.startDate);
21430 this.setEndDate(this.endDate);
21432 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21439 if(this.isInline) {
21444 picker : function()
21446 return this.pickerEl;
21447 // return this.el.select('.datepicker', true).first();
21450 fillDow: function()
21452 var dowCnt = this.weekStart;
21461 if(this.calendarWeeks){
21469 while (dowCnt < this.weekStart + 7) {
21473 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21477 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21480 fillMonths: function()
21483 var months = this.picker().select('>.datepicker-months td', true).first();
21485 months.dom.innerHTML = '';
21491 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21494 months.createChild(month);
21501 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;
21503 if (this.date < this.startDate) {
21504 this.viewDate = new Date(this.startDate);
21505 } else if (this.date > this.endDate) {
21506 this.viewDate = new Date(this.endDate);
21508 this.viewDate = new Date(this.date);
21516 var d = new Date(this.viewDate),
21517 year = d.getUTCFullYear(),
21518 month = d.getUTCMonth(),
21519 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21520 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21521 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21522 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21523 currentDate = this.date && this.date.valueOf(),
21524 today = this.UTCToday();
21526 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21528 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21530 // this.picker.select('>tfoot th.today').
21531 // .text(dates[this.language].today)
21532 // .toggle(this.todayBtn !== false);
21534 this.updateNavArrows();
21537 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21539 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21541 prevMonth.setUTCDate(day);
21543 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21545 var nextMonth = new Date(prevMonth);
21547 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21549 nextMonth = nextMonth.valueOf();
21551 var fillMonths = false;
21553 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21555 while(prevMonth.valueOf() <= nextMonth) {
21558 if (prevMonth.getUTCDay() === this.weekStart) {
21560 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21568 if(this.calendarWeeks){
21569 // ISO 8601: First week contains first thursday.
21570 // ISO also states week starts on Monday, but we can be more abstract here.
21572 // Start of current week: based on weekstart/current date
21573 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21574 // Thursday of this week
21575 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21576 // First Thursday of year, year from thursday
21577 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21578 // Calendar week: ms between thursdays, div ms per day, div 7 days
21579 calWeek = (th - yth) / 864e5 / 7 + 1;
21581 fillMonths.cn.push({
21589 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21591 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21594 if (this.todayHighlight &&
21595 prevMonth.getUTCFullYear() == today.getFullYear() &&
21596 prevMonth.getUTCMonth() == today.getMonth() &&
21597 prevMonth.getUTCDate() == today.getDate()) {
21598 clsName += ' today';
21601 if (currentDate && prevMonth.valueOf() === currentDate) {
21602 clsName += ' active';
21605 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21606 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21607 clsName += ' disabled';
21610 fillMonths.cn.push({
21612 cls: 'day ' + clsName,
21613 html: prevMonth.getDate()
21616 prevMonth.setDate(prevMonth.getDate()+1);
21619 var currentYear = this.date && this.date.getUTCFullYear();
21620 var currentMonth = this.date && this.date.getUTCMonth();
21622 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21624 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21625 v.removeClass('active');
21627 if(currentYear === year && k === currentMonth){
21628 v.addClass('active');
21631 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21632 v.addClass('disabled');
21638 year = parseInt(year/10, 10) * 10;
21640 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21642 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21645 for (var i = -1; i < 11; i++) {
21646 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21648 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21656 showMode: function(dir)
21659 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21662 Roo.each(this.picker().select('>div',true).elements, function(v){
21663 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21666 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21671 if(this.isInline) {
21675 this.picker().removeClass(['bottom', 'top']);
21677 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21679 * place to the top of element!
21683 this.picker().addClass('top');
21684 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21689 this.picker().addClass('bottom');
21691 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21694 parseDate : function(value)
21696 if(!value || value instanceof Date){
21699 var v = Date.parseDate(value, this.format);
21700 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21701 v = Date.parseDate(value, 'Y-m-d');
21703 if(!v && this.altFormats){
21704 if(!this.altFormatsArray){
21705 this.altFormatsArray = this.altFormats.split("|");
21707 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21708 v = Date.parseDate(value, this.altFormatsArray[i]);
21714 formatDate : function(date, fmt)
21716 return (!date || !(date instanceof Date)) ?
21717 date : date.dateFormat(fmt || this.format);
21720 onFocus : function()
21722 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21726 onBlur : function()
21728 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21730 var d = this.inputEl().getValue();
21737 showPopup : function()
21739 this.picker().show();
21743 this.fireEvent('showpopup', this, this.date);
21746 hidePopup : function()
21748 if(this.isInline) {
21751 this.picker().hide();
21752 this.viewMode = this.startViewMode;
21755 this.fireEvent('hidepopup', this, this.date);
21759 onMousedown: function(e)
21761 e.stopPropagation();
21762 e.preventDefault();
21767 Roo.bootstrap.DateField.superclass.keyup.call(this);
21771 setValue: function(v)
21773 if(this.fireEvent('beforeselect', this, v) !== false){
21774 var d = new Date(this.parseDate(v) ).clearTime();
21776 if(isNaN(d.getTime())){
21777 this.date = this.viewDate = '';
21778 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21782 v = this.formatDate(d);
21784 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21786 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21790 this.fireEvent('select', this, this.date);
21794 getValue: function()
21796 return this.formatDate(this.date);
21799 fireKey: function(e)
21801 if (!this.picker().isVisible()){
21802 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21808 var dateChanged = false,
21810 newDate, newViewDate;
21815 e.preventDefault();
21819 if (!this.keyboardNavigation) {
21822 dir = e.keyCode == 37 ? -1 : 1;
21825 newDate = this.moveYear(this.date, dir);
21826 newViewDate = this.moveYear(this.viewDate, dir);
21827 } else if (e.shiftKey){
21828 newDate = this.moveMonth(this.date, dir);
21829 newViewDate = this.moveMonth(this.viewDate, dir);
21831 newDate = new Date(this.date);
21832 newDate.setUTCDate(this.date.getUTCDate() + dir);
21833 newViewDate = new Date(this.viewDate);
21834 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21836 if (this.dateWithinRange(newDate)){
21837 this.date = newDate;
21838 this.viewDate = newViewDate;
21839 this.setValue(this.formatDate(this.date));
21841 e.preventDefault();
21842 dateChanged = true;
21847 if (!this.keyboardNavigation) {
21850 dir = e.keyCode == 38 ? -1 : 1;
21852 newDate = this.moveYear(this.date, dir);
21853 newViewDate = this.moveYear(this.viewDate, dir);
21854 } else if (e.shiftKey){
21855 newDate = this.moveMonth(this.date, dir);
21856 newViewDate = this.moveMonth(this.viewDate, dir);
21858 newDate = new Date(this.date);
21859 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21860 newViewDate = new Date(this.viewDate);
21861 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21863 if (this.dateWithinRange(newDate)){
21864 this.date = newDate;
21865 this.viewDate = newViewDate;
21866 this.setValue(this.formatDate(this.date));
21868 e.preventDefault();
21869 dateChanged = true;
21873 this.setValue(this.formatDate(this.date));
21875 e.preventDefault();
21878 this.setValue(this.formatDate(this.date));
21892 onClick: function(e)
21894 e.stopPropagation();
21895 e.preventDefault();
21897 var target = e.getTarget();
21899 if(target.nodeName.toLowerCase() === 'i'){
21900 target = Roo.get(target).dom.parentNode;
21903 var nodeName = target.nodeName;
21904 var className = target.className;
21905 var html = target.innerHTML;
21906 //Roo.log(nodeName);
21908 switch(nodeName.toLowerCase()) {
21910 switch(className) {
21916 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21917 switch(this.viewMode){
21919 this.viewDate = this.moveMonth(this.viewDate, dir);
21923 this.viewDate = this.moveYear(this.viewDate, dir);
21929 var date = new Date();
21930 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21932 this.setValue(this.formatDate(this.date));
21939 if (className.indexOf('disabled') < 0) {
21940 this.viewDate.setUTCDate(1);
21941 if (className.indexOf('month') > -1) {
21942 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21944 var year = parseInt(html, 10) || 0;
21945 this.viewDate.setUTCFullYear(year);
21949 if(this.singleMode){
21950 this.setValue(this.formatDate(this.viewDate));
21961 //Roo.log(className);
21962 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21963 var day = parseInt(html, 10) || 1;
21964 var year = (this.viewDate || new Date()).getUTCFullYear(),
21965 month = (this.viewDate || new Date()).getUTCMonth();
21967 if (className.indexOf('old') > -1) {
21974 } else if (className.indexOf('new') > -1) {
21982 //Roo.log([year,month,day]);
21983 this.date = this.UTCDate(year, month, day,0,0,0,0);
21984 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21986 //Roo.log(this.formatDate(this.date));
21987 this.setValue(this.formatDate(this.date));
21994 setStartDate: function(startDate)
21996 this.startDate = startDate || -Infinity;
21997 if (this.startDate !== -Infinity) {
21998 this.startDate = this.parseDate(this.startDate);
22001 this.updateNavArrows();
22004 setEndDate: function(endDate)
22006 this.endDate = endDate || Infinity;
22007 if (this.endDate !== Infinity) {
22008 this.endDate = this.parseDate(this.endDate);
22011 this.updateNavArrows();
22014 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22016 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22017 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22018 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22020 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22021 return parseInt(d, 10);
22024 this.updateNavArrows();
22027 updateNavArrows: function()
22029 if(this.singleMode){
22033 var d = new Date(this.viewDate),
22034 year = d.getUTCFullYear(),
22035 month = d.getUTCMonth();
22037 Roo.each(this.picker().select('.prev', true).elements, function(v){
22039 switch (this.viewMode) {
22042 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22048 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22055 Roo.each(this.picker().select('.next', true).elements, function(v){
22057 switch (this.viewMode) {
22060 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22066 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22074 moveMonth: function(date, dir)
22079 var new_date = new Date(date.valueOf()),
22080 day = new_date.getUTCDate(),
22081 month = new_date.getUTCMonth(),
22082 mag = Math.abs(dir),
22084 dir = dir > 0 ? 1 : -1;
22087 // If going back one month, make sure month is not current month
22088 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22090 return new_date.getUTCMonth() == month;
22092 // If going forward one month, make sure month is as expected
22093 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22095 return new_date.getUTCMonth() != new_month;
22097 new_month = month + dir;
22098 new_date.setUTCMonth(new_month);
22099 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22100 if (new_month < 0 || new_month > 11) {
22101 new_month = (new_month + 12) % 12;
22104 // For magnitudes >1, move one month at a time...
22105 for (var i=0; i<mag; i++) {
22106 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22107 new_date = this.moveMonth(new_date, dir);
22109 // ...then reset the day, keeping it in the new month
22110 new_month = new_date.getUTCMonth();
22111 new_date.setUTCDate(day);
22113 return new_month != new_date.getUTCMonth();
22116 // Common date-resetting loop -- if date is beyond end of month, make it
22119 new_date.setUTCDate(--day);
22120 new_date.setUTCMonth(new_month);
22125 moveYear: function(date, dir)
22127 return this.moveMonth(date, dir*12);
22130 dateWithinRange: function(date)
22132 return date >= this.startDate && date <= this.endDate;
22138 this.picker().remove();
22141 validateValue : function(value)
22143 if(this.getVisibilityEl().hasClass('hidden')){
22147 if(value.length < 1) {
22148 if(this.allowBlank){
22154 if(value.length < this.minLength){
22157 if(value.length > this.maxLength){
22161 var vt = Roo.form.VTypes;
22162 if(!vt[this.vtype](value, this)){
22166 if(typeof this.validator == "function"){
22167 var msg = this.validator(value);
22173 if(this.regex && !this.regex.test(value)){
22177 if(typeof(this.parseDate(value)) == 'undefined'){
22181 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22185 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22195 this.date = this.viewDate = '';
22197 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22202 Roo.apply(Roo.bootstrap.DateField, {
22213 html: '<i class="fa fa-arrow-left"/>'
22223 html: '<i class="fa fa-arrow-right"/>'
22265 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22266 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22267 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22268 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22269 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22282 navFnc: 'FullYear',
22287 navFnc: 'FullYear',
22292 Roo.apply(Roo.bootstrap.DateField, {
22296 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22300 cls: 'datepicker-days',
22304 cls: 'table-condensed',
22306 Roo.bootstrap.DateField.head,
22310 Roo.bootstrap.DateField.footer
22317 cls: 'datepicker-months',
22321 cls: 'table-condensed',
22323 Roo.bootstrap.DateField.head,
22324 Roo.bootstrap.DateField.content,
22325 Roo.bootstrap.DateField.footer
22332 cls: 'datepicker-years',
22336 cls: 'table-condensed',
22338 Roo.bootstrap.DateField.head,
22339 Roo.bootstrap.DateField.content,
22340 Roo.bootstrap.DateField.footer
22359 * @class Roo.bootstrap.TimeField
22360 * @extends Roo.bootstrap.Input
22361 * Bootstrap DateField class
22365 * Create a new TimeField
22366 * @param {Object} config The config object
22369 Roo.bootstrap.TimeField = function(config){
22370 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22374 * Fires when this field show.
22375 * @param {Roo.bootstrap.DateField} thisthis
22376 * @param {Mixed} date The date value
22381 * Fires when this field hide.
22382 * @param {Roo.bootstrap.DateField} this
22383 * @param {Mixed} date The date value
22388 * Fires when select a date.
22389 * @param {Roo.bootstrap.DateField} this
22390 * @param {Mixed} date The date value
22396 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22399 * @cfg {String} format
22400 * The default time format string which can be overriden for localization support. The format must be
22401 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22405 getAutoCreate : function()
22407 this.after = '<i class="fa far fa-clock"></i>';
22408 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22412 onRender: function(ct, position)
22415 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22417 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22419 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22421 this.pop = this.picker().select('>.datepicker-time',true).first();
22422 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22424 this.picker().on('mousedown', this.onMousedown, this);
22425 this.picker().on('click', this.onClick, this);
22427 this.picker().addClass('datepicker-dropdown');
22432 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22433 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22434 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22435 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22436 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22437 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22441 fireKey: function(e){
22442 if (!this.picker().isVisible()){
22443 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22449 e.preventDefault();
22457 this.onTogglePeriod();
22460 this.onIncrementMinutes();
22463 this.onDecrementMinutes();
22472 onClick: function(e) {
22473 e.stopPropagation();
22474 e.preventDefault();
22477 picker : function()
22479 return this.pickerEl;
22482 fillTime: function()
22484 var time = this.pop.select('tbody', true).first();
22486 time.dom.innerHTML = '';
22501 cls: 'hours-up fa fas fa-chevron-up'
22521 cls: 'minutes-up fa fas fa-chevron-up'
22542 cls: 'timepicker-hour',
22557 cls: 'timepicker-minute',
22572 cls: 'btn btn-primary period',
22594 cls: 'hours-down fa fas fa-chevron-down'
22614 cls: 'minutes-down fa fas fa-chevron-down'
22632 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22639 var hours = this.time.getHours();
22640 var minutes = this.time.getMinutes();
22653 hours = hours - 12;
22657 hours = '0' + hours;
22661 minutes = '0' + minutes;
22664 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22665 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22666 this.pop.select('button', true).first().dom.innerHTML = period;
22672 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22674 var cls = ['bottom'];
22676 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22683 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22687 //this.picker().setXY(20000,20000);
22688 this.picker().addClass(cls.join('-'));
22692 Roo.each(cls, function(c){
22697 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22698 //_this.picker().setTop(_this.inputEl().getHeight());
22702 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22704 //_this.picker().setTop(0 - _this.picker().getHeight());
22709 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22713 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22721 onFocus : function()
22723 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22727 onBlur : function()
22729 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22735 this.picker().show();
22740 this.fireEvent('show', this, this.date);
22745 this.picker().hide();
22748 this.fireEvent('hide', this, this.date);
22751 setTime : function()
22754 this.setValue(this.time.format(this.format));
22756 this.fireEvent('select', this, this.date);
22761 onMousedown: function(e){
22762 e.stopPropagation();
22763 e.preventDefault();
22766 onIncrementHours: function()
22768 Roo.log('onIncrementHours');
22769 this.time = this.time.add(Date.HOUR, 1);
22774 onDecrementHours: function()
22776 Roo.log('onDecrementHours');
22777 this.time = this.time.add(Date.HOUR, -1);
22781 onIncrementMinutes: function()
22783 Roo.log('onIncrementMinutes');
22784 this.time = this.time.add(Date.MINUTE, 1);
22788 onDecrementMinutes: function()
22790 Roo.log('onDecrementMinutes');
22791 this.time = this.time.add(Date.MINUTE, -1);
22795 onTogglePeriod: function()
22797 Roo.log('onTogglePeriod');
22798 this.time = this.time.add(Date.HOUR, 12);
22806 Roo.apply(Roo.bootstrap.TimeField, {
22810 cls: 'datepicker dropdown-menu',
22814 cls: 'datepicker-time',
22818 cls: 'table-condensed',
22847 cls: 'btn btn-info ok',
22875 * @class Roo.bootstrap.MonthField
22876 * @extends Roo.bootstrap.Input
22877 * Bootstrap MonthField class
22879 * @cfg {String} language default en
22882 * Create a new MonthField
22883 * @param {Object} config The config object
22886 Roo.bootstrap.MonthField = function(config){
22887 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22892 * Fires when this field show.
22893 * @param {Roo.bootstrap.MonthField} this
22894 * @param {Mixed} date The date value
22899 * Fires when this field hide.
22900 * @param {Roo.bootstrap.MonthField} this
22901 * @param {Mixed} date The date value
22906 * Fires when select a date.
22907 * @param {Roo.bootstrap.MonthField} this
22908 * @param {String} oldvalue The old value
22909 * @param {String} newvalue The new value
22915 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22917 onRender: function(ct, position)
22920 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22922 this.language = this.language || 'en';
22923 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22924 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22926 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22927 this.isInline = false;
22928 this.isInput = true;
22929 this.component = this.el.select('.add-on', true).first() || false;
22930 this.component = (this.component && this.component.length === 0) ? false : this.component;
22931 this.hasInput = this.component && this.inputEL().length;
22933 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22935 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22937 this.picker().on('mousedown', this.onMousedown, this);
22938 this.picker().on('click', this.onClick, this);
22940 this.picker().addClass('datepicker-dropdown');
22942 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22943 v.setStyle('width', '189px');
22950 if(this.isInline) {
22956 setValue: function(v, suppressEvent)
22958 var o = this.getValue();
22960 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22964 if(suppressEvent !== true){
22965 this.fireEvent('select', this, o, v);
22970 getValue: function()
22975 onClick: function(e)
22977 e.stopPropagation();
22978 e.preventDefault();
22980 var target = e.getTarget();
22982 if(target.nodeName.toLowerCase() === 'i'){
22983 target = Roo.get(target).dom.parentNode;
22986 var nodeName = target.nodeName;
22987 var className = target.className;
22988 var html = target.innerHTML;
22990 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22994 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22996 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23002 picker : function()
23004 return this.pickerEl;
23007 fillMonths: function()
23010 var months = this.picker().select('>.datepicker-months td', true).first();
23012 months.dom.innerHTML = '';
23018 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23021 months.createChild(month);
23030 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23031 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23034 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23035 e.removeClass('active');
23037 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23038 e.addClass('active');
23045 if(this.isInline) {
23049 this.picker().removeClass(['bottom', 'top']);
23051 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23053 * place to the top of element!
23057 this.picker().addClass('top');
23058 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23063 this.picker().addClass('bottom');
23065 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23068 onFocus : function()
23070 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23074 onBlur : function()
23076 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23078 var d = this.inputEl().getValue();
23087 this.picker().show();
23088 this.picker().select('>.datepicker-months', true).first().show();
23092 this.fireEvent('show', this, this.date);
23097 if(this.isInline) {
23100 this.picker().hide();
23101 this.fireEvent('hide', this, this.date);
23105 onMousedown: function(e)
23107 e.stopPropagation();
23108 e.preventDefault();
23113 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23117 fireKey: function(e)
23119 if (!this.picker().isVisible()){
23120 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23131 e.preventDefault();
23135 dir = e.keyCode == 37 ? -1 : 1;
23137 this.vIndex = this.vIndex + dir;
23139 if(this.vIndex < 0){
23143 if(this.vIndex > 11){
23147 if(isNaN(this.vIndex)){
23151 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23157 dir = e.keyCode == 38 ? -1 : 1;
23159 this.vIndex = this.vIndex + dir * 4;
23161 if(this.vIndex < 0){
23165 if(this.vIndex > 11){
23169 if(isNaN(this.vIndex)){
23173 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23178 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23179 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23183 e.preventDefault();
23186 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23187 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23203 this.picker().remove();
23208 Roo.apply(Roo.bootstrap.MonthField, {
23227 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23228 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23233 Roo.apply(Roo.bootstrap.MonthField, {
23237 cls: 'datepicker dropdown-menu roo-dynamic',
23241 cls: 'datepicker-months',
23245 cls: 'table-condensed',
23247 Roo.bootstrap.DateField.content
23267 * @class Roo.bootstrap.CheckBox
23268 * @extends Roo.bootstrap.Input
23269 * Bootstrap CheckBox class
23271 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23272 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23273 * @cfg {String} boxLabel The text that appears beside the checkbox
23274 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23275 * @cfg {Boolean} checked initnal the element
23276 * @cfg {Boolean} inline inline the element (default false)
23277 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23278 * @cfg {String} tooltip label tooltip
23281 * Create a new CheckBox
23282 * @param {Object} config The config object
23285 Roo.bootstrap.CheckBox = function(config){
23286 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23291 * Fires when the element is checked or unchecked.
23292 * @param {Roo.bootstrap.CheckBox} this This input
23293 * @param {Boolean} checked The new checked value
23298 * Fires when the element is click.
23299 * @param {Roo.bootstrap.CheckBox} this This input
23306 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23308 inputType: 'checkbox',
23317 // checkbox success does not make any sense really..
23322 getAutoCreate : function()
23324 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23330 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23333 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23339 type : this.inputType,
23340 value : this.inputValue,
23341 cls : 'roo-' + this.inputType, //'form-box',
23342 placeholder : this.placeholder || ''
23346 if(this.inputType != 'radio'){
23350 cls : 'roo-hidden-value',
23351 value : this.checked ? this.inputValue : this.valueOff
23356 if (this.weight) { // Validity check?
23357 cfg.cls += " " + this.inputType + "-" + this.weight;
23360 if (this.disabled) {
23361 input.disabled=true;
23365 input.checked = this.checked;
23370 input.name = this.name;
23372 if(this.inputType != 'radio'){
23373 hidden.name = this.name;
23374 input.name = '_hidden_' + this.name;
23379 input.cls += ' input-' + this.size;
23384 ['xs','sm','md','lg'].map(function(size){
23385 if (settings[size]) {
23386 cfg.cls += ' col-' + size + '-' + settings[size];
23390 var inputblock = input;
23392 if (this.before || this.after) {
23395 cls : 'input-group',
23400 inputblock.cn.push({
23402 cls : 'input-group-addon',
23407 inputblock.cn.push(input);
23409 if(this.inputType != 'radio'){
23410 inputblock.cn.push(hidden);
23414 inputblock.cn.push({
23416 cls : 'input-group-addon',
23422 var boxLabelCfg = false;
23428 //'for': id, // box label is handled by onclick - so no for...
23430 html: this.boxLabel
23433 boxLabelCfg.tooltip = this.tooltip;
23439 if (align ==='left' && this.fieldLabel.length) {
23440 // Roo.log("left and has label");
23445 cls : 'control-label',
23446 html : this.fieldLabel
23457 cfg.cn[1].cn.push(boxLabelCfg);
23460 if(this.labelWidth > 12){
23461 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23464 if(this.labelWidth < 13 && this.labelmd == 0){
23465 this.labelmd = this.labelWidth;
23468 if(this.labellg > 0){
23469 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23470 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23473 if(this.labelmd > 0){
23474 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23475 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23478 if(this.labelsm > 0){
23479 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23480 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23483 if(this.labelxs > 0){
23484 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23485 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23488 } else if ( this.fieldLabel.length) {
23489 // Roo.log(" label");
23493 tag: this.boxLabel ? 'span' : 'label',
23495 cls: 'control-label box-input-label',
23496 //cls : 'input-group-addon',
23497 html : this.fieldLabel
23504 cfg.cn.push(boxLabelCfg);
23509 // Roo.log(" no label && no align");
23510 cfg.cn = [ inputblock ] ;
23512 cfg.cn.push(boxLabelCfg);
23520 if(this.inputType != 'radio'){
23521 cfg.cn.push(hidden);
23529 * return the real input element.
23531 inputEl: function ()
23533 return this.el.select('input.roo-' + this.inputType,true).first();
23535 hiddenEl: function ()
23537 return this.el.select('input.roo-hidden-value',true).first();
23540 labelEl: function()
23542 return this.el.select('label.control-label',true).first();
23544 /* depricated... */
23548 return this.labelEl();
23551 boxLabelEl: function()
23553 return this.el.select('label.box-label',true).first();
23556 initEvents : function()
23558 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23560 this.inputEl().on('click', this.onClick, this);
23562 if (this.boxLabel) {
23563 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23566 this.startValue = this.getValue();
23569 Roo.bootstrap.CheckBox.register(this);
23573 onClick : function(e)
23575 if(this.fireEvent('click', this, e) !== false){
23576 this.setChecked(!this.checked);
23581 setChecked : function(state,suppressEvent)
23583 this.startValue = this.getValue();
23585 if(this.inputType == 'radio'){
23587 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23588 e.dom.checked = false;
23591 this.inputEl().dom.checked = true;
23593 this.inputEl().dom.value = this.inputValue;
23595 if(suppressEvent !== true){
23596 this.fireEvent('check', this, true);
23604 this.checked = state;
23606 this.inputEl().dom.checked = state;
23609 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23611 if(suppressEvent !== true){
23612 this.fireEvent('check', this, state);
23618 getValue : function()
23620 if(this.inputType == 'radio'){
23621 return this.getGroupValue();
23624 return this.hiddenEl().dom.value;
23628 getGroupValue : function()
23630 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23634 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23637 setValue : function(v,suppressEvent)
23639 if(this.inputType == 'radio'){
23640 this.setGroupValue(v, suppressEvent);
23644 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23649 setGroupValue : function(v, suppressEvent)
23651 this.startValue = this.getValue();
23653 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23654 e.dom.checked = false;
23656 if(e.dom.value == v){
23657 e.dom.checked = true;
23661 if(suppressEvent !== true){
23662 this.fireEvent('check', this, true);
23670 validate : function()
23672 if(this.getVisibilityEl().hasClass('hidden')){
23678 (this.inputType == 'radio' && this.validateRadio()) ||
23679 (this.inputType == 'checkbox' && this.validateCheckbox())
23685 this.markInvalid();
23689 validateRadio : function()
23691 if(this.getVisibilityEl().hasClass('hidden')){
23695 if(this.allowBlank){
23701 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23702 if(!e.dom.checked){
23714 validateCheckbox : function()
23717 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23718 //return (this.getValue() == this.inputValue) ? true : false;
23721 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23729 for(var i in group){
23730 if(group[i].el.isVisible(true)){
23738 for(var i in group){
23743 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23750 * Mark this field as valid
23752 markValid : function()
23756 this.fireEvent('valid', this);
23758 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23761 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23768 if(this.inputType == 'radio'){
23769 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23770 var fg = e.findParent('.form-group', false, true);
23771 if (Roo.bootstrap.version == 3) {
23772 fg.removeClass([_this.invalidClass, _this.validClass]);
23773 fg.addClass(_this.validClass);
23775 fg.removeClass(['is-valid', 'is-invalid']);
23776 fg.addClass('is-valid');
23784 var fg = this.el.findParent('.form-group', false, true);
23785 if (Roo.bootstrap.version == 3) {
23786 fg.removeClass([this.invalidClass, this.validClass]);
23787 fg.addClass(this.validClass);
23789 fg.removeClass(['is-valid', 'is-invalid']);
23790 fg.addClass('is-valid');
23795 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23801 for(var i in group){
23802 var fg = group[i].el.findParent('.form-group', false, true);
23803 if (Roo.bootstrap.version == 3) {
23804 fg.removeClass([this.invalidClass, this.validClass]);
23805 fg.addClass(this.validClass);
23807 fg.removeClass(['is-valid', 'is-invalid']);
23808 fg.addClass('is-valid');
23814 * Mark this field as invalid
23815 * @param {String} msg The validation message
23817 markInvalid : function(msg)
23819 if(this.allowBlank){
23825 this.fireEvent('invalid', this, msg);
23827 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23830 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23834 label.markInvalid();
23837 if(this.inputType == 'radio'){
23839 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23840 var fg = e.findParent('.form-group', false, true);
23841 if (Roo.bootstrap.version == 3) {
23842 fg.removeClass([_this.invalidClass, _this.validClass]);
23843 fg.addClass(_this.invalidClass);
23845 fg.removeClass(['is-invalid', 'is-valid']);
23846 fg.addClass('is-invalid');
23854 var fg = this.el.findParent('.form-group', false, true);
23855 if (Roo.bootstrap.version == 3) {
23856 fg.removeClass([_this.invalidClass, _this.validClass]);
23857 fg.addClass(_this.invalidClass);
23859 fg.removeClass(['is-invalid', 'is-valid']);
23860 fg.addClass('is-invalid');
23865 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23871 for(var i in group){
23872 var fg = group[i].el.findParent('.form-group', false, true);
23873 if (Roo.bootstrap.version == 3) {
23874 fg.removeClass([_this.invalidClass, _this.validClass]);
23875 fg.addClass(_this.invalidClass);
23877 fg.removeClass(['is-invalid', 'is-valid']);
23878 fg.addClass('is-invalid');
23884 clearInvalid : function()
23886 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23888 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23890 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23892 if (label && label.iconEl) {
23893 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23894 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23898 disable : function()
23900 if(this.inputType != 'radio'){
23901 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23908 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23909 _this.getActionEl().addClass(this.disabledClass);
23910 e.dom.disabled = true;
23914 this.disabled = true;
23915 this.fireEvent("disable", this);
23919 enable : function()
23921 if(this.inputType != 'radio'){
23922 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23929 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23930 _this.getActionEl().removeClass(this.disabledClass);
23931 e.dom.disabled = false;
23935 this.disabled = false;
23936 this.fireEvent("enable", this);
23940 setBoxLabel : function(v)
23945 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23951 Roo.apply(Roo.bootstrap.CheckBox, {
23956 * register a CheckBox Group
23957 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23959 register : function(checkbox)
23961 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23962 this.groups[checkbox.groupId] = {};
23965 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23969 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23973 * fetch a CheckBox Group based on the group ID
23974 * @param {string} the group ID
23975 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23977 get: function(groupId) {
23978 if (typeof(this.groups[groupId]) == 'undefined') {
23982 return this.groups[groupId] ;
23995 * @class Roo.bootstrap.Radio
23996 * @extends Roo.bootstrap.Component
23997 * Bootstrap Radio class
23998 * @cfg {String} boxLabel - the label associated
23999 * @cfg {String} value - the value of radio
24002 * Create a new Radio
24003 * @param {Object} config The config object
24005 Roo.bootstrap.Radio = function(config){
24006 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24010 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24016 getAutoCreate : function()
24020 cls : 'form-group radio',
24025 html : this.boxLabel
24033 initEvents : function()
24035 this.parent().register(this);
24037 this.el.on('click', this.onClick, this);
24041 onClick : function(e)
24043 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24044 this.setChecked(true);
24048 setChecked : function(state, suppressEvent)
24050 this.parent().setValue(this.value, suppressEvent);
24054 setBoxLabel : function(v)
24059 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24074 * @class Roo.bootstrap.SecurePass
24075 * @extends Roo.bootstrap.Input
24076 * Bootstrap SecurePass class
24080 * Create a new SecurePass
24081 * @param {Object} config The config object
24084 Roo.bootstrap.SecurePass = function (config) {
24085 // these go here, so the translation tool can replace them..
24087 PwdEmpty: "Please type a password, and then retype it to confirm.",
24088 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24089 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24090 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24091 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24092 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24093 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24094 TooWeak: "Your password is Too Weak."
24096 this.meterLabel = "Password strength:";
24097 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24098 this.meterClass = [
24099 "roo-password-meter-tooweak",
24100 "roo-password-meter-weak",
24101 "roo-password-meter-medium",
24102 "roo-password-meter-strong",
24103 "roo-password-meter-grey"
24108 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24111 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24113 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24115 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24116 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24117 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24118 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24119 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24120 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24121 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24131 * @cfg {String/Object} Label for the strength meter (defaults to
24132 * 'Password strength:')
24137 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24138 * ['Weak', 'Medium', 'Strong'])
24141 pwdStrengths: false,
24154 initEvents: function ()
24156 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24158 if (this.el.is('input[type=password]') && Roo.isSafari) {
24159 this.el.on('keydown', this.SafariOnKeyDown, this);
24162 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24165 onRender: function (ct, position)
24167 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24168 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24169 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24171 this.trigger.createChild({
24176 cls: 'roo-password-meter-grey col-xs-12',
24179 //width: this.meterWidth + 'px'
24183 cls: 'roo-password-meter-text'
24189 if (this.hideTrigger) {
24190 this.trigger.setDisplayed(false);
24192 this.setSize(this.width || '', this.height || '');
24195 onDestroy: function ()
24197 if (this.trigger) {
24198 this.trigger.removeAllListeners();
24199 this.trigger.remove();
24202 this.wrap.remove();
24204 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24207 checkStrength: function ()
24209 var pwd = this.inputEl().getValue();
24210 if (pwd == this._lastPwd) {
24215 if (this.ClientSideStrongPassword(pwd)) {
24217 } else if (this.ClientSideMediumPassword(pwd)) {
24219 } else if (this.ClientSideWeakPassword(pwd)) {
24225 Roo.log('strength1: ' + strength);
24227 //var pm = this.trigger.child('div/div/div').dom;
24228 var pm = this.trigger.child('div/div');
24229 pm.removeClass(this.meterClass);
24230 pm.addClass(this.meterClass[strength]);
24233 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24235 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24237 this._lastPwd = pwd;
24241 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24243 this._lastPwd = '';
24245 var pm = this.trigger.child('div/div');
24246 pm.removeClass(this.meterClass);
24247 pm.addClass('roo-password-meter-grey');
24250 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24253 this.inputEl().dom.type='password';
24256 validateValue: function (value)
24258 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24261 if (value.length == 0) {
24262 if (this.allowBlank) {
24263 this.clearInvalid();
24267 this.markInvalid(this.errors.PwdEmpty);
24268 this.errorMsg = this.errors.PwdEmpty;
24276 if (!value.match(/[\x21-\x7e]+/)) {
24277 this.markInvalid(this.errors.PwdBadChar);
24278 this.errorMsg = this.errors.PwdBadChar;
24281 if (value.length < 6) {
24282 this.markInvalid(this.errors.PwdShort);
24283 this.errorMsg = this.errors.PwdShort;
24286 if (value.length > 16) {
24287 this.markInvalid(this.errors.PwdLong);
24288 this.errorMsg = this.errors.PwdLong;
24292 if (this.ClientSideStrongPassword(value)) {
24294 } else if (this.ClientSideMediumPassword(value)) {
24296 } else if (this.ClientSideWeakPassword(value)) {
24303 if (strength < 2) {
24304 //this.markInvalid(this.errors.TooWeak);
24305 this.errorMsg = this.errors.TooWeak;
24310 console.log('strength2: ' + strength);
24312 //var pm = this.trigger.child('div/div/div').dom;
24314 var pm = this.trigger.child('div/div');
24315 pm.removeClass(this.meterClass);
24316 pm.addClass(this.meterClass[strength]);
24318 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24320 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24322 this.errorMsg = '';
24326 CharacterSetChecks: function (type)
24329 this.fResult = false;
24332 isctype: function (character, type)
24335 case this.kCapitalLetter:
24336 if (character >= 'A' && character <= 'Z') {
24341 case this.kSmallLetter:
24342 if (character >= 'a' && character <= 'z') {
24348 if (character >= '0' && character <= '9') {
24353 case this.kPunctuation:
24354 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24365 IsLongEnough: function (pwd, size)
24367 return !(pwd == null || isNaN(size) || pwd.length < size);
24370 SpansEnoughCharacterSets: function (word, nb)
24372 if (!this.IsLongEnough(word, nb))
24377 var characterSetChecks = new Array(
24378 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24379 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24382 for (var index = 0; index < word.length; ++index) {
24383 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24384 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24385 characterSetChecks[nCharSet].fResult = true;
24392 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24393 if (characterSetChecks[nCharSet].fResult) {
24398 if (nCharSets < nb) {
24404 ClientSideStrongPassword: function (pwd)
24406 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24409 ClientSideMediumPassword: function (pwd)
24411 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24414 ClientSideWeakPassword: function (pwd)
24416 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24419 })//<script type="text/javascript">
24422 * Based Ext JS Library 1.1.1
24423 * Copyright(c) 2006-2007, Ext JS, LLC.
24429 * @class Roo.HtmlEditorCore
24430 * @extends Roo.Component
24431 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24433 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24436 Roo.HtmlEditorCore = function(config){
24439 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24444 * @event initialize
24445 * Fires when the editor is fully initialized (including the iframe)
24446 * @param {Roo.HtmlEditorCore} this
24451 * Fires when the editor is first receives the focus. Any insertion must wait
24452 * until after this event.
24453 * @param {Roo.HtmlEditorCore} this
24457 * @event beforesync
24458 * Fires before the textarea is updated with content from the editor iframe. Return false
24459 * to cancel the sync.
24460 * @param {Roo.HtmlEditorCore} this
24461 * @param {String} html
24465 * @event beforepush
24466 * Fires before the iframe editor is updated with content from the textarea. Return false
24467 * to cancel the push.
24468 * @param {Roo.HtmlEditorCore} this
24469 * @param {String} html
24474 * Fires when the textarea is updated with content from the editor iframe.
24475 * @param {Roo.HtmlEditorCore} this
24476 * @param {String} html
24481 * Fires when the iframe editor is updated with content from the textarea.
24482 * @param {Roo.HtmlEditorCore} this
24483 * @param {String} html
24488 * @event editorevent
24489 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24490 * @param {Roo.HtmlEditorCore} this
24496 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24498 // defaults : white / black...
24499 this.applyBlacklists();
24506 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24510 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24516 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24521 * @cfg {Number} height (in pixels)
24525 * @cfg {Number} width (in pixels)
24530 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24533 stylesheets: false,
24538 // private properties
24539 validationEvent : false,
24541 initialized : false,
24543 sourceEditMode : false,
24544 onFocus : Roo.emptyFn,
24546 hideMode:'offsets',
24550 // blacklist + whitelisted elements..
24557 * Protected method that will not generally be called directly. It
24558 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24559 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24561 getDocMarkup : function(){
24565 // inherit styels from page...??
24566 if (this.stylesheets === false) {
24568 Roo.get(document.head).select('style').each(function(node) {
24569 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24572 Roo.get(document.head).select('link').each(function(node) {
24573 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24576 } else if (!this.stylesheets.length) {
24578 st = '<style type="text/css">' +
24579 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24582 for (var i in this.stylesheets) {
24583 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24588 st += '<style type="text/css">' +
24589 'IMG { cursor: pointer } ' +
24592 var cls = 'roo-htmleditor-body';
24594 if(this.bodyCls.length){
24595 cls += ' ' + this.bodyCls;
24598 return '<html><head>' + st +
24599 //<style type="text/css">' +
24600 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24602 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24606 onRender : function(ct, position)
24609 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24610 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24613 this.el.dom.style.border = '0 none';
24614 this.el.dom.setAttribute('tabIndex', -1);
24615 this.el.addClass('x-hidden hide');
24619 if(Roo.isIE){ // fix IE 1px bogus margin
24620 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24624 this.frameId = Roo.id();
24628 var iframe = this.owner.wrap.createChild({
24630 cls: 'form-control', // bootstrap..
24632 name: this.frameId,
24633 frameBorder : 'no',
24634 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24639 this.iframe = iframe.dom;
24641 this.assignDocWin();
24643 this.doc.designMode = 'on';
24646 this.doc.write(this.getDocMarkup());
24650 var task = { // must defer to wait for browser to be ready
24652 //console.log("run task?" + this.doc.readyState);
24653 this.assignDocWin();
24654 if(this.doc.body || this.doc.readyState == 'complete'){
24656 this.doc.designMode="on";
24660 Roo.TaskMgr.stop(task);
24661 this.initEditor.defer(10, this);
24668 Roo.TaskMgr.start(task);
24673 onResize : function(w, h)
24675 Roo.log('resize: ' +w + ',' + h );
24676 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24680 if(typeof w == 'number'){
24682 this.iframe.style.width = w + 'px';
24684 if(typeof h == 'number'){
24686 this.iframe.style.height = h + 'px';
24688 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24695 * Toggles the editor between standard and source edit mode.
24696 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24698 toggleSourceEdit : function(sourceEditMode){
24700 this.sourceEditMode = sourceEditMode === true;
24702 if(this.sourceEditMode){
24704 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24707 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24708 //this.iframe.className = '';
24711 //this.setSize(this.owner.wrap.getSize());
24712 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24719 * Protected method that will not generally be called directly. If you need/want
24720 * custom HTML cleanup, this is the method you should override.
24721 * @param {String} html The HTML to be cleaned
24722 * return {String} The cleaned HTML
24724 cleanHtml : function(html){
24725 html = String(html);
24726 if(html.length > 5){
24727 if(Roo.isSafari){ // strip safari nonsense
24728 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24731 if(html == ' '){
24738 * HTML Editor -> Textarea
24739 * Protected method that will not generally be called directly. Syncs the contents
24740 * of the editor iframe with the textarea.
24742 syncValue : function(){
24743 if(this.initialized){
24744 var bd = (this.doc.body || this.doc.documentElement);
24745 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24746 var html = bd.innerHTML;
24748 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24749 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24751 html = '<div style="'+m[0]+'">' + html + '</div>';
24754 html = this.cleanHtml(html);
24755 // fix up the special chars.. normaly like back quotes in word...
24756 // however we do not want to do this with chinese..
24757 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24759 var cc = match.charCodeAt();
24761 // Get the character value, handling surrogate pairs
24762 if (match.length == 2) {
24763 // It's a surrogate pair, calculate the Unicode code point
24764 var high = match.charCodeAt(0) - 0xD800;
24765 var low = match.charCodeAt(1) - 0xDC00;
24766 cc = (high * 0x400) + low + 0x10000;
24768 (cc >= 0x4E00 && cc < 0xA000 ) ||
24769 (cc >= 0x3400 && cc < 0x4E00 ) ||
24770 (cc >= 0xf900 && cc < 0xfb00 )
24775 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24776 return "&#" + cc + ";";
24783 if(this.owner.fireEvent('beforesync', this, html) !== false){
24784 this.el.dom.value = html;
24785 this.owner.fireEvent('sync', this, html);
24791 * Protected method that will not generally be called directly. Pushes the value of the textarea
24792 * into the iframe editor.
24794 pushValue : function(){
24795 if(this.initialized){
24796 var v = this.el.dom.value.trim();
24798 // if(v.length < 1){
24802 if(this.owner.fireEvent('beforepush', this, v) !== false){
24803 var d = (this.doc.body || this.doc.documentElement);
24805 this.cleanUpPaste();
24806 this.el.dom.value = d.innerHTML;
24807 this.owner.fireEvent('push', this, v);
24813 deferFocus : function(){
24814 this.focus.defer(10, this);
24818 focus : function(){
24819 if(this.win && !this.sourceEditMode){
24826 assignDocWin: function()
24828 var iframe = this.iframe;
24831 this.doc = iframe.contentWindow.document;
24832 this.win = iframe.contentWindow;
24834 // if (!Roo.get(this.frameId)) {
24837 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24838 // this.win = Roo.get(this.frameId).dom.contentWindow;
24840 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24844 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24845 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24850 initEditor : function(){
24851 //console.log("INIT EDITOR");
24852 this.assignDocWin();
24856 this.doc.designMode="on";
24858 this.doc.write(this.getDocMarkup());
24861 var dbody = (this.doc.body || this.doc.documentElement);
24862 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24863 // this copies styles from the containing element into thsi one..
24864 // not sure why we need all of this..
24865 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24867 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24868 //ss['background-attachment'] = 'fixed'; // w3c
24869 dbody.bgProperties = 'fixed'; // ie
24870 //Roo.DomHelper.applyStyles(dbody, ss);
24871 Roo.EventManager.on(this.doc, {
24872 //'mousedown': this.onEditorEvent,
24873 'mouseup': this.onEditorEvent,
24874 'dblclick': this.onEditorEvent,
24875 'click': this.onEditorEvent,
24876 'keyup': this.onEditorEvent,
24881 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24883 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24884 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24886 this.initialized = true;
24888 this.owner.fireEvent('initialize', this);
24893 onDestroy : function(){
24899 //for (var i =0; i < this.toolbars.length;i++) {
24900 // // fixme - ask toolbars for heights?
24901 // this.toolbars[i].onDestroy();
24904 //this.wrap.dom.innerHTML = '';
24905 //this.wrap.remove();
24910 onFirstFocus : function(){
24912 this.assignDocWin();
24915 this.activated = true;
24918 if(Roo.isGecko){ // prevent silly gecko errors
24920 var s = this.win.getSelection();
24921 if(!s.focusNode || s.focusNode.nodeType != 3){
24922 var r = s.getRangeAt(0);
24923 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24928 this.execCmd('useCSS', true);
24929 this.execCmd('styleWithCSS', false);
24932 this.owner.fireEvent('activate', this);
24936 adjustFont: function(btn){
24937 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24938 //if(Roo.isSafari){ // safari
24941 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24942 if(Roo.isSafari){ // safari
24943 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24944 v = (v < 10) ? 10 : v;
24945 v = (v > 48) ? 48 : v;
24946 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24951 v = Math.max(1, v+adjust);
24953 this.execCmd('FontSize', v );
24956 onEditorEvent : function(e)
24958 this.owner.fireEvent('editorevent', this, e);
24959 // this.updateToolbar();
24960 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24963 insertTag : function(tg)
24965 // could be a bit smarter... -> wrap the current selected tRoo..
24966 if (tg.toLowerCase() == 'span' ||
24967 tg.toLowerCase() == 'code' ||
24968 tg.toLowerCase() == 'sup' ||
24969 tg.toLowerCase() == 'sub'
24972 range = this.createRange(this.getSelection());
24973 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24974 wrappingNode.appendChild(range.extractContents());
24975 range.insertNode(wrappingNode);
24982 this.execCmd("formatblock", tg);
24986 insertText : function(txt)
24990 var range = this.createRange();
24991 range.deleteContents();
24992 //alert(Sender.getAttribute('label'));
24994 range.insertNode(this.doc.createTextNode(txt));
25000 * Executes a Midas editor command on the editor document and performs necessary focus and
25001 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25002 * @param {String} cmd The Midas command
25003 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25005 relayCmd : function(cmd, value){
25007 this.execCmd(cmd, value);
25008 this.owner.fireEvent('editorevent', this);
25009 //this.updateToolbar();
25010 this.owner.deferFocus();
25014 * Executes a Midas editor command directly on the editor document.
25015 * For visual commands, you should use {@link #relayCmd} instead.
25016 * <b>This should only be called after the editor is initialized.</b>
25017 * @param {String} cmd The Midas command
25018 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25020 execCmd : function(cmd, value){
25021 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25028 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25030 * @param {String} text | dom node..
25032 insertAtCursor : function(text)
25035 if(!this.activated){
25041 var r = this.doc.selection.createRange();
25052 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25056 // from jquery ui (MIT licenced)
25058 var win = this.win;
25060 if (win.getSelection && win.getSelection().getRangeAt) {
25061 range = win.getSelection().getRangeAt(0);
25062 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25063 range.insertNode(node);
25064 } else if (win.document.selection && win.document.selection.createRange) {
25065 // no firefox support
25066 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25067 win.document.selection.createRange().pasteHTML(txt);
25069 // no firefox support
25070 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25071 this.execCmd('InsertHTML', txt);
25080 mozKeyPress : function(e){
25082 var c = e.getCharCode(), cmd;
25085 c = String.fromCharCode(c).toLowerCase();
25099 this.cleanUpPaste.defer(100, this);
25107 e.preventDefault();
25115 fixKeys : function(){ // load time branching for fastest keydown performance
25117 return function(e){
25118 var k = e.getKey(), r;
25121 r = this.doc.selection.createRange();
25124 r.pasteHTML('    ');
25131 r = this.doc.selection.createRange();
25133 var target = r.parentElement();
25134 if(!target || target.tagName.toLowerCase() != 'li'){
25136 r.pasteHTML('<br />');
25142 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25143 this.cleanUpPaste.defer(100, this);
25149 }else if(Roo.isOpera){
25150 return function(e){
25151 var k = e.getKey();
25155 this.execCmd('InsertHTML','    ');
25158 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25159 this.cleanUpPaste.defer(100, this);
25164 }else if(Roo.isSafari){
25165 return function(e){
25166 var k = e.getKey();
25170 this.execCmd('InsertText','\t');
25174 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25175 this.cleanUpPaste.defer(100, this);
25183 getAllAncestors: function()
25185 var p = this.getSelectedNode();
25188 a.push(p); // push blank onto stack..
25189 p = this.getParentElement();
25193 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25197 a.push(this.doc.body);
25201 lastSelNode : false,
25204 getSelection : function()
25206 this.assignDocWin();
25207 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25210 getSelectedNode: function()
25212 // this may only work on Gecko!!!
25214 // should we cache this!!!!
25219 var range = this.createRange(this.getSelection()).cloneRange();
25222 var parent = range.parentElement();
25224 var testRange = range.duplicate();
25225 testRange.moveToElementText(parent);
25226 if (testRange.inRange(range)) {
25229 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25232 parent = parent.parentElement;
25237 // is ancestor a text element.
25238 var ac = range.commonAncestorContainer;
25239 if (ac.nodeType == 3) {
25240 ac = ac.parentNode;
25243 var ar = ac.childNodes;
25246 var other_nodes = [];
25247 var has_other_nodes = false;
25248 for (var i=0;i<ar.length;i++) {
25249 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25252 // fullly contained node.
25254 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25259 // probably selected..
25260 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25261 other_nodes.push(ar[i]);
25265 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25270 has_other_nodes = true;
25272 if (!nodes.length && other_nodes.length) {
25273 nodes= other_nodes;
25275 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25281 createRange: function(sel)
25283 // this has strange effects when using with
25284 // top toolbar - not sure if it's a great idea.
25285 //this.editor.contentWindow.focus();
25286 if (typeof sel != "undefined") {
25288 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25290 return this.doc.createRange();
25293 return this.doc.createRange();
25296 getParentElement: function()
25299 this.assignDocWin();
25300 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25302 var range = this.createRange(sel);
25305 var p = range.commonAncestorContainer;
25306 while (p.nodeType == 3) { // text node
25317 * Range intersection.. the hard stuff...
25321 * [ -- selected range --- ]
25325 * if end is before start or hits it. fail.
25326 * if start is after end or hits it fail.
25328 * if either hits (but other is outside. - then it's not
25334 // @see http://www.thismuchiknow.co.uk/?p=64.
25335 rangeIntersectsNode : function(range, node)
25337 var nodeRange = node.ownerDocument.createRange();
25339 nodeRange.selectNode(node);
25341 nodeRange.selectNodeContents(node);
25344 var rangeStartRange = range.cloneRange();
25345 rangeStartRange.collapse(true);
25347 var rangeEndRange = range.cloneRange();
25348 rangeEndRange.collapse(false);
25350 var nodeStartRange = nodeRange.cloneRange();
25351 nodeStartRange.collapse(true);
25353 var nodeEndRange = nodeRange.cloneRange();
25354 nodeEndRange.collapse(false);
25356 return rangeStartRange.compareBoundaryPoints(
25357 Range.START_TO_START, nodeEndRange) == -1 &&
25358 rangeEndRange.compareBoundaryPoints(
25359 Range.START_TO_START, nodeStartRange) == 1;
25363 rangeCompareNode : function(range, node)
25365 var nodeRange = node.ownerDocument.createRange();
25367 nodeRange.selectNode(node);
25369 nodeRange.selectNodeContents(node);
25373 range.collapse(true);
25375 nodeRange.collapse(true);
25377 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25378 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25380 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25382 var nodeIsBefore = ss == 1;
25383 var nodeIsAfter = ee == -1;
25385 if (nodeIsBefore && nodeIsAfter) {
25388 if (!nodeIsBefore && nodeIsAfter) {
25389 return 1; //right trailed.
25392 if (nodeIsBefore && !nodeIsAfter) {
25393 return 2; // left trailed.
25399 // private? - in a new class?
25400 cleanUpPaste : function()
25402 // cleans up the whole document..
25403 Roo.log('cleanuppaste');
25405 this.cleanUpChildren(this.doc.body);
25406 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25407 if (clean != this.doc.body.innerHTML) {
25408 this.doc.body.innerHTML = clean;
25413 cleanWordChars : function(input) {// change the chars to hex code
25414 var he = Roo.HtmlEditorCore;
25416 var output = input;
25417 Roo.each(he.swapCodes, function(sw) {
25418 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25420 output = output.replace(swapper, sw[1]);
25427 cleanUpChildren : function (n)
25429 if (!n.childNodes.length) {
25432 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25433 this.cleanUpChild(n.childNodes[i]);
25440 cleanUpChild : function (node)
25443 //console.log(node);
25444 if (node.nodeName == "#text") {
25445 // clean up silly Windows -- stuff?
25448 if (node.nodeName == "#comment") {
25449 node.parentNode.removeChild(node);
25450 // clean up silly Windows -- stuff?
25453 var lcname = node.tagName.toLowerCase();
25454 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25455 // whitelist of tags..
25457 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25459 node.parentNode.removeChild(node);
25464 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25466 // spans with no attributes - just remove them..
25467 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25468 remove_keep_children = true;
25471 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25472 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25474 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25475 // remove_keep_children = true;
25478 if (remove_keep_children) {
25479 this.cleanUpChildren(node);
25480 // inserts everything just before this node...
25481 while (node.childNodes.length) {
25482 var cn = node.childNodes[0];
25483 node.removeChild(cn);
25484 node.parentNode.insertBefore(cn, node);
25486 node.parentNode.removeChild(node);
25490 if (!node.attributes || !node.attributes.length) {
25495 this.cleanUpChildren(node);
25499 function cleanAttr(n,v)
25502 if (v.match(/^\./) || v.match(/^\//)) {
25505 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25508 if (v.match(/^#/)) {
25511 if (v.match(/^\{/)) { // allow template editing.
25514 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25515 node.removeAttribute(n);
25519 var cwhite = this.cwhite;
25520 var cblack = this.cblack;
25522 function cleanStyle(n,v)
25524 if (v.match(/expression/)) { //XSS?? should we even bother..
25525 node.removeAttribute(n);
25529 var parts = v.split(/;/);
25532 Roo.each(parts, function(p) {
25533 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25537 var l = p.split(':').shift().replace(/\s+/g,'');
25538 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25540 if ( cwhite.length && cblack.indexOf(l) > -1) {
25541 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25542 //node.removeAttribute(n);
25546 // only allow 'c whitelisted system attributes'
25547 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25548 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25549 //node.removeAttribute(n);
25559 if (clean.length) {
25560 node.setAttribute(n, clean.join(';'));
25562 node.removeAttribute(n);
25568 for (var i = node.attributes.length-1; i > -1 ; i--) {
25569 var a = node.attributes[i];
25572 if (a.name.toLowerCase().substr(0,2)=='on') {
25573 node.removeAttribute(a.name);
25576 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25577 node.removeAttribute(a.name);
25580 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25581 cleanAttr(a.name,a.value); // fixme..
25584 if (a.name == 'style') {
25585 cleanStyle(a.name,a.value);
25588 /// clean up MS crap..
25589 // tecnically this should be a list of valid class'es..
25592 if (a.name == 'class') {
25593 if (a.value.match(/^Mso/)) {
25594 node.removeAttribute('class');
25597 if (a.value.match(/^body$/)) {
25598 node.removeAttribute('class');
25609 this.cleanUpChildren(node);
25615 * Clean up MS wordisms...
25617 cleanWord : function(node)
25620 this.cleanWord(this.doc.body);
25625 node.nodeName == 'SPAN' &&
25626 !node.hasAttributes() &&
25627 node.childNodes.length == 1 &&
25628 node.firstChild.nodeName == "#text"
25630 var textNode = node.firstChild;
25631 node.removeChild(textNode);
25632 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25633 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25635 node.parentNode.insertBefore(textNode, node);
25636 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25637 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25639 node.parentNode.removeChild(node);
25642 if (node.nodeName == "#text") {
25643 // clean up silly Windows -- stuff?
25646 if (node.nodeName == "#comment") {
25647 node.parentNode.removeChild(node);
25648 // clean up silly Windows -- stuff?
25652 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25653 node.parentNode.removeChild(node);
25656 //Roo.log(node.tagName);
25657 // remove - but keep children..
25658 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25659 //Roo.log('-- removed');
25660 while (node.childNodes.length) {
25661 var cn = node.childNodes[0];
25662 node.removeChild(cn);
25663 node.parentNode.insertBefore(cn, node);
25664 // move node to parent - and clean it..
25665 this.cleanWord(cn);
25667 node.parentNode.removeChild(node);
25668 /// no need to iterate chidlren = it's got none..
25669 //this.iterateChildren(node, this.cleanWord);
25673 if (node.className.length) {
25675 var cn = node.className.split(/\W+/);
25677 Roo.each(cn, function(cls) {
25678 if (cls.match(/Mso[a-zA-Z]+/)) {
25683 node.className = cna.length ? cna.join(' ') : '';
25685 node.removeAttribute("class");
25689 if (node.hasAttribute("lang")) {
25690 node.removeAttribute("lang");
25693 if (node.hasAttribute("style")) {
25695 var styles = node.getAttribute("style").split(";");
25697 Roo.each(styles, function(s) {
25698 if (!s.match(/:/)) {
25701 var kv = s.split(":");
25702 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25705 // what ever is left... we allow.
25708 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25709 if (!nstyle.length) {
25710 node.removeAttribute('style');
25713 this.iterateChildren(node, this.cleanWord);
25719 * iterateChildren of a Node, calling fn each time, using this as the scole..
25720 * @param {DomNode} node node to iterate children of.
25721 * @param {Function} fn method of this class to call on each item.
25723 iterateChildren : function(node, fn)
25725 if (!node.childNodes.length) {
25728 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25729 fn.call(this, node.childNodes[i])
25735 * cleanTableWidths.
25737 * Quite often pasting from word etc.. results in tables with column and widths.
25738 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25741 cleanTableWidths : function(node)
25746 this.cleanTableWidths(this.doc.body);
25751 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25754 Roo.log(node.tagName);
25755 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25756 this.iterateChildren(node, this.cleanTableWidths);
25759 if (node.hasAttribute('width')) {
25760 node.removeAttribute('width');
25764 if (node.hasAttribute("style")) {
25767 var styles = node.getAttribute("style").split(";");
25769 Roo.each(styles, function(s) {
25770 if (!s.match(/:/)) {
25773 var kv = s.split(":");
25774 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25777 // what ever is left... we allow.
25780 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25781 if (!nstyle.length) {
25782 node.removeAttribute('style');
25786 this.iterateChildren(node, this.cleanTableWidths);
25794 domToHTML : function(currentElement, depth, nopadtext) {
25796 depth = depth || 0;
25797 nopadtext = nopadtext || false;
25799 if (!currentElement) {
25800 return this.domToHTML(this.doc.body);
25803 //Roo.log(currentElement);
25805 var allText = false;
25806 var nodeName = currentElement.nodeName;
25807 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25809 if (nodeName == '#text') {
25811 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25816 if (nodeName != 'BODY') {
25819 // Prints the node tagName, such as <A>, <IMG>, etc
25822 for(i = 0; i < currentElement.attributes.length;i++) {
25824 var aname = currentElement.attributes.item(i).name;
25825 if (!currentElement.attributes.item(i).value.length) {
25828 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25831 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25840 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25843 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25848 // Traverse the tree
25850 var currentElementChild = currentElement.childNodes.item(i);
25851 var allText = true;
25852 var innerHTML = '';
25854 while (currentElementChild) {
25855 // Formatting code (indent the tree so it looks nice on the screen)
25856 var nopad = nopadtext;
25857 if (lastnode == 'SPAN') {
25861 if (currentElementChild.nodeName == '#text') {
25862 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25863 toadd = nopadtext ? toadd : toadd.trim();
25864 if (!nopad && toadd.length > 80) {
25865 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25867 innerHTML += toadd;
25870 currentElementChild = currentElement.childNodes.item(i);
25876 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25878 // Recursively traverse the tree structure of the child node
25879 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25880 lastnode = currentElementChild.nodeName;
25882 currentElementChild=currentElement.childNodes.item(i);
25888 // The remaining code is mostly for formatting the tree
25889 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25894 ret+= "</"+tagName+">";
25900 applyBlacklists : function()
25902 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25903 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25907 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25908 if (b.indexOf(tag) > -1) {
25911 this.white.push(tag);
25915 Roo.each(w, function(tag) {
25916 if (b.indexOf(tag) > -1) {
25919 if (this.white.indexOf(tag) > -1) {
25922 this.white.push(tag);
25927 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25928 if (w.indexOf(tag) > -1) {
25931 this.black.push(tag);
25935 Roo.each(b, function(tag) {
25936 if (w.indexOf(tag) > -1) {
25939 if (this.black.indexOf(tag) > -1) {
25942 this.black.push(tag);
25947 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25948 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25952 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25953 if (b.indexOf(tag) > -1) {
25956 this.cwhite.push(tag);
25960 Roo.each(w, function(tag) {
25961 if (b.indexOf(tag) > -1) {
25964 if (this.cwhite.indexOf(tag) > -1) {
25967 this.cwhite.push(tag);
25972 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25973 if (w.indexOf(tag) > -1) {
25976 this.cblack.push(tag);
25980 Roo.each(b, function(tag) {
25981 if (w.indexOf(tag) > -1) {
25984 if (this.cblack.indexOf(tag) > -1) {
25987 this.cblack.push(tag);
25992 setStylesheets : function(stylesheets)
25994 if(typeof(stylesheets) == 'string'){
25995 Roo.get(this.iframe.contentDocument.head).createChild({
25997 rel : 'stylesheet',
26006 Roo.each(stylesheets, function(s) {
26011 Roo.get(_this.iframe.contentDocument.head).createChild({
26013 rel : 'stylesheet',
26022 removeStylesheets : function()
26026 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26031 setStyle : function(style)
26033 Roo.get(this.iframe.contentDocument.head).createChild({
26042 // hide stuff that is not compatible
26056 * @event specialkey
26060 * @cfg {String} fieldClass @hide
26063 * @cfg {String} focusClass @hide
26066 * @cfg {String} autoCreate @hide
26069 * @cfg {String} inputType @hide
26072 * @cfg {String} invalidClass @hide
26075 * @cfg {String} invalidText @hide
26078 * @cfg {String} msgFx @hide
26081 * @cfg {String} validateOnBlur @hide
26085 Roo.HtmlEditorCore.white = [
26086 'area', 'br', 'img', 'input', 'hr', 'wbr',
26088 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26089 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26090 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26091 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26092 'table', 'ul', 'xmp',
26094 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26097 'dir', 'menu', 'ol', 'ul', 'dl',
26103 Roo.HtmlEditorCore.black = [
26104 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26106 'base', 'basefont', 'bgsound', 'blink', 'body',
26107 'frame', 'frameset', 'head', 'html', 'ilayer',
26108 'iframe', 'layer', 'link', 'meta', 'object',
26109 'script', 'style' ,'title', 'xml' // clean later..
26111 Roo.HtmlEditorCore.clean = [
26112 'script', 'style', 'title', 'xml'
26114 Roo.HtmlEditorCore.remove = [
26119 Roo.HtmlEditorCore.ablack = [
26123 Roo.HtmlEditorCore.aclean = [
26124 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26128 Roo.HtmlEditorCore.pwhite= [
26129 'http', 'https', 'mailto'
26132 // white listed style attributes.
26133 Roo.HtmlEditorCore.cwhite= [
26134 // 'text-align', /// default is to allow most things..
26140 // black listed style attributes.
26141 Roo.HtmlEditorCore.cblack= [
26142 // 'font-size' -- this can be set by the project
26146 Roo.HtmlEditorCore.swapCodes =[
26147 [ 8211, "–" ],
26148 [ 8212, "—" ],
26165 * @class Roo.bootstrap.HtmlEditor
26166 * @extends Roo.bootstrap.TextArea
26167 * Bootstrap HtmlEditor class
26170 * Create a new HtmlEditor
26171 * @param {Object} config The config object
26174 Roo.bootstrap.HtmlEditor = function(config){
26175 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26176 if (!this.toolbars) {
26177 this.toolbars = [];
26180 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26183 * @event initialize
26184 * Fires when the editor is fully initialized (including the iframe)
26185 * @param {HtmlEditor} this
26190 * Fires when the editor is first receives the focus. Any insertion must wait
26191 * until after this event.
26192 * @param {HtmlEditor} this
26196 * @event beforesync
26197 * Fires before the textarea is updated with content from the editor iframe. Return false
26198 * to cancel the sync.
26199 * @param {HtmlEditor} this
26200 * @param {String} html
26204 * @event beforepush
26205 * Fires before the iframe editor is updated with content from the textarea. Return false
26206 * to cancel the push.
26207 * @param {HtmlEditor} this
26208 * @param {String} html
26213 * Fires when the textarea is updated with content from the editor iframe.
26214 * @param {HtmlEditor} this
26215 * @param {String} html
26220 * Fires when the iframe editor is updated with content from the textarea.
26221 * @param {HtmlEditor} this
26222 * @param {String} html
26226 * @event editmodechange
26227 * Fires when the editor switches edit modes
26228 * @param {HtmlEditor} this
26229 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26231 editmodechange: true,
26233 * @event editorevent
26234 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26235 * @param {HtmlEditor} this
26239 * @event firstfocus
26240 * Fires when on first focus - needed by toolbars..
26241 * @param {HtmlEditor} this
26246 * Auto save the htmlEditor value as a file into Events
26247 * @param {HtmlEditor} this
26251 * @event savedpreview
26252 * preview the saved version of htmlEditor
26253 * @param {HtmlEditor} this
26260 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26264 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26269 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26274 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26279 * @cfg {Number} height (in pixels)
26283 * @cfg {Number} width (in pixels)
26288 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26291 stylesheets: false,
26296 // private properties
26297 validationEvent : false,
26299 initialized : false,
26302 onFocus : Roo.emptyFn,
26304 hideMode:'offsets',
26306 tbContainer : false,
26310 toolbarContainer :function() {
26311 return this.wrap.select('.x-html-editor-tb',true).first();
26315 * Protected method that will not generally be called directly. It
26316 * is called when the editor creates its toolbar. Override this method if you need to
26317 * add custom toolbar buttons.
26318 * @param {HtmlEditor} editor
26320 createToolbar : function(){
26321 Roo.log('renewing');
26322 Roo.log("create toolbars");
26324 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26325 this.toolbars[0].render(this.toolbarContainer());
26329 // if (!editor.toolbars || !editor.toolbars.length) {
26330 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26333 // for (var i =0 ; i < editor.toolbars.length;i++) {
26334 // editor.toolbars[i] = Roo.factory(
26335 // typeof(editor.toolbars[i]) == 'string' ?
26336 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26337 // Roo.bootstrap.HtmlEditor);
26338 // editor.toolbars[i].init(editor);
26344 onRender : function(ct, position)
26346 // Roo.log("Call onRender: " + this.xtype);
26348 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26350 this.wrap = this.inputEl().wrap({
26351 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26354 this.editorcore.onRender(ct, position);
26356 if (this.resizable) {
26357 this.resizeEl = new Roo.Resizable(this.wrap, {
26361 minHeight : this.height,
26362 height: this.height,
26363 handles : this.resizable,
26366 resize : function(r, w, h) {
26367 _t.onResize(w,h); // -something
26373 this.createToolbar(this);
26376 if(!this.width && this.resizable){
26377 this.setSize(this.wrap.getSize());
26379 if (this.resizeEl) {
26380 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26381 // should trigger onReize..
26387 onResize : function(w, h)
26389 Roo.log('resize: ' +w + ',' + h );
26390 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26394 if(this.inputEl() ){
26395 if(typeof w == 'number'){
26396 var aw = w - this.wrap.getFrameWidth('lr');
26397 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26400 if(typeof h == 'number'){
26401 var tbh = -11; // fixme it needs to tool bar size!
26402 for (var i =0; i < this.toolbars.length;i++) {
26403 // fixme - ask toolbars for heights?
26404 tbh += this.toolbars[i].el.getHeight();
26405 //if (this.toolbars[i].footer) {
26406 // tbh += this.toolbars[i].footer.el.getHeight();
26414 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26415 ah -= 5; // knock a few pixes off for look..
26416 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26420 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26421 this.editorcore.onResize(ew,eh);
26426 * Toggles the editor between standard and source edit mode.
26427 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26429 toggleSourceEdit : function(sourceEditMode)
26431 this.editorcore.toggleSourceEdit(sourceEditMode);
26433 if(this.editorcore.sourceEditMode){
26434 Roo.log('editor - showing textarea');
26437 // Roo.log(this.syncValue());
26439 this.inputEl().removeClass(['hide', 'x-hidden']);
26440 this.inputEl().dom.removeAttribute('tabIndex');
26441 this.inputEl().focus();
26443 Roo.log('editor - hiding textarea');
26445 // Roo.log(this.pushValue());
26448 this.inputEl().addClass(['hide', 'x-hidden']);
26449 this.inputEl().dom.setAttribute('tabIndex', -1);
26450 //this.deferFocus();
26453 if(this.resizable){
26454 this.setSize(this.wrap.getSize());
26457 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26460 // private (for BoxComponent)
26461 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26463 // private (for BoxComponent)
26464 getResizeEl : function(){
26468 // private (for BoxComponent)
26469 getPositionEl : function(){
26474 initEvents : function(){
26475 this.originalValue = this.getValue();
26479 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26482 // markInvalid : Roo.emptyFn,
26484 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26487 // clearInvalid : Roo.emptyFn,
26489 setValue : function(v){
26490 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26491 this.editorcore.pushValue();
26496 deferFocus : function(){
26497 this.focus.defer(10, this);
26501 focus : function(){
26502 this.editorcore.focus();
26508 onDestroy : function(){
26514 for (var i =0; i < this.toolbars.length;i++) {
26515 // fixme - ask toolbars for heights?
26516 this.toolbars[i].onDestroy();
26519 this.wrap.dom.innerHTML = '';
26520 this.wrap.remove();
26525 onFirstFocus : function(){
26526 //Roo.log("onFirstFocus");
26527 this.editorcore.onFirstFocus();
26528 for (var i =0; i < this.toolbars.length;i++) {
26529 this.toolbars[i].onFirstFocus();
26535 syncValue : function()
26537 this.editorcore.syncValue();
26540 pushValue : function()
26542 this.editorcore.pushValue();
26546 // hide stuff that is not compatible
26560 * @event specialkey
26564 * @cfg {String} fieldClass @hide
26567 * @cfg {String} focusClass @hide
26570 * @cfg {String} autoCreate @hide
26573 * @cfg {String} inputType @hide
26577 * @cfg {String} invalidText @hide
26580 * @cfg {String} msgFx @hide
26583 * @cfg {String} validateOnBlur @hide
26592 Roo.namespace('Roo.bootstrap.htmleditor');
26594 * @class Roo.bootstrap.HtmlEditorToolbar1
26600 new Roo.bootstrap.HtmlEditor({
26603 new Roo.bootstrap.HtmlEditorToolbar1({
26604 disable : { fonts: 1 , format: 1, ..., ... , ...],
26610 * @cfg {Object} disable List of elements to disable..
26611 * @cfg {Array} btns List of additional buttons.
26615 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26618 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26621 Roo.apply(this, config);
26623 // default disabled, based on 'good practice'..
26624 this.disable = this.disable || {};
26625 Roo.applyIf(this.disable, {
26628 specialElements : true
26630 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26632 this.editor = config.editor;
26633 this.editorcore = config.editor.editorcore;
26635 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26637 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26638 // dont call parent... till later.
26640 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26645 editorcore : false,
26650 "h1","h2","h3","h4","h5","h6",
26652 "abbr", "acronym", "address", "cite", "samp", "var",
26656 onRender : function(ct, position)
26658 // Roo.log("Call onRender: " + this.xtype);
26660 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26662 this.el.dom.style.marginBottom = '0';
26664 var editorcore = this.editorcore;
26665 var editor= this.editor;
26668 var btn = function(id,cmd , toggle, handler, html){
26670 var event = toggle ? 'toggle' : 'click';
26675 xns: Roo.bootstrap,
26679 enableToggle:toggle !== false,
26681 pressed : toggle ? false : null,
26684 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26685 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26691 // var cb_box = function...
26696 xns: Roo.bootstrap,
26701 xns: Roo.bootstrap,
26705 Roo.each(this.formats, function(f) {
26706 style.menu.items.push({
26708 xns: Roo.bootstrap,
26709 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26714 editorcore.insertTag(this.tagname);
26721 children.push(style);
26723 btn('bold',false,true);
26724 btn('italic',false,true);
26725 btn('align-left', 'justifyleft',true);
26726 btn('align-center', 'justifycenter',true);
26727 btn('align-right' , 'justifyright',true);
26728 btn('link', false, false, function(btn) {
26729 //Roo.log("create link?");
26730 var url = prompt(this.createLinkText, this.defaultLinkValue);
26731 if(url && url != 'http:/'+'/'){
26732 this.editorcore.relayCmd('createlink', url);
26735 btn('list','insertunorderedlist',true);
26736 btn('pencil', false,true, function(btn){
26738 this.toggleSourceEdit(btn.pressed);
26741 if (this.editor.btns.length > 0) {
26742 for (var i = 0; i<this.editor.btns.length; i++) {
26743 children.push(this.editor.btns[i]);
26751 xns: Roo.bootstrap,
26756 xns: Roo.bootstrap,
26761 cog.menu.items.push({
26763 xns: Roo.bootstrap,
26764 html : Clean styles,
26769 editorcore.insertTag(this.tagname);
26778 this.xtype = 'NavSimplebar';
26780 for(var i=0;i< children.length;i++) {
26782 this.buttons.add(this.addxtypeChild(children[i]));
26786 editor.on('editorevent', this.updateToolbar, this);
26788 onBtnClick : function(id)
26790 this.editorcore.relayCmd(id);
26791 this.editorcore.focus();
26795 * Protected method that will not generally be called directly. It triggers
26796 * a toolbar update by reading the markup state of the current selection in the editor.
26798 updateToolbar: function(){
26800 if(!this.editorcore.activated){
26801 this.editor.onFirstFocus(); // is this neeed?
26805 var btns = this.buttons;
26806 var doc = this.editorcore.doc;
26807 btns.get('bold').setActive(doc.queryCommandState('bold'));
26808 btns.get('italic').setActive(doc.queryCommandState('italic'));
26809 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26811 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26812 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26813 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26815 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26816 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26819 var ans = this.editorcore.getAllAncestors();
26820 if (this.formatCombo) {
26823 var store = this.formatCombo.store;
26824 this.formatCombo.setValue("");
26825 for (var i =0; i < ans.length;i++) {
26826 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26828 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26836 // hides menus... - so this cant be on a menu...
26837 Roo.bootstrap.MenuMgr.hideAll();
26839 Roo.bootstrap.MenuMgr.hideAll();
26840 //this.editorsyncValue();
26842 onFirstFocus: function() {
26843 this.buttons.each(function(item){
26847 toggleSourceEdit : function(sourceEditMode){
26850 if(sourceEditMode){
26851 Roo.log("disabling buttons");
26852 this.buttons.each( function(item){
26853 if(item.cmd != 'pencil'){
26859 Roo.log("enabling buttons");
26860 if(this.editorcore.initialized){
26861 this.buttons.each( function(item){
26867 Roo.log("calling toggole on editor");
26868 // tell the editor that it's been pressed..
26869 this.editor.toggleSourceEdit(sourceEditMode);
26883 * @class Roo.bootstrap.Markdown
26884 * @extends Roo.bootstrap.TextArea
26885 * Bootstrap Showdown editable area
26886 * @cfg {string} content
26889 * Create a new Showdown
26892 Roo.bootstrap.Markdown = function(config){
26893 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26897 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26901 initEvents : function()
26904 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26905 this.markdownEl = this.el.createChild({
26906 cls : 'roo-markdown-area'
26908 this.inputEl().addClass('d-none');
26909 if (this.getValue() == '') {
26910 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26913 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26915 this.markdownEl.on('click', this.toggleTextEdit, this);
26916 this.on('blur', this.toggleTextEdit, this);
26917 this.on('specialkey', this.resizeTextArea, this);
26920 toggleTextEdit : function()
26922 var sh = this.markdownEl.getHeight();
26923 this.inputEl().addClass('d-none');
26924 this.markdownEl.addClass('d-none');
26925 if (!this.editing) {
26927 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26928 this.inputEl().removeClass('d-none');
26929 this.inputEl().focus();
26930 this.editing = true;
26933 // show showdown...
26934 this.updateMarkdown();
26935 this.markdownEl.removeClass('d-none');
26936 this.editing = false;
26939 updateMarkdown : function()
26941 if (this.getValue() == '') {
26942 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26946 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26949 resizeTextArea: function () {
26952 Roo.log([sh, this.getValue().split("\n").length * 30]);
26953 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26955 setValue : function(val)
26957 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26958 if (!this.editing) {
26959 this.updateMarkdown();
26965 if (!this.editing) {
26966 this.toggleTextEdit();
26974 * @class Roo.bootstrap.Table.AbstractSelectionModel
26975 * @extends Roo.util.Observable
26976 * Abstract base class for grid SelectionModels. It provides the interface that should be
26977 * implemented by descendant classes. This class should not be directly instantiated.
26980 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26981 this.locked = false;
26982 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26986 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26987 /** @ignore Called by the grid automatically. Do not call directly. */
26988 init : function(grid){
26994 * Locks the selections.
26997 this.locked = true;
27001 * Unlocks the selections.
27003 unlock : function(){
27004 this.locked = false;
27008 * Returns true if the selections are locked.
27009 * @return {Boolean}
27011 isLocked : function(){
27012 return this.locked;
27016 initEvents : function ()
27022 * @extends Roo.bootstrap.Table.AbstractSelectionModel
27023 * @class Roo.bootstrap.Table.RowSelectionModel
27024 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27025 * It supports multiple selections and keyboard selection/navigation.
27027 * @param {Object} config
27030 Roo.bootstrap.Table.RowSelectionModel = function(config){
27031 Roo.apply(this, config);
27032 this.selections = new Roo.util.MixedCollection(false, function(o){
27037 this.lastActive = false;
27041 * @event selectionchange
27042 * Fires when the selection changes
27043 * @param {SelectionModel} this
27045 "selectionchange" : true,
27047 * @event afterselectionchange
27048 * Fires after the selection changes (eg. by key press or clicking)
27049 * @param {SelectionModel} this
27051 "afterselectionchange" : true,
27053 * @event beforerowselect
27054 * Fires when a row is selected being selected, return false to cancel.
27055 * @param {SelectionModel} this
27056 * @param {Number} rowIndex The selected index
27057 * @param {Boolean} keepExisting False if other selections will be cleared
27059 "beforerowselect" : true,
27062 * Fires when a row is selected.
27063 * @param {SelectionModel} this
27064 * @param {Number} rowIndex The selected index
27065 * @param {Roo.data.Record} r The record
27067 "rowselect" : true,
27069 * @event rowdeselect
27070 * Fires when a row is deselected.
27071 * @param {SelectionModel} this
27072 * @param {Number} rowIndex The selected index
27074 "rowdeselect" : true
27076 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27077 this.locked = false;
27080 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27082 * @cfg {Boolean} singleSelect
27083 * True to allow selection of only one row at a time (defaults to false)
27085 singleSelect : false,
27088 initEvents : function()
27091 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27092 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27093 //}else{ // allow click to work like normal
27094 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27096 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27097 this.grid.on("rowclick", this.handleMouseDown, this);
27099 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27100 "up" : function(e){
27102 this.selectPrevious(e.shiftKey);
27103 }else if(this.last !== false && this.lastActive !== false){
27104 var last = this.last;
27105 this.selectRange(this.last, this.lastActive-1);
27106 this.grid.getView().focusRow(this.lastActive);
27107 if(last !== false){
27111 this.selectFirstRow();
27113 this.fireEvent("afterselectionchange", this);
27115 "down" : function(e){
27117 this.selectNext(e.shiftKey);
27118 }else if(this.last !== false && this.lastActive !== false){
27119 var last = this.last;
27120 this.selectRange(this.last, this.lastActive+1);
27121 this.grid.getView().focusRow(this.lastActive);
27122 if(last !== false){
27126 this.selectFirstRow();
27128 this.fireEvent("afterselectionchange", this);
27132 this.grid.store.on('load', function(){
27133 this.selections.clear();
27136 var view = this.grid.view;
27137 view.on("refresh", this.onRefresh, this);
27138 view.on("rowupdated", this.onRowUpdated, this);
27139 view.on("rowremoved", this.onRemove, this);
27144 onRefresh : function()
27146 var ds = this.grid.store, i, v = this.grid.view;
27147 var s = this.selections;
27148 s.each(function(r){
27149 if((i = ds.indexOfId(r.id)) != -1){
27158 onRemove : function(v, index, r){
27159 this.selections.remove(r);
27163 onRowUpdated : function(v, index, r){
27164 if(this.isSelected(r)){
27165 v.onRowSelect(index);
27171 * @param {Array} records The records to select
27172 * @param {Boolean} keepExisting (optional) True to keep existing selections
27174 selectRecords : function(records, keepExisting)
27177 this.clearSelections();
27179 var ds = this.grid.store;
27180 for(var i = 0, len = records.length; i < len; i++){
27181 this.selectRow(ds.indexOf(records[i]), true);
27186 * Gets the number of selected rows.
27189 getCount : function(){
27190 return this.selections.length;
27194 * Selects the first row in the grid.
27196 selectFirstRow : function(){
27201 * Select the last row.
27202 * @param {Boolean} keepExisting (optional) True to keep existing selections
27204 selectLastRow : function(keepExisting){
27205 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27206 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27210 * Selects the row immediately following the last selected row.
27211 * @param {Boolean} keepExisting (optional) True to keep existing selections
27213 selectNext : function(keepExisting)
27215 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27216 this.selectRow(this.last+1, keepExisting);
27217 this.grid.getView().focusRow(this.last);
27222 * Selects the row that precedes the last selected row.
27223 * @param {Boolean} keepExisting (optional) True to keep existing selections
27225 selectPrevious : function(keepExisting){
27227 this.selectRow(this.last-1, keepExisting);
27228 this.grid.getView().focusRow(this.last);
27233 * Returns the selected records
27234 * @return {Array} Array of selected records
27236 getSelections : function(){
27237 return [].concat(this.selections.items);
27241 * Returns the first selected record.
27244 getSelected : function(){
27245 return this.selections.itemAt(0);
27250 * Clears all selections.
27252 clearSelections : function(fast)
27258 var ds = this.grid.store;
27259 var s = this.selections;
27260 s.each(function(r){
27261 this.deselectRow(ds.indexOfId(r.id));
27265 this.selections.clear();
27272 * Selects all rows.
27274 selectAll : function(){
27278 this.selections.clear();
27279 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27280 this.selectRow(i, true);
27285 * Returns True if there is a selection.
27286 * @return {Boolean}
27288 hasSelection : function(){
27289 return this.selections.length > 0;
27293 * Returns True if the specified row is selected.
27294 * @param {Number/Record} record The record or index of the record to check
27295 * @return {Boolean}
27297 isSelected : function(index){
27298 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27299 return (r && this.selections.key(r.id) ? true : false);
27303 * Returns True if the specified record id is selected.
27304 * @param {String} id The id of record to check
27305 * @return {Boolean}
27307 isIdSelected : function(id){
27308 return (this.selections.key(id) ? true : false);
27313 handleMouseDBClick : function(e, t){
27317 handleMouseDown : function(e, t)
27319 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27320 if(this.isLocked() || rowIndex < 0 ){
27323 if(e.shiftKey && this.last !== false){
27324 var last = this.last;
27325 this.selectRange(last, rowIndex, e.ctrlKey);
27326 this.last = last; // reset the last
27330 var isSelected = this.isSelected(rowIndex);
27331 //Roo.log("select row:" + rowIndex);
27333 this.deselectRow(rowIndex);
27335 this.selectRow(rowIndex, true);
27339 if(e.button !== 0 && isSelected){
27340 alert('rowIndex 2: ' + rowIndex);
27341 view.focusRow(rowIndex);
27342 }else if(e.ctrlKey && isSelected){
27343 this.deselectRow(rowIndex);
27344 }else if(!isSelected){
27345 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27346 view.focusRow(rowIndex);
27350 this.fireEvent("afterselectionchange", this);
27353 handleDragableRowClick : function(grid, rowIndex, e)
27355 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27356 this.selectRow(rowIndex, false);
27357 grid.view.focusRow(rowIndex);
27358 this.fireEvent("afterselectionchange", this);
27363 * Selects multiple rows.
27364 * @param {Array} rows Array of the indexes of the row to select
27365 * @param {Boolean} keepExisting (optional) True to keep existing selections
27367 selectRows : function(rows, keepExisting){
27369 this.clearSelections();
27371 for(var i = 0, len = rows.length; i < len; i++){
27372 this.selectRow(rows[i], true);
27377 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27378 * @param {Number} startRow The index of the first row in the range
27379 * @param {Number} endRow The index of the last row in the range
27380 * @param {Boolean} keepExisting (optional) True to retain existing selections
27382 selectRange : function(startRow, endRow, keepExisting){
27387 this.clearSelections();
27389 if(startRow <= endRow){
27390 for(var i = startRow; i <= endRow; i++){
27391 this.selectRow(i, true);
27394 for(var i = startRow; i >= endRow; i--){
27395 this.selectRow(i, true);
27401 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27402 * @param {Number} startRow The index of the first row in the range
27403 * @param {Number} endRow The index of the last row in the range
27405 deselectRange : function(startRow, endRow, preventViewNotify){
27409 for(var i = startRow; i <= endRow; i++){
27410 this.deselectRow(i, preventViewNotify);
27416 * @param {Number} row The index of the row to select
27417 * @param {Boolean} keepExisting (optional) True to keep existing selections
27419 selectRow : function(index, keepExisting, preventViewNotify)
27421 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27424 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27425 if(!keepExisting || this.singleSelect){
27426 this.clearSelections();
27429 var r = this.grid.store.getAt(index);
27430 //console.log('selectRow - record id :' + r.id);
27432 this.selections.add(r);
27433 this.last = this.lastActive = index;
27434 if(!preventViewNotify){
27435 var proxy = new Roo.Element(
27436 this.grid.getRowDom(index)
27438 proxy.addClass('bg-info info');
27440 this.fireEvent("rowselect", this, index, r);
27441 this.fireEvent("selectionchange", this);
27447 * @param {Number} row The index of the row to deselect
27449 deselectRow : function(index, preventViewNotify)
27454 if(this.last == index){
27457 if(this.lastActive == index){
27458 this.lastActive = false;
27461 var r = this.grid.store.getAt(index);
27466 this.selections.remove(r);
27467 //.console.log('deselectRow - record id :' + r.id);
27468 if(!preventViewNotify){
27470 var proxy = new Roo.Element(
27471 this.grid.getRowDom(index)
27473 proxy.removeClass('bg-info info');
27475 this.fireEvent("rowdeselect", this, index);
27476 this.fireEvent("selectionchange", this);
27480 restoreLast : function(){
27482 this.last = this._last;
27487 acceptsNav : function(row, col, cm){
27488 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27492 onEditorKey : function(field, e){
27493 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27498 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27500 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27502 }else if(k == e.ENTER && !e.ctrlKey){
27506 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27508 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27510 }else if(k == e.ESC){
27514 g.startEditing(newCell[0], newCell[1]);
27520 * Ext JS Library 1.1.1
27521 * Copyright(c) 2006-2007, Ext JS, LLC.
27523 * Originally Released Under LGPL - original licence link has changed is not relivant.
27526 * <script type="text/javascript">
27530 * @class Roo.bootstrap.PagingToolbar
27531 * @extends Roo.bootstrap.NavSimplebar
27532 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27534 * Create a new PagingToolbar
27535 * @param {Object} config The config object
27536 * @param {Roo.data.Store} store
27538 Roo.bootstrap.PagingToolbar = function(config)
27540 // old args format still supported... - xtype is prefered..
27541 // created from xtype...
27543 this.ds = config.dataSource;
27545 if (config.store && !this.ds) {
27546 this.store= Roo.factory(config.store, Roo.data);
27547 this.ds = this.store;
27548 this.ds.xmodule = this.xmodule || false;
27551 this.toolbarItems = [];
27552 if (config.items) {
27553 this.toolbarItems = config.items;
27556 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27561 this.bind(this.ds);
27564 if (Roo.bootstrap.version == 4) {
27565 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27567 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27572 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27574 * @cfg {Roo.data.Store} dataSource
27575 * The underlying data store providing the paged data
27578 * @cfg {String/HTMLElement/Element} container
27579 * container The id or element that will contain the toolbar
27582 * @cfg {Boolean} displayInfo
27583 * True to display the displayMsg (defaults to false)
27586 * @cfg {Number} pageSize
27587 * The number of records to display per page (defaults to 20)
27591 * @cfg {String} displayMsg
27592 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27594 displayMsg : 'Displaying {0} - {1} of {2}',
27596 * @cfg {String} emptyMsg
27597 * The message to display when no records are found (defaults to "No data to display")
27599 emptyMsg : 'No data to display',
27601 * Customizable piece of the default paging text (defaults to "Page")
27604 beforePageText : "Page",
27606 * Customizable piece of the default paging text (defaults to "of %0")
27609 afterPageText : "of {0}",
27611 * Customizable piece of the default paging text (defaults to "First Page")
27614 firstText : "First Page",
27616 * Customizable piece of the default paging text (defaults to "Previous Page")
27619 prevText : "Previous Page",
27621 * Customizable piece of the default paging text (defaults to "Next Page")
27624 nextText : "Next Page",
27626 * Customizable piece of the default paging text (defaults to "Last Page")
27629 lastText : "Last Page",
27631 * Customizable piece of the default paging text (defaults to "Refresh")
27634 refreshText : "Refresh",
27638 onRender : function(ct, position)
27640 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27641 this.navgroup.parentId = this.id;
27642 this.navgroup.onRender(this.el, null);
27643 // add the buttons to the navgroup
27645 if(this.displayInfo){
27646 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27647 this.displayEl = this.el.select('.x-paging-info', true).first();
27648 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27649 // this.displayEl = navel.el.select('span',true).first();
27655 Roo.each(_this.buttons, function(e){ // this might need to use render????
27656 Roo.factory(e).render(_this.el);
27660 Roo.each(_this.toolbarItems, function(e) {
27661 _this.navgroup.addItem(e);
27665 this.first = this.navgroup.addItem({
27666 tooltip: this.firstText,
27667 cls: "prev btn-outline-secondary",
27668 html : ' <i class="fa fa-step-backward"></i>',
27670 preventDefault: true,
27671 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27674 this.prev = this.navgroup.addItem({
27675 tooltip: this.prevText,
27676 cls: "prev btn-outline-secondary",
27677 html : ' <i class="fa fa-backward"></i>',
27679 preventDefault: true,
27680 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27682 //this.addSeparator();
27685 var field = this.navgroup.addItem( {
27687 cls : 'x-paging-position btn-outline-secondary',
27689 html : this.beforePageText +
27690 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27691 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27694 this.field = field.el.select('input', true).first();
27695 this.field.on("keydown", this.onPagingKeydown, this);
27696 this.field.on("focus", function(){this.dom.select();});
27699 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27700 //this.field.setHeight(18);
27701 //this.addSeparator();
27702 this.next = this.navgroup.addItem({
27703 tooltip: this.nextText,
27704 cls: "next btn-outline-secondary",
27705 html : ' <i class="fa fa-forward"></i>',
27707 preventDefault: true,
27708 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27710 this.last = this.navgroup.addItem({
27711 tooltip: this.lastText,
27712 html : ' <i class="fa fa-step-forward"></i>',
27713 cls: "next btn-outline-secondary",
27715 preventDefault: true,
27716 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27718 //this.addSeparator();
27719 this.loading = this.navgroup.addItem({
27720 tooltip: this.refreshText,
27721 cls: "btn-outline-secondary",
27722 html : ' <i class="fa fa-refresh"></i>',
27723 preventDefault: true,
27724 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27730 updateInfo : function(){
27731 if(this.displayEl){
27732 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27733 var msg = count == 0 ?
27737 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27739 this.displayEl.update(msg);
27744 onLoad : function(ds, r, o)
27746 this.cursor = o.params && o.params.start ? o.params.start : 0;
27748 var d = this.getPageData(),
27753 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27754 this.field.dom.value = ap;
27755 this.first.setDisabled(ap == 1);
27756 this.prev.setDisabled(ap == 1);
27757 this.next.setDisabled(ap == ps);
27758 this.last.setDisabled(ap == ps);
27759 this.loading.enable();
27764 getPageData : function(){
27765 var total = this.ds.getTotalCount();
27768 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27769 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27774 onLoadError : function(){
27775 this.loading.enable();
27779 onPagingKeydown : function(e){
27780 var k = e.getKey();
27781 var d = this.getPageData();
27783 var v = this.field.dom.value, pageNum;
27784 if(!v || isNaN(pageNum = parseInt(v, 10))){
27785 this.field.dom.value = d.activePage;
27788 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27789 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27792 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))
27794 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27795 this.field.dom.value = pageNum;
27796 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27799 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27801 var v = this.field.dom.value, pageNum;
27802 var increment = (e.shiftKey) ? 10 : 1;
27803 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27806 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27807 this.field.dom.value = d.activePage;
27810 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27812 this.field.dom.value = parseInt(v, 10) + increment;
27813 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27814 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27821 beforeLoad : function(){
27823 this.loading.disable();
27828 onClick : function(which){
27837 ds.load({params:{start: 0, limit: this.pageSize}});
27840 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27843 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27846 var total = ds.getTotalCount();
27847 var extra = total % this.pageSize;
27848 var lastStart = extra ? (total - extra) : total-this.pageSize;
27849 ds.load({params:{start: lastStart, limit: this.pageSize}});
27852 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27858 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27859 * @param {Roo.data.Store} store The data store to unbind
27861 unbind : function(ds){
27862 ds.un("beforeload", this.beforeLoad, this);
27863 ds.un("load", this.onLoad, this);
27864 ds.un("loadexception", this.onLoadError, this);
27865 ds.un("remove", this.updateInfo, this);
27866 ds.un("add", this.updateInfo, this);
27867 this.ds = undefined;
27871 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27872 * @param {Roo.data.Store} store The data store to bind
27874 bind : function(ds){
27875 ds.on("beforeload", this.beforeLoad, this);
27876 ds.on("load", this.onLoad, this);
27877 ds.on("loadexception", this.onLoadError, this);
27878 ds.on("remove", this.updateInfo, this);
27879 ds.on("add", this.updateInfo, this);
27890 * @class Roo.bootstrap.MessageBar
27891 * @extends Roo.bootstrap.Component
27892 * Bootstrap MessageBar class
27893 * @cfg {String} html contents of the MessageBar
27894 * @cfg {String} weight (info | success | warning | danger) default info
27895 * @cfg {String} beforeClass insert the bar before the given class
27896 * @cfg {Boolean} closable (true | false) default false
27897 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27900 * Create a new Element
27901 * @param {Object} config The config object
27904 Roo.bootstrap.MessageBar = function(config){
27905 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27908 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27914 beforeClass: 'bootstrap-sticky-wrap',
27916 getAutoCreate : function(){
27920 cls: 'alert alert-dismissable alert-' + this.weight,
27925 html: this.html || ''
27931 cfg.cls += ' alert-messages-fixed';
27945 onRender : function(ct, position)
27947 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27950 var cfg = Roo.apply({}, this.getAutoCreate());
27954 cfg.cls += ' ' + this.cls;
27957 cfg.style = this.style;
27959 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27961 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27964 this.el.select('>button.close').on('click', this.hide, this);
27970 if (!this.rendered) {
27976 this.fireEvent('show', this);
27982 if (!this.rendered) {
27988 this.fireEvent('hide', this);
27991 update : function()
27993 // var e = this.el.dom.firstChild;
27995 // if(this.closable){
27996 // e = e.nextSibling;
27999 // e.data = this.html || '';
28001 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28017 * @class Roo.bootstrap.Graph
28018 * @extends Roo.bootstrap.Component
28019 * Bootstrap Graph class
28023 @cfg {String} graphtype bar | vbar | pie
28024 @cfg {number} g_x coodinator | centre x (pie)
28025 @cfg {number} g_y coodinator | centre y (pie)
28026 @cfg {number} g_r radius (pie)
28027 @cfg {number} g_height height of the chart (respected by all elements in the set)
28028 @cfg {number} g_width width of the chart (respected by all elements in the set)
28029 @cfg {Object} title The title of the chart
28032 -opts (object) options for the chart
28034 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28035 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28037 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.
28038 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28040 o stretch (boolean)
28042 -opts (object) options for the pie
28045 o startAngle (number)
28046 o endAngle (number)
28050 * Create a new Input
28051 * @param {Object} config The config object
28054 Roo.bootstrap.Graph = function(config){
28055 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28061 * The img click event for the img.
28062 * @param {Roo.EventObject} e
28068 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28079 //g_colors: this.colors,
28086 getAutoCreate : function(){
28097 onRender : function(ct,position){
28100 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28102 if (typeof(Raphael) == 'undefined') {
28103 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28107 this.raphael = Raphael(this.el.dom);
28109 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28110 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28111 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28112 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28114 r.text(160, 10, "Single Series Chart").attr(txtattr);
28115 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28116 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28117 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28119 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28120 r.barchart(330, 10, 300, 220, data1);
28121 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28122 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28125 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28126 // r.barchart(30, 30, 560, 250, xdata, {
28127 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28128 // axis : "0 0 1 1",
28129 // axisxlabels : xdata
28130 // //yvalues : cols,
28133 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28135 // this.load(null,xdata,{
28136 // axis : "0 0 1 1",
28137 // axisxlabels : xdata
28142 load : function(graphtype,xdata,opts)
28144 this.raphael.clear();
28146 graphtype = this.graphtype;
28151 var r = this.raphael,
28152 fin = function () {
28153 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28155 fout = function () {
28156 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28158 pfin = function() {
28159 this.sector.stop();
28160 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28163 this.label[0].stop();
28164 this.label[0].attr({ r: 7.5 });
28165 this.label[1].attr({ "font-weight": 800 });
28168 pfout = function() {
28169 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28172 this.label[0].animate({ r: 5 }, 500, "bounce");
28173 this.label[1].attr({ "font-weight": 400 });
28179 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28182 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28185 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28186 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28188 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28195 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28200 setTitle: function(o)
28205 initEvents: function() {
28208 this.el.on('click', this.onClick, this);
28212 onClick : function(e)
28214 Roo.log('img onclick');
28215 this.fireEvent('click', this, e);
28227 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28230 * @class Roo.bootstrap.dash.NumberBox
28231 * @extends Roo.bootstrap.Component
28232 * Bootstrap NumberBox class
28233 * @cfg {String} headline Box headline
28234 * @cfg {String} content Box content
28235 * @cfg {String} icon Box icon
28236 * @cfg {String} footer Footer text
28237 * @cfg {String} fhref Footer href
28240 * Create a new NumberBox
28241 * @param {Object} config The config object
28245 Roo.bootstrap.dash.NumberBox = function(config){
28246 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28250 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28259 getAutoCreate : function(){
28263 cls : 'small-box ',
28271 cls : 'roo-headline',
28272 html : this.headline
28276 cls : 'roo-content',
28277 html : this.content
28291 cls : 'ion ' + this.icon
28300 cls : 'small-box-footer',
28301 href : this.fhref || '#',
28305 cfg.cn.push(footer);
28312 onRender : function(ct,position){
28313 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28320 setHeadline: function (value)
28322 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28325 setFooter: function (value, href)
28327 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28330 this.el.select('a.small-box-footer',true).first().attr('href', href);
28335 setContent: function (value)
28337 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28340 initEvents: function()
28354 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28357 * @class Roo.bootstrap.dash.TabBox
28358 * @extends Roo.bootstrap.Component
28359 * Bootstrap TabBox class
28360 * @cfg {String} title Title of the TabBox
28361 * @cfg {String} icon Icon of the TabBox
28362 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28363 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28366 * Create a new TabBox
28367 * @param {Object} config The config object
28371 Roo.bootstrap.dash.TabBox = function(config){
28372 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28377 * When a pane is added
28378 * @param {Roo.bootstrap.dash.TabPane} pane
28382 * @event activatepane
28383 * When a pane is activated
28384 * @param {Roo.bootstrap.dash.TabPane} pane
28386 "activatepane" : true
28394 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28399 tabScrollable : false,
28401 getChildContainer : function()
28403 return this.el.select('.tab-content', true).first();
28406 getAutoCreate : function(){
28410 cls: 'pull-left header',
28418 cls: 'fa ' + this.icon
28424 cls: 'nav nav-tabs pull-right',
28430 if(this.tabScrollable){
28437 cls: 'nav nav-tabs pull-right',
28448 cls: 'nav-tabs-custom',
28453 cls: 'tab-content no-padding',
28461 initEvents : function()
28463 //Roo.log('add add pane handler');
28464 this.on('addpane', this.onAddPane, this);
28467 * Updates the box title
28468 * @param {String} html to set the title to.
28470 setTitle : function(value)
28472 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28474 onAddPane : function(pane)
28476 this.panes.push(pane);
28477 //Roo.log('addpane');
28479 // tabs are rendere left to right..
28480 if(!this.showtabs){
28484 var ctr = this.el.select('.nav-tabs', true).first();
28487 var existing = ctr.select('.nav-tab',true);
28488 var qty = existing.getCount();;
28491 var tab = ctr.createChild({
28493 cls : 'nav-tab' + (qty ? '' : ' active'),
28501 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28504 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28506 pane.el.addClass('active');
28511 onTabClick : function(ev,un,ob,pane)
28513 //Roo.log('tab - prev default');
28514 ev.preventDefault();
28517 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28518 pane.tab.addClass('active');
28519 //Roo.log(pane.title);
28520 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28521 // technically we should have a deactivate event.. but maybe add later.
28522 // and it should not de-activate the selected tab...
28523 this.fireEvent('activatepane', pane);
28524 pane.el.addClass('active');
28525 pane.fireEvent('activate');
28530 getActivePane : function()
28533 Roo.each(this.panes, function(p) {
28534 if(p.el.hasClass('active')){
28555 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28557 * @class Roo.bootstrap.TabPane
28558 * @extends Roo.bootstrap.Component
28559 * Bootstrap TabPane class
28560 * @cfg {Boolean} active (false | true) Default false
28561 * @cfg {String} title title of panel
28565 * Create a new TabPane
28566 * @param {Object} config The config object
28569 Roo.bootstrap.dash.TabPane = function(config){
28570 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28576 * When a pane is activated
28577 * @param {Roo.bootstrap.dash.TabPane} pane
28584 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28589 // the tabBox that this is attached to.
28592 getAutoCreate : function()
28600 cfg.cls += ' active';
28605 initEvents : function()
28607 //Roo.log('trigger add pane handler');
28608 this.parent().fireEvent('addpane', this)
28612 * Updates the tab title
28613 * @param {String} html to set the title to.
28615 setTitle: function(str)
28621 this.tab.select('a', true).first().dom.innerHTML = str;
28638 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28641 * @class Roo.bootstrap.menu.Menu
28642 * @extends Roo.bootstrap.Component
28643 * Bootstrap Menu class - container for Menu
28644 * @cfg {String} html Text of the menu
28645 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28646 * @cfg {String} icon Font awesome icon
28647 * @cfg {String} pos Menu align to (top | bottom) default bottom
28651 * Create a new Menu
28652 * @param {Object} config The config object
28656 Roo.bootstrap.menu.Menu = function(config){
28657 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28661 * @event beforeshow
28662 * Fires before this menu is displayed
28663 * @param {Roo.bootstrap.menu.Menu} this
28667 * @event beforehide
28668 * Fires before this menu is hidden
28669 * @param {Roo.bootstrap.menu.Menu} this
28674 * Fires after this menu is displayed
28675 * @param {Roo.bootstrap.menu.Menu} this
28680 * Fires after this menu is hidden
28681 * @param {Roo.bootstrap.menu.Menu} this
28686 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28687 * @param {Roo.bootstrap.menu.Menu} this
28688 * @param {Roo.EventObject} e
28695 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28699 weight : 'default',
28704 getChildContainer : function() {
28705 if(this.isSubMenu){
28709 return this.el.select('ul.dropdown-menu', true).first();
28712 getAutoCreate : function()
28717 cls : 'roo-menu-text',
28725 cls : 'fa ' + this.icon
28736 cls : 'dropdown-button btn btn-' + this.weight,
28741 cls : 'dropdown-toggle btn btn-' + this.weight,
28751 cls : 'dropdown-menu'
28757 if(this.pos == 'top'){
28758 cfg.cls += ' dropup';
28761 if(this.isSubMenu){
28764 cls : 'dropdown-menu'
28771 onRender : function(ct, position)
28773 this.isSubMenu = ct.hasClass('dropdown-submenu');
28775 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28778 initEvents : function()
28780 if(this.isSubMenu){
28784 this.hidden = true;
28786 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28787 this.triggerEl.on('click', this.onTriggerPress, this);
28789 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28790 this.buttonEl.on('click', this.onClick, this);
28796 if(this.isSubMenu){
28800 return this.el.select('ul.dropdown-menu', true).first();
28803 onClick : function(e)
28805 this.fireEvent("click", this, e);
28808 onTriggerPress : function(e)
28810 if (this.isVisible()) {
28817 isVisible : function(){
28818 return !this.hidden;
28823 this.fireEvent("beforeshow", this);
28825 this.hidden = false;
28826 this.el.addClass('open');
28828 Roo.get(document).on("mouseup", this.onMouseUp, this);
28830 this.fireEvent("show", this);
28837 this.fireEvent("beforehide", this);
28839 this.hidden = true;
28840 this.el.removeClass('open');
28842 Roo.get(document).un("mouseup", this.onMouseUp);
28844 this.fireEvent("hide", this);
28847 onMouseUp : function()
28861 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28864 * @class Roo.bootstrap.menu.Item
28865 * @extends Roo.bootstrap.Component
28866 * Bootstrap MenuItem class
28867 * @cfg {Boolean} submenu (true | false) default false
28868 * @cfg {String} html text of the item
28869 * @cfg {String} href the link
28870 * @cfg {Boolean} disable (true | false) default false
28871 * @cfg {Boolean} preventDefault (true | false) default true
28872 * @cfg {String} icon Font awesome icon
28873 * @cfg {String} pos Submenu align to (left | right) default right
28877 * Create a new Item
28878 * @param {Object} config The config object
28882 Roo.bootstrap.menu.Item = function(config){
28883 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28887 * Fires when the mouse is hovering over this menu
28888 * @param {Roo.bootstrap.menu.Item} this
28889 * @param {Roo.EventObject} e
28894 * Fires when the mouse exits this menu
28895 * @param {Roo.bootstrap.menu.Item} this
28896 * @param {Roo.EventObject} e
28902 * The raw click event for the entire grid.
28903 * @param {Roo.EventObject} e
28909 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28914 preventDefault: true,
28919 getAutoCreate : function()
28924 cls : 'roo-menu-item-text',
28932 cls : 'fa ' + this.icon
28941 href : this.href || '#',
28948 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28952 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28954 if(this.pos == 'left'){
28955 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28962 initEvents : function()
28964 this.el.on('mouseover', this.onMouseOver, this);
28965 this.el.on('mouseout', this.onMouseOut, this);
28967 this.el.select('a', true).first().on('click', this.onClick, this);
28971 onClick : function(e)
28973 if(this.preventDefault){
28974 e.preventDefault();
28977 this.fireEvent("click", this, e);
28980 onMouseOver : function(e)
28982 if(this.submenu && this.pos == 'left'){
28983 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28986 this.fireEvent("mouseover", this, e);
28989 onMouseOut : function(e)
28991 this.fireEvent("mouseout", this, e);
29003 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29006 * @class Roo.bootstrap.menu.Separator
29007 * @extends Roo.bootstrap.Component
29008 * Bootstrap Separator class
29011 * Create a new Separator
29012 * @param {Object} config The config object
29016 Roo.bootstrap.menu.Separator = function(config){
29017 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29020 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29022 getAutoCreate : function(){
29025 cls: 'dropdown-divider divider'
29043 * @class Roo.bootstrap.Tooltip
29044 * Bootstrap Tooltip class
29045 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29046 * to determine which dom element triggers the tooltip.
29048 * It needs to add support for additional attributes like tooltip-position
29051 * Create a new Toolti
29052 * @param {Object} config The config object
29055 Roo.bootstrap.Tooltip = function(config){
29056 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29058 this.alignment = Roo.bootstrap.Tooltip.alignment;
29060 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29061 this.alignment = config.alignment;
29066 Roo.apply(Roo.bootstrap.Tooltip, {
29068 * @function init initialize tooltip monitoring.
29072 currentTip : false,
29073 currentRegion : false,
29079 Roo.get(document).on('mouseover', this.enter ,this);
29080 Roo.get(document).on('mouseout', this.leave, this);
29083 this.currentTip = new Roo.bootstrap.Tooltip();
29086 enter : function(ev)
29088 var dom = ev.getTarget();
29090 //Roo.log(['enter',dom]);
29091 var el = Roo.fly(dom);
29092 if (this.currentEl) {
29094 //Roo.log(this.currentEl);
29095 //Roo.log(this.currentEl.contains(dom));
29096 if (this.currentEl == el) {
29099 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29105 if (this.currentTip.el) {
29106 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29110 if(!el || el.dom == document){
29116 if (!el.attr('tooltip')) {
29117 pel = el.findParent("[tooltip]");
29119 bindEl = Roo.get(pel);
29125 // you can not look for children, as if el is the body.. then everythign is the child..
29126 if (!pel && !el.attr('tooltip')) { //
29127 if (!el.select("[tooltip]").elements.length) {
29130 // is the mouse over this child...?
29131 bindEl = el.select("[tooltip]").first();
29132 var xy = ev.getXY();
29133 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29134 //Roo.log("not in region.");
29137 //Roo.log("child element over..");
29140 this.currentEl = el;
29141 this.currentTip.bind(bindEl);
29142 this.currentRegion = Roo.lib.Region.getRegion(dom);
29143 this.currentTip.enter();
29146 leave : function(ev)
29148 var dom = ev.getTarget();
29149 //Roo.log(['leave',dom]);
29150 if (!this.currentEl) {
29155 if (dom != this.currentEl.dom) {
29158 var xy = ev.getXY();
29159 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29162 // only activate leave if mouse cursor is outside... bounding box..
29167 if (this.currentTip) {
29168 this.currentTip.leave();
29170 //Roo.log('clear currentEl');
29171 this.currentEl = false;
29176 'left' : ['r-l', [-2,0], 'right'],
29177 'right' : ['l-r', [2,0], 'left'],
29178 'bottom' : ['t-b', [0,2], 'top'],
29179 'top' : [ 'b-t', [0,-2], 'bottom']
29185 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29190 delay : null, // can be { show : 300 , hide: 500}
29194 hoverState : null, //???
29196 placement : 'bottom',
29200 getAutoCreate : function(){
29207 cls : 'tooltip-arrow arrow'
29210 cls : 'tooltip-inner'
29217 bind : function(el)
29222 initEvents : function()
29224 this.arrowEl = this.el.select('.arrow', true).first();
29225 this.innerEl = this.el.select('.tooltip-inner', true).first();
29228 enter : function () {
29230 if (this.timeout != null) {
29231 clearTimeout(this.timeout);
29234 this.hoverState = 'in';
29235 //Roo.log("enter - show");
29236 if (!this.delay || !this.delay.show) {
29241 this.timeout = setTimeout(function () {
29242 if (_t.hoverState == 'in') {
29245 }, this.delay.show);
29249 clearTimeout(this.timeout);
29251 this.hoverState = 'out';
29252 if (!this.delay || !this.delay.hide) {
29258 this.timeout = setTimeout(function () {
29259 //Roo.log("leave - timeout");
29261 if (_t.hoverState == 'out') {
29263 Roo.bootstrap.Tooltip.currentEl = false;
29268 show : function (msg)
29271 this.render(document.body);
29274 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29276 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29278 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29280 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29281 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29283 var placement = typeof this.placement == 'function' ?
29284 this.placement.call(this, this.el, on_el) :
29287 var autoToken = /\s?auto?\s?/i;
29288 var autoPlace = autoToken.test(placement);
29290 placement = placement.replace(autoToken, '') || 'top';
29294 //this.el.setXY([0,0]);
29296 //this.el.dom.style.display='block';
29298 //this.el.appendTo(on_el);
29300 var p = this.getPosition();
29301 var box = this.el.getBox();
29307 var align = this.alignment[placement];
29309 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29311 if(placement == 'top' || placement == 'bottom'){
29313 placement = 'right';
29316 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29317 placement = 'left';
29320 var scroll = Roo.select('body', true).first().getScroll();
29322 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29326 align = this.alignment[placement];
29328 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29332 var elems = document.getElementsByTagName('div');
29333 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29334 for (var i = 0; i < elems.length; i++) {
29335 var zindex = Number.parseInt(
29336 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29339 if (zindex > highest) {
29346 this.el.dom.style.zIndex = highest;
29348 this.el.alignTo(this.bindEl, align[0],align[1]);
29349 //var arrow = this.el.select('.arrow',true).first();
29350 //arrow.set(align[2],
29352 this.el.addClass(placement);
29353 this.el.addClass("bs-tooltip-"+ placement);
29355 this.el.addClass('in fade show');
29357 this.hoverState = null;
29359 if (this.el.hasClass('fade')) {
29374 //this.el.setXY([0,0]);
29375 this.el.removeClass(['show', 'in']);
29391 * @class Roo.bootstrap.LocationPicker
29392 * @extends Roo.bootstrap.Component
29393 * Bootstrap LocationPicker class
29394 * @cfg {Number} latitude Position when init default 0
29395 * @cfg {Number} longitude Position when init default 0
29396 * @cfg {Number} zoom default 15
29397 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29398 * @cfg {Boolean} mapTypeControl default false
29399 * @cfg {Boolean} disableDoubleClickZoom default false
29400 * @cfg {Boolean} scrollwheel default true
29401 * @cfg {Boolean} streetViewControl default false
29402 * @cfg {Number} radius default 0
29403 * @cfg {String} locationName
29404 * @cfg {Boolean} draggable default true
29405 * @cfg {Boolean} enableAutocomplete default false
29406 * @cfg {Boolean} enableReverseGeocode default true
29407 * @cfg {String} markerTitle
29410 * Create a new LocationPicker
29411 * @param {Object} config The config object
29415 Roo.bootstrap.LocationPicker = function(config){
29417 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29422 * Fires when the picker initialized.
29423 * @param {Roo.bootstrap.LocationPicker} this
29424 * @param {Google Location} location
29428 * @event positionchanged
29429 * Fires when the picker position changed.
29430 * @param {Roo.bootstrap.LocationPicker} this
29431 * @param {Google Location} location
29433 positionchanged : true,
29436 * Fires when the map resize.
29437 * @param {Roo.bootstrap.LocationPicker} this
29442 * Fires when the map show.
29443 * @param {Roo.bootstrap.LocationPicker} this
29448 * Fires when the map hide.
29449 * @param {Roo.bootstrap.LocationPicker} this
29454 * Fires when click the map.
29455 * @param {Roo.bootstrap.LocationPicker} this
29456 * @param {Map event} e
29460 * @event mapRightClick
29461 * Fires when right click the map.
29462 * @param {Roo.bootstrap.LocationPicker} this
29463 * @param {Map event} e
29465 mapRightClick : true,
29467 * @event markerClick
29468 * Fires when click the marker.
29469 * @param {Roo.bootstrap.LocationPicker} this
29470 * @param {Map event} e
29472 markerClick : true,
29474 * @event markerRightClick
29475 * Fires when right click the marker.
29476 * @param {Roo.bootstrap.LocationPicker} this
29477 * @param {Map event} e
29479 markerRightClick : true,
29481 * @event OverlayViewDraw
29482 * Fires when OverlayView Draw
29483 * @param {Roo.bootstrap.LocationPicker} this
29485 OverlayViewDraw : true,
29487 * @event OverlayViewOnAdd
29488 * Fires when OverlayView Draw
29489 * @param {Roo.bootstrap.LocationPicker} this
29491 OverlayViewOnAdd : true,
29493 * @event OverlayViewOnRemove
29494 * Fires when OverlayView Draw
29495 * @param {Roo.bootstrap.LocationPicker} this
29497 OverlayViewOnRemove : true,
29499 * @event OverlayViewShow
29500 * Fires when OverlayView Draw
29501 * @param {Roo.bootstrap.LocationPicker} this
29502 * @param {Pixel} cpx
29504 OverlayViewShow : true,
29506 * @event OverlayViewHide
29507 * Fires when OverlayView Draw
29508 * @param {Roo.bootstrap.LocationPicker} this
29510 OverlayViewHide : true,
29512 * @event loadexception
29513 * Fires when load google lib failed.
29514 * @param {Roo.bootstrap.LocationPicker} this
29516 loadexception : true
29521 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29523 gMapContext: false,
29529 mapTypeControl: false,
29530 disableDoubleClickZoom: false,
29532 streetViewControl: false,
29536 enableAutocomplete: false,
29537 enableReverseGeocode: true,
29540 getAutoCreate: function()
29545 cls: 'roo-location-picker'
29551 initEvents: function(ct, position)
29553 if(!this.el.getWidth() || this.isApplied()){
29557 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29562 initial: function()
29564 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29565 this.fireEvent('loadexception', this);
29569 if(!this.mapTypeId){
29570 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29573 this.gMapContext = this.GMapContext();
29575 this.initOverlayView();
29577 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29581 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29582 _this.setPosition(_this.gMapContext.marker.position);
29585 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29586 _this.fireEvent('mapClick', this, event);
29590 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29591 _this.fireEvent('mapRightClick', this, event);
29595 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29596 _this.fireEvent('markerClick', this, event);
29600 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29601 _this.fireEvent('markerRightClick', this, event);
29605 this.setPosition(this.gMapContext.location);
29607 this.fireEvent('initial', this, this.gMapContext.location);
29610 initOverlayView: function()
29614 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29618 _this.fireEvent('OverlayViewDraw', _this);
29623 _this.fireEvent('OverlayViewOnAdd', _this);
29626 onRemove: function()
29628 _this.fireEvent('OverlayViewOnRemove', _this);
29631 show: function(cpx)
29633 _this.fireEvent('OverlayViewShow', _this, cpx);
29638 _this.fireEvent('OverlayViewHide', _this);
29644 fromLatLngToContainerPixel: function(event)
29646 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29649 isApplied: function()
29651 return this.getGmapContext() == false ? false : true;
29654 getGmapContext: function()
29656 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29659 GMapContext: function()
29661 var position = new google.maps.LatLng(this.latitude, this.longitude);
29663 var _map = new google.maps.Map(this.el.dom, {
29666 mapTypeId: this.mapTypeId,
29667 mapTypeControl: this.mapTypeControl,
29668 disableDoubleClickZoom: this.disableDoubleClickZoom,
29669 scrollwheel: this.scrollwheel,
29670 streetViewControl: this.streetViewControl,
29671 locationName: this.locationName,
29672 draggable: this.draggable,
29673 enableAutocomplete: this.enableAutocomplete,
29674 enableReverseGeocode: this.enableReverseGeocode
29677 var _marker = new google.maps.Marker({
29678 position: position,
29680 title: this.markerTitle,
29681 draggable: this.draggable
29688 location: position,
29689 radius: this.radius,
29690 locationName: this.locationName,
29691 addressComponents: {
29692 formatted_address: null,
29693 addressLine1: null,
29694 addressLine2: null,
29696 streetNumber: null,
29700 stateOrProvince: null
29703 domContainer: this.el.dom,
29704 geodecoder: new google.maps.Geocoder()
29708 drawCircle: function(center, radius, options)
29710 if (this.gMapContext.circle != null) {
29711 this.gMapContext.circle.setMap(null);
29715 options = Roo.apply({}, options, {
29716 strokeColor: "#0000FF",
29717 strokeOpacity: .35,
29719 fillColor: "#0000FF",
29723 options.map = this.gMapContext.map;
29724 options.radius = radius;
29725 options.center = center;
29726 this.gMapContext.circle = new google.maps.Circle(options);
29727 return this.gMapContext.circle;
29733 setPosition: function(location)
29735 this.gMapContext.location = location;
29736 this.gMapContext.marker.setPosition(location);
29737 this.gMapContext.map.panTo(location);
29738 this.drawCircle(location, this.gMapContext.radius, {});
29742 if (this.gMapContext.settings.enableReverseGeocode) {
29743 this.gMapContext.geodecoder.geocode({
29744 latLng: this.gMapContext.location
29745 }, function(results, status) {
29747 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29748 _this.gMapContext.locationName = results[0].formatted_address;
29749 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29751 _this.fireEvent('positionchanged', this, location);
29758 this.fireEvent('positionchanged', this, location);
29763 google.maps.event.trigger(this.gMapContext.map, "resize");
29765 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29767 this.fireEvent('resize', this);
29770 setPositionByLatLng: function(latitude, longitude)
29772 this.setPosition(new google.maps.LatLng(latitude, longitude));
29775 getCurrentPosition: function()
29778 latitude: this.gMapContext.location.lat(),
29779 longitude: this.gMapContext.location.lng()
29783 getAddressName: function()
29785 return this.gMapContext.locationName;
29788 getAddressComponents: function()
29790 return this.gMapContext.addressComponents;
29793 address_component_from_google_geocode: function(address_components)
29797 for (var i = 0; i < address_components.length; i++) {
29798 var component = address_components[i];
29799 if (component.types.indexOf("postal_code") >= 0) {
29800 result.postalCode = component.short_name;
29801 } else if (component.types.indexOf("street_number") >= 0) {
29802 result.streetNumber = component.short_name;
29803 } else if (component.types.indexOf("route") >= 0) {
29804 result.streetName = component.short_name;
29805 } else if (component.types.indexOf("neighborhood") >= 0) {
29806 result.city = component.short_name;
29807 } else if (component.types.indexOf("locality") >= 0) {
29808 result.city = component.short_name;
29809 } else if (component.types.indexOf("sublocality") >= 0) {
29810 result.district = component.short_name;
29811 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29812 result.stateOrProvince = component.short_name;
29813 } else if (component.types.indexOf("country") >= 0) {
29814 result.country = component.short_name;
29818 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29819 result.addressLine2 = "";
29823 setZoomLevel: function(zoom)
29825 this.gMapContext.map.setZoom(zoom);
29838 this.fireEvent('show', this);
29849 this.fireEvent('hide', this);
29854 Roo.apply(Roo.bootstrap.LocationPicker, {
29856 OverlayView : function(map, options)
29858 options = options || {};
29865 * @class Roo.bootstrap.Alert
29866 * @extends Roo.bootstrap.Component
29867 * Bootstrap Alert class - shows an alert area box
29869 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29870 Enter a valid email address
29873 * @cfg {String} title The title of alert
29874 * @cfg {String} html The content of alert
29875 * @cfg {String} weight ( success | info | warning | danger )
29876 * @cfg {String} fa font-awesomeicon
29877 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29878 * @cfg {Boolean} close true to show a x closer
29882 * Create a new alert
29883 * @param {Object} config The config object
29887 Roo.bootstrap.Alert = function(config){
29888 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29892 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29898 faicon: false, // BC
29902 getAutoCreate : function()
29914 style : this.close ? '' : 'display:none'
29918 cls : 'roo-alert-icon'
29923 cls : 'roo-alert-title',
29928 cls : 'roo-alert-text',
29935 cfg.cn[0].cls += ' fa ' + this.faicon;
29938 cfg.cn[0].cls += ' fa ' + this.fa;
29942 cfg.cls += ' alert-' + this.weight;
29948 initEvents: function()
29950 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29951 this.titleEl = this.el.select('.roo-alert-title',true).first();
29952 this.iconEl = this.el.select('.roo-alert-icon',true).first();
29953 if (this.seconds > 0) {
29954 this.hide.defer(this.seconds, this);
29958 setTitle : function(str)
29960 this.titleEl.dom.innerHTML = str;
29963 setText : function(str)
29965 this.titleEl.dom.innerHTML = str;
29968 setWeight : function(weight)
29971 this.el.removeClass('alert-' + this.weight);
29974 this.weight = weight;
29976 this.el.addClass('alert-' + this.weight);
29979 setIcon : function(icon)
29982 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29985 this.faicon = icon;
29987 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30008 * @class Roo.bootstrap.UploadCropbox
30009 * @extends Roo.bootstrap.Component
30010 * Bootstrap UploadCropbox class
30011 * @cfg {String} emptyText show when image has been loaded
30012 * @cfg {String} rotateNotify show when image too small to rotate
30013 * @cfg {Number} errorTimeout default 3000
30014 * @cfg {Number} minWidth default 300
30015 * @cfg {Number} minHeight default 300
30016 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30017 * @cfg {Boolean} isDocument (true|false) default false
30018 * @cfg {String} url action url
30019 * @cfg {String} paramName default 'imageUpload'
30020 * @cfg {String} method default POST
30021 * @cfg {Boolean} loadMask (true|false) default true
30022 * @cfg {Boolean} loadingText default 'Loading...'
30025 * Create a new UploadCropbox
30026 * @param {Object} config The config object
30029 Roo.bootstrap.UploadCropbox = function(config){
30030 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30034 * @event beforeselectfile
30035 * Fire before select file
30036 * @param {Roo.bootstrap.UploadCropbox} this
30038 "beforeselectfile" : true,
30041 * Fire after initEvent
30042 * @param {Roo.bootstrap.UploadCropbox} this
30047 * Fire after initEvent
30048 * @param {Roo.bootstrap.UploadCropbox} this
30049 * @param {String} data
30054 * Fire when preparing the file data
30055 * @param {Roo.bootstrap.UploadCropbox} this
30056 * @param {Object} file
30061 * Fire when get exception
30062 * @param {Roo.bootstrap.UploadCropbox} this
30063 * @param {XMLHttpRequest} xhr
30065 "exception" : true,
30067 * @event beforeloadcanvas
30068 * Fire before load the canvas
30069 * @param {Roo.bootstrap.UploadCropbox} this
30070 * @param {String} src
30072 "beforeloadcanvas" : true,
30075 * Fire when trash image
30076 * @param {Roo.bootstrap.UploadCropbox} this
30081 * Fire when download the image
30082 * @param {Roo.bootstrap.UploadCropbox} this
30086 * @event footerbuttonclick
30087 * Fire when footerbuttonclick
30088 * @param {Roo.bootstrap.UploadCropbox} this
30089 * @param {String} type
30091 "footerbuttonclick" : true,
30095 * @param {Roo.bootstrap.UploadCropbox} this
30100 * Fire when rotate the image
30101 * @param {Roo.bootstrap.UploadCropbox} this
30102 * @param {String} pos
30107 * Fire when inspect the file
30108 * @param {Roo.bootstrap.UploadCropbox} this
30109 * @param {Object} file
30114 * Fire when xhr upload the file
30115 * @param {Roo.bootstrap.UploadCropbox} this
30116 * @param {Object} data
30121 * Fire when arrange the file data
30122 * @param {Roo.bootstrap.UploadCropbox} this
30123 * @param {Object} formData
30128 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30131 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30133 emptyText : 'Click to upload image',
30134 rotateNotify : 'Image is too small to rotate',
30135 errorTimeout : 3000,
30149 cropType : 'image/jpeg',
30151 canvasLoaded : false,
30152 isDocument : false,
30154 paramName : 'imageUpload',
30156 loadingText : 'Loading...',
30159 getAutoCreate : function()
30163 cls : 'roo-upload-cropbox',
30167 cls : 'roo-upload-cropbox-selector',
30172 cls : 'roo-upload-cropbox-body',
30173 style : 'cursor:pointer',
30177 cls : 'roo-upload-cropbox-preview'
30181 cls : 'roo-upload-cropbox-thumb'
30185 cls : 'roo-upload-cropbox-empty-notify',
30186 html : this.emptyText
30190 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30191 html : this.rotateNotify
30197 cls : 'roo-upload-cropbox-footer',
30200 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30210 onRender : function(ct, position)
30212 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30214 if (this.buttons.length) {
30216 Roo.each(this.buttons, function(bb) {
30218 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30220 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30226 this.maskEl = this.el;
30230 initEvents : function()
30232 this.urlAPI = (window.createObjectURL && window) ||
30233 (window.URL && URL.revokeObjectURL && URL) ||
30234 (window.webkitURL && webkitURL);
30236 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30237 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30239 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30240 this.selectorEl.hide();
30242 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30243 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30245 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30246 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30247 this.thumbEl.hide();
30249 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30250 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30252 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30253 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30254 this.errorEl.hide();
30256 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30257 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30258 this.footerEl.hide();
30260 this.setThumbBoxSize();
30266 this.fireEvent('initial', this);
30273 window.addEventListener("resize", function() { _this.resize(); } );
30275 this.bodyEl.on('click', this.beforeSelectFile, this);
30278 this.bodyEl.on('touchstart', this.onTouchStart, this);
30279 this.bodyEl.on('touchmove', this.onTouchMove, this);
30280 this.bodyEl.on('touchend', this.onTouchEnd, this);
30284 this.bodyEl.on('mousedown', this.onMouseDown, this);
30285 this.bodyEl.on('mousemove', this.onMouseMove, this);
30286 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30287 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30288 Roo.get(document).on('mouseup', this.onMouseUp, this);
30291 this.selectorEl.on('change', this.onFileSelected, this);
30297 this.baseScale = 1;
30299 this.baseRotate = 1;
30300 this.dragable = false;
30301 this.pinching = false;
30304 this.cropData = false;
30305 this.notifyEl.dom.innerHTML = this.emptyText;
30307 this.selectorEl.dom.value = '';
30311 resize : function()
30313 if(this.fireEvent('resize', this) != false){
30314 this.setThumbBoxPosition();
30315 this.setCanvasPosition();
30319 onFooterButtonClick : function(e, el, o, type)
30322 case 'rotate-left' :
30323 this.onRotateLeft(e);
30325 case 'rotate-right' :
30326 this.onRotateRight(e);
30329 this.beforeSelectFile(e);
30344 this.fireEvent('footerbuttonclick', this, type);
30347 beforeSelectFile : function(e)
30349 e.preventDefault();
30351 if(this.fireEvent('beforeselectfile', this) != false){
30352 this.selectorEl.dom.click();
30356 onFileSelected : function(e)
30358 e.preventDefault();
30360 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30364 var file = this.selectorEl.dom.files[0];
30366 if(this.fireEvent('inspect', this, file) != false){
30367 this.prepare(file);
30372 trash : function(e)
30374 this.fireEvent('trash', this);
30377 download : function(e)
30379 this.fireEvent('download', this);
30382 loadCanvas : function(src)
30384 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30388 this.imageEl = document.createElement('img');
30392 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30394 this.imageEl.src = src;
30398 onLoadCanvas : function()
30400 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30401 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30403 this.bodyEl.un('click', this.beforeSelectFile, this);
30405 this.notifyEl.hide();
30406 this.thumbEl.show();
30407 this.footerEl.show();
30409 this.baseRotateLevel();
30411 if(this.isDocument){
30412 this.setThumbBoxSize();
30415 this.setThumbBoxPosition();
30417 this.baseScaleLevel();
30423 this.canvasLoaded = true;
30426 this.maskEl.unmask();
30431 setCanvasPosition : function()
30433 if(!this.canvasEl){
30437 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30438 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30440 this.previewEl.setLeft(pw);
30441 this.previewEl.setTop(ph);
30445 onMouseDown : function(e)
30449 this.dragable = true;
30450 this.pinching = false;
30452 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30453 this.dragable = false;
30457 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30458 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30462 onMouseMove : function(e)
30466 if(!this.canvasLoaded){
30470 if (!this.dragable){
30474 var minX = Math.ceil(this.thumbEl.getLeft(true));
30475 var minY = Math.ceil(this.thumbEl.getTop(true));
30477 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30478 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30480 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30481 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30483 x = x - this.mouseX;
30484 y = y - this.mouseY;
30486 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30487 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30489 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30490 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30492 this.previewEl.setLeft(bgX);
30493 this.previewEl.setTop(bgY);
30495 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30496 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30499 onMouseUp : function(e)
30503 this.dragable = false;
30506 onMouseWheel : function(e)
30510 this.startScale = this.scale;
30512 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30514 if(!this.zoomable()){
30515 this.scale = this.startScale;
30524 zoomable : function()
30526 var minScale = this.thumbEl.getWidth() / this.minWidth;
30528 if(this.minWidth < this.minHeight){
30529 minScale = this.thumbEl.getHeight() / this.minHeight;
30532 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30533 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30537 (this.rotate == 0 || this.rotate == 180) &&
30539 width > this.imageEl.OriginWidth ||
30540 height > this.imageEl.OriginHeight ||
30541 (width < this.minWidth && height < this.minHeight)
30549 (this.rotate == 90 || this.rotate == 270) &&
30551 width > this.imageEl.OriginWidth ||
30552 height > this.imageEl.OriginHeight ||
30553 (width < this.minHeight && height < this.minWidth)
30560 !this.isDocument &&
30561 (this.rotate == 0 || this.rotate == 180) &&
30563 width < this.minWidth ||
30564 width > this.imageEl.OriginWidth ||
30565 height < this.minHeight ||
30566 height > this.imageEl.OriginHeight
30573 !this.isDocument &&
30574 (this.rotate == 90 || this.rotate == 270) &&
30576 width < this.minHeight ||
30577 width > this.imageEl.OriginWidth ||
30578 height < this.minWidth ||
30579 height > this.imageEl.OriginHeight
30589 onRotateLeft : function(e)
30591 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30593 var minScale = this.thumbEl.getWidth() / this.minWidth;
30595 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30596 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30598 this.startScale = this.scale;
30600 while (this.getScaleLevel() < minScale){
30602 this.scale = this.scale + 1;
30604 if(!this.zoomable()){
30609 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30610 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30615 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30622 this.scale = this.startScale;
30624 this.onRotateFail();
30629 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30631 if(this.isDocument){
30632 this.setThumbBoxSize();
30633 this.setThumbBoxPosition();
30634 this.setCanvasPosition();
30639 this.fireEvent('rotate', this, 'left');
30643 onRotateRight : function(e)
30645 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30647 var minScale = this.thumbEl.getWidth() / this.minWidth;
30649 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30650 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30652 this.startScale = this.scale;
30654 while (this.getScaleLevel() < minScale){
30656 this.scale = this.scale + 1;
30658 if(!this.zoomable()){
30663 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30664 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30669 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30676 this.scale = this.startScale;
30678 this.onRotateFail();
30683 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30685 if(this.isDocument){
30686 this.setThumbBoxSize();
30687 this.setThumbBoxPosition();
30688 this.setCanvasPosition();
30693 this.fireEvent('rotate', this, 'right');
30696 onRotateFail : function()
30698 this.errorEl.show(true);
30702 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30707 this.previewEl.dom.innerHTML = '';
30709 var canvasEl = document.createElement("canvas");
30711 var contextEl = canvasEl.getContext("2d");
30713 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30714 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30715 var center = this.imageEl.OriginWidth / 2;
30717 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30718 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30719 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30720 center = this.imageEl.OriginHeight / 2;
30723 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30725 contextEl.translate(center, center);
30726 contextEl.rotate(this.rotate * Math.PI / 180);
30728 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30730 this.canvasEl = document.createElement("canvas");
30732 this.contextEl = this.canvasEl.getContext("2d");
30734 switch (this.rotate) {
30737 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30738 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30740 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30745 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30746 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30748 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30749 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);
30753 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30758 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30759 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30761 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30762 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);
30766 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);
30771 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30772 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30774 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30775 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30779 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);
30786 this.previewEl.appendChild(this.canvasEl);
30788 this.setCanvasPosition();
30793 if(!this.canvasLoaded){
30797 var imageCanvas = document.createElement("canvas");
30799 var imageContext = imageCanvas.getContext("2d");
30801 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30802 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30804 var center = imageCanvas.width / 2;
30806 imageContext.translate(center, center);
30808 imageContext.rotate(this.rotate * Math.PI / 180);
30810 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30812 var canvas = document.createElement("canvas");
30814 var context = canvas.getContext("2d");
30816 canvas.width = this.minWidth;
30817 canvas.height = this.minHeight;
30819 switch (this.rotate) {
30822 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30823 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30825 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30826 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30828 var targetWidth = this.minWidth - 2 * x;
30829 var targetHeight = this.minHeight - 2 * y;
30833 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30834 scale = targetWidth / width;
30837 if(x > 0 && y == 0){
30838 scale = targetHeight / height;
30841 if(x > 0 && y > 0){
30842 scale = targetWidth / width;
30844 if(width < height){
30845 scale = targetHeight / height;
30849 context.scale(scale, scale);
30851 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30852 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30854 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30855 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30857 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30862 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30863 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30865 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30866 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30868 var targetWidth = this.minWidth - 2 * x;
30869 var targetHeight = this.minHeight - 2 * y;
30873 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30874 scale = targetWidth / width;
30877 if(x > 0 && y == 0){
30878 scale = targetHeight / height;
30881 if(x > 0 && y > 0){
30882 scale = targetWidth / width;
30884 if(width < height){
30885 scale = targetHeight / height;
30889 context.scale(scale, scale);
30891 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30892 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30894 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30895 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30897 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30899 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30904 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30905 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30907 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30908 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30910 var targetWidth = this.minWidth - 2 * x;
30911 var targetHeight = this.minHeight - 2 * y;
30915 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30916 scale = targetWidth / width;
30919 if(x > 0 && y == 0){
30920 scale = targetHeight / height;
30923 if(x > 0 && y > 0){
30924 scale = targetWidth / width;
30926 if(width < height){
30927 scale = targetHeight / height;
30931 context.scale(scale, scale);
30933 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30934 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30936 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30937 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30939 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30940 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30942 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30947 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30948 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30950 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30951 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30953 var targetWidth = this.minWidth - 2 * x;
30954 var targetHeight = this.minHeight - 2 * y;
30958 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30959 scale = targetWidth / width;
30962 if(x > 0 && y == 0){
30963 scale = targetHeight / height;
30966 if(x > 0 && y > 0){
30967 scale = targetWidth / width;
30969 if(width < height){
30970 scale = targetHeight / height;
30974 context.scale(scale, scale);
30976 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30977 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30979 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30980 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30982 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30984 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30991 this.cropData = canvas.toDataURL(this.cropType);
30993 if(this.fireEvent('crop', this, this.cropData) !== false){
30994 this.process(this.file, this.cropData);
31001 setThumbBoxSize : function()
31005 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31006 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31007 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31009 this.minWidth = width;
31010 this.minHeight = height;
31012 if(this.rotate == 90 || this.rotate == 270){
31013 this.minWidth = height;
31014 this.minHeight = width;
31019 width = Math.ceil(this.minWidth * height / this.minHeight);
31021 if(this.minWidth > this.minHeight){
31023 height = Math.ceil(this.minHeight * width / this.minWidth);
31026 this.thumbEl.setStyle({
31027 width : width + 'px',
31028 height : height + 'px'
31035 setThumbBoxPosition : function()
31037 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31038 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31040 this.thumbEl.setLeft(x);
31041 this.thumbEl.setTop(y);
31045 baseRotateLevel : function()
31047 this.baseRotate = 1;
31050 typeof(this.exif) != 'undefined' &&
31051 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31052 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31054 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31057 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31061 baseScaleLevel : function()
31065 if(this.isDocument){
31067 if(this.baseRotate == 6 || this.baseRotate == 8){
31069 height = this.thumbEl.getHeight();
31070 this.baseScale = height / this.imageEl.OriginWidth;
31072 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31073 width = this.thumbEl.getWidth();
31074 this.baseScale = width / this.imageEl.OriginHeight;
31080 height = this.thumbEl.getHeight();
31081 this.baseScale = height / this.imageEl.OriginHeight;
31083 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31084 width = this.thumbEl.getWidth();
31085 this.baseScale = width / this.imageEl.OriginWidth;
31091 if(this.baseRotate == 6 || this.baseRotate == 8){
31093 width = this.thumbEl.getHeight();
31094 this.baseScale = width / this.imageEl.OriginHeight;
31096 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31097 height = this.thumbEl.getWidth();
31098 this.baseScale = height / this.imageEl.OriginHeight;
31101 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31102 height = this.thumbEl.getWidth();
31103 this.baseScale = height / this.imageEl.OriginHeight;
31105 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31106 width = this.thumbEl.getHeight();
31107 this.baseScale = width / this.imageEl.OriginWidth;
31114 width = this.thumbEl.getWidth();
31115 this.baseScale = width / this.imageEl.OriginWidth;
31117 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31118 height = this.thumbEl.getHeight();
31119 this.baseScale = height / this.imageEl.OriginHeight;
31122 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31124 height = this.thumbEl.getHeight();
31125 this.baseScale = height / this.imageEl.OriginHeight;
31127 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31128 width = this.thumbEl.getWidth();
31129 this.baseScale = width / this.imageEl.OriginWidth;
31137 getScaleLevel : function()
31139 return this.baseScale * Math.pow(1.1, this.scale);
31142 onTouchStart : function(e)
31144 if(!this.canvasLoaded){
31145 this.beforeSelectFile(e);
31149 var touches = e.browserEvent.touches;
31155 if(touches.length == 1){
31156 this.onMouseDown(e);
31160 if(touches.length != 2){
31166 for(var i = 0, finger; finger = touches[i]; i++){
31167 coords.push(finger.pageX, finger.pageY);
31170 var x = Math.pow(coords[0] - coords[2], 2);
31171 var y = Math.pow(coords[1] - coords[3], 2);
31173 this.startDistance = Math.sqrt(x + y);
31175 this.startScale = this.scale;
31177 this.pinching = true;
31178 this.dragable = false;
31182 onTouchMove : function(e)
31184 if(!this.pinching && !this.dragable){
31188 var touches = e.browserEvent.touches;
31195 this.onMouseMove(e);
31201 for(var i = 0, finger; finger = touches[i]; i++){
31202 coords.push(finger.pageX, finger.pageY);
31205 var x = Math.pow(coords[0] - coords[2], 2);
31206 var y = Math.pow(coords[1] - coords[3], 2);
31208 this.endDistance = Math.sqrt(x + y);
31210 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31212 if(!this.zoomable()){
31213 this.scale = this.startScale;
31221 onTouchEnd : function(e)
31223 this.pinching = false;
31224 this.dragable = false;
31228 process : function(file, crop)
31231 this.maskEl.mask(this.loadingText);
31234 this.xhr = new XMLHttpRequest();
31236 file.xhr = this.xhr;
31238 this.xhr.open(this.method, this.url, true);
31241 "Accept": "application/json",
31242 "Cache-Control": "no-cache",
31243 "X-Requested-With": "XMLHttpRequest"
31246 for (var headerName in headers) {
31247 var headerValue = headers[headerName];
31249 this.xhr.setRequestHeader(headerName, headerValue);
31255 this.xhr.onload = function()
31257 _this.xhrOnLoad(_this.xhr);
31260 this.xhr.onerror = function()
31262 _this.xhrOnError(_this.xhr);
31265 var formData = new FormData();
31267 formData.append('returnHTML', 'NO');
31270 formData.append('crop', crop);
31273 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31274 formData.append(this.paramName, file, file.name);
31277 if(typeof(file.filename) != 'undefined'){
31278 formData.append('filename', file.filename);
31281 if(typeof(file.mimetype) != 'undefined'){
31282 formData.append('mimetype', file.mimetype);
31285 if(this.fireEvent('arrange', this, formData) != false){
31286 this.xhr.send(formData);
31290 xhrOnLoad : function(xhr)
31293 this.maskEl.unmask();
31296 if (xhr.readyState !== 4) {
31297 this.fireEvent('exception', this, xhr);
31301 var response = Roo.decode(xhr.responseText);
31303 if(!response.success){
31304 this.fireEvent('exception', this, xhr);
31308 var response = Roo.decode(xhr.responseText);
31310 this.fireEvent('upload', this, response);
31314 xhrOnError : function()
31317 this.maskEl.unmask();
31320 Roo.log('xhr on error');
31322 var response = Roo.decode(xhr.responseText);
31328 prepare : function(file)
31331 this.maskEl.mask(this.loadingText);
31337 if(typeof(file) === 'string'){
31338 this.loadCanvas(file);
31342 if(!file || !this.urlAPI){
31347 this.cropType = file.type;
31351 if(this.fireEvent('prepare', this, this.file) != false){
31353 var reader = new FileReader();
31355 reader.onload = function (e) {
31356 if (e.target.error) {
31357 Roo.log(e.target.error);
31361 var buffer = e.target.result,
31362 dataView = new DataView(buffer),
31364 maxOffset = dataView.byteLength - 4,
31368 if (dataView.getUint16(0) === 0xffd8) {
31369 while (offset < maxOffset) {
31370 markerBytes = dataView.getUint16(offset);
31372 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31373 markerLength = dataView.getUint16(offset + 2) + 2;
31374 if (offset + markerLength > dataView.byteLength) {
31375 Roo.log('Invalid meta data: Invalid segment size.');
31379 if(markerBytes == 0xffe1){
31380 _this.parseExifData(
31387 offset += markerLength;
31397 var url = _this.urlAPI.createObjectURL(_this.file);
31399 _this.loadCanvas(url);
31404 reader.readAsArrayBuffer(this.file);
31410 parseExifData : function(dataView, offset, length)
31412 var tiffOffset = offset + 10,
31416 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31417 // No Exif data, might be XMP data instead
31421 // Check for the ASCII code for "Exif" (0x45786966):
31422 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31423 // No Exif data, might be XMP data instead
31426 if (tiffOffset + 8 > dataView.byteLength) {
31427 Roo.log('Invalid Exif data: Invalid segment size.');
31430 // Check for the two null bytes:
31431 if (dataView.getUint16(offset + 8) !== 0x0000) {
31432 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31435 // Check the byte alignment:
31436 switch (dataView.getUint16(tiffOffset)) {
31438 littleEndian = true;
31441 littleEndian = false;
31444 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31447 // Check for the TIFF tag marker (0x002A):
31448 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31449 Roo.log('Invalid Exif data: Missing TIFF marker.');
31452 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31453 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31455 this.parseExifTags(
31458 tiffOffset + dirOffset,
31463 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31468 if (dirOffset + 6 > dataView.byteLength) {
31469 Roo.log('Invalid Exif data: Invalid directory offset.');
31472 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31473 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31474 if (dirEndOffset + 4 > dataView.byteLength) {
31475 Roo.log('Invalid Exif data: Invalid directory size.');
31478 for (i = 0; i < tagsNumber; i += 1) {
31482 dirOffset + 2 + 12 * i, // tag offset
31486 // Return the offset to the next directory:
31487 return dataView.getUint32(dirEndOffset, littleEndian);
31490 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31492 var tag = dataView.getUint16(offset, littleEndian);
31494 this.exif[tag] = this.getExifValue(
31498 dataView.getUint16(offset + 2, littleEndian), // tag type
31499 dataView.getUint32(offset + 4, littleEndian), // tag length
31504 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31506 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31515 Roo.log('Invalid Exif data: Invalid tag type.');
31519 tagSize = tagType.size * length;
31520 // Determine if the value is contained in the dataOffset bytes,
31521 // or if the value at the dataOffset is a pointer to the actual data:
31522 dataOffset = tagSize > 4 ?
31523 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31524 if (dataOffset + tagSize > dataView.byteLength) {
31525 Roo.log('Invalid Exif data: Invalid data offset.');
31528 if (length === 1) {
31529 return tagType.getValue(dataView, dataOffset, littleEndian);
31532 for (i = 0; i < length; i += 1) {
31533 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31536 if (tagType.ascii) {
31538 // Concatenate the chars:
31539 for (i = 0; i < values.length; i += 1) {
31541 // Ignore the terminating NULL byte(s):
31542 if (c === '\u0000') {
31554 Roo.apply(Roo.bootstrap.UploadCropbox, {
31556 'Orientation': 0x0112
31560 1: 0, //'top-left',
31562 3: 180, //'bottom-right',
31563 // 4: 'bottom-left',
31565 6: 90, //'right-top',
31566 // 7: 'right-bottom',
31567 8: 270 //'left-bottom'
31571 // byte, 8-bit unsigned int:
31573 getValue: function (dataView, dataOffset) {
31574 return dataView.getUint8(dataOffset);
31578 // ascii, 8-bit byte:
31580 getValue: function (dataView, dataOffset) {
31581 return String.fromCharCode(dataView.getUint8(dataOffset));
31586 // short, 16 bit int:
31588 getValue: function (dataView, dataOffset, littleEndian) {
31589 return dataView.getUint16(dataOffset, littleEndian);
31593 // long, 32 bit int:
31595 getValue: function (dataView, dataOffset, littleEndian) {
31596 return dataView.getUint32(dataOffset, littleEndian);
31600 // rational = two long values, first is numerator, second is denominator:
31602 getValue: function (dataView, dataOffset, littleEndian) {
31603 return dataView.getUint32(dataOffset, littleEndian) /
31604 dataView.getUint32(dataOffset + 4, littleEndian);
31608 // slong, 32 bit signed int:
31610 getValue: function (dataView, dataOffset, littleEndian) {
31611 return dataView.getInt32(dataOffset, littleEndian);
31615 // srational, two slongs, first is numerator, second is denominator:
31617 getValue: function (dataView, dataOffset, littleEndian) {
31618 return dataView.getInt32(dataOffset, littleEndian) /
31619 dataView.getInt32(dataOffset + 4, littleEndian);
31629 cls : 'btn-group roo-upload-cropbox-rotate-left',
31630 action : 'rotate-left',
31634 cls : 'btn btn-default',
31635 html : '<i class="fa fa-undo"></i>'
31641 cls : 'btn-group roo-upload-cropbox-picture',
31642 action : 'picture',
31646 cls : 'btn btn-default',
31647 html : '<i class="fa fa-picture-o"></i>'
31653 cls : 'btn-group roo-upload-cropbox-rotate-right',
31654 action : 'rotate-right',
31658 cls : 'btn btn-default',
31659 html : '<i class="fa fa-repeat"></i>'
31667 cls : 'btn-group roo-upload-cropbox-rotate-left',
31668 action : 'rotate-left',
31672 cls : 'btn btn-default',
31673 html : '<i class="fa fa-undo"></i>'
31679 cls : 'btn-group roo-upload-cropbox-download',
31680 action : 'download',
31684 cls : 'btn btn-default',
31685 html : '<i class="fa fa-download"></i>'
31691 cls : 'btn-group roo-upload-cropbox-crop',
31696 cls : 'btn btn-default',
31697 html : '<i class="fa fa-crop"></i>'
31703 cls : 'btn-group roo-upload-cropbox-trash',
31708 cls : 'btn btn-default',
31709 html : '<i class="fa fa-trash"></i>'
31715 cls : 'btn-group roo-upload-cropbox-rotate-right',
31716 action : 'rotate-right',
31720 cls : 'btn btn-default',
31721 html : '<i class="fa fa-repeat"></i>'
31729 cls : 'btn-group roo-upload-cropbox-rotate-left',
31730 action : 'rotate-left',
31734 cls : 'btn btn-default',
31735 html : '<i class="fa fa-undo"></i>'
31741 cls : 'btn-group roo-upload-cropbox-rotate-right',
31742 action : 'rotate-right',
31746 cls : 'btn btn-default',
31747 html : '<i class="fa fa-repeat"></i>'
31760 * @class Roo.bootstrap.DocumentManager
31761 * @extends Roo.bootstrap.Component
31762 * Bootstrap DocumentManager class
31763 * @cfg {String} paramName default 'imageUpload'
31764 * @cfg {String} toolTipName default 'filename'
31765 * @cfg {String} method default POST
31766 * @cfg {String} url action url
31767 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31768 * @cfg {Boolean} multiple multiple upload default true
31769 * @cfg {Number} thumbSize default 300
31770 * @cfg {String} fieldLabel
31771 * @cfg {Number} labelWidth default 4
31772 * @cfg {String} labelAlign (left|top) default left
31773 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31774 * @cfg {Number} labellg set the width of label (1-12)
31775 * @cfg {Number} labelmd set the width of label (1-12)
31776 * @cfg {Number} labelsm set the width of label (1-12)
31777 * @cfg {Number} labelxs set the width of label (1-12)
31780 * Create a new DocumentManager
31781 * @param {Object} config The config object
31784 Roo.bootstrap.DocumentManager = function(config){
31785 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31788 this.delegates = [];
31793 * Fire when initial the DocumentManager
31794 * @param {Roo.bootstrap.DocumentManager} this
31799 * inspect selected file
31800 * @param {Roo.bootstrap.DocumentManager} this
31801 * @param {File} file
31806 * Fire when xhr load exception
31807 * @param {Roo.bootstrap.DocumentManager} this
31808 * @param {XMLHttpRequest} xhr
31810 "exception" : true,
31812 * @event afterupload
31813 * Fire when xhr load exception
31814 * @param {Roo.bootstrap.DocumentManager} this
31815 * @param {XMLHttpRequest} xhr
31817 "afterupload" : true,
31820 * prepare the form data
31821 * @param {Roo.bootstrap.DocumentManager} this
31822 * @param {Object} formData
31827 * Fire when remove the file
31828 * @param {Roo.bootstrap.DocumentManager} this
31829 * @param {Object} file
31834 * Fire after refresh the file
31835 * @param {Roo.bootstrap.DocumentManager} this
31840 * Fire after click the image
31841 * @param {Roo.bootstrap.DocumentManager} this
31842 * @param {Object} file
31847 * Fire when upload a image and editable set to true
31848 * @param {Roo.bootstrap.DocumentManager} this
31849 * @param {Object} file
31853 * @event beforeselectfile
31854 * Fire before select file
31855 * @param {Roo.bootstrap.DocumentManager} this
31857 "beforeselectfile" : true,
31860 * Fire before process file
31861 * @param {Roo.bootstrap.DocumentManager} this
31862 * @param {Object} file
31866 * @event previewrendered
31867 * Fire when preview rendered
31868 * @param {Roo.bootstrap.DocumentManager} this
31869 * @param {Object} file
31871 "previewrendered" : true,
31874 "previewResize" : true
31879 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31888 paramName : 'imageUpload',
31889 toolTipName : 'filename',
31892 labelAlign : 'left',
31902 getAutoCreate : function()
31904 var managerWidget = {
31906 cls : 'roo-document-manager',
31910 cls : 'roo-document-manager-selector',
31915 cls : 'roo-document-manager-uploader',
31919 cls : 'roo-document-manager-upload-btn',
31920 html : '<i class="fa fa-plus"></i>'
31931 cls : 'column col-md-12',
31936 if(this.fieldLabel.length){
31941 cls : 'column col-md-12',
31942 html : this.fieldLabel
31946 cls : 'column col-md-12',
31951 if(this.labelAlign == 'left'){
31956 html : this.fieldLabel
31965 if(this.labelWidth > 12){
31966 content[0].style = "width: " + this.labelWidth + 'px';
31969 if(this.labelWidth < 13 && this.labelmd == 0){
31970 this.labelmd = this.labelWidth;
31973 if(this.labellg > 0){
31974 content[0].cls += ' col-lg-' + this.labellg;
31975 content[1].cls += ' col-lg-' + (12 - this.labellg);
31978 if(this.labelmd > 0){
31979 content[0].cls += ' col-md-' + this.labelmd;
31980 content[1].cls += ' col-md-' + (12 - this.labelmd);
31983 if(this.labelsm > 0){
31984 content[0].cls += ' col-sm-' + this.labelsm;
31985 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31988 if(this.labelxs > 0){
31989 content[0].cls += ' col-xs-' + this.labelxs;
31990 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31998 cls : 'row clearfix',
32006 initEvents : function()
32008 this.managerEl = this.el.select('.roo-document-manager', true).first();
32009 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32011 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32012 this.selectorEl.hide();
32015 this.selectorEl.attr('multiple', 'multiple');
32018 this.selectorEl.on('change', this.onFileSelected, this);
32020 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32021 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32023 this.uploader.on('click', this.onUploaderClick, this);
32025 this.renderProgressDialog();
32029 window.addEventListener("resize", function() { _this.refresh(); } );
32031 this.fireEvent('initial', this);
32034 renderProgressDialog : function()
32038 this.progressDialog = new Roo.bootstrap.Modal({
32039 cls : 'roo-document-manager-progress-dialog',
32040 allow_close : false,
32051 btnclick : function() {
32052 _this.uploadCancel();
32058 this.progressDialog.render(Roo.get(document.body));
32060 this.progress = new Roo.bootstrap.Progress({
32061 cls : 'roo-document-manager-progress',
32066 this.progress.render(this.progressDialog.getChildContainer());
32068 this.progressBar = new Roo.bootstrap.ProgressBar({
32069 cls : 'roo-document-manager-progress-bar',
32072 aria_valuemax : 12,
32076 this.progressBar.render(this.progress.getChildContainer());
32079 onUploaderClick : function(e)
32081 e.preventDefault();
32083 if(this.fireEvent('beforeselectfile', this) != false){
32084 this.selectorEl.dom.click();
32089 onFileSelected : function(e)
32091 e.preventDefault();
32093 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32097 Roo.each(this.selectorEl.dom.files, function(file){
32098 if(this.fireEvent('inspect', this, file) != false){
32099 this.files.push(file);
32109 this.selectorEl.dom.value = '';
32111 if(!this.files || !this.files.length){
32115 if(this.boxes > 0 && this.files.length > this.boxes){
32116 this.files = this.files.slice(0, this.boxes);
32119 this.uploader.show();
32121 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32122 this.uploader.hide();
32131 Roo.each(this.files, function(file){
32133 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32134 var f = this.renderPreview(file);
32139 if(file.type.indexOf('image') != -1){
32140 this.delegates.push(
32142 _this.process(file);
32143 }).createDelegate(this)
32151 _this.process(file);
32152 }).createDelegate(this)
32157 this.files = files;
32159 this.delegates = this.delegates.concat(docs);
32161 if(!this.delegates.length){
32166 this.progressBar.aria_valuemax = this.delegates.length;
32173 arrange : function()
32175 if(!this.delegates.length){
32176 this.progressDialog.hide();
32181 var delegate = this.delegates.shift();
32183 this.progressDialog.show();
32185 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32187 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32192 refresh : function()
32194 this.uploader.show();
32196 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32197 this.uploader.hide();
32200 Roo.isTouch ? this.closable(false) : this.closable(true);
32202 this.fireEvent('refresh', this);
32205 onRemove : function(e, el, o)
32207 e.preventDefault();
32209 this.fireEvent('remove', this, o);
32213 remove : function(o)
32217 Roo.each(this.files, function(file){
32218 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32227 this.files = files;
32234 Roo.each(this.files, function(file){
32239 file.target.remove();
32248 onClick : function(e, el, o)
32250 e.preventDefault();
32252 this.fireEvent('click', this, o);
32256 closable : function(closable)
32258 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32260 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32272 xhrOnLoad : function(xhr)
32274 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32278 if (xhr.readyState !== 4) {
32280 this.fireEvent('exception', this, xhr);
32284 var response = Roo.decode(xhr.responseText);
32286 if(!response.success){
32288 this.fireEvent('exception', this, xhr);
32292 var file = this.renderPreview(response.data);
32294 this.files.push(file);
32298 this.fireEvent('afterupload', this, xhr);
32302 xhrOnError : function(xhr)
32304 Roo.log('xhr on error');
32306 var response = Roo.decode(xhr.responseText);
32313 process : function(file)
32315 if(this.fireEvent('process', this, file) !== false){
32316 if(this.editable && file.type.indexOf('image') != -1){
32317 this.fireEvent('edit', this, file);
32321 this.uploadStart(file, false);
32328 uploadStart : function(file, crop)
32330 this.xhr = new XMLHttpRequest();
32332 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32337 file.xhr = this.xhr;
32339 this.managerEl.createChild({
32341 cls : 'roo-document-manager-loading',
32345 tooltip : file.name,
32346 cls : 'roo-document-manager-thumb',
32347 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32353 this.xhr.open(this.method, this.url, true);
32356 "Accept": "application/json",
32357 "Cache-Control": "no-cache",
32358 "X-Requested-With": "XMLHttpRequest"
32361 for (var headerName in headers) {
32362 var headerValue = headers[headerName];
32364 this.xhr.setRequestHeader(headerName, headerValue);
32370 this.xhr.onload = function()
32372 _this.xhrOnLoad(_this.xhr);
32375 this.xhr.onerror = function()
32377 _this.xhrOnError(_this.xhr);
32380 var formData = new FormData();
32382 formData.append('returnHTML', 'NO');
32385 formData.append('crop', crop);
32388 formData.append(this.paramName, file, file.name);
32395 if(this.fireEvent('prepare', this, formData, options) != false){
32397 if(options.manually){
32401 this.xhr.send(formData);
32405 this.uploadCancel();
32408 uploadCancel : function()
32414 this.delegates = [];
32416 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32423 renderPreview : function(file)
32425 if(typeof(file.target) != 'undefined' && file.target){
32429 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32431 var previewEl = this.managerEl.createChild({
32433 cls : 'roo-document-manager-preview',
32437 tooltip : file[this.toolTipName],
32438 cls : 'roo-document-manager-thumb',
32439 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32444 html : '<i class="fa fa-times-circle"></i>'
32449 var close = previewEl.select('button.close', true).first();
32451 close.on('click', this.onRemove, this, file);
32453 file.target = previewEl;
32455 var image = previewEl.select('img', true).first();
32459 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32461 image.on('click', this.onClick, this, file);
32463 this.fireEvent('previewrendered', this, file);
32469 onPreviewLoad : function(file, image)
32471 if(typeof(file.target) == 'undefined' || !file.target){
32475 var width = image.dom.naturalWidth || image.dom.width;
32476 var height = image.dom.naturalHeight || image.dom.height;
32478 if(!this.previewResize) {
32482 if(width > height){
32483 file.target.addClass('wide');
32487 file.target.addClass('tall');
32492 uploadFromSource : function(file, crop)
32494 this.xhr = new XMLHttpRequest();
32496 this.managerEl.createChild({
32498 cls : 'roo-document-manager-loading',
32502 tooltip : file.name,
32503 cls : 'roo-document-manager-thumb',
32504 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32510 this.xhr.open(this.method, this.url, true);
32513 "Accept": "application/json",
32514 "Cache-Control": "no-cache",
32515 "X-Requested-With": "XMLHttpRequest"
32518 for (var headerName in headers) {
32519 var headerValue = headers[headerName];
32521 this.xhr.setRequestHeader(headerName, headerValue);
32527 this.xhr.onload = function()
32529 _this.xhrOnLoad(_this.xhr);
32532 this.xhr.onerror = function()
32534 _this.xhrOnError(_this.xhr);
32537 var formData = new FormData();
32539 formData.append('returnHTML', 'NO');
32541 formData.append('crop', crop);
32543 if(typeof(file.filename) != 'undefined'){
32544 formData.append('filename', file.filename);
32547 if(typeof(file.mimetype) != 'undefined'){
32548 formData.append('mimetype', file.mimetype);
32553 if(this.fireEvent('prepare', this, formData) != false){
32554 this.xhr.send(formData);
32564 * @class Roo.bootstrap.DocumentViewer
32565 * @extends Roo.bootstrap.Component
32566 * Bootstrap DocumentViewer class
32567 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32568 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32571 * Create a new DocumentViewer
32572 * @param {Object} config The config object
32575 Roo.bootstrap.DocumentViewer = function(config){
32576 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32581 * Fire after initEvent
32582 * @param {Roo.bootstrap.DocumentViewer} this
32588 * @param {Roo.bootstrap.DocumentViewer} this
32593 * Fire after download button
32594 * @param {Roo.bootstrap.DocumentViewer} this
32599 * Fire after trash button
32600 * @param {Roo.bootstrap.DocumentViewer} this
32607 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32609 showDownload : true,
32613 getAutoCreate : function()
32617 cls : 'roo-document-viewer',
32621 cls : 'roo-document-viewer-body',
32625 cls : 'roo-document-viewer-thumb',
32629 cls : 'roo-document-viewer-image'
32637 cls : 'roo-document-viewer-footer',
32640 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32644 cls : 'btn-group roo-document-viewer-download',
32648 cls : 'btn btn-default',
32649 html : '<i class="fa fa-download"></i>'
32655 cls : 'btn-group roo-document-viewer-trash',
32659 cls : 'btn btn-default',
32660 html : '<i class="fa fa-trash"></i>'
32673 initEvents : function()
32675 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32676 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32678 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32679 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32681 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32682 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32684 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32685 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32687 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32688 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32690 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32691 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32693 this.bodyEl.on('click', this.onClick, this);
32694 this.downloadBtn.on('click', this.onDownload, this);
32695 this.trashBtn.on('click', this.onTrash, this);
32697 this.downloadBtn.hide();
32698 this.trashBtn.hide();
32700 if(this.showDownload){
32701 this.downloadBtn.show();
32704 if(this.showTrash){
32705 this.trashBtn.show();
32708 if(!this.showDownload && !this.showTrash) {
32709 this.footerEl.hide();
32714 initial : function()
32716 this.fireEvent('initial', this);
32720 onClick : function(e)
32722 e.preventDefault();
32724 this.fireEvent('click', this);
32727 onDownload : function(e)
32729 e.preventDefault();
32731 this.fireEvent('download', this);
32734 onTrash : function(e)
32736 e.preventDefault();
32738 this.fireEvent('trash', this);
32750 * @class Roo.bootstrap.NavProgressBar
32751 * @extends Roo.bootstrap.Component
32752 * Bootstrap NavProgressBar class
32755 * Create a new nav progress bar
32756 * @param {Object} config The config object
32759 Roo.bootstrap.NavProgressBar = function(config){
32760 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32762 this.bullets = this.bullets || [];
32764 // Roo.bootstrap.NavProgressBar.register(this);
32768 * Fires when the active item changes
32769 * @param {Roo.bootstrap.NavProgressBar} this
32770 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32771 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32778 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32783 getAutoCreate : function()
32785 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32789 cls : 'roo-navigation-bar-group',
32793 cls : 'roo-navigation-top-bar'
32797 cls : 'roo-navigation-bullets-bar',
32801 cls : 'roo-navigation-bar'
32808 cls : 'roo-navigation-bottom-bar'
32818 initEvents: function()
32823 onRender : function(ct, position)
32825 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32827 if(this.bullets.length){
32828 Roo.each(this.bullets, function(b){
32837 addItem : function(cfg)
32839 var item = new Roo.bootstrap.NavProgressItem(cfg);
32841 item.parentId = this.id;
32842 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32845 var top = new Roo.bootstrap.Element({
32847 cls : 'roo-navigation-bar-text'
32850 var bottom = new Roo.bootstrap.Element({
32852 cls : 'roo-navigation-bar-text'
32855 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32856 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32858 var topText = new Roo.bootstrap.Element({
32860 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32863 var bottomText = new Roo.bootstrap.Element({
32865 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32868 topText.onRender(top.el, null);
32869 bottomText.onRender(bottom.el, null);
32872 item.bottomEl = bottom;
32875 this.barItems.push(item);
32880 getActive : function()
32882 var active = false;
32884 Roo.each(this.barItems, function(v){
32886 if (!v.isActive()) {
32898 setActiveItem : function(item)
32902 Roo.each(this.barItems, function(v){
32903 if (v.rid == item.rid) {
32907 if (v.isActive()) {
32908 v.setActive(false);
32913 item.setActive(true);
32915 this.fireEvent('changed', this, item, prev);
32918 getBarItem: function(rid)
32922 Roo.each(this.barItems, function(e) {
32923 if (e.rid != rid) {
32934 indexOfItem : function(item)
32938 Roo.each(this.barItems, function(v, i){
32940 if (v.rid != item.rid) {
32951 setActiveNext : function()
32953 var i = this.indexOfItem(this.getActive());
32955 if (i > this.barItems.length) {
32959 this.setActiveItem(this.barItems[i+1]);
32962 setActivePrev : function()
32964 var i = this.indexOfItem(this.getActive());
32970 this.setActiveItem(this.barItems[i-1]);
32973 format : function()
32975 if(!this.barItems.length){
32979 var width = 100 / this.barItems.length;
32981 Roo.each(this.barItems, function(i){
32982 i.el.setStyle('width', width + '%');
32983 i.topEl.el.setStyle('width', width + '%');
32984 i.bottomEl.el.setStyle('width', width + '%');
32993 * Nav Progress Item
32998 * @class Roo.bootstrap.NavProgressItem
32999 * @extends Roo.bootstrap.Component
33000 * Bootstrap NavProgressItem class
33001 * @cfg {String} rid the reference id
33002 * @cfg {Boolean} active (true|false) Is item active default false
33003 * @cfg {Boolean} disabled (true|false) Is item active default false
33004 * @cfg {String} html
33005 * @cfg {String} position (top|bottom) text position default bottom
33006 * @cfg {String} icon show icon instead of number
33009 * Create a new NavProgressItem
33010 * @param {Object} config The config object
33012 Roo.bootstrap.NavProgressItem = function(config){
33013 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33018 * The raw click event for the entire grid.
33019 * @param {Roo.bootstrap.NavProgressItem} this
33020 * @param {Roo.EventObject} e
33027 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33033 position : 'bottom',
33036 getAutoCreate : function()
33038 var iconCls = 'roo-navigation-bar-item-icon';
33040 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33044 cls: 'roo-navigation-bar-item',
33054 cfg.cls += ' active';
33057 cfg.cls += ' disabled';
33063 disable : function()
33065 this.setDisabled(true);
33068 enable : function()
33070 this.setDisabled(false);
33073 initEvents: function()
33075 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33077 this.iconEl.on('click', this.onClick, this);
33080 onClick : function(e)
33082 e.preventDefault();
33088 if(this.fireEvent('click', this, e) === false){
33092 this.parent().setActiveItem(this);
33095 isActive: function ()
33097 return this.active;
33100 setActive : function(state)
33102 if(this.active == state){
33106 this.active = state;
33109 this.el.addClass('active');
33113 this.el.removeClass('active');
33118 setDisabled : function(state)
33120 if(this.disabled == state){
33124 this.disabled = state;
33127 this.el.addClass('disabled');
33131 this.el.removeClass('disabled');
33134 tooltipEl : function()
33136 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33149 * @class Roo.bootstrap.FieldLabel
33150 * @extends Roo.bootstrap.Component
33151 * Bootstrap FieldLabel class
33152 * @cfg {String} html contents of the element
33153 * @cfg {String} tag tag of the element default label
33154 * @cfg {String} cls class of the element
33155 * @cfg {String} target label target
33156 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33157 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33158 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33159 * @cfg {String} iconTooltip default "This field is required"
33160 * @cfg {String} indicatorpos (left|right) default left
33163 * Create a new FieldLabel
33164 * @param {Object} config The config object
33167 Roo.bootstrap.FieldLabel = function(config){
33168 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33173 * Fires after the field has been marked as invalid.
33174 * @param {Roo.form.FieldLabel} this
33175 * @param {String} msg The validation message
33180 * Fires after the field has been validated with no errors.
33181 * @param {Roo.form.FieldLabel} this
33187 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33194 invalidClass : 'has-warning',
33195 validClass : 'has-success',
33196 iconTooltip : 'This field is required',
33197 indicatorpos : 'left',
33199 getAutoCreate : function(){
33202 if (!this.allowBlank) {
33208 cls : 'roo-bootstrap-field-label ' + this.cls,
33213 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33214 tooltip : this.iconTooltip
33223 if(this.indicatorpos == 'right'){
33226 cls : 'roo-bootstrap-field-label ' + this.cls,
33235 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33236 tooltip : this.iconTooltip
33245 initEvents: function()
33247 Roo.bootstrap.Element.superclass.initEvents.call(this);
33249 this.indicator = this.indicatorEl();
33251 if(this.indicator){
33252 this.indicator.removeClass('visible');
33253 this.indicator.addClass('invisible');
33256 Roo.bootstrap.FieldLabel.register(this);
33259 indicatorEl : function()
33261 var indicator = this.el.select('i.roo-required-indicator',true).first();
33272 * Mark this field as valid
33274 markValid : function()
33276 if(this.indicator){
33277 this.indicator.removeClass('visible');
33278 this.indicator.addClass('invisible');
33280 if (Roo.bootstrap.version == 3) {
33281 this.el.removeClass(this.invalidClass);
33282 this.el.addClass(this.validClass);
33284 this.el.removeClass('is-invalid');
33285 this.el.addClass('is-valid');
33289 this.fireEvent('valid', this);
33293 * Mark this field as invalid
33294 * @param {String} msg The validation message
33296 markInvalid : function(msg)
33298 if(this.indicator){
33299 this.indicator.removeClass('invisible');
33300 this.indicator.addClass('visible');
33302 if (Roo.bootstrap.version == 3) {
33303 this.el.removeClass(this.validClass);
33304 this.el.addClass(this.invalidClass);
33306 this.el.removeClass('is-valid');
33307 this.el.addClass('is-invalid');
33311 this.fireEvent('invalid', this, msg);
33317 Roo.apply(Roo.bootstrap.FieldLabel, {
33322 * register a FieldLabel Group
33323 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33325 register : function(label)
33327 if(this.groups.hasOwnProperty(label.target)){
33331 this.groups[label.target] = label;
33335 * fetch a FieldLabel Group based on the target
33336 * @param {string} target
33337 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33339 get: function(target) {
33340 if (typeof(this.groups[target]) == 'undefined') {
33344 return this.groups[target] ;
33353 * page DateSplitField.
33359 * @class Roo.bootstrap.DateSplitField
33360 * @extends Roo.bootstrap.Component
33361 * Bootstrap DateSplitField class
33362 * @cfg {string} fieldLabel - the label associated
33363 * @cfg {Number} labelWidth set the width of label (0-12)
33364 * @cfg {String} labelAlign (top|left)
33365 * @cfg {Boolean} dayAllowBlank (true|false) default false
33366 * @cfg {Boolean} monthAllowBlank (true|false) default false
33367 * @cfg {Boolean} yearAllowBlank (true|false) default false
33368 * @cfg {string} dayPlaceholder
33369 * @cfg {string} monthPlaceholder
33370 * @cfg {string} yearPlaceholder
33371 * @cfg {string} dayFormat default 'd'
33372 * @cfg {string} monthFormat default 'm'
33373 * @cfg {string} yearFormat default 'Y'
33374 * @cfg {Number} labellg set the width of label (1-12)
33375 * @cfg {Number} labelmd set the width of label (1-12)
33376 * @cfg {Number} labelsm set the width of label (1-12)
33377 * @cfg {Number} labelxs set the width of label (1-12)
33381 * Create a new DateSplitField
33382 * @param {Object} config The config object
33385 Roo.bootstrap.DateSplitField = function(config){
33386 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33392 * getting the data of years
33393 * @param {Roo.bootstrap.DateSplitField} this
33394 * @param {Object} years
33399 * getting the data of days
33400 * @param {Roo.bootstrap.DateSplitField} this
33401 * @param {Object} days
33406 * Fires after the field has been marked as invalid.
33407 * @param {Roo.form.Field} this
33408 * @param {String} msg The validation message
33413 * Fires after the field has been validated with no errors.
33414 * @param {Roo.form.Field} this
33420 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33423 labelAlign : 'top',
33425 dayAllowBlank : false,
33426 monthAllowBlank : false,
33427 yearAllowBlank : false,
33428 dayPlaceholder : '',
33429 monthPlaceholder : '',
33430 yearPlaceholder : '',
33434 isFormField : true,
33440 getAutoCreate : function()
33444 cls : 'row roo-date-split-field-group',
33449 cls : 'form-hidden-field roo-date-split-field-group-value',
33455 var labelCls = 'col-md-12';
33456 var contentCls = 'col-md-4';
33458 if(this.fieldLabel){
33462 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33466 html : this.fieldLabel
33471 if(this.labelAlign == 'left'){
33473 if(this.labelWidth > 12){
33474 label.style = "width: " + this.labelWidth + 'px';
33477 if(this.labelWidth < 13 && this.labelmd == 0){
33478 this.labelmd = this.labelWidth;
33481 if(this.labellg > 0){
33482 labelCls = ' col-lg-' + this.labellg;
33483 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33486 if(this.labelmd > 0){
33487 labelCls = ' col-md-' + this.labelmd;
33488 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33491 if(this.labelsm > 0){
33492 labelCls = ' col-sm-' + this.labelsm;
33493 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33496 if(this.labelxs > 0){
33497 labelCls = ' col-xs-' + this.labelxs;
33498 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33502 label.cls += ' ' + labelCls;
33504 cfg.cn.push(label);
33507 Roo.each(['day', 'month', 'year'], function(t){
33510 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33517 inputEl: function ()
33519 return this.el.select('.roo-date-split-field-group-value', true).first();
33522 onRender : function(ct, position)
33526 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33528 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33530 this.dayField = new Roo.bootstrap.ComboBox({
33531 allowBlank : this.dayAllowBlank,
33532 alwaysQuery : true,
33533 displayField : 'value',
33536 forceSelection : true,
33538 placeholder : this.dayPlaceholder,
33539 selectOnFocus : true,
33540 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33541 triggerAction : 'all',
33543 valueField : 'value',
33544 store : new Roo.data.SimpleStore({
33545 data : (function() {
33547 _this.fireEvent('days', _this, days);
33550 fields : [ 'value' ]
33553 select : function (_self, record, index)
33555 _this.setValue(_this.getValue());
33560 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33562 this.monthField = new Roo.bootstrap.MonthField({
33563 after : '<i class=\"fa fa-calendar\"></i>',
33564 allowBlank : this.monthAllowBlank,
33565 placeholder : this.monthPlaceholder,
33568 render : function (_self)
33570 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33571 e.preventDefault();
33575 select : function (_self, oldvalue, newvalue)
33577 _this.setValue(_this.getValue());
33582 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33584 this.yearField = new Roo.bootstrap.ComboBox({
33585 allowBlank : this.yearAllowBlank,
33586 alwaysQuery : true,
33587 displayField : 'value',
33590 forceSelection : true,
33592 placeholder : this.yearPlaceholder,
33593 selectOnFocus : true,
33594 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33595 triggerAction : 'all',
33597 valueField : 'value',
33598 store : new Roo.data.SimpleStore({
33599 data : (function() {
33601 _this.fireEvent('years', _this, years);
33604 fields : [ 'value' ]
33607 select : function (_self, record, index)
33609 _this.setValue(_this.getValue());
33614 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33617 setValue : function(v, format)
33619 this.inputEl.dom.value = v;
33621 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33623 var d = Date.parseDate(v, f);
33630 this.setDay(d.format(this.dayFormat));
33631 this.setMonth(d.format(this.monthFormat));
33632 this.setYear(d.format(this.yearFormat));
33639 setDay : function(v)
33641 this.dayField.setValue(v);
33642 this.inputEl.dom.value = this.getValue();
33647 setMonth : function(v)
33649 this.monthField.setValue(v, true);
33650 this.inputEl.dom.value = this.getValue();
33655 setYear : function(v)
33657 this.yearField.setValue(v);
33658 this.inputEl.dom.value = this.getValue();
33663 getDay : function()
33665 return this.dayField.getValue();
33668 getMonth : function()
33670 return this.monthField.getValue();
33673 getYear : function()
33675 return this.yearField.getValue();
33678 getValue : function()
33680 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33682 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33692 this.inputEl.dom.value = '';
33697 validate : function()
33699 var d = this.dayField.validate();
33700 var m = this.monthField.validate();
33701 var y = this.yearField.validate();
33706 (!this.dayAllowBlank && !d) ||
33707 (!this.monthAllowBlank && !m) ||
33708 (!this.yearAllowBlank && !y)
33713 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33722 this.markInvalid();
33727 markValid : function()
33730 var label = this.el.select('label', true).first();
33731 var icon = this.el.select('i.fa-star', true).first();
33737 this.fireEvent('valid', this);
33741 * Mark this field as invalid
33742 * @param {String} msg The validation message
33744 markInvalid : function(msg)
33747 var label = this.el.select('label', true).first();
33748 var icon = this.el.select('i.fa-star', true).first();
33750 if(label && !icon){
33751 this.el.select('.roo-date-split-field-label', true).createChild({
33753 cls : 'text-danger fa fa-lg fa-star',
33754 tooltip : 'This field is required',
33755 style : 'margin-right:5px;'
33759 this.fireEvent('invalid', this, msg);
33762 clearInvalid : function()
33764 var label = this.el.select('label', true).first();
33765 var icon = this.el.select('i.fa-star', true).first();
33771 this.fireEvent('valid', this);
33774 getName: function()
33784 * http://masonry.desandro.com
33786 * The idea is to render all the bricks based on vertical width...
33788 * The original code extends 'outlayer' - we might need to use that....
33794 * @class Roo.bootstrap.LayoutMasonry
33795 * @extends Roo.bootstrap.Component
33796 * Bootstrap Layout Masonry class
33799 * Create a new Element
33800 * @param {Object} config The config object
33803 Roo.bootstrap.LayoutMasonry = function(config){
33805 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33809 Roo.bootstrap.LayoutMasonry.register(this);
33815 * Fire after layout the items
33816 * @param {Roo.bootstrap.LayoutMasonry} this
33817 * @param {Roo.EventObject} e
33824 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33827 * @cfg {Boolean} isLayoutInstant = no animation?
33829 isLayoutInstant : false, // needed?
33832 * @cfg {Number} boxWidth width of the columns
33837 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33842 * @cfg {Number} padWidth padding below box..
33847 * @cfg {Number} gutter gutter width..
33852 * @cfg {Number} maxCols maximum number of columns
33858 * @cfg {Boolean} isAutoInitial defalut true
33860 isAutoInitial : true,
33865 * @cfg {Boolean} isHorizontal defalut false
33867 isHorizontal : false,
33869 currentSize : null,
33875 bricks: null, //CompositeElement
33879 _isLayoutInited : false,
33881 // isAlternative : false, // only use for vertical layout...
33884 * @cfg {Number} alternativePadWidth padding below box..
33886 alternativePadWidth : 50,
33888 selectedBrick : [],
33890 getAutoCreate : function(){
33892 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33896 cls: 'blog-masonary-wrapper ' + this.cls,
33898 cls : 'mas-boxes masonary'
33905 getChildContainer: function( )
33907 if (this.boxesEl) {
33908 return this.boxesEl;
33911 this.boxesEl = this.el.select('.mas-boxes').first();
33913 return this.boxesEl;
33917 initEvents : function()
33921 if(this.isAutoInitial){
33922 Roo.log('hook children rendered');
33923 this.on('childrenrendered', function() {
33924 Roo.log('children rendered');
33930 initial : function()
33932 this.selectedBrick = [];
33934 this.currentSize = this.el.getBox(true);
33936 Roo.EventManager.onWindowResize(this.resize, this);
33938 if(!this.isAutoInitial){
33946 //this.layout.defer(500,this);
33950 resize : function()
33952 var cs = this.el.getBox(true);
33955 this.currentSize.width == cs.width &&
33956 this.currentSize.x == cs.x &&
33957 this.currentSize.height == cs.height &&
33958 this.currentSize.y == cs.y
33960 Roo.log("no change in with or X or Y");
33964 this.currentSize = cs;
33970 layout : function()
33972 this._resetLayout();
33974 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33976 this.layoutItems( isInstant );
33978 this._isLayoutInited = true;
33980 this.fireEvent('layout', this);
33984 _resetLayout : function()
33986 if(this.isHorizontal){
33987 this.horizontalMeasureColumns();
33991 this.verticalMeasureColumns();
33995 verticalMeasureColumns : function()
33997 this.getContainerWidth();
33999 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34000 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34004 var boxWidth = this.boxWidth + this.padWidth;
34006 if(this.containerWidth < this.boxWidth){
34007 boxWidth = this.containerWidth
34010 var containerWidth = this.containerWidth;
34012 var cols = Math.floor(containerWidth / boxWidth);
34014 this.cols = Math.max( cols, 1 );
34016 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34018 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34020 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34022 this.colWidth = boxWidth + avail - this.padWidth;
34024 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34025 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34028 horizontalMeasureColumns : function()
34030 this.getContainerWidth();
34032 var boxWidth = this.boxWidth;
34034 if(this.containerWidth < boxWidth){
34035 boxWidth = this.containerWidth;
34038 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34040 this.el.setHeight(boxWidth);
34044 getContainerWidth : function()
34046 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34049 layoutItems : function( isInstant )
34051 Roo.log(this.bricks);
34053 var items = Roo.apply([], this.bricks);
34055 if(this.isHorizontal){
34056 this._horizontalLayoutItems( items , isInstant );
34060 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34061 // this._verticalAlternativeLayoutItems( items , isInstant );
34065 this._verticalLayoutItems( items , isInstant );
34069 _verticalLayoutItems : function ( items , isInstant)
34071 if ( !items || !items.length ) {
34076 ['xs', 'xs', 'xs', 'tall'],
34077 ['xs', 'xs', 'tall'],
34078 ['xs', 'xs', 'sm'],
34079 ['xs', 'xs', 'xs'],
34085 ['sm', 'xs', 'xs'],
34089 ['tall', 'xs', 'xs', 'xs'],
34090 ['tall', 'xs', 'xs'],
34102 Roo.each(items, function(item, k){
34104 switch (item.size) {
34105 // these layouts take up a full box,
34116 boxes.push([item]);
34139 var filterPattern = function(box, length)
34147 var pattern = box.slice(0, length);
34151 Roo.each(pattern, function(i){
34152 format.push(i.size);
34155 Roo.each(standard, function(s){
34157 if(String(s) != String(format)){
34166 if(!match && length == 1){
34171 filterPattern(box, length - 1);
34175 queue.push(pattern);
34177 box = box.slice(length, box.length);
34179 filterPattern(box, 4);
34185 Roo.each(boxes, function(box, k){
34191 if(box.length == 1){
34196 filterPattern(box, 4);
34200 this._processVerticalLayoutQueue( queue, isInstant );
34204 // _verticalAlternativeLayoutItems : function( items , isInstant )
34206 // if ( !items || !items.length ) {
34210 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34214 _horizontalLayoutItems : function ( items , isInstant)
34216 if ( !items || !items.length || items.length < 3) {
34222 var eItems = items.slice(0, 3);
34224 items = items.slice(3, items.length);
34227 ['xs', 'xs', 'xs', 'wide'],
34228 ['xs', 'xs', 'wide'],
34229 ['xs', 'xs', 'sm'],
34230 ['xs', 'xs', 'xs'],
34236 ['sm', 'xs', 'xs'],
34240 ['wide', 'xs', 'xs', 'xs'],
34241 ['wide', 'xs', 'xs'],
34254 Roo.each(items, function(item, k){
34256 switch (item.size) {
34267 boxes.push([item]);
34291 var filterPattern = function(box, length)
34299 var pattern = box.slice(0, length);
34303 Roo.each(pattern, function(i){
34304 format.push(i.size);
34307 Roo.each(standard, function(s){
34309 if(String(s) != String(format)){
34318 if(!match && length == 1){
34323 filterPattern(box, length - 1);
34327 queue.push(pattern);
34329 box = box.slice(length, box.length);
34331 filterPattern(box, 4);
34337 Roo.each(boxes, function(box, k){
34343 if(box.length == 1){
34348 filterPattern(box, 4);
34355 var pos = this.el.getBox(true);
34359 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34361 var hit_end = false;
34363 Roo.each(queue, function(box){
34367 Roo.each(box, function(b){
34369 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34379 Roo.each(box, function(b){
34381 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34384 mx = Math.max(mx, b.x);
34388 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34392 Roo.each(box, function(b){
34394 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34408 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34411 /** Sets position of item in DOM
34412 * @param {Element} item
34413 * @param {Number} x - horizontal position
34414 * @param {Number} y - vertical position
34415 * @param {Boolean} isInstant - disables transitions
34417 _processVerticalLayoutQueue : function( queue, isInstant )
34419 var pos = this.el.getBox(true);
34424 for (var i = 0; i < this.cols; i++){
34428 Roo.each(queue, function(box, k){
34430 var col = k % this.cols;
34432 Roo.each(box, function(b,kk){
34434 b.el.position('absolute');
34436 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34437 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34439 if(b.size == 'md-left' || b.size == 'md-right'){
34440 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34441 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34444 b.el.setWidth(width);
34445 b.el.setHeight(height);
34447 b.el.select('iframe',true).setSize(width,height);
34451 for (var i = 0; i < this.cols; i++){
34453 if(maxY[i] < maxY[col]){
34458 col = Math.min(col, i);
34462 x = pos.x + col * (this.colWidth + this.padWidth);
34466 var positions = [];
34468 switch (box.length){
34470 positions = this.getVerticalOneBoxColPositions(x, y, box);
34473 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34476 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34479 positions = this.getVerticalFourBoxColPositions(x, y, box);
34485 Roo.each(box, function(b,kk){
34487 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34489 var sz = b.el.getSize();
34491 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34499 for (var i = 0; i < this.cols; i++){
34500 mY = Math.max(mY, maxY[i]);
34503 this.el.setHeight(mY - pos.y);
34507 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34509 // var pos = this.el.getBox(true);
34512 // var maxX = pos.right;
34514 // var maxHeight = 0;
34516 // Roo.each(items, function(item, k){
34520 // item.el.position('absolute');
34522 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34524 // item.el.setWidth(width);
34526 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34528 // item.el.setHeight(height);
34531 // item.el.setXY([x, y], isInstant ? false : true);
34533 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34536 // y = y + height + this.alternativePadWidth;
34538 // maxHeight = maxHeight + height + this.alternativePadWidth;
34542 // this.el.setHeight(maxHeight);
34546 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34548 var pos = this.el.getBox(true);
34553 var maxX = pos.right;
34555 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34557 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34559 Roo.each(queue, function(box, k){
34561 Roo.each(box, function(b, kk){
34563 b.el.position('absolute');
34565 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34566 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34568 if(b.size == 'md-left' || b.size == 'md-right'){
34569 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34570 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34573 b.el.setWidth(width);
34574 b.el.setHeight(height);
34582 var positions = [];
34584 switch (box.length){
34586 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34589 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34592 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34595 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34601 Roo.each(box, function(b,kk){
34603 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34605 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34613 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34615 Roo.each(eItems, function(b,k){
34617 b.size = (k == 0) ? 'sm' : 'xs';
34618 b.x = (k == 0) ? 2 : 1;
34619 b.y = (k == 0) ? 2 : 1;
34621 b.el.position('absolute');
34623 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34625 b.el.setWidth(width);
34627 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34629 b.el.setHeight(height);
34633 var positions = [];
34636 x : maxX - this.unitWidth * 2 - this.gutter,
34641 x : maxX - this.unitWidth,
34642 y : minY + (this.unitWidth + this.gutter) * 2
34646 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34650 Roo.each(eItems, function(b,k){
34652 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34658 getVerticalOneBoxColPositions : function(x, y, box)
34662 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34664 if(box[0].size == 'md-left'){
34668 if(box[0].size == 'md-right'){
34673 x : x + (this.unitWidth + this.gutter) * rand,
34680 getVerticalTwoBoxColPositions : function(x, y, box)
34684 if(box[0].size == 'xs'){
34688 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34692 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34706 x : x + (this.unitWidth + this.gutter) * 2,
34707 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34714 getVerticalThreeBoxColPositions : function(x, y, box)
34718 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34726 x : x + (this.unitWidth + this.gutter) * 1,
34731 x : x + (this.unitWidth + this.gutter) * 2,
34739 if(box[0].size == 'xs' && box[1].size == 'xs'){
34748 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34752 x : x + (this.unitWidth + this.gutter) * 1,
34766 x : x + (this.unitWidth + this.gutter) * 2,
34771 x : x + (this.unitWidth + this.gutter) * 2,
34772 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34779 getVerticalFourBoxColPositions : function(x, y, box)
34783 if(box[0].size == 'xs'){
34792 y : y + (this.unitHeight + this.gutter) * 1
34797 y : y + (this.unitHeight + this.gutter) * 2
34801 x : x + (this.unitWidth + this.gutter) * 1,
34815 x : x + (this.unitWidth + this.gutter) * 2,
34820 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34821 y : y + (this.unitHeight + this.gutter) * 1
34825 x : x + (this.unitWidth + this.gutter) * 2,
34826 y : y + (this.unitWidth + this.gutter) * 2
34833 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34837 if(box[0].size == 'md-left'){
34839 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34846 if(box[0].size == 'md-right'){
34848 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34849 y : minY + (this.unitWidth + this.gutter) * 1
34855 var rand = Math.floor(Math.random() * (4 - box[0].y));
34858 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34859 y : minY + (this.unitWidth + this.gutter) * rand
34866 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34870 if(box[0].size == 'xs'){
34873 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34878 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34879 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34887 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34892 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34893 y : minY + (this.unitWidth + this.gutter) * 2
34900 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34904 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34907 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34912 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34913 y : minY + (this.unitWidth + this.gutter) * 1
34917 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34918 y : minY + (this.unitWidth + this.gutter) * 2
34925 if(box[0].size == 'xs' && box[1].size == 'xs'){
34928 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34933 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34938 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34939 y : minY + (this.unitWidth + this.gutter) * 1
34947 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34952 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34953 y : minY + (this.unitWidth + this.gutter) * 2
34957 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34958 y : minY + (this.unitWidth + this.gutter) * 2
34965 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34969 if(box[0].size == 'xs'){
34972 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34977 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34982 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),
34987 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34988 y : minY + (this.unitWidth + this.gutter) * 1
34996 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35001 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35002 y : minY + (this.unitWidth + this.gutter) * 2
35006 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35007 y : minY + (this.unitWidth + this.gutter) * 2
35011 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),
35012 y : minY + (this.unitWidth + this.gutter) * 2
35020 * remove a Masonry Brick
35021 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35023 removeBrick : function(brick_id)
35029 for (var i = 0; i<this.bricks.length; i++) {
35030 if (this.bricks[i].id == brick_id) {
35031 this.bricks.splice(i,1);
35032 this.el.dom.removeChild(Roo.get(brick_id).dom);
35039 * adds a Masonry Brick
35040 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35042 addBrick : function(cfg)
35044 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35045 //this.register(cn);
35046 cn.parentId = this.id;
35047 cn.render(this.el);
35052 * register a Masonry Brick
35053 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35056 register : function(brick)
35058 this.bricks.push(brick);
35059 brick.masonryId = this.id;
35063 * clear all the Masonry Brick
35065 clearAll : function()
35068 //this.getChildContainer().dom.innerHTML = "";
35069 this.el.dom.innerHTML = '';
35072 getSelected : function()
35074 if (!this.selectedBrick) {
35078 return this.selectedBrick;
35082 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35086 * register a Masonry Layout
35087 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35090 register : function(layout)
35092 this.groups[layout.id] = layout;
35095 * fetch a Masonry Layout based on the masonry layout ID
35096 * @param {string} the masonry layout to add
35097 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35100 get: function(layout_id) {
35101 if (typeof(this.groups[layout_id]) == 'undefined') {
35104 return this.groups[layout_id] ;
35116 * http://masonry.desandro.com
35118 * The idea is to render all the bricks based on vertical width...
35120 * The original code extends 'outlayer' - we might need to use that....
35126 * @class Roo.bootstrap.LayoutMasonryAuto
35127 * @extends Roo.bootstrap.Component
35128 * Bootstrap Layout Masonry class
35131 * Create a new Element
35132 * @param {Object} config The config object
35135 Roo.bootstrap.LayoutMasonryAuto = function(config){
35136 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35139 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35142 * @cfg {Boolean} isFitWidth - resize the width..
35144 isFitWidth : false, // options..
35146 * @cfg {Boolean} isOriginLeft = left align?
35148 isOriginLeft : true,
35150 * @cfg {Boolean} isOriginTop = top align?
35152 isOriginTop : false,
35154 * @cfg {Boolean} isLayoutInstant = no animation?
35156 isLayoutInstant : false, // needed?
35158 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35160 isResizingContainer : true,
35162 * @cfg {Number} columnWidth width of the columns
35168 * @cfg {Number} maxCols maximum number of columns
35173 * @cfg {Number} padHeight padding below box..
35179 * @cfg {Boolean} isAutoInitial defalut true
35182 isAutoInitial : true,
35188 initialColumnWidth : 0,
35189 currentSize : null,
35191 colYs : null, // array.
35198 bricks: null, //CompositeElement
35199 cols : 0, // array?
35200 // element : null, // wrapped now this.el
35201 _isLayoutInited : null,
35204 getAutoCreate : function(){
35208 cls: 'blog-masonary-wrapper ' + this.cls,
35210 cls : 'mas-boxes masonary'
35217 getChildContainer: function( )
35219 if (this.boxesEl) {
35220 return this.boxesEl;
35223 this.boxesEl = this.el.select('.mas-boxes').first();
35225 return this.boxesEl;
35229 initEvents : function()
35233 if(this.isAutoInitial){
35234 Roo.log('hook children rendered');
35235 this.on('childrenrendered', function() {
35236 Roo.log('children rendered');
35243 initial : function()
35245 this.reloadItems();
35247 this.currentSize = this.el.getBox(true);
35249 /// was window resize... - let's see if this works..
35250 Roo.EventManager.onWindowResize(this.resize, this);
35252 if(!this.isAutoInitial){
35257 this.layout.defer(500,this);
35260 reloadItems: function()
35262 this.bricks = this.el.select('.masonry-brick', true);
35264 this.bricks.each(function(b) {
35265 //Roo.log(b.getSize());
35266 if (!b.attr('originalwidth')) {
35267 b.attr('originalwidth', b.getSize().width);
35272 Roo.log(this.bricks.elements.length);
35275 resize : function()
35278 var cs = this.el.getBox(true);
35280 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35281 Roo.log("no change in with or X");
35284 this.currentSize = cs;
35288 layout : function()
35291 this._resetLayout();
35292 //this._manageStamps();
35294 // don't animate first layout
35295 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35296 this.layoutItems( isInstant );
35298 // flag for initalized
35299 this._isLayoutInited = true;
35302 layoutItems : function( isInstant )
35304 //var items = this._getItemsForLayout( this.items );
35305 // original code supports filtering layout items.. we just ignore it..
35307 this._layoutItems( this.bricks , isInstant );
35309 this._postLayout();
35311 _layoutItems : function ( items , isInstant)
35313 //this.fireEvent( 'layout', this, items );
35316 if ( !items || !items.elements.length ) {
35317 // no items, emit event with empty array
35322 items.each(function(item) {
35323 Roo.log("layout item");
35325 // get x/y object from method
35326 var position = this._getItemLayoutPosition( item );
35328 position.item = item;
35329 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35330 queue.push( position );
35333 this._processLayoutQueue( queue );
35335 /** Sets position of item in DOM
35336 * @param {Element} item
35337 * @param {Number} x - horizontal position
35338 * @param {Number} y - vertical position
35339 * @param {Boolean} isInstant - disables transitions
35341 _processLayoutQueue : function( queue )
35343 for ( var i=0, len = queue.length; i < len; i++ ) {
35344 var obj = queue[i];
35345 obj.item.position('absolute');
35346 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35352 * Any logic you want to do after each layout,
35353 * i.e. size the container
35355 _postLayout : function()
35357 this.resizeContainer();
35360 resizeContainer : function()
35362 if ( !this.isResizingContainer ) {
35365 var size = this._getContainerSize();
35367 this.el.setSize(size.width,size.height);
35368 this.boxesEl.setSize(size.width,size.height);
35374 _resetLayout : function()
35376 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35377 this.colWidth = this.el.getWidth();
35378 //this.gutter = this.el.getWidth();
35380 this.measureColumns();
35386 this.colYs.push( 0 );
35392 measureColumns : function()
35394 this.getContainerWidth();
35395 // if columnWidth is 0, default to outerWidth of first item
35396 if ( !this.columnWidth ) {
35397 var firstItem = this.bricks.first();
35398 Roo.log(firstItem);
35399 this.columnWidth = this.containerWidth;
35400 if (firstItem && firstItem.attr('originalwidth') ) {
35401 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35403 // columnWidth fall back to item of first element
35404 Roo.log("set column width?");
35405 this.initialColumnWidth = this.columnWidth ;
35407 // if first elem has no width, default to size of container
35412 if (this.initialColumnWidth) {
35413 this.columnWidth = this.initialColumnWidth;
35418 // column width is fixed at the top - however if container width get's smaller we should
35421 // this bit calcs how man columns..
35423 var columnWidth = this.columnWidth += this.gutter;
35425 // calculate columns
35426 var containerWidth = this.containerWidth + this.gutter;
35428 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35429 // fix rounding errors, typically with gutters
35430 var excess = columnWidth - containerWidth % columnWidth;
35433 // if overshoot is less than a pixel, round up, otherwise floor it
35434 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35435 cols = Math[ mathMethod ]( cols );
35436 this.cols = Math.max( cols, 1 );
35437 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35439 // padding positioning..
35440 var totalColWidth = this.cols * this.columnWidth;
35441 var padavail = this.containerWidth - totalColWidth;
35442 // so for 2 columns - we need 3 'pads'
35444 var padNeeded = (1+this.cols) * this.padWidth;
35446 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35448 this.columnWidth += padExtra
35449 //this.padWidth = Math.floor(padavail / ( this.cols));
35451 // adjust colum width so that padding is fixed??
35453 // we have 3 columns ... total = width * 3
35454 // we have X left over... that should be used by
35456 //if (this.expandC) {
35464 getContainerWidth : function()
35466 /* // container is parent if fit width
35467 var container = this.isFitWidth ? this.element.parentNode : this.element;
35468 // check that this.size and size are there
35469 // IE8 triggers resize on body size change, so they might not be
35471 var size = getSize( container ); //FIXME
35472 this.containerWidth = size && size.innerWidth; //FIXME
35475 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35479 _getItemLayoutPosition : function( item ) // what is item?
35481 // we resize the item to our columnWidth..
35483 item.setWidth(this.columnWidth);
35484 item.autoBoxAdjust = false;
35486 var sz = item.getSize();
35488 // how many columns does this brick span
35489 var remainder = this.containerWidth % this.columnWidth;
35491 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35492 // round if off by 1 pixel, otherwise use ceil
35493 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35494 colSpan = Math.min( colSpan, this.cols );
35496 // normally this should be '1' as we dont' currently allow multi width columns..
35498 var colGroup = this._getColGroup( colSpan );
35499 // get the minimum Y value from the columns
35500 var minimumY = Math.min.apply( Math, colGroup );
35501 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35503 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35505 // position the brick
35507 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35508 y: this.currentSize.y + minimumY + this.padHeight
35512 // apply setHeight to necessary columns
35513 var setHeight = minimumY + sz.height + this.padHeight;
35514 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35516 var setSpan = this.cols + 1 - colGroup.length;
35517 for ( var i = 0; i < setSpan; i++ ) {
35518 this.colYs[ shortColIndex + i ] = setHeight ;
35525 * @param {Number} colSpan - number of columns the element spans
35526 * @returns {Array} colGroup
35528 _getColGroup : function( colSpan )
35530 if ( colSpan < 2 ) {
35531 // if brick spans only one column, use all the column Ys
35536 // how many different places could this brick fit horizontally
35537 var groupCount = this.cols + 1 - colSpan;
35538 // for each group potential horizontal position
35539 for ( var i = 0; i < groupCount; i++ ) {
35540 // make an array of colY values for that one group
35541 var groupColYs = this.colYs.slice( i, i + colSpan );
35542 // and get the max value of the array
35543 colGroup[i] = Math.max.apply( Math, groupColYs );
35548 _manageStamp : function( stamp )
35550 var stampSize = stamp.getSize();
35551 var offset = stamp.getBox();
35552 // get the columns that this stamp affects
35553 var firstX = this.isOriginLeft ? offset.x : offset.right;
35554 var lastX = firstX + stampSize.width;
35555 var firstCol = Math.floor( firstX / this.columnWidth );
35556 firstCol = Math.max( 0, firstCol );
35558 var lastCol = Math.floor( lastX / this.columnWidth );
35559 // lastCol should not go over if multiple of columnWidth #425
35560 lastCol -= lastX % this.columnWidth ? 0 : 1;
35561 lastCol = Math.min( this.cols - 1, lastCol );
35563 // set colYs to bottom of the stamp
35564 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35567 for ( var i = firstCol; i <= lastCol; i++ ) {
35568 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35573 _getContainerSize : function()
35575 this.maxY = Math.max.apply( Math, this.colYs );
35580 if ( this.isFitWidth ) {
35581 size.width = this._getContainerFitWidth();
35587 _getContainerFitWidth : function()
35589 var unusedCols = 0;
35590 // count unused columns
35593 if ( this.colYs[i] !== 0 ) {
35598 // fit container to columns that have been used
35599 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35602 needsResizeLayout : function()
35604 var previousWidth = this.containerWidth;
35605 this.getContainerWidth();
35606 return previousWidth !== this.containerWidth;
35621 * @class Roo.bootstrap.MasonryBrick
35622 * @extends Roo.bootstrap.Component
35623 * Bootstrap MasonryBrick class
35626 * Create a new MasonryBrick
35627 * @param {Object} config The config object
35630 Roo.bootstrap.MasonryBrick = function(config){
35632 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35634 Roo.bootstrap.MasonryBrick.register(this);
35640 * When a MasonryBrick is clcik
35641 * @param {Roo.bootstrap.MasonryBrick} this
35642 * @param {Roo.EventObject} e
35648 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35651 * @cfg {String} title
35655 * @cfg {String} html
35659 * @cfg {String} bgimage
35663 * @cfg {String} videourl
35667 * @cfg {String} cls
35671 * @cfg {String} href
35675 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35680 * @cfg {String} placetitle (center|bottom)
35685 * @cfg {Boolean} isFitContainer defalut true
35687 isFitContainer : true,
35690 * @cfg {Boolean} preventDefault defalut false
35692 preventDefault : false,
35695 * @cfg {Boolean} inverse defalut false
35697 maskInverse : false,
35699 getAutoCreate : function()
35701 if(!this.isFitContainer){
35702 return this.getSplitAutoCreate();
35705 var cls = 'masonry-brick masonry-brick-full';
35707 if(this.href.length){
35708 cls += ' masonry-brick-link';
35711 if(this.bgimage.length){
35712 cls += ' masonry-brick-image';
35715 if(this.maskInverse){
35716 cls += ' mask-inverse';
35719 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35720 cls += ' enable-mask';
35724 cls += ' masonry-' + this.size + '-brick';
35727 if(this.placetitle.length){
35729 switch (this.placetitle) {
35731 cls += ' masonry-center-title';
35734 cls += ' masonry-bottom-title';
35741 if(!this.html.length && !this.bgimage.length){
35742 cls += ' masonry-center-title';
35745 if(!this.html.length && this.bgimage.length){
35746 cls += ' masonry-bottom-title';
35751 cls += ' ' + this.cls;
35755 tag: (this.href.length) ? 'a' : 'div',
35760 cls: 'masonry-brick-mask'
35764 cls: 'masonry-brick-paragraph',
35770 if(this.href.length){
35771 cfg.href = this.href;
35774 var cn = cfg.cn[1].cn;
35776 if(this.title.length){
35779 cls: 'masonry-brick-title',
35784 if(this.html.length){
35787 cls: 'masonry-brick-text',
35792 if (!this.title.length && !this.html.length) {
35793 cfg.cn[1].cls += ' hide';
35796 if(this.bgimage.length){
35799 cls: 'masonry-brick-image-view',
35804 if(this.videourl.length){
35805 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35806 // youtube support only?
35809 cls: 'masonry-brick-image-view',
35812 allowfullscreen : true
35820 getSplitAutoCreate : function()
35822 var cls = 'masonry-brick masonry-brick-split';
35824 if(this.href.length){
35825 cls += ' masonry-brick-link';
35828 if(this.bgimage.length){
35829 cls += ' masonry-brick-image';
35833 cls += ' masonry-' + this.size + '-brick';
35836 switch (this.placetitle) {
35838 cls += ' masonry-center-title';
35841 cls += ' masonry-bottom-title';
35844 if(!this.bgimage.length){
35845 cls += ' masonry-center-title';
35848 if(this.bgimage.length){
35849 cls += ' masonry-bottom-title';
35855 cls += ' ' + this.cls;
35859 tag: (this.href.length) ? 'a' : 'div',
35864 cls: 'masonry-brick-split-head',
35868 cls: 'masonry-brick-paragraph',
35875 cls: 'masonry-brick-split-body',
35881 if(this.href.length){
35882 cfg.href = this.href;
35885 if(this.title.length){
35886 cfg.cn[0].cn[0].cn.push({
35888 cls: 'masonry-brick-title',
35893 if(this.html.length){
35894 cfg.cn[1].cn.push({
35896 cls: 'masonry-brick-text',
35901 if(this.bgimage.length){
35902 cfg.cn[0].cn.push({
35904 cls: 'masonry-brick-image-view',
35909 if(this.videourl.length){
35910 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35911 // youtube support only?
35912 cfg.cn[0].cn.cn.push({
35914 cls: 'masonry-brick-image-view',
35917 allowfullscreen : true
35924 initEvents: function()
35926 switch (this.size) {
35959 this.el.on('touchstart', this.onTouchStart, this);
35960 this.el.on('touchmove', this.onTouchMove, this);
35961 this.el.on('touchend', this.onTouchEnd, this);
35962 this.el.on('contextmenu', this.onContextMenu, this);
35964 this.el.on('mouseenter' ,this.enter, this);
35965 this.el.on('mouseleave', this.leave, this);
35966 this.el.on('click', this.onClick, this);
35969 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35970 this.parent().bricks.push(this);
35975 onClick: function(e, el)
35977 var time = this.endTimer - this.startTimer;
35978 // Roo.log(e.preventDefault());
35981 e.preventDefault();
35986 if(!this.preventDefault){
35990 e.preventDefault();
35992 if (this.activeClass != '') {
35993 this.selectBrick();
35996 this.fireEvent('click', this, e);
35999 enter: function(e, el)
36001 e.preventDefault();
36003 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36007 if(this.bgimage.length && this.html.length){
36008 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36012 leave: function(e, el)
36014 e.preventDefault();
36016 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36020 if(this.bgimage.length && this.html.length){
36021 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36025 onTouchStart: function(e, el)
36027 // e.preventDefault();
36029 this.touchmoved = false;
36031 if(!this.isFitContainer){
36035 if(!this.bgimage.length || !this.html.length){
36039 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36041 this.timer = new Date().getTime();
36045 onTouchMove: function(e, el)
36047 this.touchmoved = true;
36050 onContextMenu : function(e,el)
36052 e.preventDefault();
36053 e.stopPropagation();
36057 onTouchEnd: function(e, el)
36059 // e.preventDefault();
36061 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36068 if(!this.bgimage.length || !this.html.length){
36070 if(this.href.length){
36071 window.location.href = this.href;
36077 if(!this.isFitContainer){
36081 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36083 window.location.href = this.href;
36086 //selection on single brick only
36087 selectBrick : function() {
36089 if (!this.parentId) {
36093 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36094 var index = m.selectedBrick.indexOf(this.id);
36097 m.selectedBrick.splice(index,1);
36098 this.el.removeClass(this.activeClass);
36102 for(var i = 0; i < m.selectedBrick.length; i++) {
36103 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36104 b.el.removeClass(b.activeClass);
36107 m.selectedBrick = [];
36109 m.selectedBrick.push(this.id);
36110 this.el.addClass(this.activeClass);
36114 isSelected : function(){
36115 return this.el.hasClass(this.activeClass);
36120 Roo.apply(Roo.bootstrap.MasonryBrick, {
36123 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36125 * register a Masonry Brick
36126 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36129 register : function(brick)
36131 //this.groups[brick.id] = brick;
36132 this.groups.add(brick.id, brick);
36135 * fetch a masonry brick based on the masonry brick ID
36136 * @param {string} the masonry brick to add
36137 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36140 get: function(brick_id)
36142 // if (typeof(this.groups[brick_id]) == 'undefined') {
36145 // return this.groups[brick_id] ;
36147 if(this.groups.key(brick_id)) {
36148 return this.groups.key(brick_id);
36166 * @class Roo.bootstrap.Brick
36167 * @extends Roo.bootstrap.Component
36168 * Bootstrap Brick class
36171 * Create a new Brick
36172 * @param {Object} config The config object
36175 Roo.bootstrap.Brick = function(config){
36176 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36182 * When a Brick is click
36183 * @param {Roo.bootstrap.Brick} this
36184 * @param {Roo.EventObject} e
36190 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36193 * @cfg {String} title
36197 * @cfg {String} html
36201 * @cfg {String} bgimage
36205 * @cfg {String} cls
36209 * @cfg {String} href
36213 * @cfg {String} video
36217 * @cfg {Boolean} square
36221 getAutoCreate : function()
36223 var cls = 'roo-brick';
36225 if(this.href.length){
36226 cls += ' roo-brick-link';
36229 if(this.bgimage.length){
36230 cls += ' roo-brick-image';
36233 if(!this.html.length && !this.bgimage.length){
36234 cls += ' roo-brick-center-title';
36237 if(!this.html.length && this.bgimage.length){
36238 cls += ' roo-brick-bottom-title';
36242 cls += ' ' + this.cls;
36246 tag: (this.href.length) ? 'a' : 'div',
36251 cls: 'roo-brick-paragraph',
36257 if(this.href.length){
36258 cfg.href = this.href;
36261 var cn = cfg.cn[0].cn;
36263 if(this.title.length){
36266 cls: 'roo-brick-title',
36271 if(this.html.length){
36274 cls: 'roo-brick-text',
36281 if(this.bgimage.length){
36284 cls: 'roo-brick-image-view',
36292 initEvents: function()
36294 if(this.title.length || this.html.length){
36295 this.el.on('mouseenter' ,this.enter, this);
36296 this.el.on('mouseleave', this.leave, this);
36299 Roo.EventManager.onWindowResize(this.resize, this);
36301 if(this.bgimage.length){
36302 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36303 this.imageEl.on('load', this.onImageLoad, this);
36310 onImageLoad : function()
36315 resize : function()
36317 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36319 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36321 if(this.bgimage.length){
36322 var image = this.el.select('.roo-brick-image-view', true).first();
36324 image.setWidth(paragraph.getWidth());
36327 image.setHeight(paragraph.getWidth());
36330 this.el.setHeight(image.getHeight());
36331 paragraph.setHeight(image.getHeight());
36337 enter: function(e, el)
36339 e.preventDefault();
36341 if(this.bgimage.length){
36342 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36343 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36347 leave: function(e, el)
36349 e.preventDefault();
36351 if(this.bgimage.length){
36352 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36353 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36368 * @class Roo.bootstrap.NumberField
36369 * @extends Roo.bootstrap.Input
36370 * Bootstrap NumberField class
36376 * Create a new NumberField
36377 * @param {Object} config The config object
36380 Roo.bootstrap.NumberField = function(config){
36381 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36384 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36387 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36389 allowDecimals : true,
36391 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36393 decimalSeparator : ".",
36395 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36397 decimalPrecision : 2,
36399 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36401 allowNegative : true,
36404 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36408 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36410 minValue : Number.NEGATIVE_INFINITY,
36412 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36414 maxValue : Number.MAX_VALUE,
36416 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36418 minText : "The minimum value for this field is {0}",
36420 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36422 maxText : "The maximum value for this field is {0}",
36424 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36425 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36427 nanText : "{0} is not a valid number",
36429 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36431 thousandsDelimiter : false,
36433 * @cfg {String} valueAlign alignment of value
36435 valueAlign : "left",
36437 getAutoCreate : function()
36439 var hiddenInput = {
36443 cls: 'hidden-number-input'
36447 hiddenInput.name = this.name;
36452 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36454 this.name = hiddenInput.name;
36456 if(cfg.cn.length > 0) {
36457 cfg.cn.push(hiddenInput);
36464 initEvents : function()
36466 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36468 var allowed = "0123456789";
36470 if(this.allowDecimals){
36471 allowed += this.decimalSeparator;
36474 if(this.allowNegative){
36478 if(this.thousandsDelimiter) {
36482 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36484 var keyPress = function(e){
36486 var k = e.getKey();
36488 var c = e.getCharCode();
36491 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36492 allowed.indexOf(String.fromCharCode(c)) === -1
36498 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36502 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36507 this.el.on("keypress", keyPress, this);
36510 validateValue : function(value)
36513 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36517 var num = this.parseValue(value);
36520 this.markInvalid(String.format(this.nanText, value));
36524 if(num < this.minValue){
36525 this.markInvalid(String.format(this.minText, this.minValue));
36529 if(num > this.maxValue){
36530 this.markInvalid(String.format(this.maxText, this.maxValue));
36537 getValue : function()
36539 var v = this.hiddenEl().getValue();
36541 return this.fixPrecision(this.parseValue(v));
36544 parseValue : function(value)
36546 if(this.thousandsDelimiter) {
36548 r = new RegExp(",", "g");
36549 value = value.replace(r, "");
36552 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36553 return isNaN(value) ? '' : value;
36556 fixPrecision : function(value)
36558 if(this.thousandsDelimiter) {
36560 r = new RegExp(",", "g");
36561 value = value.replace(r, "");
36564 var nan = isNaN(value);
36566 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36567 return nan ? '' : value;
36569 return parseFloat(value).toFixed(this.decimalPrecision);
36572 setValue : function(v)
36574 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36580 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36582 this.inputEl().dom.value = (v == '') ? '' :
36583 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36585 if(!this.allowZero && v === '0') {
36586 this.hiddenEl().dom.value = '';
36587 this.inputEl().dom.value = '';
36594 decimalPrecisionFcn : function(v)
36596 return Math.floor(v);
36599 beforeBlur : function()
36601 var v = this.parseValue(this.getRawValue());
36603 if(v || v === 0 || v === ''){
36608 hiddenEl : function()
36610 return this.el.select('input.hidden-number-input',true).first();
36622 * @class Roo.bootstrap.DocumentSlider
36623 * @extends Roo.bootstrap.Component
36624 * Bootstrap DocumentSlider class
36627 * Create a new DocumentViewer
36628 * @param {Object} config The config object
36631 Roo.bootstrap.DocumentSlider = function(config){
36632 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36639 * Fire after initEvent
36640 * @param {Roo.bootstrap.DocumentSlider} this
36645 * Fire after update
36646 * @param {Roo.bootstrap.DocumentSlider} this
36652 * @param {Roo.bootstrap.DocumentSlider} this
36658 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36664 getAutoCreate : function()
36668 cls : 'roo-document-slider',
36672 cls : 'roo-document-slider-header',
36676 cls : 'roo-document-slider-header-title'
36682 cls : 'roo-document-slider-body',
36686 cls : 'roo-document-slider-prev',
36690 cls : 'fa fa-chevron-left'
36696 cls : 'roo-document-slider-thumb',
36700 cls : 'roo-document-slider-image'
36706 cls : 'roo-document-slider-next',
36710 cls : 'fa fa-chevron-right'
36722 initEvents : function()
36724 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36725 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36727 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36728 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36730 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36731 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36733 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36734 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36736 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36737 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36739 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36740 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36742 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36743 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36745 this.thumbEl.on('click', this.onClick, this);
36747 this.prevIndicator.on('click', this.prev, this);
36749 this.nextIndicator.on('click', this.next, this);
36753 initial : function()
36755 if(this.files.length){
36756 this.indicator = 1;
36760 this.fireEvent('initial', this);
36763 update : function()
36765 this.imageEl.attr('src', this.files[this.indicator - 1]);
36767 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36769 this.prevIndicator.show();
36771 if(this.indicator == 1){
36772 this.prevIndicator.hide();
36775 this.nextIndicator.show();
36777 if(this.indicator == this.files.length){
36778 this.nextIndicator.hide();
36781 this.thumbEl.scrollTo('top');
36783 this.fireEvent('update', this);
36786 onClick : function(e)
36788 e.preventDefault();
36790 this.fireEvent('click', this);
36795 e.preventDefault();
36797 this.indicator = Math.max(1, this.indicator - 1);
36804 e.preventDefault();
36806 this.indicator = Math.min(this.files.length, this.indicator + 1);
36820 * @class Roo.bootstrap.RadioSet
36821 * @extends Roo.bootstrap.Input
36822 * Bootstrap RadioSet class
36823 * @cfg {String} indicatorpos (left|right) default left
36824 * @cfg {Boolean} inline (true|false) inline the element (default true)
36825 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36827 * Create a new RadioSet
36828 * @param {Object} config The config object
36831 Roo.bootstrap.RadioSet = function(config){
36833 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36837 Roo.bootstrap.RadioSet.register(this);
36842 * Fires when the element is checked or unchecked.
36843 * @param {Roo.bootstrap.RadioSet} this This radio
36844 * @param {Roo.bootstrap.Radio} item The checked item
36849 * Fires when the element is click.
36850 * @param {Roo.bootstrap.RadioSet} this This radio set
36851 * @param {Roo.bootstrap.Radio} item The checked item
36852 * @param {Roo.EventObject} e The event object
36859 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36867 indicatorpos : 'left',
36869 getAutoCreate : function()
36873 cls : 'roo-radio-set-label',
36877 html : this.fieldLabel
36881 if (Roo.bootstrap.version == 3) {
36884 if(this.indicatorpos == 'left'){
36887 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36888 tooltip : 'This field is required'
36893 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36894 tooltip : 'This field is required'
36900 cls : 'roo-radio-set-items'
36903 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36905 if (align === 'left' && this.fieldLabel.length) {
36908 cls : "roo-radio-set-right",
36914 if(this.labelWidth > 12){
36915 label.style = "width: " + this.labelWidth + 'px';
36918 if(this.labelWidth < 13 && this.labelmd == 0){
36919 this.labelmd = this.labelWidth;
36922 if(this.labellg > 0){
36923 label.cls += ' col-lg-' + this.labellg;
36924 items.cls += ' col-lg-' + (12 - this.labellg);
36927 if(this.labelmd > 0){
36928 label.cls += ' col-md-' + this.labelmd;
36929 items.cls += ' col-md-' + (12 - this.labelmd);
36932 if(this.labelsm > 0){
36933 label.cls += ' col-sm-' + this.labelsm;
36934 items.cls += ' col-sm-' + (12 - this.labelsm);
36937 if(this.labelxs > 0){
36938 label.cls += ' col-xs-' + this.labelxs;
36939 items.cls += ' col-xs-' + (12 - this.labelxs);
36945 cls : 'roo-radio-set',
36949 cls : 'roo-radio-set-input',
36952 value : this.value ? this.value : ''
36959 if(this.weight.length){
36960 cfg.cls += ' roo-radio-' + this.weight;
36964 cfg.cls += ' roo-radio-set-inline';
36968 ['xs','sm','md','lg'].map(function(size){
36969 if (settings[size]) {
36970 cfg.cls += ' col-' + size + '-' + settings[size];
36978 initEvents : function()
36980 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36981 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36983 if(!this.fieldLabel.length){
36984 this.labelEl.hide();
36987 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36988 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36990 this.indicator = this.indicatorEl();
36992 if(this.indicator){
36993 this.indicator.addClass('invisible');
36996 this.originalValue = this.getValue();
37000 inputEl: function ()
37002 return this.el.select('.roo-radio-set-input', true).first();
37005 getChildContainer : function()
37007 return this.itemsEl;
37010 register : function(item)
37012 this.radioes.push(item);
37016 validate : function()
37018 if(this.getVisibilityEl().hasClass('hidden')){
37024 Roo.each(this.radioes, function(i){
37033 if(this.allowBlank) {
37037 if(this.disabled || valid){
37042 this.markInvalid();
37047 markValid : function()
37049 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37050 this.indicatorEl().removeClass('visible');
37051 this.indicatorEl().addClass('invisible');
37055 if (Roo.bootstrap.version == 3) {
37056 this.el.removeClass([this.invalidClass, this.validClass]);
37057 this.el.addClass(this.validClass);
37059 this.el.removeClass(['is-invalid','is-valid']);
37060 this.el.addClass(['is-valid']);
37062 this.fireEvent('valid', this);
37065 markInvalid : function(msg)
37067 if(this.allowBlank || this.disabled){
37071 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37072 this.indicatorEl().removeClass('invisible');
37073 this.indicatorEl().addClass('visible');
37075 if (Roo.bootstrap.version == 3) {
37076 this.el.removeClass([this.invalidClass, this.validClass]);
37077 this.el.addClass(this.invalidClass);
37079 this.el.removeClass(['is-invalid','is-valid']);
37080 this.el.addClass(['is-invalid']);
37083 this.fireEvent('invalid', this, msg);
37087 setValue : function(v, suppressEvent)
37089 if(this.value === v){
37096 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37099 Roo.each(this.radioes, function(i){
37101 i.el.removeClass('checked');
37104 Roo.each(this.radioes, function(i){
37106 if(i.value === v || i.value.toString() === v.toString()){
37108 i.el.addClass('checked');
37110 if(suppressEvent !== true){
37111 this.fireEvent('check', this, i);
37122 clearInvalid : function(){
37124 if(!this.el || this.preventMark){
37128 this.el.removeClass([this.invalidClass]);
37130 this.fireEvent('valid', this);
37135 Roo.apply(Roo.bootstrap.RadioSet, {
37139 register : function(set)
37141 this.groups[set.name] = set;
37144 get: function(name)
37146 if (typeof(this.groups[name]) == 'undefined') {
37150 return this.groups[name] ;
37156 * Ext JS Library 1.1.1
37157 * Copyright(c) 2006-2007, Ext JS, LLC.
37159 * Originally Released Under LGPL - original licence link has changed is not relivant.
37162 * <script type="text/javascript">
37167 * @class Roo.bootstrap.SplitBar
37168 * @extends Roo.util.Observable
37169 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37173 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37174 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37175 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37176 split.minSize = 100;
37177 split.maxSize = 600;
37178 split.animate = true;
37179 split.on('moved', splitterMoved);
37182 * Create a new SplitBar
37183 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37184 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37185 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37186 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37187 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37188 position of the SplitBar).
37190 Roo.bootstrap.SplitBar = function(cfg){
37195 // dragElement : elm
37196 // resizingElement: el,
37198 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37199 // placement : Roo.bootstrap.SplitBar.LEFT ,
37200 // existingProxy ???
37203 this.el = Roo.get(cfg.dragElement, true);
37204 this.el.dom.unselectable = "on";
37206 this.resizingEl = Roo.get(cfg.resizingElement, true);
37210 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37211 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37214 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37217 * The minimum size of the resizing element. (Defaults to 0)
37223 * The maximum size of the resizing element. (Defaults to 2000)
37226 this.maxSize = 2000;
37229 * Whether to animate the transition to the new size
37232 this.animate = false;
37235 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37238 this.useShim = false;
37243 if(!cfg.existingProxy){
37245 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37247 this.proxy = Roo.get(cfg.existingProxy).dom;
37250 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37253 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37256 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37259 this.dragSpecs = {};
37262 * @private The adapter to use to positon and resize elements
37264 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37265 this.adapter.init(this);
37267 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37269 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37270 this.el.addClass("roo-splitbar-h");
37273 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37274 this.el.addClass("roo-splitbar-v");
37280 * Fires when the splitter is moved (alias for {@link #event-moved})
37281 * @param {Roo.bootstrap.SplitBar} this
37282 * @param {Number} newSize the new width or height
37287 * Fires when the splitter is moved
37288 * @param {Roo.bootstrap.SplitBar} this
37289 * @param {Number} newSize the new width or height
37293 * @event beforeresize
37294 * Fires before the splitter is dragged
37295 * @param {Roo.bootstrap.SplitBar} this
37297 "beforeresize" : true,
37299 "beforeapply" : true
37302 Roo.util.Observable.call(this);
37305 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37306 onStartProxyDrag : function(x, y){
37307 this.fireEvent("beforeresize", this);
37309 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37311 o.enableDisplayMode("block");
37312 // all splitbars share the same overlay
37313 Roo.bootstrap.SplitBar.prototype.overlay = o;
37315 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37316 this.overlay.show();
37317 Roo.get(this.proxy).setDisplayed("block");
37318 var size = this.adapter.getElementSize(this);
37319 this.activeMinSize = this.getMinimumSize();;
37320 this.activeMaxSize = this.getMaximumSize();;
37321 var c1 = size - this.activeMinSize;
37322 var c2 = Math.max(this.activeMaxSize - size, 0);
37323 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37324 this.dd.resetConstraints();
37325 this.dd.setXConstraint(
37326 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37327 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37329 this.dd.setYConstraint(0, 0);
37331 this.dd.resetConstraints();
37332 this.dd.setXConstraint(0, 0);
37333 this.dd.setYConstraint(
37334 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37335 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37338 this.dragSpecs.startSize = size;
37339 this.dragSpecs.startPoint = [x, y];
37340 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37344 * @private Called after the drag operation by the DDProxy
37346 onEndProxyDrag : function(e){
37347 Roo.get(this.proxy).setDisplayed(false);
37348 var endPoint = Roo.lib.Event.getXY(e);
37350 this.overlay.hide();
37353 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37354 newSize = this.dragSpecs.startSize +
37355 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37356 endPoint[0] - this.dragSpecs.startPoint[0] :
37357 this.dragSpecs.startPoint[0] - endPoint[0]
37360 newSize = this.dragSpecs.startSize +
37361 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37362 endPoint[1] - this.dragSpecs.startPoint[1] :
37363 this.dragSpecs.startPoint[1] - endPoint[1]
37366 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37367 if(newSize != this.dragSpecs.startSize){
37368 if(this.fireEvent('beforeapply', this, newSize) !== false){
37369 this.adapter.setElementSize(this, newSize);
37370 this.fireEvent("moved", this, newSize);
37371 this.fireEvent("resize", this, newSize);
37377 * Get the adapter this SplitBar uses
37378 * @return The adapter object
37380 getAdapter : function(){
37381 return this.adapter;
37385 * Set the adapter this SplitBar uses
37386 * @param {Object} adapter A SplitBar adapter object
37388 setAdapter : function(adapter){
37389 this.adapter = adapter;
37390 this.adapter.init(this);
37394 * Gets the minimum size for the resizing element
37395 * @return {Number} The minimum size
37397 getMinimumSize : function(){
37398 return this.minSize;
37402 * Sets the minimum size for the resizing element
37403 * @param {Number} minSize The minimum size
37405 setMinimumSize : function(minSize){
37406 this.minSize = minSize;
37410 * Gets the maximum size for the resizing element
37411 * @return {Number} The maximum size
37413 getMaximumSize : function(){
37414 return this.maxSize;
37418 * Sets the maximum size for the resizing element
37419 * @param {Number} maxSize The maximum size
37421 setMaximumSize : function(maxSize){
37422 this.maxSize = maxSize;
37426 * Sets the initialize size for the resizing element
37427 * @param {Number} size The initial size
37429 setCurrentSize : function(size){
37430 var oldAnimate = this.animate;
37431 this.animate = false;
37432 this.adapter.setElementSize(this, size);
37433 this.animate = oldAnimate;
37437 * Destroy this splitbar.
37438 * @param {Boolean} removeEl True to remove the element
37440 destroy : function(removeEl){
37442 this.shim.remove();
37445 this.proxy.parentNode.removeChild(this.proxy);
37453 * @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.
37455 Roo.bootstrap.SplitBar.createProxy = function(dir){
37456 var proxy = new Roo.Element(document.createElement("div"));
37457 proxy.unselectable();
37458 var cls = 'roo-splitbar-proxy';
37459 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37460 document.body.appendChild(proxy.dom);
37465 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37466 * Default Adapter. It assumes the splitter and resizing element are not positioned
37467 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37469 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37472 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37473 // do nothing for now
37474 init : function(s){
37478 * Called before drag operations to get the current size of the resizing element.
37479 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37481 getElementSize : function(s){
37482 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37483 return s.resizingEl.getWidth();
37485 return s.resizingEl.getHeight();
37490 * Called after drag operations to set the size of the resizing element.
37491 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37492 * @param {Number} newSize The new size to set
37493 * @param {Function} onComplete A function to be invoked when resizing is complete
37495 setElementSize : function(s, newSize, onComplete){
37496 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37498 s.resizingEl.setWidth(newSize);
37500 onComplete(s, newSize);
37503 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37508 s.resizingEl.setHeight(newSize);
37510 onComplete(s, newSize);
37513 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37520 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37521 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37522 * Adapter that moves the splitter element to align with the resized sizing element.
37523 * Used with an absolute positioned SplitBar.
37524 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37525 * document.body, make sure you assign an id to the body element.
37527 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37528 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37529 this.container = Roo.get(container);
37532 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37533 init : function(s){
37534 this.basic.init(s);
37537 getElementSize : function(s){
37538 return this.basic.getElementSize(s);
37541 setElementSize : function(s, newSize, onComplete){
37542 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37545 moveSplitter : function(s){
37546 var yes = Roo.bootstrap.SplitBar;
37547 switch(s.placement){
37549 s.el.setX(s.resizingEl.getRight());
37552 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37555 s.el.setY(s.resizingEl.getBottom());
37558 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37565 * Orientation constant - Create a vertical SplitBar
37569 Roo.bootstrap.SplitBar.VERTICAL = 1;
37572 * Orientation constant - Create a horizontal SplitBar
37576 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37579 * Placement constant - The resizing element is to the left of the splitter element
37583 Roo.bootstrap.SplitBar.LEFT = 1;
37586 * Placement constant - The resizing element is to the right of the splitter element
37590 Roo.bootstrap.SplitBar.RIGHT = 2;
37593 * Placement constant - The resizing element is positioned above the splitter element
37597 Roo.bootstrap.SplitBar.TOP = 3;
37600 * Placement constant - The resizing element is positioned under splitter element
37604 Roo.bootstrap.SplitBar.BOTTOM = 4;
37605 Roo.namespace("Roo.bootstrap.layout");/*
37607 * Ext JS Library 1.1.1
37608 * Copyright(c) 2006-2007, Ext JS, LLC.
37610 * Originally Released Under LGPL - original licence link has changed is not relivant.
37613 * <script type="text/javascript">
37617 * @class Roo.bootstrap.layout.Manager
37618 * @extends Roo.bootstrap.Component
37619 * Base class for layout managers.
37621 Roo.bootstrap.layout.Manager = function(config)
37623 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37629 /** false to disable window resize monitoring @type Boolean */
37630 this.monitorWindowResize = true;
37635 * Fires when a layout is performed.
37636 * @param {Roo.LayoutManager} this
37640 * @event regionresized
37641 * Fires when the user resizes a region.
37642 * @param {Roo.LayoutRegion} region The resized region
37643 * @param {Number} newSize The new size (width for east/west, height for north/south)
37645 "regionresized" : true,
37647 * @event regioncollapsed
37648 * Fires when a region is collapsed.
37649 * @param {Roo.LayoutRegion} region The collapsed region
37651 "regioncollapsed" : true,
37653 * @event regionexpanded
37654 * Fires when a region is expanded.
37655 * @param {Roo.LayoutRegion} region The expanded region
37657 "regionexpanded" : true
37659 this.updating = false;
37662 this.el = Roo.get(config.el);
37668 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37673 monitorWindowResize : true,
37679 onRender : function(ct, position)
37682 this.el = Roo.get(ct);
37685 //this.fireEvent('render',this);
37689 initEvents: function()
37693 // ie scrollbar fix
37694 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37695 document.body.scroll = "no";
37696 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37697 this.el.position('relative');
37699 this.id = this.el.id;
37700 this.el.addClass("roo-layout-container");
37701 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37702 if(this.el.dom != document.body ) {
37703 this.el.on('resize', this.layout,this);
37704 this.el.on('show', this.layout,this);
37710 * Returns true if this layout is currently being updated
37711 * @return {Boolean}
37713 isUpdating : function(){
37714 return this.updating;
37718 * Suspend the LayoutManager from doing auto-layouts while
37719 * making multiple add or remove calls
37721 beginUpdate : function(){
37722 this.updating = true;
37726 * Restore auto-layouts and optionally disable the manager from performing a layout
37727 * @param {Boolean} noLayout true to disable a layout update
37729 endUpdate : function(noLayout){
37730 this.updating = false;
37736 layout: function(){
37740 onRegionResized : function(region, newSize){
37741 this.fireEvent("regionresized", region, newSize);
37745 onRegionCollapsed : function(region){
37746 this.fireEvent("regioncollapsed", region);
37749 onRegionExpanded : function(region){
37750 this.fireEvent("regionexpanded", region);
37754 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37755 * performs box-model adjustments.
37756 * @return {Object} The size as an object {width: (the width), height: (the height)}
37758 getViewSize : function()
37761 if(this.el.dom != document.body){
37762 size = this.el.getSize();
37764 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37766 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37767 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37772 * Returns the Element this layout is bound to.
37773 * @return {Roo.Element}
37775 getEl : function(){
37780 * Returns the specified region.
37781 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37782 * @return {Roo.LayoutRegion}
37784 getRegion : function(target){
37785 return this.regions[target.toLowerCase()];
37788 onWindowResize : function(){
37789 if(this.monitorWindowResize){
37796 * Ext JS Library 1.1.1
37797 * Copyright(c) 2006-2007, Ext JS, LLC.
37799 * Originally Released Under LGPL - original licence link has changed is not relivant.
37802 * <script type="text/javascript">
37805 * @class Roo.bootstrap.layout.Border
37806 * @extends Roo.bootstrap.layout.Manager
37807 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37808 * please see: examples/bootstrap/nested.html<br><br>
37810 <b>The container the layout is rendered into can be either the body element or any other element.
37811 If it is not the body element, the container needs to either be an absolute positioned element,
37812 or you will need to add "position:relative" to the css of the container. You will also need to specify
37813 the container size if it is not the body element.</b>
37816 * Create a new Border
37817 * @param {Object} config Configuration options
37819 Roo.bootstrap.layout.Border = function(config){
37820 config = config || {};
37821 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37825 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37826 if(config[region]){
37827 config[region].region = region;
37828 this.addRegion(config[region]);
37834 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37836 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37838 parent : false, // this might point to a 'nest' or a ???
37841 * Creates and adds a new region if it doesn't already exist.
37842 * @param {String} target The target region key (north, south, east, west or center).
37843 * @param {Object} config The regions config object
37844 * @return {BorderLayoutRegion} The new region
37846 addRegion : function(config)
37848 if(!this.regions[config.region]){
37849 var r = this.factory(config);
37850 this.bindRegion(r);
37852 return this.regions[config.region];
37856 bindRegion : function(r){
37857 this.regions[r.config.region] = r;
37859 r.on("visibilitychange", this.layout, this);
37860 r.on("paneladded", this.layout, this);
37861 r.on("panelremoved", this.layout, this);
37862 r.on("invalidated", this.layout, this);
37863 r.on("resized", this.onRegionResized, this);
37864 r.on("collapsed", this.onRegionCollapsed, this);
37865 r.on("expanded", this.onRegionExpanded, this);
37869 * Performs a layout update.
37871 layout : function()
37873 if(this.updating) {
37877 // render all the rebions if they have not been done alreayd?
37878 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37879 if(this.regions[region] && !this.regions[region].bodyEl){
37880 this.regions[region].onRender(this.el)
37884 var size = this.getViewSize();
37885 var w = size.width;
37886 var h = size.height;
37891 //var x = 0, y = 0;
37893 var rs = this.regions;
37894 var north = rs["north"];
37895 var south = rs["south"];
37896 var west = rs["west"];
37897 var east = rs["east"];
37898 var center = rs["center"];
37899 //if(this.hideOnLayout){ // not supported anymore
37900 //c.el.setStyle("display", "none");
37902 if(north && north.isVisible()){
37903 var b = north.getBox();
37904 var m = north.getMargins();
37905 b.width = w - (m.left+m.right);
37908 centerY = b.height + b.y + m.bottom;
37909 centerH -= centerY;
37910 north.updateBox(this.safeBox(b));
37912 if(south && south.isVisible()){
37913 var b = south.getBox();
37914 var m = south.getMargins();
37915 b.width = w - (m.left+m.right);
37917 var totalHeight = (b.height + m.top + m.bottom);
37918 b.y = h - totalHeight + m.top;
37919 centerH -= totalHeight;
37920 south.updateBox(this.safeBox(b));
37922 if(west && west.isVisible()){
37923 var b = west.getBox();
37924 var m = west.getMargins();
37925 b.height = centerH - (m.top+m.bottom);
37927 b.y = centerY + m.top;
37928 var totalWidth = (b.width + m.left + m.right);
37929 centerX += totalWidth;
37930 centerW -= totalWidth;
37931 west.updateBox(this.safeBox(b));
37933 if(east && east.isVisible()){
37934 var b = east.getBox();
37935 var m = east.getMargins();
37936 b.height = centerH - (m.top+m.bottom);
37937 var totalWidth = (b.width + m.left + m.right);
37938 b.x = w - totalWidth + m.left;
37939 b.y = centerY + m.top;
37940 centerW -= totalWidth;
37941 east.updateBox(this.safeBox(b));
37944 var m = center.getMargins();
37946 x: centerX + m.left,
37947 y: centerY + m.top,
37948 width: centerW - (m.left+m.right),
37949 height: centerH - (m.top+m.bottom)
37951 //if(this.hideOnLayout){
37952 //center.el.setStyle("display", "block");
37954 center.updateBox(this.safeBox(centerBox));
37957 this.fireEvent("layout", this);
37961 safeBox : function(box){
37962 box.width = Math.max(0, box.width);
37963 box.height = Math.max(0, box.height);
37968 * Adds a ContentPanel (or subclass) to this layout.
37969 * @param {String} target The target region key (north, south, east, west or center).
37970 * @param {Roo.ContentPanel} panel The panel to add
37971 * @return {Roo.ContentPanel} The added panel
37973 add : function(target, panel){
37975 target = target.toLowerCase();
37976 return this.regions[target].add(panel);
37980 * Remove a ContentPanel (or subclass) to this layout.
37981 * @param {String} target The target region key (north, south, east, west or center).
37982 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37983 * @return {Roo.ContentPanel} The removed panel
37985 remove : function(target, panel){
37986 target = target.toLowerCase();
37987 return this.regions[target].remove(panel);
37991 * Searches all regions for a panel with the specified id
37992 * @param {String} panelId
37993 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37995 findPanel : function(panelId){
37996 var rs = this.regions;
37997 for(var target in rs){
37998 if(typeof rs[target] != "function"){
37999 var p = rs[target].getPanel(panelId);
38009 * Searches all regions for a panel with the specified id and activates (shows) it.
38010 * @param {String/ContentPanel} panelId The panels id or the panel itself
38011 * @return {Roo.ContentPanel} The shown panel or null
38013 showPanel : function(panelId) {
38014 var rs = this.regions;
38015 for(var target in rs){
38016 var r = rs[target];
38017 if(typeof r != "function"){
38018 if(r.hasPanel(panelId)){
38019 return r.showPanel(panelId);
38027 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38028 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38031 restoreState : function(provider){
38033 provider = Roo.state.Manager;
38035 var sm = new Roo.LayoutStateManager();
38036 sm.init(this, provider);
38042 * Adds a xtype elements to the layout.
38046 xtype : 'ContentPanel',
38053 xtype : 'NestedLayoutPanel',
38059 items : [ ... list of content panels or nested layout panels.. ]
38063 * @param {Object} cfg Xtype definition of item to add.
38065 addxtype : function(cfg)
38067 // basically accepts a pannel...
38068 // can accept a layout region..!?!?
38069 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38072 // theory? children can only be panels??
38074 //if (!cfg.xtype.match(/Panel$/)) {
38079 if (typeof(cfg.region) == 'undefined') {
38080 Roo.log("Failed to add Panel, region was not set");
38084 var region = cfg.region;
38090 xitems = cfg.items;
38095 if ( region == 'center') {
38096 Roo.log("Center: " + cfg.title);
38102 case 'Content': // ContentPanel (el, cfg)
38103 case 'Scroll': // ContentPanel (el, cfg)
38105 cfg.autoCreate = cfg.autoCreate || true;
38106 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38108 // var el = this.el.createChild();
38109 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38112 this.add(region, ret);
38116 case 'TreePanel': // our new panel!
38117 cfg.el = this.el.createChild();
38118 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38119 this.add(region, ret);
38124 // create a new Layout (which is a Border Layout...
38126 var clayout = cfg.layout;
38127 clayout.el = this.el.createChild();
38128 clayout.items = clayout.items || [];
38132 // replace this exitems with the clayout ones..
38133 xitems = clayout.items;
38135 // force background off if it's in center...
38136 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38137 cfg.background = false;
38139 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38142 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38143 //console.log('adding nested layout panel ' + cfg.toSource());
38144 this.add(region, ret);
38145 nb = {}; /// find first...
38150 // needs grid and region
38152 //var el = this.getRegion(region).el.createChild();
38154 *var el = this.el.createChild();
38155 // create the grid first...
38156 cfg.grid.container = el;
38157 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38160 if (region == 'center' && this.active ) {
38161 cfg.background = false;
38164 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38166 this.add(region, ret);
38168 if (cfg.background) {
38169 // render grid on panel activation (if panel background)
38170 ret.on('activate', function(gp) {
38171 if (!gp.grid.rendered) {
38172 // gp.grid.render(el);
38176 // cfg.grid.render(el);
38182 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38183 // it was the old xcomponent building that caused this before.
38184 // espeically if border is the top element in the tree.
38194 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38196 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38197 this.add(region, ret);
38201 throw "Can not add '" + cfg.xtype + "' to Border";
38207 this.beginUpdate();
38211 Roo.each(xitems, function(i) {
38212 region = nb && i.region ? i.region : false;
38214 var add = ret.addxtype(i);
38217 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38218 if (!i.background) {
38219 abn[region] = nb[region] ;
38226 // make the last non-background panel active..
38227 //if (nb) { Roo.log(abn); }
38230 for(var r in abn) {
38231 region = this.getRegion(r);
38233 // tried using nb[r], but it does not work..
38235 region.showPanel(abn[r]);
38246 factory : function(cfg)
38249 var validRegions = Roo.bootstrap.layout.Border.regions;
38251 var target = cfg.region;
38254 var r = Roo.bootstrap.layout;
38258 return new r.North(cfg);
38260 return new r.South(cfg);
38262 return new r.East(cfg);
38264 return new r.West(cfg);
38266 return new r.Center(cfg);
38268 throw 'Layout region "'+target+'" not supported.';
38275 * Ext JS Library 1.1.1
38276 * Copyright(c) 2006-2007, Ext JS, LLC.
38278 * Originally Released Under LGPL - original licence link has changed is not relivant.
38281 * <script type="text/javascript">
38285 * @class Roo.bootstrap.layout.Basic
38286 * @extends Roo.util.Observable
38287 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38288 * and does not have a titlebar, tabs or any other features. All it does is size and position
38289 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38290 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38291 * @cfg {string} region the region that it inhabits..
38292 * @cfg {bool} skipConfig skip config?
38296 Roo.bootstrap.layout.Basic = function(config){
38298 this.mgr = config.mgr;
38300 this.position = config.region;
38302 var skipConfig = config.skipConfig;
38306 * @scope Roo.BasicLayoutRegion
38310 * @event beforeremove
38311 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38312 * @param {Roo.LayoutRegion} this
38313 * @param {Roo.ContentPanel} panel The panel
38314 * @param {Object} e The cancel event object
38316 "beforeremove" : true,
38318 * @event invalidated
38319 * Fires when the layout for this region is changed.
38320 * @param {Roo.LayoutRegion} this
38322 "invalidated" : true,
38324 * @event visibilitychange
38325 * Fires when this region is shown or hidden
38326 * @param {Roo.LayoutRegion} this
38327 * @param {Boolean} visibility true or false
38329 "visibilitychange" : true,
38331 * @event paneladded
38332 * Fires when a panel is added.
38333 * @param {Roo.LayoutRegion} this
38334 * @param {Roo.ContentPanel} panel The panel
38336 "paneladded" : true,
38338 * @event panelremoved
38339 * Fires when a panel is removed.
38340 * @param {Roo.LayoutRegion} this
38341 * @param {Roo.ContentPanel} panel The panel
38343 "panelremoved" : true,
38345 * @event beforecollapse
38346 * Fires when this region before collapse.
38347 * @param {Roo.LayoutRegion} this
38349 "beforecollapse" : true,
38352 * Fires when this region is collapsed.
38353 * @param {Roo.LayoutRegion} this
38355 "collapsed" : true,
38358 * Fires when this region is expanded.
38359 * @param {Roo.LayoutRegion} this
38364 * Fires when this region is slid into view.
38365 * @param {Roo.LayoutRegion} this
38367 "slideshow" : true,
38370 * Fires when this region slides out of view.
38371 * @param {Roo.LayoutRegion} this
38373 "slidehide" : true,
38375 * @event panelactivated
38376 * Fires when a panel is activated.
38377 * @param {Roo.LayoutRegion} this
38378 * @param {Roo.ContentPanel} panel The activated panel
38380 "panelactivated" : true,
38383 * Fires when the user resizes this region.
38384 * @param {Roo.LayoutRegion} this
38385 * @param {Number} newSize The new size (width for east/west, height for north/south)
38389 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38390 this.panels = new Roo.util.MixedCollection();
38391 this.panels.getKey = this.getPanelId.createDelegate(this);
38393 this.activePanel = null;
38394 // ensure listeners are added...
38396 if (config.listeners || config.events) {
38397 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38398 listeners : config.listeners || {},
38399 events : config.events || {}
38403 if(skipConfig !== true){
38404 this.applyConfig(config);
38408 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38410 getPanelId : function(p){
38414 applyConfig : function(config){
38415 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38416 this.config = config;
38421 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38422 * the width, for horizontal (north, south) the height.
38423 * @param {Number} newSize The new width or height
38425 resizeTo : function(newSize){
38426 var el = this.el ? this.el :
38427 (this.activePanel ? this.activePanel.getEl() : null);
38429 switch(this.position){
38432 el.setWidth(newSize);
38433 this.fireEvent("resized", this, newSize);
38437 el.setHeight(newSize);
38438 this.fireEvent("resized", this, newSize);
38444 getBox : function(){
38445 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38448 getMargins : function(){
38449 return this.margins;
38452 updateBox : function(box){
38454 var el = this.activePanel.getEl();
38455 el.dom.style.left = box.x + "px";
38456 el.dom.style.top = box.y + "px";
38457 this.activePanel.setSize(box.width, box.height);
38461 * Returns the container element for this region.
38462 * @return {Roo.Element}
38464 getEl : function(){
38465 return this.activePanel;
38469 * Returns true if this region is currently visible.
38470 * @return {Boolean}
38472 isVisible : function(){
38473 return this.activePanel ? true : false;
38476 setActivePanel : function(panel){
38477 panel = this.getPanel(panel);
38478 if(this.activePanel && this.activePanel != panel){
38479 this.activePanel.setActiveState(false);
38480 this.activePanel.getEl().setLeftTop(-10000,-10000);
38482 this.activePanel = panel;
38483 panel.setActiveState(true);
38485 panel.setSize(this.box.width, this.box.height);
38487 this.fireEvent("panelactivated", this, panel);
38488 this.fireEvent("invalidated");
38492 * Show the specified panel.
38493 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38494 * @return {Roo.ContentPanel} The shown panel or null
38496 showPanel : function(panel){
38497 panel = this.getPanel(panel);
38499 this.setActivePanel(panel);
38505 * Get the active panel for this region.
38506 * @return {Roo.ContentPanel} The active panel or null
38508 getActivePanel : function(){
38509 return this.activePanel;
38513 * Add the passed ContentPanel(s)
38514 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38515 * @return {Roo.ContentPanel} The panel added (if only one was added)
38517 add : function(panel){
38518 if(arguments.length > 1){
38519 for(var i = 0, len = arguments.length; i < len; i++) {
38520 this.add(arguments[i]);
38524 if(this.hasPanel(panel)){
38525 this.showPanel(panel);
38528 var el = panel.getEl();
38529 if(el.dom.parentNode != this.mgr.el.dom){
38530 this.mgr.el.dom.appendChild(el.dom);
38532 if(panel.setRegion){
38533 panel.setRegion(this);
38535 this.panels.add(panel);
38536 el.setStyle("position", "absolute");
38537 if(!panel.background){
38538 this.setActivePanel(panel);
38539 if(this.config.initialSize && this.panels.getCount()==1){
38540 this.resizeTo(this.config.initialSize);
38543 this.fireEvent("paneladded", this, panel);
38548 * Returns true if the panel is in this region.
38549 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38550 * @return {Boolean}
38552 hasPanel : function(panel){
38553 if(typeof panel == "object"){ // must be panel obj
38554 panel = panel.getId();
38556 return this.getPanel(panel) ? true : false;
38560 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38561 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38562 * @param {Boolean} preservePanel Overrides the config preservePanel option
38563 * @return {Roo.ContentPanel} The panel that was removed
38565 remove : function(panel, preservePanel){
38566 panel = this.getPanel(panel);
38571 this.fireEvent("beforeremove", this, panel, e);
38572 if(e.cancel === true){
38575 var panelId = panel.getId();
38576 this.panels.removeKey(panelId);
38581 * Returns the panel specified or null if it's not in this region.
38582 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38583 * @return {Roo.ContentPanel}
38585 getPanel : function(id){
38586 if(typeof id == "object"){ // must be panel obj
38589 return this.panels.get(id);
38593 * Returns this regions position (north/south/east/west/center).
38596 getPosition: function(){
38597 return this.position;
38601 * Ext JS Library 1.1.1
38602 * Copyright(c) 2006-2007, Ext JS, LLC.
38604 * Originally Released Under LGPL - original licence link has changed is not relivant.
38607 * <script type="text/javascript">
38611 * @class Roo.bootstrap.layout.Region
38612 * @extends Roo.bootstrap.layout.Basic
38613 * This class represents a region in a layout manager.
38615 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38616 * @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})
38617 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38618 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38619 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38620 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38621 * @cfg {String} title The title for the region (overrides panel titles)
38622 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38623 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38624 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38625 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38626 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38627 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38628 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38629 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38630 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38631 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38633 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38634 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38635 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38636 * @cfg {Number} width For East/West panels
38637 * @cfg {Number} height For North/South panels
38638 * @cfg {Boolean} split To show the splitter
38639 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38641 * @cfg {string} cls Extra CSS classes to add to region
38643 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38644 * @cfg {string} region the region that it inhabits..
38647 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38648 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38650 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38651 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38652 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38654 Roo.bootstrap.layout.Region = function(config)
38656 this.applyConfig(config);
38658 var mgr = config.mgr;
38659 var pos = config.region;
38660 config.skipConfig = true;
38661 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38664 this.onRender(mgr.el);
38667 this.visible = true;
38668 this.collapsed = false;
38669 this.unrendered_panels = [];
38672 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38674 position: '', // set by wrapper (eg. north/south etc..)
38675 unrendered_panels : null, // unrendered panels.
38677 tabPosition : false,
38679 mgr: false, // points to 'Border'
38682 createBody : function(){
38683 /** This region's body element
38684 * @type Roo.Element */
38685 this.bodyEl = this.el.createChild({
38687 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38691 onRender: function(ctr, pos)
38693 var dh = Roo.DomHelper;
38694 /** This region's container element
38695 * @type Roo.Element */
38696 this.el = dh.append(ctr.dom, {
38698 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38700 /** This region's title element
38701 * @type Roo.Element */
38703 this.titleEl = dh.append(this.el.dom, {
38705 unselectable: "on",
38706 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38708 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38709 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38713 this.titleEl.enableDisplayMode();
38714 /** This region's title text element
38715 * @type HTMLElement */
38716 this.titleTextEl = this.titleEl.dom.firstChild;
38717 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38719 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38720 this.closeBtn.enableDisplayMode();
38721 this.closeBtn.on("click", this.closeClicked, this);
38722 this.closeBtn.hide();
38724 this.createBody(this.config);
38725 if(this.config.hideWhenEmpty){
38727 this.on("paneladded", this.validateVisibility, this);
38728 this.on("panelremoved", this.validateVisibility, this);
38730 if(this.autoScroll){
38731 this.bodyEl.setStyle("overflow", "auto");
38733 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38735 //if(c.titlebar !== false){
38736 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38737 this.titleEl.hide();
38739 this.titleEl.show();
38740 if(this.config.title){
38741 this.titleTextEl.innerHTML = this.config.title;
38745 if(this.config.collapsed){
38746 this.collapse(true);
38748 if(this.config.hidden){
38752 if (this.unrendered_panels && this.unrendered_panels.length) {
38753 for (var i =0;i< this.unrendered_panels.length; i++) {
38754 this.add(this.unrendered_panels[i]);
38756 this.unrendered_panels = null;
38762 applyConfig : function(c)
38765 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38766 var dh = Roo.DomHelper;
38767 if(c.titlebar !== false){
38768 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38769 this.collapseBtn.on("click", this.collapse, this);
38770 this.collapseBtn.enableDisplayMode();
38772 if(c.showPin === true || this.showPin){
38773 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38774 this.stickBtn.enableDisplayMode();
38775 this.stickBtn.on("click", this.expand, this);
38776 this.stickBtn.hide();
38781 /** This region's collapsed element
38782 * @type Roo.Element */
38785 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38786 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38789 if(c.floatable !== false){
38790 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38791 this.collapsedEl.on("click", this.collapseClick, this);
38794 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38795 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38796 id: "message", unselectable: "on", style:{"float":"left"}});
38797 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38799 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38800 this.expandBtn.on("click", this.expand, this);
38804 if(this.collapseBtn){
38805 this.collapseBtn.setVisible(c.collapsible == true);
38808 this.cmargins = c.cmargins || this.cmargins ||
38809 (this.position == "west" || this.position == "east" ?
38810 {top: 0, left: 2, right:2, bottom: 0} :
38811 {top: 2, left: 0, right:0, bottom: 2});
38813 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38816 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38818 this.autoScroll = c.autoScroll || false;
38823 this.duration = c.duration || .30;
38824 this.slideDuration = c.slideDuration || .45;
38829 * Returns true if this region is currently visible.
38830 * @return {Boolean}
38832 isVisible : function(){
38833 return this.visible;
38837 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38838 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38840 //setCollapsedTitle : function(title){
38841 // title = title || " ";
38842 // if(this.collapsedTitleTextEl){
38843 // this.collapsedTitleTextEl.innerHTML = title;
38847 getBox : function(){
38849 // if(!this.collapsed){
38850 b = this.el.getBox(false, true);
38852 // b = this.collapsedEl.getBox(false, true);
38857 getMargins : function(){
38858 return this.margins;
38859 //return this.collapsed ? this.cmargins : this.margins;
38862 highlight : function(){
38863 this.el.addClass("x-layout-panel-dragover");
38866 unhighlight : function(){
38867 this.el.removeClass("x-layout-panel-dragover");
38870 updateBox : function(box)
38872 if (!this.bodyEl) {
38873 return; // not rendered yet..
38877 if(!this.collapsed){
38878 this.el.dom.style.left = box.x + "px";
38879 this.el.dom.style.top = box.y + "px";
38880 this.updateBody(box.width, box.height);
38882 this.collapsedEl.dom.style.left = box.x + "px";
38883 this.collapsedEl.dom.style.top = box.y + "px";
38884 this.collapsedEl.setSize(box.width, box.height);
38887 this.tabs.autoSizeTabs();
38891 updateBody : function(w, h)
38894 this.el.setWidth(w);
38895 w -= this.el.getBorderWidth("rl");
38896 if(this.config.adjustments){
38897 w += this.config.adjustments[0];
38900 if(h !== null && h > 0){
38901 this.el.setHeight(h);
38902 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38903 h -= this.el.getBorderWidth("tb");
38904 if(this.config.adjustments){
38905 h += this.config.adjustments[1];
38907 this.bodyEl.setHeight(h);
38909 h = this.tabs.syncHeight(h);
38912 if(this.panelSize){
38913 w = w !== null ? w : this.panelSize.width;
38914 h = h !== null ? h : this.panelSize.height;
38916 if(this.activePanel){
38917 var el = this.activePanel.getEl();
38918 w = w !== null ? w : el.getWidth();
38919 h = h !== null ? h : el.getHeight();
38920 this.panelSize = {width: w, height: h};
38921 this.activePanel.setSize(w, h);
38923 if(Roo.isIE && this.tabs){
38924 this.tabs.el.repaint();
38929 * Returns the container element for this region.
38930 * @return {Roo.Element}
38932 getEl : function(){
38937 * Hides this region.
38940 //if(!this.collapsed){
38941 this.el.dom.style.left = "-2000px";
38944 // this.collapsedEl.dom.style.left = "-2000px";
38945 // this.collapsedEl.hide();
38947 this.visible = false;
38948 this.fireEvent("visibilitychange", this, false);
38952 * Shows this region if it was previously hidden.
38955 //if(!this.collapsed){
38958 // this.collapsedEl.show();
38960 this.visible = true;
38961 this.fireEvent("visibilitychange", this, true);
38964 closeClicked : function(){
38965 if(this.activePanel){
38966 this.remove(this.activePanel);
38970 collapseClick : function(e){
38972 e.stopPropagation();
38975 e.stopPropagation();
38981 * Collapses this region.
38982 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38985 collapse : function(skipAnim, skipCheck = false){
38986 if(this.collapsed) {
38990 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38992 this.collapsed = true;
38994 this.split.el.hide();
38996 if(this.config.animate && skipAnim !== true){
38997 this.fireEvent("invalidated", this);
38998 this.animateCollapse();
39000 this.el.setLocation(-20000,-20000);
39002 this.collapsedEl.show();
39003 this.fireEvent("collapsed", this);
39004 this.fireEvent("invalidated", this);
39010 animateCollapse : function(){
39015 * Expands this region if it was previously collapsed.
39016 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39017 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39020 expand : function(e, skipAnim){
39022 e.stopPropagation();
39024 if(!this.collapsed || this.el.hasActiveFx()) {
39028 this.afterSlideIn();
39031 this.collapsed = false;
39032 if(this.config.animate && skipAnim !== true){
39033 this.animateExpand();
39037 this.split.el.show();
39039 this.collapsedEl.setLocation(-2000,-2000);
39040 this.collapsedEl.hide();
39041 this.fireEvent("invalidated", this);
39042 this.fireEvent("expanded", this);
39046 animateExpand : function(){
39050 initTabs : function()
39052 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39054 var ts = new Roo.bootstrap.panel.Tabs({
39055 el: this.bodyEl.dom,
39057 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39058 disableTooltips: this.config.disableTabTips,
39059 toolbar : this.config.toolbar
39062 if(this.config.hideTabs){
39063 ts.stripWrap.setDisplayed(false);
39066 ts.resizeTabs = this.config.resizeTabs === true;
39067 ts.minTabWidth = this.config.minTabWidth || 40;
39068 ts.maxTabWidth = this.config.maxTabWidth || 250;
39069 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39070 ts.monitorResize = false;
39071 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39072 ts.bodyEl.addClass('roo-layout-tabs-body');
39073 this.panels.each(this.initPanelAsTab, this);
39076 initPanelAsTab : function(panel){
39077 var ti = this.tabs.addTab(
39081 this.config.closeOnTab && panel.isClosable(),
39084 if(panel.tabTip !== undefined){
39085 ti.setTooltip(panel.tabTip);
39087 ti.on("activate", function(){
39088 this.setActivePanel(panel);
39091 if(this.config.closeOnTab){
39092 ti.on("beforeclose", function(t, e){
39094 this.remove(panel);
39098 panel.tabItem = ti;
39103 updatePanelTitle : function(panel, title)
39105 if(this.activePanel == panel){
39106 this.updateTitle(title);
39109 var ti = this.tabs.getTab(panel.getEl().id);
39111 if(panel.tabTip !== undefined){
39112 ti.setTooltip(panel.tabTip);
39117 updateTitle : function(title){
39118 if(this.titleTextEl && !this.config.title){
39119 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39123 setActivePanel : function(panel)
39125 panel = this.getPanel(panel);
39126 if(this.activePanel && this.activePanel != panel){
39127 if(this.activePanel.setActiveState(false) === false){
39131 this.activePanel = panel;
39132 panel.setActiveState(true);
39133 if(this.panelSize){
39134 panel.setSize(this.panelSize.width, this.panelSize.height);
39137 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39139 this.updateTitle(panel.getTitle());
39141 this.fireEvent("invalidated", this);
39143 this.fireEvent("panelactivated", this, panel);
39147 * Shows the specified panel.
39148 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39149 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39151 showPanel : function(panel)
39153 panel = this.getPanel(panel);
39156 var tab = this.tabs.getTab(panel.getEl().id);
39157 if(tab.isHidden()){
39158 this.tabs.unhideTab(tab.id);
39162 this.setActivePanel(panel);
39169 * Get the active panel for this region.
39170 * @return {Roo.ContentPanel} The active panel or null
39172 getActivePanel : function(){
39173 return this.activePanel;
39176 validateVisibility : function(){
39177 if(this.panels.getCount() < 1){
39178 this.updateTitle(" ");
39179 this.closeBtn.hide();
39182 if(!this.isVisible()){
39189 * Adds the passed ContentPanel(s) to this region.
39190 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39191 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39193 add : function(panel)
39195 if(arguments.length > 1){
39196 for(var i = 0, len = arguments.length; i < len; i++) {
39197 this.add(arguments[i]);
39202 // if we have not been rendered yet, then we can not really do much of this..
39203 if (!this.bodyEl) {
39204 this.unrendered_panels.push(panel);
39211 if(this.hasPanel(panel)){
39212 this.showPanel(panel);
39215 panel.setRegion(this);
39216 this.panels.add(panel);
39217 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39218 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39219 // and hide them... ???
39220 this.bodyEl.dom.appendChild(panel.getEl().dom);
39221 if(panel.background !== true){
39222 this.setActivePanel(panel);
39224 this.fireEvent("paneladded", this, panel);
39231 this.initPanelAsTab(panel);
39235 if(panel.background !== true){
39236 this.tabs.activate(panel.getEl().id);
39238 this.fireEvent("paneladded", this, panel);
39243 * Hides the tab for the specified panel.
39244 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39246 hidePanel : function(panel){
39247 if(this.tabs && (panel = this.getPanel(panel))){
39248 this.tabs.hideTab(panel.getEl().id);
39253 * Unhides the tab for a previously hidden panel.
39254 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39256 unhidePanel : function(panel){
39257 if(this.tabs && (panel = this.getPanel(panel))){
39258 this.tabs.unhideTab(panel.getEl().id);
39262 clearPanels : function(){
39263 while(this.panels.getCount() > 0){
39264 this.remove(this.panels.first());
39269 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39270 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39271 * @param {Boolean} preservePanel Overrides the config preservePanel option
39272 * @return {Roo.ContentPanel} The panel that was removed
39274 remove : function(panel, preservePanel)
39276 panel = this.getPanel(panel);
39281 this.fireEvent("beforeremove", this, panel, e);
39282 if(e.cancel === true){
39285 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39286 var panelId = panel.getId();
39287 this.panels.removeKey(panelId);
39289 document.body.appendChild(panel.getEl().dom);
39292 this.tabs.removeTab(panel.getEl().id);
39293 }else if (!preservePanel){
39294 this.bodyEl.dom.removeChild(panel.getEl().dom);
39296 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39297 var p = this.panels.first();
39298 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39299 tempEl.appendChild(p.getEl().dom);
39300 this.bodyEl.update("");
39301 this.bodyEl.dom.appendChild(p.getEl().dom);
39303 this.updateTitle(p.getTitle());
39305 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39306 this.setActivePanel(p);
39308 panel.setRegion(null);
39309 if(this.activePanel == panel){
39310 this.activePanel = null;
39312 if(this.config.autoDestroy !== false && preservePanel !== true){
39313 try{panel.destroy();}catch(e){}
39315 this.fireEvent("panelremoved", this, panel);
39320 * Returns the TabPanel component used by this region
39321 * @return {Roo.TabPanel}
39323 getTabs : function(){
39327 createTool : function(parentEl, className){
39328 var btn = Roo.DomHelper.append(parentEl, {
39330 cls: "x-layout-tools-button",
39333 cls: "roo-layout-tools-button-inner " + className,
39337 btn.addClassOnOver("roo-layout-tools-button-over");
39342 * Ext JS Library 1.1.1
39343 * Copyright(c) 2006-2007, Ext JS, LLC.
39345 * Originally Released Under LGPL - original licence link has changed is not relivant.
39348 * <script type="text/javascript">
39354 * @class Roo.SplitLayoutRegion
39355 * @extends Roo.LayoutRegion
39356 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39358 Roo.bootstrap.layout.Split = function(config){
39359 this.cursor = config.cursor;
39360 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39363 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39365 splitTip : "Drag to resize.",
39366 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39367 useSplitTips : false,
39369 applyConfig : function(config){
39370 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39373 onRender : function(ctr,pos) {
39375 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39376 if(!this.config.split){
39381 var splitEl = Roo.DomHelper.append(ctr.dom, {
39383 id: this.el.id + "-split",
39384 cls: "roo-layout-split roo-layout-split-"+this.position,
39387 /** The SplitBar for this region
39388 * @type Roo.SplitBar */
39389 // does not exist yet...
39390 Roo.log([this.position, this.orientation]);
39392 this.split = new Roo.bootstrap.SplitBar({
39393 dragElement : splitEl,
39394 resizingElement: this.el,
39395 orientation : this.orientation
39398 this.split.on("moved", this.onSplitMove, this);
39399 this.split.useShim = this.config.useShim === true;
39400 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39401 if(this.useSplitTips){
39402 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39404 //if(config.collapsible){
39405 // this.split.el.on("dblclick", this.collapse, this);
39408 if(typeof this.config.minSize != "undefined"){
39409 this.split.minSize = this.config.minSize;
39411 if(typeof this.config.maxSize != "undefined"){
39412 this.split.maxSize = this.config.maxSize;
39414 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39415 this.hideSplitter();
39420 getHMaxSize : function(){
39421 var cmax = this.config.maxSize || 10000;
39422 var center = this.mgr.getRegion("center");
39423 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39426 getVMaxSize : function(){
39427 var cmax = this.config.maxSize || 10000;
39428 var center = this.mgr.getRegion("center");
39429 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39432 onSplitMove : function(split, newSize){
39433 this.fireEvent("resized", this, newSize);
39437 * Returns the {@link Roo.SplitBar} for this region.
39438 * @return {Roo.SplitBar}
39440 getSplitBar : function(){
39445 this.hideSplitter();
39446 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39449 hideSplitter : function(){
39451 this.split.el.setLocation(-2000,-2000);
39452 this.split.el.hide();
39458 this.split.el.show();
39460 Roo.bootstrap.layout.Split.superclass.show.call(this);
39463 beforeSlide: function(){
39464 if(Roo.isGecko){// firefox overflow auto bug workaround
39465 this.bodyEl.clip();
39467 this.tabs.bodyEl.clip();
39469 if(this.activePanel){
39470 this.activePanel.getEl().clip();
39472 if(this.activePanel.beforeSlide){
39473 this.activePanel.beforeSlide();
39479 afterSlide : function(){
39480 if(Roo.isGecko){// firefox overflow auto bug workaround
39481 this.bodyEl.unclip();
39483 this.tabs.bodyEl.unclip();
39485 if(this.activePanel){
39486 this.activePanel.getEl().unclip();
39487 if(this.activePanel.afterSlide){
39488 this.activePanel.afterSlide();
39494 initAutoHide : function(){
39495 if(this.autoHide !== false){
39496 if(!this.autoHideHd){
39497 var st = new Roo.util.DelayedTask(this.slideIn, this);
39498 this.autoHideHd = {
39499 "mouseout": function(e){
39500 if(!e.within(this.el, true)){
39504 "mouseover" : function(e){
39510 this.el.on(this.autoHideHd);
39514 clearAutoHide : function(){
39515 if(this.autoHide !== false){
39516 this.el.un("mouseout", this.autoHideHd.mouseout);
39517 this.el.un("mouseover", this.autoHideHd.mouseover);
39521 clearMonitor : function(){
39522 Roo.get(document).un("click", this.slideInIf, this);
39525 // these names are backwards but not changed for compat
39526 slideOut : function(){
39527 if(this.isSlid || this.el.hasActiveFx()){
39530 this.isSlid = true;
39531 if(this.collapseBtn){
39532 this.collapseBtn.hide();
39534 this.closeBtnState = this.closeBtn.getStyle('display');
39535 this.closeBtn.hide();
39537 this.stickBtn.show();
39540 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39541 this.beforeSlide();
39542 this.el.setStyle("z-index", 10001);
39543 this.el.slideIn(this.getSlideAnchor(), {
39544 callback: function(){
39546 this.initAutoHide();
39547 Roo.get(document).on("click", this.slideInIf, this);
39548 this.fireEvent("slideshow", this);
39555 afterSlideIn : function(){
39556 this.clearAutoHide();
39557 this.isSlid = false;
39558 this.clearMonitor();
39559 this.el.setStyle("z-index", "");
39560 if(this.collapseBtn){
39561 this.collapseBtn.show();
39563 this.closeBtn.setStyle('display', this.closeBtnState);
39565 this.stickBtn.hide();
39567 this.fireEvent("slidehide", this);
39570 slideIn : function(cb){
39571 if(!this.isSlid || this.el.hasActiveFx()){
39575 this.isSlid = false;
39576 this.beforeSlide();
39577 this.el.slideOut(this.getSlideAnchor(), {
39578 callback: function(){
39579 this.el.setLeftTop(-10000, -10000);
39581 this.afterSlideIn();
39589 slideInIf : function(e){
39590 if(!e.within(this.el)){
39595 animateCollapse : function(){
39596 this.beforeSlide();
39597 this.el.setStyle("z-index", 20000);
39598 var anchor = this.getSlideAnchor();
39599 this.el.slideOut(anchor, {
39600 callback : function(){
39601 this.el.setStyle("z-index", "");
39602 this.collapsedEl.slideIn(anchor, {duration:.3});
39604 this.el.setLocation(-10000,-10000);
39606 this.fireEvent("collapsed", this);
39613 animateExpand : function(){
39614 this.beforeSlide();
39615 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39616 this.el.setStyle("z-index", 20000);
39617 this.collapsedEl.hide({
39620 this.el.slideIn(this.getSlideAnchor(), {
39621 callback : function(){
39622 this.el.setStyle("z-index", "");
39625 this.split.el.show();
39627 this.fireEvent("invalidated", this);
39628 this.fireEvent("expanded", this);
39656 getAnchor : function(){
39657 return this.anchors[this.position];
39660 getCollapseAnchor : function(){
39661 return this.canchors[this.position];
39664 getSlideAnchor : function(){
39665 return this.sanchors[this.position];
39668 getAlignAdj : function(){
39669 var cm = this.cmargins;
39670 switch(this.position){
39686 getExpandAdj : function(){
39687 var c = this.collapsedEl, cm = this.cmargins;
39688 switch(this.position){
39690 return [-(cm.right+c.getWidth()+cm.left), 0];
39693 return [cm.right+c.getWidth()+cm.left, 0];
39696 return [0, -(cm.top+cm.bottom+c.getHeight())];
39699 return [0, cm.top+cm.bottom+c.getHeight()];
39705 * Ext JS Library 1.1.1
39706 * Copyright(c) 2006-2007, Ext JS, LLC.
39708 * Originally Released Under LGPL - original licence link has changed is not relivant.
39711 * <script type="text/javascript">
39714 * These classes are private internal classes
39716 Roo.bootstrap.layout.Center = function(config){
39717 config.region = "center";
39718 Roo.bootstrap.layout.Region.call(this, config);
39719 this.visible = true;
39720 this.minWidth = config.minWidth || 20;
39721 this.minHeight = config.minHeight || 20;
39724 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39726 // center panel can't be hidden
39730 // center panel can't be hidden
39733 getMinWidth: function(){
39734 return this.minWidth;
39737 getMinHeight: function(){
39738 return this.minHeight;
39752 Roo.bootstrap.layout.North = function(config)
39754 config.region = 'north';
39755 config.cursor = 'n-resize';
39757 Roo.bootstrap.layout.Split.call(this, config);
39761 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39762 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39763 this.split.el.addClass("roo-layout-split-v");
39765 //var size = config.initialSize || config.height;
39766 //if(this.el && typeof size != "undefined"){
39767 // this.el.setHeight(size);
39770 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39772 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39775 onRender : function(ctr, pos)
39777 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39778 var size = this.config.initialSize || this.config.height;
39779 if(this.el && typeof size != "undefined"){
39780 this.el.setHeight(size);
39785 getBox : function(){
39786 if(this.collapsed){
39787 return this.collapsedEl.getBox();
39789 var box = this.el.getBox();
39791 box.height += this.split.el.getHeight();
39796 updateBox : function(box){
39797 if(this.split && !this.collapsed){
39798 box.height -= this.split.el.getHeight();
39799 this.split.el.setLeft(box.x);
39800 this.split.el.setTop(box.y+box.height);
39801 this.split.el.setWidth(box.width);
39803 if(this.collapsed){
39804 this.updateBody(box.width, null);
39806 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39814 Roo.bootstrap.layout.South = function(config){
39815 config.region = 'south';
39816 config.cursor = 's-resize';
39817 Roo.bootstrap.layout.Split.call(this, config);
39819 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39820 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39821 this.split.el.addClass("roo-layout-split-v");
39826 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39827 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39829 onRender : function(ctr, pos)
39831 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39832 var size = this.config.initialSize || this.config.height;
39833 if(this.el && typeof size != "undefined"){
39834 this.el.setHeight(size);
39839 getBox : function(){
39840 if(this.collapsed){
39841 return this.collapsedEl.getBox();
39843 var box = this.el.getBox();
39845 var sh = this.split.el.getHeight();
39852 updateBox : function(box){
39853 if(this.split && !this.collapsed){
39854 var sh = this.split.el.getHeight();
39857 this.split.el.setLeft(box.x);
39858 this.split.el.setTop(box.y-sh);
39859 this.split.el.setWidth(box.width);
39861 if(this.collapsed){
39862 this.updateBody(box.width, null);
39864 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39868 Roo.bootstrap.layout.East = function(config){
39869 config.region = "east";
39870 config.cursor = "e-resize";
39871 Roo.bootstrap.layout.Split.call(this, config);
39873 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39874 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39875 this.split.el.addClass("roo-layout-split-h");
39879 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39880 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39882 onRender : function(ctr, pos)
39884 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39885 var size = this.config.initialSize || this.config.width;
39886 if(this.el && typeof size != "undefined"){
39887 this.el.setWidth(size);
39892 getBox : function(){
39893 if(this.collapsed){
39894 return this.collapsedEl.getBox();
39896 var box = this.el.getBox();
39898 var sw = this.split.el.getWidth();
39905 updateBox : function(box){
39906 if(this.split && !this.collapsed){
39907 var sw = this.split.el.getWidth();
39909 this.split.el.setLeft(box.x);
39910 this.split.el.setTop(box.y);
39911 this.split.el.setHeight(box.height);
39914 if(this.collapsed){
39915 this.updateBody(null, box.height);
39917 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39921 Roo.bootstrap.layout.West = function(config){
39922 config.region = "west";
39923 config.cursor = "w-resize";
39925 Roo.bootstrap.layout.Split.call(this, config);
39927 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39928 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39929 this.split.el.addClass("roo-layout-split-h");
39933 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39934 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39936 onRender: function(ctr, pos)
39938 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39939 var size = this.config.initialSize || this.config.width;
39940 if(typeof size != "undefined"){
39941 this.el.setWidth(size);
39945 getBox : function(){
39946 if(this.collapsed){
39947 return this.collapsedEl.getBox();
39949 var box = this.el.getBox();
39950 if (box.width == 0) {
39951 box.width = this.config.width; // kludge?
39954 box.width += this.split.el.getWidth();
39959 updateBox : function(box){
39960 if(this.split && !this.collapsed){
39961 var sw = this.split.el.getWidth();
39963 this.split.el.setLeft(box.x+box.width);
39964 this.split.el.setTop(box.y);
39965 this.split.el.setHeight(box.height);
39967 if(this.collapsed){
39968 this.updateBody(null, box.height);
39970 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39972 });Roo.namespace("Roo.bootstrap.panel");/*
39974 * Ext JS Library 1.1.1
39975 * Copyright(c) 2006-2007, Ext JS, LLC.
39977 * Originally Released Under LGPL - original licence link has changed is not relivant.
39980 * <script type="text/javascript">
39983 * @class Roo.ContentPanel
39984 * @extends Roo.util.Observable
39985 * A basic ContentPanel element.
39986 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39987 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39988 * @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
39989 * @cfg {Boolean} closable True if the panel can be closed/removed
39990 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39991 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39992 * @cfg {Toolbar} toolbar A toolbar for this panel
39993 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39994 * @cfg {String} title The title for this panel
39995 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39996 * @cfg {String} url Calls {@link #setUrl} with this value
39997 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39998 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39999 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40000 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40001 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40002 * @cfg {Boolean} badges render the badges
40003 * @cfg {String} cls extra classes to use
40004 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40007 * Create a new ContentPanel.
40008 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40009 * @param {String/Object} config A string to set only the title or a config object
40010 * @param {String} content (optional) Set the HTML content for this panel
40011 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40013 Roo.bootstrap.panel.Content = function( config){
40015 this.tpl = config.tpl || false;
40017 var el = config.el;
40018 var content = config.content;
40020 if(config.autoCreate){ // xtype is available if this is called from factory
40023 this.el = Roo.get(el);
40024 if(!this.el && config && config.autoCreate){
40025 if(typeof config.autoCreate == "object"){
40026 if(!config.autoCreate.id){
40027 config.autoCreate.id = config.id||el;
40029 this.el = Roo.DomHelper.append(document.body,
40030 config.autoCreate, true);
40034 cls: (config.cls || '') +
40035 (config.background ? ' bg-' + config.background : '') +
40036 " roo-layout-inactive-content",
40039 if (config.iframe) {
40043 style : 'border: 0px',
40044 src : 'about:blank'
40050 elcfg.html = config.html;
40054 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40055 if (config.iframe) {
40056 this.iframeEl = this.el.select('iframe',true).first();
40061 this.closable = false;
40062 this.loaded = false;
40063 this.active = false;
40066 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40068 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40070 this.wrapEl = this.el; //this.el.wrap();
40072 if (config.toolbar.items) {
40073 ti = config.toolbar.items ;
40074 delete config.toolbar.items ;
40078 this.toolbar.render(this.wrapEl, 'before');
40079 for(var i =0;i < ti.length;i++) {
40080 // Roo.log(['add child', items[i]]);
40081 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40083 this.toolbar.items = nitems;
40084 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40085 delete config.toolbar;
40089 // xtype created footer. - not sure if will work as we normally have to render first..
40090 if (this.footer && !this.footer.el && this.footer.xtype) {
40091 if (!this.wrapEl) {
40092 this.wrapEl = this.el.wrap();
40095 this.footer.container = this.wrapEl.createChild();
40097 this.footer = Roo.factory(this.footer, Roo);
40102 if(typeof config == "string"){
40103 this.title = config;
40105 Roo.apply(this, config);
40109 this.resizeEl = Roo.get(this.resizeEl, true);
40111 this.resizeEl = this.el;
40113 // handle view.xtype
40121 * Fires when this panel is activated.
40122 * @param {Roo.ContentPanel} this
40126 * @event deactivate
40127 * Fires when this panel is activated.
40128 * @param {Roo.ContentPanel} this
40130 "deactivate" : true,
40134 * Fires when this panel is resized if fitToFrame is true.
40135 * @param {Roo.ContentPanel} this
40136 * @param {Number} width The width after any component adjustments
40137 * @param {Number} height The height after any component adjustments
40143 * Fires when this tab is created
40144 * @param {Roo.ContentPanel} this
40155 if(this.autoScroll && !this.iframe){
40156 this.resizeEl.setStyle("overflow", "auto");
40158 // fix randome scrolling
40159 //this.el.on('scroll', function() {
40160 // Roo.log('fix random scolling');
40161 // this.scrollTo('top',0);
40164 content = content || this.content;
40166 this.setContent(content);
40168 if(config && config.url){
40169 this.setUrl(this.url, this.params, this.loadOnce);
40174 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40176 if (this.view && typeof(this.view.xtype) != 'undefined') {
40177 this.view.el = this.el.appendChild(document.createElement("div"));
40178 this.view = Roo.factory(this.view);
40179 this.view.render && this.view.render(false, '');
40183 this.fireEvent('render', this);
40186 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40196 setRegion : function(region){
40197 this.region = region;
40198 this.setActiveClass(region && !this.background);
40202 setActiveClass: function(state)
40205 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40206 this.el.setStyle('position','relative');
40208 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40209 this.el.setStyle('position', 'absolute');
40214 * Returns the toolbar for this Panel if one was configured.
40215 * @return {Roo.Toolbar}
40217 getToolbar : function(){
40218 return this.toolbar;
40221 setActiveState : function(active)
40223 this.active = active;
40224 this.setActiveClass(active);
40226 if(this.fireEvent("deactivate", this) === false){
40231 this.fireEvent("activate", this);
40235 * Updates this panel's element (not for iframe)
40236 * @param {String} content The new content
40237 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40239 setContent : function(content, loadScripts){
40244 this.el.update(content, loadScripts);
40247 ignoreResize : function(w, h){
40248 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40251 this.lastSize = {width: w, height: h};
40256 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40257 * @return {Roo.UpdateManager} The UpdateManager
40259 getUpdateManager : function(){
40263 return this.el.getUpdateManager();
40266 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40267 * Does not work with IFRAME contents
40268 * @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:
40271 url: "your-url.php",
40272 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40273 callback: yourFunction,
40274 scope: yourObject, //(optional scope)
40277 text: "Loading...",
40283 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40284 * 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.
40285 * @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}
40286 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40287 * @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.
40288 * @return {Roo.ContentPanel} this
40296 var um = this.el.getUpdateManager();
40297 um.update.apply(um, arguments);
40303 * 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.
40304 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40305 * @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)
40306 * @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)
40307 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40309 setUrl : function(url, params, loadOnce){
40311 this.iframeEl.dom.src = url;
40315 if(this.refreshDelegate){
40316 this.removeListener("activate", this.refreshDelegate);
40318 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40319 this.on("activate", this.refreshDelegate);
40320 return this.el.getUpdateManager();
40323 _handleRefresh : function(url, params, loadOnce){
40324 if(!loadOnce || !this.loaded){
40325 var updater = this.el.getUpdateManager();
40326 updater.update(url, params, this._setLoaded.createDelegate(this));
40330 _setLoaded : function(){
40331 this.loaded = true;
40335 * Returns this panel's id
40338 getId : function(){
40343 * Returns this panel's element - used by regiosn to add.
40344 * @return {Roo.Element}
40346 getEl : function(){
40347 return this.wrapEl || this.el;
40352 adjustForComponents : function(width, height)
40354 //Roo.log('adjustForComponents ');
40355 if(this.resizeEl != this.el){
40356 width -= this.el.getFrameWidth('lr');
40357 height -= this.el.getFrameWidth('tb');
40360 var te = this.toolbar.getEl();
40361 te.setWidth(width);
40362 height -= te.getHeight();
40365 var te = this.footer.getEl();
40366 te.setWidth(width);
40367 height -= te.getHeight();
40371 if(this.adjustments){
40372 width += this.adjustments[0];
40373 height += this.adjustments[1];
40375 return {"width": width, "height": height};
40378 setSize : function(width, height){
40379 if(this.fitToFrame && !this.ignoreResize(width, height)){
40380 if(this.fitContainer && this.resizeEl != this.el){
40381 this.el.setSize(width, height);
40383 var size = this.adjustForComponents(width, height);
40385 this.iframeEl.setSize(width,height);
40388 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40389 this.fireEvent('resize', this, size.width, size.height);
40396 * Returns this panel's title
40399 getTitle : function(){
40401 if (typeof(this.title) != 'object') {
40406 for (var k in this.title) {
40407 if (!this.title.hasOwnProperty(k)) {
40411 if (k.indexOf('-') >= 0) {
40412 var s = k.split('-');
40413 for (var i = 0; i<s.length; i++) {
40414 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40417 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40424 * Set this panel's title
40425 * @param {String} title
40427 setTitle : function(title){
40428 this.title = title;
40430 this.region.updatePanelTitle(this, title);
40435 * Returns true is this panel was configured to be closable
40436 * @return {Boolean}
40438 isClosable : function(){
40439 return this.closable;
40442 beforeSlide : function(){
40444 this.resizeEl.clip();
40447 afterSlide : function(){
40449 this.resizeEl.unclip();
40453 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40454 * Will fail silently if the {@link #setUrl} method has not been called.
40455 * This does not activate the panel, just updates its content.
40457 refresh : function(){
40458 if(this.refreshDelegate){
40459 this.loaded = false;
40460 this.refreshDelegate();
40465 * Destroys this panel
40467 destroy : function(){
40468 this.el.removeAllListeners();
40469 var tempEl = document.createElement("span");
40470 tempEl.appendChild(this.el.dom);
40471 tempEl.innerHTML = "";
40477 * form - if the content panel contains a form - this is a reference to it.
40478 * @type {Roo.form.Form}
40482 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40483 * This contains a reference to it.
40489 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40499 * @param {Object} cfg Xtype definition of item to add.
40503 getChildContainer: function () {
40504 return this.getEl();
40509 var ret = new Roo.factory(cfg);
40514 if (cfg.xtype.match(/^Form$/)) {
40517 //if (this.footer) {
40518 // el = this.footer.container.insertSibling(false, 'before');
40520 el = this.el.createChild();
40523 this.form = new Roo.form.Form(cfg);
40526 if ( this.form.allItems.length) {
40527 this.form.render(el.dom);
40531 // should only have one of theses..
40532 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40533 // views.. should not be just added - used named prop 'view''
40535 cfg.el = this.el.appendChild(document.createElement("div"));
40538 var ret = new Roo.factory(cfg);
40540 ret.render && ret.render(false, ''); // render blank..
40550 * @class Roo.bootstrap.panel.Grid
40551 * @extends Roo.bootstrap.panel.Content
40553 * Create a new GridPanel.
40554 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40555 * @param {Object} config A the config object
40561 Roo.bootstrap.panel.Grid = function(config)
40565 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40566 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40568 config.el = this.wrapper;
40569 //this.el = this.wrapper;
40571 if (config.container) {
40572 // ctor'ed from a Border/panel.grid
40575 this.wrapper.setStyle("overflow", "hidden");
40576 this.wrapper.addClass('roo-grid-container');
40581 if(config.toolbar){
40582 var tool_el = this.wrapper.createChild();
40583 this.toolbar = Roo.factory(config.toolbar);
40585 if (config.toolbar.items) {
40586 ti = config.toolbar.items ;
40587 delete config.toolbar.items ;
40591 this.toolbar.render(tool_el);
40592 for(var i =0;i < ti.length;i++) {
40593 // Roo.log(['add child', items[i]]);
40594 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40596 this.toolbar.items = nitems;
40598 delete config.toolbar;
40601 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40602 config.grid.scrollBody = true;;
40603 config.grid.monitorWindowResize = false; // turn off autosizing
40604 config.grid.autoHeight = false;
40605 config.grid.autoWidth = false;
40607 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40609 if (config.background) {
40610 // render grid on panel activation (if panel background)
40611 this.on('activate', function(gp) {
40612 if (!gp.grid.rendered) {
40613 gp.grid.render(this.wrapper);
40614 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40619 this.grid.render(this.wrapper);
40620 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40623 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40624 // ??? needed ??? config.el = this.wrapper;
40629 // xtype created footer. - not sure if will work as we normally have to render first..
40630 if (this.footer && !this.footer.el && this.footer.xtype) {
40632 var ctr = this.grid.getView().getFooterPanel(true);
40633 this.footer.dataSource = this.grid.dataSource;
40634 this.footer = Roo.factory(this.footer, Roo);
40635 this.footer.render(ctr);
40645 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40646 getId : function(){
40647 return this.grid.id;
40651 * Returns the grid for this panel
40652 * @return {Roo.bootstrap.Table}
40654 getGrid : function(){
40658 setSize : function(width, height){
40659 if(!this.ignoreResize(width, height)){
40660 var grid = this.grid;
40661 var size = this.adjustForComponents(width, height);
40662 // tfoot is not a footer?
40665 var gridel = grid.getGridEl();
40666 gridel.setSize(size.width, size.height);
40668 var tbd = grid.getGridEl().select('tbody', true).first();
40669 var thd = grid.getGridEl().select('thead',true).first();
40670 var tbf= grid.getGridEl().select('tfoot', true).first();
40673 size.height -= tbf.getHeight();
40676 size.height -= thd.getHeight();
40679 tbd.setSize(size.width, size.height );
40680 // this is for the account management tab -seems to work there.
40681 var thd = grid.getGridEl().select('thead',true).first();
40683 // tbd.setSize(size.width, size.height - thd.getHeight());
40692 beforeSlide : function(){
40693 this.grid.getView().scroller.clip();
40696 afterSlide : function(){
40697 this.grid.getView().scroller.unclip();
40700 destroy : function(){
40701 this.grid.destroy();
40703 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40708 * @class Roo.bootstrap.panel.Nest
40709 * @extends Roo.bootstrap.panel.Content
40711 * Create a new Panel, that can contain a layout.Border.
40714 * @param {Roo.BorderLayout} layout The layout for this panel
40715 * @param {String/Object} config A string to set only the title or a config object
40717 Roo.bootstrap.panel.Nest = function(config)
40719 // construct with only one argument..
40720 /* FIXME - implement nicer consturctors
40721 if (layout.layout) {
40723 layout = config.layout;
40724 delete config.layout;
40726 if (layout.xtype && !layout.getEl) {
40727 // then layout needs constructing..
40728 layout = Roo.factory(layout, Roo);
40732 config.el = config.layout.getEl();
40734 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40736 config.layout.monitorWindowResize = false; // turn off autosizing
40737 this.layout = config.layout;
40738 this.layout.getEl().addClass("roo-layout-nested-layout");
40739 this.layout.parent = this;
40746 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40748 setSize : function(width, height){
40749 if(!this.ignoreResize(width, height)){
40750 var size = this.adjustForComponents(width, height);
40751 var el = this.layout.getEl();
40752 if (size.height < 1) {
40753 el.setWidth(size.width);
40755 el.setSize(size.width, size.height);
40757 var touch = el.dom.offsetWidth;
40758 this.layout.layout();
40759 // ie requires a double layout on the first pass
40760 if(Roo.isIE && !this.initialized){
40761 this.initialized = true;
40762 this.layout.layout();
40767 // activate all subpanels if not currently active..
40769 setActiveState : function(active){
40770 this.active = active;
40771 this.setActiveClass(active);
40774 this.fireEvent("deactivate", this);
40778 this.fireEvent("activate", this);
40779 // not sure if this should happen before or after..
40780 if (!this.layout) {
40781 return; // should not happen..
40784 for (var r in this.layout.regions) {
40785 reg = this.layout.getRegion(r);
40786 if (reg.getActivePanel()) {
40787 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40788 reg.setActivePanel(reg.getActivePanel());
40791 if (!reg.panels.length) {
40794 reg.showPanel(reg.getPanel(0));
40803 * Returns the nested BorderLayout for this panel
40804 * @return {Roo.BorderLayout}
40806 getLayout : function(){
40807 return this.layout;
40811 * Adds a xtype elements to the layout of the nested panel
40815 xtype : 'ContentPanel',
40822 xtype : 'NestedLayoutPanel',
40828 items : [ ... list of content panels or nested layout panels.. ]
40832 * @param {Object} cfg Xtype definition of item to add.
40834 addxtype : function(cfg) {
40835 return this.layout.addxtype(cfg);
40840 * Ext JS Library 1.1.1
40841 * Copyright(c) 2006-2007, Ext JS, LLC.
40843 * Originally Released Under LGPL - original licence link has changed is not relivant.
40846 * <script type="text/javascript">
40849 * @class Roo.TabPanel
40850 * @extends Roo.util.Observable
40851 * A lightweight tab container.
40855 // basic tabs 1, built from existing content
40856 var tabs = new Roo.TabPanel("tabs1");
40857 tabs.addTab("script", "View Script");
40858 tabs.addTab("markup", "View Markup");
40859 tabs.activate("script");
40861 // more advanced tabs, built from javascript
40862 var jtabs = new Roo.TabPanel("jtabs");
40863 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40865 // set up the UpdateManager
40866 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40867 var updater = tab2.getUpdateManager();
40868 updater.setDefaultUrl("ajax1.htm");
40869 tab2.on('activate', updater.refresh, updater, true);
40871 // Use setUrl for Ajax loading
40872 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40873 tab3.setUrl("ajax2.htm", null, true);
40876 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40879 jtabs.activate("jtabs-1");
40882 * Create a new TabPanel.
40883 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40884 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40886 Roo.bootstrap.panel.Tabs = function(config){
40888 * The container element for this TabPanel.
40889 * @type Roo.Element
40891 this.el = Roo.get(config.el);
40894 if(typeof config == "boolean"){
40895 this.tabPosition = config ? "bottom" : "top";
40897 Roo.apply(this, config);
40901 if(this.tabPosition == "bottom"){
40902 // if tabs are at the bottom = create the body first.
40903 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40904 this.el.addClass("roo-tabs-bottom");
40906 // next create the tabs holders
40908 if (this.tabPosition == "west"){
40910 var reg = this.region; // fake it..
40912 if (!reg.mgr.parent) {
40915 reg = reg.mgr.parent.region;
40917 Roo.log("got nest?");
40919 if (reg.mgr.getRegion('west')) {
40920 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40921 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40922 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40923 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40924 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40932 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40933 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40934 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40935 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40940 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40943 // finally - if tabs are at the top, then create the body last..
40944 if(this.tabPosition != "bottom"){
40945 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40946 * @type Roo.Element
40948 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40949 this.el.addClass("roo-tabs-top");
40953 this.bodyEl.setStyle("position", "relative");
40955 this.active = null;
40956 this.activateDelegate = this.activate.createDelegate(this);
40961 * Fires when the active tab changes
40962 * @param {Roo.TabPanel} this
40963 * @param {Roo.TabPanelItem} activePanel The new active tab
40967 * @event beforetabchange
40968 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40969 * @param {Roo.TabPanel} this
40970 * @param {Object} e Set cancel to true on this object to cancel the tab change
40971 * @param {Roo.TabPanelItem} tab The tab being changed to
40973 "beforetabchange" : true
40976 Roo.EventManager.onWindowResize(this.onResize, this);
40977 this.cpad = this.el.getPadding("lr");
40978 this.hiddenCount = 0;
40981 // toolbar on the tabbar support...
40982 if (this.toolbar) {
40983 alert("no toolbar support yet");
40984 this.toolbar = false;
40986 var tcfg = this.toolbar;
40987 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40988 this.toolbar = new Roo.Toolbar(tcfg);
40989 if (Roo.isSafari) {
40990 var tbl = tcfg.container.child('table', true);
40991 tbl.setAttribute('width', '100%');
40999 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41002 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41004 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41006 tabPosition : "top",
41008 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41010 currentTabWidth : 0,
41012 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41016 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41020 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41022 preferredTabWidth : 175,
41024 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41026 resizeTabs : false,
41028 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41030 monitorResize : true,
41032 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41034 toolbar : false, // set by caller..
41036 region : false, /// set by caller
41038 disableTooltips : true, // not used yet...
41041 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41042 * @param {String} id The id of the div to use <b>or create</b>
41043 * @param {String} text The text for the tab
41044 * @param {String} content (optional) Content to put in the TabPanelItem body
41045 * @param {Boolean} closable (optional) True to create a close icon on the tab
41046 * @return {Roo.TabPanelItem} The created TabPanelItem
41048 addTab : function(id, text, content, closable, tpl)
41050 var item = new Roo.bootstrap.panel.TabItem({
41054 closable : closable,
41057 this.addTabItem(item);
41059 item.setContent(content);
41065 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41066 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41067 * @return {Roo.TabPanelItem}
41069 getTab : function(id){
41070 return this.items[id];
41074 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41075 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41077 hideTab : function(id){
41078 var t = this.items[id];
41081 this.hiddenCount++;
41082 this.autoSizeTabs();
41087 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41088 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41090 unhideTab : function(id){
41091 var t = this.items[id];
41093 t.setHidden(false);
41094 this.hiddenCount--;
41095 this.autoSizeTabs();
41100 * Adds an existing {@link Roo.TabPanelItem}.
41101 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41103 addTabItem : function(item)
41105 this.items[item.id] = item;
41106 this.items.push(item);
41107 this.autoSizeTabs();
41108 // if(this.resizeTabs){
41109 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41110 // this.autoSizeTabs();
41112 // item.autoSize();
41117 * Removes a {@link Roo.TabPanelItem}.
41118 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41120 removeTab : function(id){
41121 var items = this.items;
41122 var tab = items[id];
41123 if(!tab) { return; }
41124 var index = items.indexOf(tab);
41125 if(this.active == tab && items.length > 1){
41126 var newTab = this.getNextAvailable(index);
41131 this.stripEl.dom.removeChild(tab.pnode.dom);
41132 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41133 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41135 items.splice(index, 1);
41136 delete this.items[tab.id];
41137 tab.fireEvent("close", tab);
41138 tab.purgeListeners();
41139 this.autoSizeTabs();
41142 getNextAvailable : function(start){
41143 var items = this.items;
41145 // look for a next tab that will slide over to
41146 // replace the one being removed
41147 while(index < items.length){
41148 var item = items[++index];
41149 if(item && !item.isHidden()){
41153 // if one isn't found select the previous tab (on the left)
41156 var item = items[--index];
41157 if(item && !item.isHidden()){
41165 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41166 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41168 disableTab : function(id){
41169 var tab = this.items[id];
41170 if(tab && this.active != tab){
41176 * Enables a {@link Roo.TabPanelItem} that is disabled.
41177 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41179 enableTab : function(id){
41180 var tab = this.items[id];
41185 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41186 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41187 * @return {Roo.TabPanelItem} The TabPanelItem.
41189 activate : function(id)
41191 //Roo.log('activite:' + id);
41193 var tab = this.items[id];
41197 if(tab == this.active || tab.disabled){
41201 this.fireEvent("beforetabchange", this, e, tab);
41202 if(e.cancel !== true && !tab.disabled){
41204 this.active.hide();
41206 this.active = this.items[id];
41207 this.active.show();
41208 this.fireEvent("tabchange", this, this.active);
41214 * Gets the active {@link Roo.TabPanelItem}.
41215 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41217 getActiveTab : function(){
41218 return this.active;
41222 * Updates the tab body element to fit the height of the container element
41223 * for overflow scrolling
41224 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41226 syncHeight : function(targetHeight){
41227 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41228 var bm = this.bodyEl.getMargins();
41229 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41230 this.bodyEl.setHeight(newHeight);
41234 onResize : function(){
41235 if(this.monitorResize){
41236 this.autoSizeTabs();
41241 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41243 beginUpdate : function(){
41244 this.updating = true;
41248 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41250 endUpdate : function(){
41251 this.updating = false;
41252 this.autoSizeTabs();
41256 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41258 autoSizeTabs : function()
41260 var count = this.items.length;
41261 var vcount = count - this.hiddenCount;
41264 this.stripEl.hide();
41266 this.stripEl.show();
41269 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41274 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41275 var availWidth = Math.floor(w / vcount);
41276 var b = this.stripBody;
41277 if(b.getWidth() > w){
41278 var tabs = this.items;
41279 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41280 if(availWidth < this.minTabWidth){
41281 /*if(!this.sleft){ // incomplete scrolling code
41282 this.createScrollButtons();
41285 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41288 if(this.currentTabWidth < this.preferredTabWidth){
41289 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41295 * Returns the number of tabs in this TabPanel.
41298 getCount : function(){
41299 return this.items.length;
41303 * Resizes all the tabs to the passed width
41304 * @param {Number} The new width
41306 setTabWidth : function(width){
41307 this.currentTabWidth = width;
41308 for(var i = 0, len = this.items.length; i < len; i++) {
41309 if(!this.items[i].isHidden()) {
41310 this.items[i].setWidth(width);
41316 * Destroys this TabPanel
41317 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41319 destroy : function(removeEl){
41320 Roo.EventManager.removeResizeListener(this.onResize, this);
41321 for(var i = 0, len = this.items.length; i < len; i++){
41322 this.items[i].purgeListeners();
41324 if(removeEl === true){
41325 this.el.update("");
41330 createStrip : function(container)
41332 var strip = document.createElement("nav");
41333 strip.className = Roo.bootstrap.version == 4 ?
41334 "navbar-light bg-light" :
41335 "navbar navbar-default"; //"x-tabs-wrap";
41336 container.appendChild(strip);
41340 createStripList : function(strip)
41342 // div wrapper for retard IE
41343 // returns the "tr" element.
41344 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41345 //'<div class="x-tabs-strip-wrap">'+
41346 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41347 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41348 return strip.firstChild; //.firstChild.firstChild.firstChild;
41350 createBody : function(container)
41352 var body = document.createElement("div");
41353 Roo.id(body, "tab-body");
41354 //Roo.fly(body).addClass("x-tabs-body");
41355 Roo.fly(body).addClass("tab-content");
41356 container.appendChild(body);
41359 createItemBody :function(bodyEl, id){
41360 var body = Roo.getDom(id);
41362 body = document.createElement("div");
41365 //Roo.fly(body).addClass("x-tabs-item-body");
41366 Roo.fly(body).addClass("tab-pane");
41367 bodyEl.insertBefore(body, bodyEl.firstChild);
41371 createStripElements : function(stripEl, text, closable, tpl)
41373 var td = document.createElement("li"); // was td..
41374 td.className = 'nav-item';
41376 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41379 stripEl.appendChild(td);
41381 td.className = "x-tabs-closable";
41382 if(!this.closeTpl){
41383 this.closeTpl = new Roo.Template(
41384 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41385 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41386 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41389 var el = this.closeTpl.overwrite(td, {"text": text});
41390 var close = el.getElementsByTagName("div")[0];
41391 var inner = el.getElementsByTagName("em")[0];
41392 return {"el": el, "close": close, "inner": inner};
41395 // not sure what this is..
41396 // if(!this.tabTpl){
41397 //this.tabTpl = new Roo.Template(
41398 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41399 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41401 // this.tabTpl = new Roo.Template(
41402 // '<a href="#">' +
41403 // '<span unselectable="on"' +
41404 // (this.disableTooltips ? '' : ' title="{text}"') +
41405 // ' >{text}</span></a>'
41411 var template = tpl || this.tabTpl || false;
41414 template = new Roo.Template(
41415 Roo.bootstrap.version == 4 ?
41417 '<a class="nav-link" href="#" unselectable="on"' +
41418 (this.disableTooltips ? '' : ' title="{text}"') +
41421 '<a class="nav-link" href="#">' +
41422 '<span unselectable="on"' +
41423 (this.disableTooltips ? '' : ' title="{text}"') +
41424 ' >{text}</span></a>'
41429 switch (typeof(template)) {
41433 template = new Roo.Template(template);
41439 var el = template.overwrite(td, {"text": text});
41441 var inner = el.getElementsByTagName("span")[0];
41443 return {"el": el, "inner": inner};
41451 * @class Roo.TabPanelItem
41452 * @extends Roo.util.Observable
41453 * Represents an individual item (tab plus body) in a TabPanel.
41454 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41455 * @param {String} id The id of this TabPanelItem
41456 * @param {String} text The text for the tab of this TabPanelItem
41457 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41459 Roo.bootstrap.panel.TabItem = function(config){
41461 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41462 * @type Roo.TabPanel
41464 this.tabPanel = config.panel;
41466 * The id for this TabPanelItem
41469 this.id = config.id;
41471 this.disabled = false;
41473 this.text = config.text;
41475 this.loaded = false;
41476 this.closable = config.closable;
41479 * The body element for this TabPanelItem.
41480 * @type Roo.Element
41482 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41483 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41484 this.bodyEl.setStyle("display", "block");
41485 this.bodyEl.setStyle("zoom", "1");
41486 //this.hideAction();
41488 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41490 this.el = Roo.get(els.el);
41491 this.inner = Roo.get(els.inner, true);
41492 this.textEl = Roo.bootstrap.version == 4 ?
41493 this.el : Roo.get(this.el.dom.firstChild, true);
41495 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41496 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41499 // this.el.on("mousedown", this.onTabMouseDown, this);
41500 this.el.on("click", this.onTabClick, this);
41502 if(config.closable){
41503 var c = Roo.get(els.close, true);
41504 c.dom.title = this.closeText;
41505 c.addClassOnOver("close-over");
41506 c.on("click", this.closeClick, this);
41512 * Fires when this tab becomes the active tab.
41513 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41514 * @param {Roo.TabPanelItem} this
41518 * @event beforeclose
41519 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41520 * @param {Roo.TabPanelItem} this
41521 * @param {Object} e Set cancel to true on this object to cancel the close.
41523 "beforeclose": true,
41526 * Fires when this tab is closed.
41527 * @param {Roo.TabPanelItem} this
41531 * @event deactivate
41532 * Fires when this tab is no longer the active tab.
41533 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41534 * @param {Roo.TabPanelItem} this
41536 "deactivate" : true
41538 this.hidden = false;
41540 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41543 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41545 purgeListeners : function(){
41546 Roo.util.Observable.prototype.purgeListeners.call(this);
41547 this.el.removeAllListeners();
41550 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41553 this.status_node.addClass("active");
41556 this.tabPanel.stripWrap.repaint();
41558 this.fireEvent("activate", this.tabPanel, this);
41562 * Returns true if this tab is the active tab.
41563 * @return {Boolean}
41565 isActive : function(){
41566 return this.tabPanel.getActiveTab() == this;
41570 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41573 this.status_node.removeClass("active");
41575 this.fireEvent("deactivate", this.tabPanel, this);
41578 hideAction : function(){
41579 this.bodyEl.hide();
41580 this.bodyEl.setStyle("position", "absolute");
41581 this.bodyEl.setLeft("-20000px");
41582 this.bodyEl.setTop("-20000px");
41585 showAction : function(){
41586 this.bodyEl.setStyle("position", "relative");
41587 this.bodyEl.setTop("");
41588 this.bodyEl.setLeft("");
41589 this.bodyEl.show();
41593 * Set the tooltip for the tab.
41594 * @param {String} tooltip The tab's tooltip
41596 setTooltip : function(text){
41597 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41598 this.textEl.dom.qtip = text;
41599 this.textEl.dom.removeAttribute('title');
41601 this.textEl.dom.title = text;
41605 onTabClick : function(e){
41606 e.preventDefault();
41607 this.tabPanel.activate(this.id);
41610 onTabMouseDown : function(e){
41611 e.preventDefault();
41612 this.tabPanel.activate(this.id);
41615 getWidth : function(){
41616 return this.inner.getWidth();
41619 setWidth : function(width){
41620 var iwidth = width - this.linode.getPadding("lr");
41621 this.inner.setWidth(iwidth);
41622 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41623 this.linode.setWidth(width);
41627 * Show or hide the tab
41628 * @param {Boolean} hidden True to hide or false to show.
41630 setHidden : function(hidden){
41631 this.hidden = hidden;
41632 this.linode.setStyle("display", hidden ? "none" : "");
41636 * Returns true if this tab is "hidden"
41637 * @return {Boolean}
41639 isHidden : function(){
41640 return this.hidden;
41644 * Returns the text for this tab
41647 getText : function(){
41651 autoSize : function(){
41652 //this.el.beginMeasure();
41653 this.textEl.setWidth(1);
41655 * #2804 [new] Tabs in Roojs
41656 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41658 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41659 //this.el.endMeasure();
41663 * Sets the text for the tab (Note: this also sets the tooltip text)
41664 * @param {String} text The tab's text and tooltip
41666 setText : function(text){
41668 this.textEl.update(text);
41669 this.setTooltip(text);
41670 //if(!this.tabPanel.resizeTabs){
41671 // this.autoSize();
41675 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41677 activate : function(){
41678 this.tabPanel.activate(this.id);
41682 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41684 disable : function(){
41685 if(this.tabPanel.active != this){
41686 this.disabled = true;
41687 this.status_node.addClass("disabled");
41692 * Enables this TabPanelItem if it was previously disabled.
41694 enable : function(){
41695 this.disabled = false;
41696 this.status_node.removeClass("disabled");
41700 * Sets the content for this TabPanelItem.
41701 * @param {String} content The content
41702 * @param {Boolean} loadScripts true to look for and load scripts
41704 setContent : function(content, loadScripts){
41705 this.bodyEl.update(content, loadScripts);
41709 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41710 * @return {Roo.UpdateManager} The UpdateManager
41712 getUpdateManager : function(){
41713 return this.bodyEl.getUpdateManager();
41717 * Set a URL to be used to load the content for this TabPanelItem.
41718 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41719 * @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)
41720 * @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)
41721 * @return {Roo.UpdateManager} The UpdateManager
41723 setUrl : function(url, params, loadOnce){
41724 if(this.refreshDelegate){
41725 this.un('activate', this.refreshDelegate);
41727 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41728 this.on("activate", this.refreshDelegate);
41729 return this.bodyEl.getUpdateManager();
41733 _handleRefresh : function(url, params, loadOnce){
41734 if(!loadOnce || !this.loaded){
41735 var updater = this.bodyEl.getUpdateManager();
41736 updater.update(url, params, this._setLoaded.createDelegate(this));
41741 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41742 * Will fail silently if the setUrl method has not been called.
41743 * This does not activate the panel, just updates its content.
41745 refresh : function(){
41746 if(this.refreshDelegate){
41747 this.loaded = false;
41748 this.refreshDelegate();
41753 _setLoaded : function(){
41754 this.loaded = true;
41758 closeClick : function(e){
41761 this.fireEvent("beforeclose", this, o);
41762 if(o.cancel !== true){
41763 this.tabPanel.removeTab(this.id);
41767 * The text displayed in the tooltip for the close icon.
41770 closeText : "Close this tab"
41773 * This script refer to:
41774 * Title: International Telephone Input
41775 * Author: Jack O'Connor
41776 * Code version: v12.1.12
41777 * Availability: https://github.com/jackocnr/intl-tel-input.git
41780 Roo.bootstrap.PhoneInputData = function() {
41783 "Afghanistan (افغانستان)",
41788 "Albania (Shqipëri)",
41793 "Algeria (الجزائر)",
41818 "Antigua and Barbuda",
41828 "Armenia (Հայաստան)",
41844 "Austria (Österreich)",
41849 "Azerbaijan (Azərbaycan)",
41859 "Bahrain (البحرين)",
41864 "Bangladesh (বাংলাদেশ)",
41874 "Belarus (Беларусь)",
41879 "Belgium (België)",
41909 "Bosnia and Herzegovina (Босна и Херцеговина)",
41924 "British Indian Ocean Territory",
41929 "British Virgin Islands",
41939 "Bulgaria (България)",
41949 "Burundi (Uburundi)",
41954 "Cambodia (កម្ពុជា)",
41959 "Cameroon (Cameroun)",
41968 ["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"]
41971 "Cape Verde (Kabu Verdi)",
41976 "Caribbean Netherlands",
41987 "Central African Republic (République centrafricaine)",
42007 "Christmas Island",
42013 "Cocos (Keeling) Islands",
42024 "Comoros (جزر القمر)",
42029 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42034 "Congo (Republic) (Congo-Brazzaville)",
42054 "Croatia (Hrvatska)",
42075 "Czech Republic (Česká republika)",
42080 "Denmark (Danmark)",
42095 "Dominican Republic (República Dominicana)",
42099 ["809", "829", "849"]
42117 "Equatorial Guinea (Guinea Ecuatorial)",
42137 "Falkland Islands (Islas Malvinas)",
42142 "Faroe Islands (Føroyar)",
42163 "French Guiana (Guyane française)",
42168 "French Polynesia (Polynésie française)",
42183 "Georgia (საქართველო)",
42188 "Germany (Deutschland)",
42208 "Greenland (Kalaallit Nunaat)",
42245 "Guinea-Bissau (Guiné Bissau)",
42270 "Hungary (Magyarország)",
42275 "Iceland (Ísland)",
42295 "Iraq (العراق)",
42311 "Israel (ישראל)",
42338 "Jordan (الأردن)",
42343 "Kazakhstan (Казахстан)",
42364 "Kuwait (الكويت)",
42369 "Kyrgyzstan (Кыргызстан)",
42379 "Latvia (Latvija)",
42384 "Lebanon (لبنان)",
42399 "Libya (ليبيا)",
42409 "Lithuania (Lietuva)",
42424 "Macedonia (FYROM) (Македонија)",
42429 "Madagascar (Madagasikara)",
42459 "Marshall Islands",
42469 "Mauritania (موريتانيا)",
42474 "Mauritius (Moris)",
42495 "Moldova (Republica Moldova)",
42505 "Mongolia (Монгол)",
42510 "Montenegro (Crna Gora)",
42520 "Morocco (المغرب)",
42526 "Mozambique (Moçambique)",
42531 "Myanmar (Burma) (မြန်မာ)",
42536 "Namibia (Namibië)",
42551 "Netherlands (Nederland)",
42556 "New Caledonia (Nouvelle-Calédonie)",
42591 "North Korea (조선 민주주의 인민 공화국)",
42596 "Northern Mariana Islands",
42612 "Pakistan (پاکستان)",
42622 "Palestine (فلسطين)",
42632 "Papua New Guinea",
42674 "Réunion (La Réunion)",
42680 "Romania (România)",
42696 "Saint Barthélemy",
42707 "Saint Kitts and Nevis",
42717 "Saint Martin (Saint-Martin (partie française))",
42723 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42728 "Saint Vincent and the Grenadines",
42743 "São Tomé and Príncipe (São Tomé e Príncipe)",
42748 "Saudi Arabia (المملكة العربية السعودية)",
42753 "Senegal (Sénégal)",
42783 "Slovakia (Slovensko)",
42788 "Slovenia (Slovenija)",
42798 "Somalia (Soomaaliya)",
42808 "South Korea (대한민국)",
42813 "South Sudan (جنوب السودان)",
42823 "Sri Lanka (ශ්රී ලංකාව)",
42828 "Sudan (السودان)",
42838 "Svalbard and Jan Mayen",
42849 "Sweden (Sverige)",
42854 "Switzerland (Schweiz)",
42859 "Syria (سوريا)",
42904 "Trinidad and Tobago",
42909 "Tunisia (تونس)",
42914 "Turkey (Türkiye)",
42924 "Turks and Caicos Islands",
42934 "U.S. Virgin Islands",
42944 "Ukraine (Україна)",
42949 "United Arab Emirates (الإمارات العربية المتحدة)",
42971 "Uzbekistan (Oʻzbekiston)",
42981 "Vatican City (Città del Vaticano)",
42992 "Vietnam (Việt Nam)",
42997 "Wallis and Futuna (Wallis-et-Futuna)",
43002 "Western Sahara (الصحراء الغربية)",
43008 "Yemen (اليمن)",
43032 * This script refer to:
43033 * Title: International Telephone Input
43034 * Author: Jack O'Connor
43035 * Code version: v12.1.12
43036 * Availability: https://github.com/jackocnr/intl-tel-input.git
43040 * @class Roo.bootstrap.PhoneInput
43041 * @extends Roo.bootstrap.TriggerField
43042 * An input with International dial-code selection
43044 * @cfg {String} defaultDialCode default '+852'
43045 * @cfg {Array} preferedCountries default []
43048 * Create a new PhoneInput.
43049 * @param {Object} config Configuration options
43052 Roo.bootstrap.PhoneInput = function(config) {
43053 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43056 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43058 listWidth: undefined,
43060 selectedClass: 'active',
43062 invalidClass : "has-warning",
43064 validClass: 'has-success',
43066 allowed: '0123456789',
43071 * @cfg {String} defaultDialCode The default dial code when initializing the input
43073 defaultDialCode: '+852',
43076 * @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
43078 preferedCountries: false,
43080 getAutoCreate : function()
43082 var data = Roo.bootstrap.PhoneInputData();
43083 var align = this.labelAlign || this.parentLabelAlign();
43086 this.allCountries = [];
43087 this.dialCodeMapping = [];
43089 for (var i = 0; i < data.length; i++) {
43091 this.allCountries[i] = {
43095 priority: c[3] || 0,
43096 areaCodes: c[4] || null
43098 this.dialCodeMapping[c[2]] = {
43101 priority: c[3] || 0,
43102 areaCodes: c[4] || null
43114 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43115 maxlength: this.max_length,
43116 cls : 'form-control tel-input',
43117 autocomplete: 'new-password'
43120 var hiddenInput = {
43123 cls: 'hidden-tel-input'
43127 hiddenInput.name = this.name;
43130 if (this.disabled) {
43131 input.disabled = true;
43134 var flag_container = {
43151 cls: this.hasFeedback ? 'has-feedback' : '',
43157 cls: 'dial-code-holder',
43164 cls: 'roo-select2-container input-group',
43171 if (this.fieldLabel.length) {
43174 tooltip: 'This field is required'
43180 cls: 'control-label',
43186 html: this.fieldLabel
43189 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43195 if(this.indicatorpos == 'right') {
43196 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43203 if(align == 'left') {
43211 if(this.labelWidth > 12){
43212 label.style = "width: " + this.labelWidth + 'px';
43214 if(this.labelWidth < 13 && this.labelmd == 0){
43215 this.labelmd = this.labelWidth;
43217 if(this.labellg > 0){
43218 label.cls += ' col-lg-' + this.labellg;
43219 input.cls += ' col-lg-' + (12 - this.labellg);
43221 if(this.labelmd > 0){
43222 label.cls += ' col-md-' + this.labelmd;
43223 container.cls += ' col-md-' + (12 - this.labelmd);
43225 if(this.labelsm > 0){
43226 label.cls += ' col-sm-' + this.labelsm;
43227 container.cls += ' col-sm-' + (12 - this.labelsm);
43229 if(this.labelxs > 0){
43230 label.cls += ' col-xs-' + this.labelxs;
43231 container.cls += ' col-xs-' + (12 - this.labelxs);
43241 var settings = this;
43243 ['xs','sm','md','lg'].map(function(size){
43244 if (settings[size]) {
43245 cfg.cls += ' col-' + size + '-' + settings[size];
43249 this.store = new Roo.data.Store({
43250 proxy : new Roo.data.MemoryProxy({}),
43251 reader : new Roo.data.JsonReader({
43262 'name' : 'dialCode',
43266 'name' : 'priority',
43270 'name' : 'areaCodes',
43277 if(!this.preferedCountries) {
43278 this.preferedCountries = [
43285 var p = this.preferedCountries.reverse();
43288 for (var i = 0; i < p.length; i++) {
43289 for (var j = 0; j < this.allCountries.length; j++) {
43290 if(this.allCountries[j].iso2 == p[i]) {
43291 var t = this.allCountries[j];
43292 this.allCountries.splice(j,1);
43293 this.allCountries.unshift(t);
43299 this.store.proxy.data = {
43301 data: this.allCountries
43307 initEvents : function()
43310 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43312 this.indicator = this.indicatorEl();
43313 this.flag = this.flagEl();
43314 this.dialCodeHolder = this.dialCodeHolderEl();
43316 this.trigger = this.el.select('div.flag-box',true).first();
43317 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43322 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43323 _this.list.setWidth(lw);
43326 this.list.on('mouseover', this.onViewOver, this);
43327 this.list.on('mousemove', this.onViewMove, this);
43328 this.inputEl().on("keyup", this.onKeyUp, this);
43329 this.inputEl().on("keypress", this.onKeyPress, this);
43331 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43333 this.view = new Roo.View(this.list, this.tpl, {
43334 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43337 this.view.on('click', this.onViewClick, this);
43338 this.setValue(this.defaultDialCode);
43341 onTriggerClick : function(e)
43343 Roo.log('trigger click');
43348 if(this.isExpanded()){
43350 this.hasFocus = false;
43352 this.store.load({});
43353 this.hasFocus = true;
43358 isExpanded : function()
43360 return this.list.isVisible();
43363 collapse : function()
43365 if(!this.isExpanded()){
43369 Roo.get(document).un('mousedown', this.collapseIf, this);
43370 Roo.get(document).un('mousewheel', this.collapseIf, this);
43371 this.fireEvent('collapse', this);
43375 expand : function()
43379 if(this.isExpanded() || !this.hasFocus){
43383 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43384 this.list.setWidth(lw);
43387 this.restrictHeight();
43389 Roo.get(document).on('mousedown', this.collapseIf, this);
43390 Roo.get(document).on('mousewheel', this.collapseIf, this);
43392 this.fireEvent('expand', this);
43395 restrictHeight : function()
43397 this.list.alignTo(this.inputEl(), this.listAlign);
43398 this.list.alignTo(this.inputEl(), this.listAlign);
43401 onViewOver : function(e, t)
43403 if(this.inKeyMode){
43406 var item = this.view.findItemFromChild(t);
43409 var index = this.view.indexOf(item);
43410 this.select(index, false);
43415 onViewClick : function(view, doFocus, el, e)
43417 var index = this.view.getSelectedIndexes()[0];
43419 var r = this.store.getAt(index);
43422 this.onSelect(r, index);
43424 if(doFocus !== false && !this.blockFocus){
43425 this.inputEl().focus();
43429 onViewMove : function(e, t)
43431 this.inKeyMode = false;
43434 select : function(index, scrollIntoView)
43436 this.selectedIndex = index;
43437 this.view.select(index);
43438 if(scrollIntoView !== false){
43439 var el = this.view.getNode(index);
43441 this.list.scrollChildIntoView(el, false);
43446 createList : function()
43448 this.list = Roo.get(document.body).createChild({
43450 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43451 style: 'display:none'
43454 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43457 collapseIf : function(e)
43459 var in_combo = e.within(this.el);
43460 var in_list = e.within(this.list);
43461 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43463 if (in_combo || in_list || is_list) {
43469 onSelect : function(record, index)
43471 if(this.fireEvent('beforeselect', this, record, index) !== false){
43473 this.setFlagClass(record.data.iso2);
43474 this.setDialCode(record.data.dialCode);
43475 this.hasFocus = false;
43477 this.fireEvent('select', this, record, index);
43481 flagEl : function()
43483 var flag = this.el.select('div.flag',true).first();
43490 dialCodeHolderEl : function()
43492 var d = this.el.select('input.dial-code-holder',true).first();
43499 setDialCode : function(v)
43501 this.dialCodeHolder.dom.value = '+'+v;
43504 setFlagClass : function(n)
43506 this.flag.dom.className = 'flag '+n;
43509 getValue : function()
43511 var v = this.inputEl().getValue();
43512 if(this.dialCodeHolder) {
43513 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43518 setValue : function(v)
43520 var d = this.getDialCode(v);
43522 //invalid dial code
43523 if(v.length == 0 || !d || d.length == 0) {
43525 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43526 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43532 this.setFlagClass(this.dialCodeMapping[d].iso2);
43533 this.setDialCode(d);
43534 this.inputEl().dom.value = v.replace('+'+d,'');
43535 this.hiddenEl().dom.value = this.getValue();
43540 getDialCode : function(v)
43544 if (v.length == 0) {
43545 return this.dialCodeHolder.dom.value;
43549 if (v.charAt(0) != "+") {
43552 var numericChars = "";
43553 for (var i = 1; i < v.length; i++) {
43554 var c = v.charAt(i);
43557 if (this.dialCodeMapping[numericChars]) {
43558 dialCode = v.substr(1, i);
43560 if (numericChars.length == 4) {
43570 this.setValue(this.defaultDialCode);
43574 hiddenEl : function()
43576 return this.el.select('input.hidden-tel-input',true).first();
43579 // after setting val
43580 onKeyUp : function(e){
43581 this.setValue(this.getValue());
43584 onKeyPress : function(e){
43585 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43592 * @class Roo.bootstrap.MoneyField
43593 * @extends Roo.bootstrap.ComboBox
43594 * Bootstrap MoneyField class
43597 * Create a new MoneyField.
43598 * @param {Object} config Configuration options
43601 Roo.bootstrap.MoneyField = function(config) {
43603 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43607 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43610 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43612 allowDecimals : true,
43614 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43616 decimalSeparator : ".",
43618 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43620 decimalPrecision : 0,
43622 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43624 allowNegative : true,
43626 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43630 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43632 minValue : Number.NEGATIVE_INFINITY,
43634 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43636 maxValue : Number.MAX_VALUE,
43638 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43640 minText : "The minimum value for this field is {0}",
43642 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43644 maxText : "The maximum value for this field is {0}",
43646 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43647 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43649 nanText : "{0} is not a valid number",
43651 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43655 * @cfg {String} defaults currency of the MoneyField
43656 * value should be in lkey
43658 defaultCurrency : false,
43660 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43662 thousandsDelimiter : false,
43664 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43675 getAutoCreate : function()
43677 var align = this.labelAlign || this.parentLabelAlign();
43689 cls : 'form-control roo-money-amount-input',
43690 autocomplete: 'new-password'
43693 var hiddenInput = {
43697 cls: 'hidden-number-input'
43700 if(this.max_length) {
43701 input.maxlength = this.max_length;
43705 hiddenInput.name = this.name;
43708 if (this.disabled) {
43709 input.disabled = true;
43712 var clg = 12 - this.inputlg;
43713 var cmd = 12 - this.inputmd;
43714 var csm = 12 - this.inputsm;
43715 var cxs = 12 - this.inputxs;
43719 cls : 'row roo-money-field',
43723 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43727 cls: 'roo-select2-container input-group',
43731 cls : 'form-control roo-money-currency-input',
43732 autocomplete: 'new-password',
43734 name : this.currencyName
43738 cls : 'input-group-addon',
43752 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43756 cls: this.hasFeedback ? 'has-feedback' : '',
43767 if (this.fieldLabel.length) {
43770 tooltip: 'This field is required'
43776 cls: 'control-label',
43782 html: this.fieldLabel
43785 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43791 if(this.indicatorpos == 'right') {
43792 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43799 if(align == 'left') {
43807 if(this.labelWidth > 12){
43808 label.style = "width: " + this.labelWidth + 'px';
43810 if(this.labelWidth < 13 && this.labelmd == 0){
43811 this.labelmd = this.labelWidth;
43813 if(this.labellg > 0){
43814 label.cls += ' col-lg-' + this.labellg;
43815 input.cls += ' col-lg-' + (12 - this.labellg);
43817 if(this.labelmd > 0){
43818 label.cls += ' col-md-' + this.labelmd;
43819 container.cls += ' col-md-' + (12 - this.labelmd);
43821 if(this.labelsm > 0){
43822 label.cls += ' col-sm-' + this.labelsm;
43823 container.cls += ' col-sm-' + (12 - this.labelsm);
43825 if(this.labelxs > 0){
43826 label.cls += ' col-xs-' + this.labelxs;
43827 container.cls += ' col-xs-' + (12 - this.labelxs);
43838 var settings = this;
43840 ['xs','sm','md','lg'].map(function(size){
43841 if (settings[size]) {
43842 cfg.cls += ' col-' + size + '-' + settings[size];
43849 initEvents : function()
43851 this.indicator = this.indicatorEl();
43853 this.initCurrencyEvent();
43855 this.initNumberEvent();
43858 initCurrencyEvent : function()
43861 throw "can not find store for combo";
43864 this.store = Roo.factory(this.store, Roo.data);
43865 this.store.parent = this;
43869 this.triggerEl = this.el.select('.input-group-addon', true).first();
43871 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43876 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43877 _this.list.setWidth(lw);
43880 this.list.on('mouseover', this.onViewOver, this);
43881 this.list.on('mousemove', this.onViewMove, this);
43882 this.list.on('scroll', this.onViewScroll, this);
43885 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43888 this.view = new Roo.View(this.list, this.tpl, {
43889 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43892 this.view.on('click', this.onViewClick, this);
43894 this.store.on('beforeload', this.onBeforeLoad, this);
43895 this.store.on('load', this.onLoad, this);
43896 this.store.on('loadexception', this.onLoadException, this);
43898 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43899 "up" : function(e){
43900 this.inKeyMode = true;
43904 "down" : function(e){
43905 if(!this.isExpanded()){
43906 this.onTriggerClick();
43908 this.inKeyMode = true;
43913 "enter" : function(e){
43916 if(this.fireEvent("specialkey", this, e)){
43917 this.onViewClick(false);
43923 "esc" : function(e){
43927 "tab" : function(e){
43930 if(this.fireEvent("specialkey", this, e)){
43931 this.onViewClick(false);
43939 doRelay : function(foo, bar, hname){
43940 if(hname == 'down' || this.scope.isExpanded()){
43941 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43949 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43953 initNumberEvent : function(e)
43955 this.inputEl().on("keydown" , this.fireKey, this);
43956 this.inputEl().on("focus", this.onFocus, this);
43957 this.inputEl().on("blur", this.onBlur, this);
43959 this.inputEl().relayEvent('keyup', this);
43961 if(this.indicator){
43962 this.indicator.addClass('invisible');
43965 this.originalValue = this.getValue();
43967 if(this.validationEvent == 'keyup'){
43968 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43969 this.inputEl().on('keyup', this.filterValidation, this);
43971 else if(this.validationEvent !== false){
43972 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43975 if(this.selectOnFocus){
43976 this.on("focus", this.preFocus, this);
43979 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43980 this.inputEl().on("keypress", this.filterKeys, this);
43982 this.inputEl().relayEvent('keypress', this);
43985 var allowed = "0123456789";
43987 if(this.allowDecimals){
43988 allowed += this.decimalSeparator;
43991 if(this.allowNegative){
43995 if(this.thousandsDelimiter) {
43999 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44001 var keyPress = function(e){
44003 var k = e.getKey();
44005 var c = e.getCharCode();
44008 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44009 allowed.indexOf(String.fromCharCode(c)) === -1
44015 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44019 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44024 this.inputEl().on("keypress", keyPress, this);
44028 onTriggerClick : function(e)
44035 this.loadNext = false;
44037 if(this.isExpanded()){
44042 this.hasFocus = true;
44044 if(this.triggerAction == 'all') {
44045 this.doQuery(this.allQuery, true);
44049 this.doQuery(this.getRawValue());
44052 getCurrency : function()
44054 var v = this.currencyEl().getValue();
44059 restrictHeight : function()
44061 this.list.alignTo(this.currencyEl(), this.listAlign);
44062 this.list.alignTo(this.currencyEl(), this.listAlign);
44065 onViewClick : function(view, doFocus, el, e)
44067 var index = this.view.getSelectedIndexes()[0];
44069 var r = this.store.getAt(index);
44072 this.onSelect(r, index);
44076 onSelect : function(record, index){
44078 if(this.fireEvent('beforeselect', this, record, index) !== false){
44080 this.setFromCurrencyData(index > -1 ? record.data : false);
44084 this.fireEvent('select', this, record, index);
44088 setFromCurrencyData : function(o)
44092 this.lastCurrency = o;
44094 if (this.currencyField) {
44095 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44097 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44100 this.lastSelectionText = currency;
44102 //setting default currency
44103 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44104 this.setCurrency(this.defaultCurrency);
44108 this.setCurrency(currency);
44111 setFromData : function(o)
44115 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44117 this.setFromCurrencyData(c);
44122 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44124 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44127 this.setValue(value);
44131 setCurrency : function(v)
44133 this.currencyValue = v;
44136 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44141 setValue : function(v)
44143 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44149 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44151 this.inputEl().dom.value = (v == '') ? '' :
44152 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44154 if(!this.allowZero && v === '0') {
44155 this.hiddenEl().dom.value = '';
44156 this.inputEl().dom.value = '';
44163 getRawValue : function()
44165 var v = this.inputEl().getValue();
44170 getValue : function()
44172 return this.fixPrecision(this.parseValue(this.getRawValue()));
44175 parseValue : function(value)
44177 if(this.thousandsDelimiter) {
44179 r = new RegExp(",", "g");
44180 value = value.replace(r, "");
44183 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44184 return isNaN(value) ? '' : value;
44188 fixPrecision : function(value)
44190 if(this.thousandsDelimiter) {
44192 r = new RegExp(",", "g");
44193 value = value.replace(r, "");
44196 var nan = isNaN(value);
44198 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44199 return nan ? '' : value;
44201 return parseFloat(value).toFixed(this.decimalPrecision);
44204 decimalPrecisionFcn : function(v)
44206 return Math.floor(v);
44209 validateValue : function(value)
44211 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44215 var num = this.parseValue(value);
44218 this.markInvalid(String.format(this.nanText, value));
44222 if(num < this.minValue){
44223 this.markInvalid(String.format(this.minText, this.minValue));
44227 if(num > this.maxValue){
44228 this.markInvalid(String.format(this.maxText, this.maxValue));
44235 validate : function()
44237 if(this.disabled || this.allowBlank){
44242 var currency = this.getCurrency();
44244 if(this.validateValue(this.getRawValue()) && currency.length){
44249 this.markInvalid();
44253 getName: function()
44258 beforeBlur : function()
44264 var v = this.parseValue(this.getRawValue());
44271 onBlur : function()
44275 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44276 //this.el.removeClass(this.focusClass);
44279 this.hasFocus = false;
44281 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44285 var v = this.getValue();
44287 if(String(v) !== String(this.startValue)){
44288 this.fireEvent('change', this, v, this.startValue);
44291 this.fireEvent("blur", this);
44294 inputEl : function()
44296 return this.el.select('.roo-money-amount-input', true).first();
44299 currencyEl : function()
44301 return this.el.select('.roo-money-currency-input', true).first();
44304 hiddenEl : function()
44306 return this.el.select('input.hidden-number-input',true).first();
44310 * @class Roo.bootstrap.BezierSignature
44311 * @extends Roo.bootstrap.Component
44312 * Bootstrap BezierSignature class
44313 * This script refer to:
44314 * Title: Signature Pad
44316 * Availability: https://github.com/szimek/signature_pad
44319 * Create a new BezierSignature
44320 * @param {Object} config The config object
44323 Roo.bootstrap.BezierSignature = function(config){
44324 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44330 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44337 mouse_btn_down: true,
44340 * @cfg {int} canvas height
44342 canvas_height: '200px',
44345 * @cfg {float|function} Radius of a single dot.
44350 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44355 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44360 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44365 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44370 * @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.
44372 bg_color: 'rgba(0, 0, 0, 0)',
44375 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44377 dot_color: 'black',
44380 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44382 velocity_filter_weight: 0.7,
44385 * @cfg {function} Callback when stroke begin.
44390 * @cfg {function} Callback when stroke end.
44394 getAutoCreate : function()
44396 var cls = 'roo-signature column';
44399 cls += ' ' + this.cls;
44409 for(var i = 0; i < col_sizes.length; i++) {
44410 if(this[col_sizes[i]]) {
44411 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44421 cls: 'roo-signature-body',
44425 cls: 'roo-signature-body-canvas',
44426 height: this.canvas_height,
44427 width: this.canvas_width
44434 style: 'display: none'
44442 initEvents: function()
44444 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44446 var canvas = this.canvasEl();
44448 // mouse && touch event swapping...
44449 canvas.dom.style.touchAction = 'none';
44450 canvas.dom.style.msTouchAction = 'none';
44452 this.mouse_btn_down = false;
44453 canvas.on('mousedown', this._handleMouseDown, this);
44454 canvas.on('mousemove', this._handleMouseMove, this);
44455 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44457 if (window.PointerEvent) {
44458 canvas.on('pointerdown', this._handleMouseDown, this);
44459 canvas.on('pointermove', this._handleMouseMove, this);
44460 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44463 if ('ontouchstart' in window) {
44464 canvas.on('touchstart', this._handleTouchStart, this);
44465 canvas.on('touchmove', this._handleTouchMove, this);
44466 canvas.on('touchend', this._handleTouchEnd, this);
44469 Roo.EventManager.onWindowResize(this.resize, this, true);
44471 // file input event
44472 this.fileEl().on('change', this.uploadImage, this);
44479 resize: function(){
44481 var canvas = this.canvasEl().dom;
44482 var ctx = this.canvasElCtx();
44483 var img_data = false;
44485 if(canvas.width > 0) {
44486 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44488 // setting canvas width will clean img data
44491 var style = window.getComputedStyle ?
44492 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44494 var padding_left = parseInt(style.paddingLeft) || 0;
44495 var padding_right = parseInt(style.paddingRight) || 0;
44497 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44500 ctx.putImageData(img_data, 0, 0);
44504 _handleMouseDown: function(e)
44506 if (e.browserEvent.which === 1) {
44507 this.mouse_btn_down = true;
44508 this.strokeBegin(e);
44512 _handleMouseMove: function (e)
44514 if (this.mouse_btn_down) {
44515 this.strokeMoveUpdate(e);
44519 _handleMouseUp: function (e)
44521 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44522 this.mouse_btn_down = false;
44527 _handleTouchStart: function (e) {
44529 e.preventDefault();
44530 if (e.browserEvent.targetTouches.length === 1) {
44531 // var touch = e.browserEvent.changedTouches[0];
44532 // this.strokeBegin(touch);
44534 this.strokeBegin(e); // assume e catching the correct xy...
44538 _handleTouchMove: function (e) {
44539 e.preventDefault();
44540 // var touch = event.targetTouches[0];
44541 // _this._strokeMoveUpdate(touch);
44542 this.strokeMoveUpdate(e);
44545 _handleTouchEnd: function (e) {
44546 var wasCanvasTouched = e.target === this.canvasEl().dom;
44547 if (wasCanvasTouched) {
44548 e.preventDefault();
44549 // var touch = event.changedTouches[0];
44550 // _this._strokeEnd(touch);
44555 reset: function () {
44556 this._lastPoints = [];
44557 this._lastVelocity = 0;
44558 this._lastWidth = (this.min_width + this.max_width) / 2;
44559 this.canvasElCtx().fillStyle = this.dot_color;
44562 strokeMoveUpdate: function(e)
44564 this.strokeUpdate(e);
44566 if (this.throttle) {
44567 this.throttleStroke(this.strokeUpdate, this.throttle);
44570 this.strokeUpdate(e);
44574 strokeBegin: function(e)
44576 var newPointGroup = {
44577 color: this.dot_color,
44581 if (typeof this.onBegin === 'function') {
44585 this.curve_data.push(newPointGroup);
44587 this.strokeUpdate(e);
44590 strokeUpdate: function(e)
44592 var rect = this.canvasEl().dom.getBoundingClientRect();
44593 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44594 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44595 var lastPoints = lastPointGroup.points;
44596 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44597 var isLastPointTooClose = lastPoint
44598 ? point.distanceTo(lastPoint) <= this.min_distance
44600 var color = lastPointGroup.color;
44601 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44602 var curve = this.addPoint(point);
44604 this.drawDot({color: color, point: point});
44607 this.drawCurve({color: color, curve: curve});
44617 strokeEnd: function(e)
44619 this.strokeUpdate(e);
44620 if (typeof this.onEnd === 'function') {
44625 addPoint: function (point) {
44626 var _lastPoints = this._lastPoints;
44627 _lastPoints.push(point);
44628 if (_lastPoints.length > 2) {
44629 if (_lastPoints.length === 3) {
44630 _lastPoints.unshift(_lastPoints[0]);
44632 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44633 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44634 _lastPoints.shift();
44640 calculateCurveWidths: function (startPoint, endPoint) {
44641 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44642 (1 - this.velocity_filter_weight) * this._lastVelocity;
44644 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44647 start: this._lastWidth
44650 this._lastVelocity = velocity;
44651 this._lastWidth = newWidth;
44655 drawDot: function (_a) {
44656 var color = _a.color, point = _a.point;
44657 var ctx = this.canvasElCtx();
44658 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44660 this.drawCurveSegment(point.x, point.y, width);
44662 ctx.fillStyle = color;
44666 drawCurve: function (_a) {
44667 var color = _a.color, curve = _a.curve;
44668 var ctx = this.canvasElCtx();
44669 var widthDelta = curve.endWidth - curve.startWidth;
44670 var drawSteps = Math.floor(curve.length()) * 2;
44672 ctx.fillStyle = color;
44673 for (var i = 0; i < drawSteps; i += 1) {
44674 var t = i / drawSteps;
44680 var x = uuu * curve.startPoint.x;
44681 x += 3 * uu * t * curve.control1.x;
44682 x += 3 * u * tt * curve.control2.x;
44683 x += ttt * curve.endPoint.x;
44684 var y = uuu * curve.startPoint.y;
44685 y += 3 * uu * t * curve.control1.y;
44686 y += 3 * u * tt * curve.control2.y;
44687 y += ttt * curve.endPoint.y;
44688 var width = curve.startWidth + ttt * widthDelta;
44689 this.drawCurveSegment(x, y, width);
44695 drawCurveSegment: function (x, y, width) {
44696 var ctx = this.canvasElCtx();
44698 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44699 this.is_empty = false;
44704 var ctx = this.canvasElCtx();
44705 var canvas = this.canvasEl().dom;
44706 ctx.fillStyle = this.bg_color;
44707 ctx.clearRect(0, 0, canvas.width, canvas.height);
44708 ctx.fillRect(0, 0, canvas.width, canvas.height);
44709 this.curve_data = [];
44711 this.is_empty = true;
44716 return this.el.select('input',true).first();
44719 canvasEl: function()
44721 return this.el.select('canvas',true).first();
44724 canvasElCtx: function()
44726 return this.el.select('canvas',true).first().dom.getContext('2d');
44729 getImage: function(type)
44731 if(this.is_empty) {
44736 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44739 drawFromImage: function(img_src)
44741 var img = new Image();
44743 img.onload = function(){
44744 this.canvasElCtx().drawImage(img, 0, 0);
44749 this.is_empty = false;
44752 selectImage: function()
44754 this.fileEl().dom.click();
44757 uploadImage: function(e)
44759 var reader = new FileReader();
44761 reader.onload = function(e){
44762 var img = new Image();
44763 img.onload = function(){
44765 this.canvasElCtx().drawImage(img, 0, 0);
44767 img.src = e.target.result;
44770 reader.readAsDataURL(e.target.files[0]);
44773 // Bezier Point Constructor
44774 Point: (function () {
44775 function Point(x, y, time) {
44778 this.time = time || Date.now();
44780 Point.prototype.distanceTo = function (start) {
44781 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44783 Point.prototype.equals = function (other) {
44784 return this.x === other.x && this.y === other.y && this.time === other.time;
44786 Point.prototype.velocityFrom = function (start) {
44787 return this.time !== start.time
44788 ? this.distanceTo(start) / (this.time - start.time)
44795 // Bezier Constructor
44796 Bezier: (function () {
44797 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44798 this.startPoint = startPoint;
44799 this.control2 = control2;
44800 this.control1 = control1;
44801 this.endPoint = endPoint;
44802 this.startWidth = startWidth;
44803 this.endWidth = endWidth;
44805 Bezier.fromPoints = function (points, widths, scope) {
44806 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44807 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44808 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44810 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44811 var dx1 = s1.x - s2.x;
44812 var dy1 = s1.y - s2.y;
44813 var dx2 = s2.x - s3.x;
44814 var dy2 = s2.y - s3.y;
44815 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44816 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44817 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44818 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44819 var dxm = m1.x - m2.x;
44820 var dym = m1.y - m2.y;
44821 var k = l2 / (l1 + l2);
44822 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44823 var tx = s2.x - cm.x;
44824 var ty = s2.y - cm.y;
44826 c1: new scope.Point(m1.x + tx, m1.y + ty),
44827 c2: new scope.Point(m2.x + tx, m2.y + ty)
44830 Bezier.prototype.length = function () {
44835 for (var i = 0; i <= steps; i += 1) {
44837 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44838 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44840 var xdiff = cx - px;
44841 var ydiff = cy - py;
44842 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44849 Bezier.prototype.point = function (t, start, c1, c2, end) {
44850 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44851 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44852 + (3.0 * c2 * (1.0 - t) * t * t)
44853 + (end * t * t * t);
44858 throttleStroke: function(fn, wait) {
44859 if (wait === void 0) { wait = 250; }
44861 var timeout = null;
44865 var later = function () {
44866 previous = Date.now();
44868 result = fn.apply(storedContext, storedArgs);
44870 storedContext = null;
44874 return function wrapper() {
44876 for (var _i = 0; _i < arguments.length; _i++) {
44877 args[_i] = arguments[_i];
44879 var now = Date.now();
44880 var remaining = wait - (now - previous);
44881 storedContext = this;
44883 if (remaining <= 0 || remaining > wait) {
44885 clearTimeout(timeout);
44889 result = fn.apply(storedContext, storedArgs);
44891 storedContext = null;
44895 else if (!timeout) {
44896 timeout = window.setTimeout(later, remaining);