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);
3724 this.triggerEl.on('click', this.onTriggerClick, this);
3726 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3728 if (!this.hideTrigger) {
3729 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3730 // dropdown toggle on the 'a' in BS4?
3731 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3733 this.triggerEl.addClass('dropdown-toggle');
3737 this.el.on('touchstart' , this.onTouch, this);
3739 this.el.on('click' , this.onClick, this);
3741 this.el.on("mouseover", this.onMouseOver, this);
3742 this.el.on("mouseout", this.onMouseOut, this);
3746 findTargetItem : function(e)
3748 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3752 //Roo.log(t); Roo.log(t.id);
3754 //Roo.log(this.menuitems);
3755 return this.menuitems.get(t.id);
3757 //return this.items.get(t.menuItemId);
3763 onTouch : function(e)
3765 Roo.log("menu.onTouch");
3766 //e.stopEvent(); this make the user popdown broken
3770 onClick : function(e)
3772 Roo.log("menu.onClick");
3774 var t = this.findTargetItem(e);
3775 if(!t || t.isContainer){
3780 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3781 if(t == this.activeItem && t.shouldDeactivate(e)){
3782 this.activeItem.deactivate();
3783 delete this.activeItem;
3787 this.setActiveItem(t, true);
3795 Roo.log('pass click event');
3799 this.fireEvent("click", this, t, e);
3803 if(!t.href.length || t.href == '#'){
3804 (function() { _this.hide(); }).defer(100);
3809 onMouseOver : function(e){
3810 var t = this.findTargetItem(e);
3813 // if(t.canActivate && !t.disabled){
3814 // this.setActiveItem(t, true);
3818 this.fireEvent("mouseover", this, e, t);
3820 isVisible : function(){
3821 return !this.hidden;
3823 onMouseOut : function(e){
3824 var t = this.findTargetItem(e);
3827 // if(t == this.activeItem && t.shouldDeactivate(e)){
3828 // this.activeItem.deactivate();
3829 // delete this.activeItem;
3832 this.fireEvent("mouseout", this, e, t);
3837 * Displays this menu relative to another element
3838 * @param {String/HTMLElement/Roo.Element} element The element to align to
3839 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3840 * the element (defaults to this.defaultAlign)
3841 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3843 show : function(el, pos, parentMenu)
3845 if (false === this.fireEvent("beforeshow", this)) {
3846 Roo.log("show canceled");
3849 this.parentMenu = parentMenu;
3853 this.el.addClass('show'); // show otherwise we do not know how big we are..
3855 var xy = this.el.getAlignToXY(el, pos);
3857 // bl-tl << left align below
3858 // tl-bl << left align
3860 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3861 // if it goes to far to the right.. -> align left.
3862 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3865 // was left align - go right?
3866 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3869 // goes down the bottom
3870 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3872 var a = this.align.replace('?', '').split('-');
3873 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3877 this.showAt( xy , parentMenu, false);
3880 * Displays this menu at a specific xy position
3881 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3882 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3884 showAt : function(xy, parentMenu, /* private: */_e){
3885 this.parentMenu = parentMenu;
3890 this.fireEvent("beforeshow", this);
3891 //xy = this.el.adjustForConstraints(xy);
3895 this.hideMenuItems();
3896 this.hidden = false;
3897 this.triggerEl.addClass('open');
3898 this.el.addClass('show');
3902 // reassign x when hitting right
3904 // reassign y when hitting bottom
3906 // but the list may align on trigger left or trigger top... should it be a properity?
3908 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3913 this.fireEvent("show", this);
3919 this.doFocus.defer(50, this);
3923 doFocus : function(){
3925 this.focusEl.focus();
3930 * Hides this menu and optionally all parent menus
3931 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3933 hide : function(deep)
3935 if (false === this.fireEvent("beforehide", this)) {
3936 Roo.log("hide canceled");
3939 this.hideMenuItems();
3940 if(this.el && this.isVisible()){
3942 if(this.activeItem){
3943 this.activeItem.deactivate();
3944 this.activeItem = null;
3946 this.triggerEl.removeClass('open');;
3947 this.el.removeClass('show');
3949 this.fireEvent("hide", this);
3951 if(deep === true && this.parentMenu){
3952 this.parentMenu.hide(true);
3956 onTriggerClick : function(e)
3958 Roo.log('trigger click');
3960 var target = e.getTarget();
3962 Roo.log(target.nodeName.toLowerCase());
3964 if(target.nodeName.toLowerCase() === 'i'){
3970 onTriggerPress : function(e)
3972 Roo.log('trigger press');
3973 //Roo.log(e.getTarget());
3974 // Roo.log(this.triggerEl.dom);
3976 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3977 var pel = Roo.get(e.getTarget());
3978 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3979 Roo.log('is treeview or dropdown?');
3983 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3987 if (this.isVisible()) {
3993 this.show(this.triggerEl, this.align, false);
3996 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4003 hideMenuItems : function()
4005 Roo.log("hide Menu Items");
4010 this.el.select('.open',true).each(function(aa) {
4012 aa.removeClass('open');
4016 addxtypeChild : function (tree, cntr) {
4017 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4019 this.menuitems.add(comp);
4031 this.getEl().dom.innerHTML = '';
4032 this.menuitems.clear();
4046 * @class Roo.bootstrap.MenuItem
4047 * @extends Roo.bootstrap.Component
4048 * Bootstrap MenuItem class
4049 * @cfg {String} html the menu label
4050 * @cfg {String} href the link
4051 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4052 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4053 * @cfg {Boolean} active used on sidebars to highlight active itesm
4054 * @cfg {String} fa favicon to show on left of menu item.
4055 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4059 * Create a new MenuItem
4060 * @param {Object} config The config object
4064 Roo.bootstrap.MenuItem = function(config){
4065 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4070 * The raw click event for the entire grid.
4071 * @param {Roo.bootstrap.MenuItem} this
4072 * @param {Roo.EventObject} e
4078 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4082 preventDefault: false,
4083 isContainer : false,
4087 getAutoCreate : function(){
4089 if(this.isContainer){
4092 cls: 'dropdown-menu-item '
4102 cls : 'dropdown-item',
4107 if (this.fa !== false) {
4110 cls : 'fa fa-' + this.fa
4119 cls: 'dropdown-menu-item',
4122 if (this.parent().type == 'treeview') {
4123 cfg.cls = 'treeview-menu';
4126 cfg.cls += ' active';
4131 anc.href = this.href || cfg.cn[0].href ;
4132 ctag.html = this.html || cfg.cn[0].html ;
4136 initEvents: function()
4138 if (this.parent().type == 'treeview') {
4139 this.el.select('a').on('click', this.onClick, this);
4143 this.menu.parentType = this.xtype;
4144 this.menu.triggerEl = this.el;
4145 this.menu = this.addxtype(Roo.apply({}, this.menu));
4149 onClick : function(e)
4151 Roo.log('item on click ');
4153 if(this.preventDefault){
4156 //this.parent().hideMenuItems();
4158 this.fireEvent('click', this, e);
4177 * @class Roo.bootstrap.MenuSeparator
4178 * @extends Roo.bootstrap.Component
4179 * Bootstrap MenuSeparator class
4182 * Create a new MenuItem
4183 * @param {Object} config The config object
4187 Roo.bootstrap.MenuSeparator = function(config){
4188 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4191 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4193 getAutoCreate : function(){
4212 * @class Roo.bootstrap.Modal
4213 * @extends Roo.bootstrap.Component
4214 * Bootstrap Modal class
4215 * @cfg {String} title Title of dialog
4216 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4217 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4218 * @cfg {Boolean} specificTitle default false
4219 * @cfg {Array} buttons Array of buttons or standard button set..
4220 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4221 * @cfg {Boolean} animate default true
4222 * @cfg {Boolean} allow_close default true
4223 * @cfg {Boolean} fitwindow default false
4224 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4225 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4226 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4227 * @cfg {String} size (sm|lg|xl) default empty
4228 * @cfg {Number} max_width set the max width of modal
4229 * @cfg {Boolean} editableTitle can the title be edited
4234 * Create a new Modal Dialog
4235 * @param {Object} config The config object
4238 Roo.bootstrap.Modal = function(config){
4239 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4244 * The raw btnclick event for the button
4245 * @param {Roo.EventObject} e
4250 * Fire when dialog resize
4251 * @param {Roo.bootstrap.Modal} this
4252 * @param {Roo.EventObject} e
4256 * @event titlechanged
4257 * Fire when the editable title has been changed
4258 * @param {Roo.bootstrap.Modal} this
4259 * @param {Roo.EventObject} value
4261 "titlechanged" : true
4264 this.buttons = this.buttons || [];
4267 this.tmpl = Roo.factory(this.tmpl);
4272 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4274 title : 'test dialog',
4284 specificTitle: false,
4286 buttonPosition: 'right',
4308 editableTitle : false,
4310 onRender : function(ct, position)
4312 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4315 var cfg = Roo.apply({}, this.getAutoCreate());
4318 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4320 //if (!cfg.name.length) {
4324 cfg.cls += ' ' + this.cls;
4327 cfg.style = this.style;
4329 this.el = Roo.get(document.body).createChild(cfg, position);
4331 //var type = this.el.dom.type;
4334 if(this.tabIndex !== undefined){
4335 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4338 this.dialogEl = this.el.select('.modal-dialog',true).first();
4339 this.bodyEl = this.el.select('.modal-body',true).first();
4340 this.closeEl = this.el.select('.modal-header .close', true).first();
4341 this.headerEl = this.el.select('.modal-header',true).first();
4342 this.titleEl = this.el.select('.modal-title',true).first();
4343 this.footerEl = this.el.select('.modal-footer',true).first();
4345 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4347 //this.el.addClass("x-dlg-modal");
4349 if (this.buttons.length) {
4350 Roo.each(this.buttons, function(bb) {
4351 var b = Roo.apply({}, bb);
4352 b.xns = b.xns || Roo.bootstrap;
4353 b.xtype = b.xtype || 'Button';
4354 if (typeof(b.listeners) == 'undefined') {
4355 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4358 var btn = Roo.factory(b);
4360 btn.render(this.getButtonContainer());
4364 // render the children.
4367 if(typeof(this.items) != 'undefined'){
4368 var items = this.items;
4371 for(var i =0;i < items.length;i++) {
4372 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4376 this.items = nitems;
4378 // where are these used - they used to be body/close/footer
4382 //this.el.addClass([this.fieldClass, this.cls]);
4386 getAutoCreate : function()
4388 // we will default to modal-body-overflow - might need to remove or make optional later.
4390 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4391 html : this.html || ''
4396 cls : 'modal-title',
4400 if(this.specificTitle){ // WTF is this?
4405 if (this.allow_close && Roo.bootstrap.version == 3) {
4415 if (this.editableTitle) {
4417 cls: 'form-control roo-editable-title d-none',
4423 if (this.allow_close && Roo.bootstrap.version == 4) {
4433 if(this.size.length){
4434 size = 'modal-' + this.size;
4437 var footer = Roo.bootstrap.version == 3 ?
4439 cls : 'modal-footer',
4443 cls: 'btn-' + this.buttonPosition
4448 { // BS4 uses mr-auto on left buttons....
4449 cls : 'modal-footer'
4460 cls: "modal-dialog " + size,
4463 cls : "modal-content",
4466 cls : 'modal-header',
4481 modal.cls += ' fade';
4487 getChildContainer : function() {
4492 getButtonContainer : function() {
4494 return Roo.bootstrap.version == 4 ?
4495 this.el.select('.modal-footer',true).first()
4496 : this.el.select('.modal-footer div',true).first();
4499 initEvents : function()
4501 if (this.allow_close) {
4502 this.closeEl.on('click', this.hide, this);
4504 Roo.EventManager.onWindowResize(this.resize, this, true);
4505 if (this.editableTitle) {
4506 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4507 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4508 this.headerEditEl.on('keyup', function(e) {
4509 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4510 this.toggleHeaderInput(false)
4513 this.headerEditEl.on('blur', function(e) {
4514 this.toggleHeaderInput(false)
4523 this.maskEl.setSize(
4524 Roo.lib.Dom.getViewWidth(true),
4525 Roo.lib.Dom.getViewHeight(true)
4528 if (this.fitwindow) {
4530 this.dialogEl.setStyle( { 'max-width' : '100%' });
4532 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4533 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4538 if(this.max_width !== 0) {
4540 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4543 this.setSize(w, this.height);
4547 if(this.max_height) {
4548 this.setSize(w,Math.min(
4550 Roo.lib.Dom.getViewportHeight(true) - 60
4556 if(!this.fit_content) {
4557 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4561 this.setSize(w, Math.min(
4563 this.headerEl.getHeight() +
4564 this.footerEl.getHeight() +
4565 this.getChildHeight(this.bodyEl.dom.childNodes),
4566 Roo.lib.Dom.getViewportHeight(true) - 60)
4572 setSize : function(w,h)
4583 if (!this.rendered) {
4586 this.toggleHeaderInput(false);
4587 //this.el.setStyle('display', 'block');
4588 this.el.removeClass('hideing');
4589 this.el.dom.style.display='block';
4591 Roo.get(document.body).addClass('modal-open');
4593 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4596 this.el.addClass('show');
4597 this.el.addClass('in');
4600 this.el.addClass('show');
4601 this.el.addClass('in');
4604 // not sure how we can show data in here..
4606 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4609 Roo.get(document.body).addClass("x-body-masked");
4611 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4612 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4613 this.maskEl.dom.style.display = 'block';
4614 this.maskEl.addClass('show');
4619 this.fireEvent('show', this);
4621 // set zindex here - otherwise it appears to be ignored...
4622 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4625 this.items.forEach( function(e) {
4626 e.layout ? e.layout() : false;
4634 if(this.fireEvent("beforehide", this) !== false){
4636 this.maskEl.removeClass('show');
4638 this.maskEl.dom.style.display = '';
4639 Roo.get(document.body).removeClass("x-body-masked");
4640 this.el.removeClass('in');
4641 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4643 if(this.animate){ // why
4644 this.el.addClass('hideing');
4645 this.el.removeClass('show');
4647 if (!this.el.hasClass('hideing')) {
4648 return; // it's been shown again...
4651 this.el.dom.style.display='';
4653 Roo.get(document.body).removeClass('modal-open');
4654 this.el.removeClass('hideing');
4658 this.el.removeClass('show');
4659 this.el.dom.style.display='';
4660 Roo.get(document.body).removeClass('modal-open');
4663 this.fireEvent('hide', this);
4666 isVisible : function()
4669 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4673 addButton : function(str, cb)
4677 var b = Roo.apply({}, { html : str } );
4678 b.xns = b.xns || Roo.bootstrap;
4679 b.xtype = b.xtype || 'Button';
4680 if (typeof(b.listeners) == 'undefined') {
4681 b.listeners = { click : cb.createDelegate(this) };
4684 var btn = Roo.factory(b);
4686 btn.render(this.getButtonContainer());
4692 setDefaultButton : function(btn)
4694 //this.el.select('.modal-footer').()
4697 resizeTo: function(w,h)
4699 this.dialogEl.setWidth(w);
4701 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4703 this.bodyEl.setHeight(h - diff);
4705 this.fireEvent('resize', this);
4708 setContentSize : function(w, h)
4712 onButtonClick: function(btn,e)
4715 this.fireEvent('btnclick', btn.name, e);
4718 * Set the title of the Dialog
4719 * @param {String} str new Title
4721 setTitle: function(str) {
4722 this.titleEl.dom.innerHTML = str;
4726 * Set the body of the Dialog
4727 * @param {String} str new Title
4729 setBody: function(str) {
4730 this.bodyEl.dom.innerHTML = str;
4733 * Set the body of the Dialog using the template
4734 * @param {Obj} data - apply this data to the template and replace the body contents.
4736 applyBody: function(obj)
4739 Roo.log("Error - using apply Body without a template");
4742 this.tmpl.overwrite(this.bodyEl, obj);
4745 getChildHeight : function(child_nodes)
4749 child_nodes.length == 0
4754 var child_height = 0;
4756 for(var i = 0; i < child_nodes.length; i++) {
4759 * for modal with tabs...
4760 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4762 var layout_childs = child_nodes[i].childNodes;
4764 for(var j = 0; j < layout_childs.length; j++) {
4766 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4768 var layout_body_childs = layout_childs[j].childNodes;
4770 for(var k = 0; k < layout_body_childs.length; k++) {
4772 if(layout_body_childs[k].classList.contains('navbar')) {
4773 child_height += layout_body_childs[k].offsetHeight;
4777 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4779 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4781 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4783 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4784 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4799 child_height += child_nodes[i].offsetHeight;
4800 // Roo.log(child_nodes[i].offsetHeight);
4803 return child_height;
4805 toggleHeaderInput : function(is_edit)
4807 if (!this.editableTitle) {
4808 return; // not editable.
4810 if (is_edit && this.is_header_editing) {
4811 return; // already editing..
4815 this.headerEditEl.dom.value = this.title;
4816 this.headerEditEl.removeClass('d-none');
4817 this.headerEditEl.dom.focus();
4818 this.titleEl.addClass('d-none');
4820 this.is_header_editing = true;
4823 // flip back to not editing.
4824 this.title = this.headerEditEl.dom.value;
4825 this.headerEditEl.addClass('d-none');
4826 this.titleEl.removeClass('d-none');
4827 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4828 this.is_header_editing = false;
4829 this.fireEvent('titlechanged', this, this.title);
4838 Roo.apply(Roo.bootstrap.Modal, {
4840 * Button config that displays a single OK button
4849 * Button config that displays Yes and No buttons
4865 * Button config that displays OK and Cancel buttons
4880 * Button config that displays Yes, No and Cancel buttons
4905 * messagebox - can be used as a replace
4909 * @class Roo.MessageBox
4910 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4914 Roo.Msg.alert('Status', 'Changes saved successfully.');
4916 // Prompt for user data:
4917 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4919 // process text value...
4923 // Show a dialog using config options:
4925 title:'Save Changes?',
4926 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4927 buttons: Roo.Msg.YESNOCANCEL,
4934 Roo.bootstrap.MessageBox = function(){
4935 var dlg, opt, mask, waitTimer;
4936 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4937 var buttons, activeTextEl, bwidth;
4941 var handleButton = function(button){
4943 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4947 var handleHide = function(){
4949 dlg.el.removeClass(opt.cls);
4952 // Roo.TaskMgr.stop(waitTimer);
4953 // waitTimer = null;
4958 var updateButtons = function(b){
4961 buttons["ok"].hide();
4962 buttons["cancel"].hide();
4963 buttons["yes"].hide();
4964 buttons["no"].hide();
4965 dlg.footerEl.hide();
4969 dlg.footerEl.show();
4970 for(var k in buttons){
4971 if(typeof buttons[k] != "function"){
4974 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4975 width += buttons[k].el.getWidth()+15;
4985 var handleEsc = function(d, k, e){
4986 if(opt && opt.closable !== false){
4996 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4997 * @return {Roo.BasicDialog} The BasicDialog element
4999 getDialog : function(){
5001 dlg = new Roo.bootstrap.Modal( {
5004 //constraintoviewport:false,
5006 //collapsible : false,
5011 //buttonAlign:"center",
5012 closeClick : function(){
5013 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5016 handleButton("cancel");
5021 dlg.on("hide", handleHide);
5023 //dlg.addKeyListener(27, handleEsc);
5025 this.buttons = buttons;
5026 var bt = this.buttonText;
5027 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5028 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5029 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5030 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5032 bodyEl = dlg.bodyEl.createChild({
5034 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5035 '<textarea class="roo-mb-textarea"></textarea>' +
5036 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5038 msgEl = bodyEl.dom.firstChild;
5039 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5040 textboxEl.enableDisplayMode();
5041 textboxEl.addKeyListener([10,13], function(){
5042 if(dlg.isVisible() && opt && opt.buttons){
5045 }else if(opt.buttons.yes){
5046 handleButton("yes");
5050 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5051 textareaEl.enableDisplayMode();
5052 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5053 progressEl.enableDisplayMode();
5055 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5056 var pf = progressEl.dom.firstChild;
5058 pp = Roo.get(pf.firstChild);
5059 pp.setHeight(pf.offsetHeight);
5067 * Updates the message box body text
5068 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5069 * the XHTML-compliant non-breaking space character '&#160;')
5070 * @return {Roo.MessageBox} This message box
5072 updateText : function(text)
5074 if(!dlg.isVisible() && !opt.width){
5075 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5076 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5078 msgEl.innerHTML = text || ' ';
5080 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5081 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5083 Math.min(opt.width || cw , this.maxWidth),
5084 Math.max(opt.minWidth || this.minWidth, bwidth)
5087 activeTextEl.setWidth(w);
5089 if(dlg.isVisible()){
5090 dlg.fixedcenter = false;
5092 // to big, make it scroll. = But as usual stupid IE does not support
5095 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5096 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5097 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5099 bodyEl.dom.style.height = '';
5100 bodyEl.dom.style.overflowY = '';
5103 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5105 bodyEl.dom.style.overflowX = '';
5108 dlg.setContentSize(w, bodyEl.getHeight());
5109 if(dlg.isVisible()){
5110 dlg.fixedcenter = true;
5116 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5117 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5118 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5119 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5120 * @return {Roo.MessageBox} This message box
5122 updateProgress : function(value, text){
5124 this.updateText(text);
5127 if (pp) { // weird bug on my firefox - for some reason this is not defined
5128 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5129 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5135 * Returns true if the message box is currently displayed
5136 * @return {Boolean} True if the message box is visible, else false
5138 isVisible : function(){
5139 return dlg && dlg.isVisible();
5143 * Hides the message box if it is displayed
5146 if(this.isVisible()){
5152 * Displays a new message box, or reinitializes an existing message box, based on the config options
5153 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5154 * The following config object properties are supported:
5156 Property Type Description
5157 ---------- --------------- ------------------------------------------------------------------------------------
5158 animEl String/Element An id or Element from which the message box should animate as it opens and
5159 closes (defaults to undefined)
5160 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5161 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5162 closable Boolean False to hide the top-right close button (defaults to true). Note that
5163 progress and wait dialogs will ignore this property and always hide the
5164 close button as they can only be closed programmatically.
5165 cls String A custom CSS class to apply to the message box element
5166 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5167 displayed (defaults to 75)
5168 fn Function A callback function to execute after closing the dialog. The arguments to the
5169 function will be btn (the name of the button that was clicked, if applicable,
5170 e.g. "ok"), and text (the value of the active text field, if applicable).
5171 Progress and wait dialogs will ignore this option since they do not respond to
5172 user actions and can only be closed programmatically, so any required function
5173 should be called by the same code after it closes the dialog.
5174 icon String A CSS class that provides a background image to be used as an icon for
5175 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5176 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5177 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5178 modal Boolean False to allow user interaction with the page while the message box is
5179 displayed (defaults to true)
5180 msg String A string that will replace the existing message box body text (defaults
5181 to the XHTML-compliant non-breaking space character ' ')
5182 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5183 progress Boolean True to display a progress bar (defaults to false)
5184 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5185 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5186 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5187 title String The title text
5188 value String The string value to set into the active textbox element if displayed
5189 wait Boolean True to display a progress bar (defaults to false)
5190 width Number The width of the dialog in pixels
5197 msg: 'Please enter your address:',
5199 buttons: Roo.MessageBox.OKCANCEL,
5202 animEl: 'addAddressBtn'
5205 * @param {Object} config Configuration options
5206 * @return {Roo.MessageBox} This message box
5208 show : function(options)
5211 // this causes nightmares if you show one dialog after another
5212 // especially on callbacks..
5214 if(this.isVisible()){
5217 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5218 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5219 Roo.log("New Dialog Message:" + options.msg )
5220 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5221 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5224 var d = this.getDialog();
5226 d.setTitle(opt.title || " ");
5227 d.closeEl.setDisplayed(opt.closable !== false);
5228 activeTextEl = textboxEl;
5229 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5234 textareaEl.setHeight(typeof opt.multiline == "number" ?
5235 opt.multiline : this.defaultTextHeight);
5236 activeTextEl = textareaEl;
5245 progressEl.setDisplayed(opt.progress === true);
5247 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5249 this.updateProgress(0);
5250 activeTextEl.dom.value = opt.value || "";
5252 dlg.setDefaultButton(activeTextEl);
5254 var bs = opt.buttons;
5258 }else if(bs && bs.yes){
5259 db = buttons["yes"];
5261 dlg.setDefaultButton(db);
5263 bwidth = updateButtons(opt.buttons);
5264 this.updateText(opt.msg);
5266 d.el.addClass(opt.cls);
5268 d.proxyDrag = opt.proxyDrag === true;
5269 d.modal = opt.modal !== false;
5270 d.mask = opt.modal !== false ? mask : false;
5272 // force it to the end of the z-index stack so it gets a cursor in FF
5273 document.body.appendChild(dlg.el.dom);
5274 d.animateTarget = null;
5275 d.show(options.animEl);
5281 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5282 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5283 * and closing the message box when the process is complete.
5284 * @param {String} title The title bar text
5285 * @param {String} msg The message box body text
5286 * @return {Roo.MessageBox} This message box
5288 progress : function(title, msg){
5295 minWidth: this.minProgressWidth,
5302 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5303 * If a callback function is passed it will be called after the user clicks the button, and the
5304 * id of the button that was clicked will be passed as the only parameter to the callback
5305 * (could also be the top-right close button).
5306 * @param {String} title The title bar text
5307 * @param {String} msg The message box body text
5308 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5309 * @param {Object} scope (optional) The scope of the callback function
5310 * @return {Roo.MessageBox} This message box
5312 alert : function(title, msg, fn, scope)
5327 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5328 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5329 * You are responsible for closing the message box when the process is complete.
5330 * @param {String} msg The message box body text
5331 * @param {String} title (optional) The title bar text
5332 * @return {Roo.MessageBox} This message box
5334 wait : function(msg, title){
5345 waitTimer = Roo.TaskMgr.start({
5347 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5355 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5356 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5357 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5358 * @param {String} title The title bar text
5359 * @param {String} msg The message box body text
5360 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5361 * @param {Object} scope (optional) The scope of the callback function
5362 * @return {Roo.MessageBox} This message box
5364 confirm : function(title, msg, fn, scope){
5368 buttons: this.YESNO,
5377 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5378 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5379 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5380 * (could also be the top-right close button) and the text that was entered will be passed as the two
5381 * parameters to the callback.
5382 * @param {String} title The title bar text
5383 * @param {String} msg The message box body text
5384 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5385 * @param {Object} scope (optional) The scope of the callback function
5386 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5387 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5388 * @return {Roo.MessageBox} This message box
5390 prompt : function(title, msg, fn, scope, multiline){
5394 buttons: this.OKCANCEL,
5399 multiline: multiline,
5406 * Button config that displays a single OK button
5411 * Button config that displays Yes and No buttons
5414 YESNO : {yes:true, no:true},
5416 * Button config that displays OK and Cancel buttons
5419 OKCANCEL : {ok:true, cancel:true},
5421 * Button config that displays Yes, No and Cancel buttons
5424 YESNOCANCEL : {yes:true, no:true, cancel:true},
5427 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5430 defaultTextHeight : 75,
5432 * The maximum width in pixels of the message box (defaults to 600)
5437 * The minimum width in pixels of the message box (defaults to 100)
5442 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5443 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5446 minProgressWidth : 250,
5448 * An object containing the default button text strings that can be overriden for localized language support.
5449 * Supported properties are: ok, cancel, yes and no.
5450 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5463 * Shorthand for {@link Roo.MessageBox}
5465 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5466 Roo.Msg = Roo.Msg || Roo.MessageBox;
5475 * @class Roo.bootstrap.Navbar
5476 * @extends Roo.bootstrap.Component
5477 * Bootstrap Navbar class
5480 * Create a new Navbar
5481 * @param {Object} config The config object
5485 Roo.bootstrap.Navbar = function(config){
5486 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5490 * @event beforetoggle
5491 * Fire before toggle the menu
5492 * @param {Roo.EventObject} e
5494 "beforetoggle" : true
5498 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5507 getAutoCreate : function(){
5510 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5514 initEvents :function ()
5516 //Roo.log(this.el.select('.navbar-toggle',true));
5517 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5524 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5526 var size = this.el.getSize();
5527 this.maskEl.setSize(size.width, size.height);
5528 this.maskEl.enableDisplayMode("block");
5537 getChildContainer : function()
5539 if (this.el && this.el.select('.collapse').getCount()) {
5540 return this.el.select('.collapse',true).first();
5555 onToggle : function()
5558 if(this.fireEvent('beforetoggle', this) === false){
5561 var ce = this.el.select('.navbar-collapse',true).first();
5563 if (!ce.hasClass('show')) {
5573 * Expand the navbar pulldown
5575 expand : function ()
5578 var ce = this.el.select('.navbar-collapse',true).first();
5579 if (ce.hasClass('collapsing')) {
5582 ce.dom.style.height = '';
5584 ce.addClass('in'); // old...
5585 ce.removeClass('collapse');
5586 ce.addClass('show');
5587 var h = ce.getHeight();
5589 ce.removeClass('show');
5590 // at this point we should be able to see it..
5591 ce.addClass('collapsing');
5593 ce.setHeight(0); // resize it ...
5594 ce.on('transitionend', function() {
5595 //Roo.log('done transition');
5596 ce.removeClass('collapsing');
5597 ce.addClass('show');
5598 ce.removeClass('collapse');
5600 ce.dom.style.height = '';
5601 }, this, { single: true} );
5603 ce.dom.scrollTop = 0;
5606 * Collapse the navbar pulldown
5608 collapse : function()
5610 var ce = this.el.select('.navbar-collapse',true).first();
5612 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5613 // it's collapsed or collapsing..
5616 ce.removeClass('in'); // old...
5617 ce.setHeight(ce.getHeight());
5618 ce.removeClass('show');
5619 ce.addClass('collapsing');
5621 ce.on('transitionend', function() {
5622 ce.dom.style.height = '';
5623 ce.removeClass('collapsing');
5624 ce.addClass('collapse');
5625 }, this, { single: true} );
5645 * @class Roo.bootstrap.NavSimplebar
5646 * @extends Roo.bootstrap.Navbar
5647 * Bootstrap Sidebar class
5649 * @cfg {Boolean} inverse is inverted color
5651 * @cfg {String} type (nav | pills | tabs)
5652 * @cfg {Boolean} arrangement stacked | justified
5653 * @cfg {String} align (left | right) alignment
5655 * @cfg {Boolean} main (true|false) main nav bar? default false
5656 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5658 * @cfg {String} tag (header|footer|nav|div) default is nav
5660 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5664 * Create a new Sidebar
5665 * @param {Object} config The config object
5669 Roo.bootstrap.NavSimplebar = function(config){
5670 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5673 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5689 getAutoCreate : function(){
5693 tag : this.tag || 'div',
5694 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5696 if (['light','white'].indexOf(this.weight) > -1) {
5697 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5699 cfg.cls += ' bg-' + this.weight;
5702 cfg.cls += ' navbar-inverse';
5706 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5708 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5717 cls: 'nav nav-' + this.xtype,
5723 this.type = this.type || 'nav';
5724 if (['tabs','pills'].indexOf(this.type) != -1) {
5725 cfg.cn[0].cls += ' nav-' + this.type
5729 if (this.type!=='nav') {
5730 Roo.log('nav type must be nav/tabs/pills')
5732 cfg.cn[0].cls += ' navbar-nav'
5738 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5739 cfg.cn[0].cls += ' nav-' + this.arrangement;
5743 if (this.align === 'right') {
5744 cfg.cn[0].cls += ' navbar-right';
5769 * navbar-expand-md fixed-top
5773 * @class Roo.bootstrap.NavHeaderbar
5774 * @extends Roo.bootstrap.NavSimplebar
5775 * Bootstrap Sidebar class
5777 * @cfg {String} brand what is brand
5778 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5779 * @cfg {String} brand_href href of the brand
5780 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5781 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5782 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5783 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5786 * Create a new Sidebar
5787 * @param {Object} config The config object
5791 Roo.bootstrap.NavHeaderbar = function(config){
5792 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5796 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5803 desktopCenter : false,
5806 getAutoCreate : function(){
5809 tag: this.nav || 'nav',
5810 cls: 'navbar navbar-expand-md',
5816 if (this.desktopCenter) {
5817 cn.push({cls : 'container', cn : []});
5825 cls: 'navbar-toggle navbar-toggler',
5826 'data-toggle': 'collapse',
5831 html: 'Toggle navigation'
5835 cls: 'icon-bar navbar-toggler-icon'
5848 cn.push( Roo.bootstrap.version == 4 ? btn : {
5850 cls: 'navbar-header',
5859 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5863 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5865 if (['light','white'].indexOf(this.weight) > -1) {
5866 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5868 cfg.cls += ' bg-' + this.weight;
5871 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5872 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5874 // tag can override this..
5876 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5879 if (this.brand !== '') {
5880 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5881 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5883 href: this.brand_href ? this.brand_href : '#',
5884 cls: 'navbar-brand',
5892 cfg.cls += ' main-nav';
5900 getHeaderChildContainer : function()
5902 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5903 return this.el.select('.navbar-header',true).first();
5906 return this.getChildContainer();
5909 getChildContainer : function()
5912 return this.el.select('.roo-navbar-collapse',true).first();
5917 initEvents : function()
5919 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5921 if (this.autohide) {
5926 Roo.get(document).on('scroll',function(e) {
5927 var ns = Roo.get(document).getScroll().top;
5928 var os = prevScroll;
5932 ft.removeClass('slideDown');
5933 ft.addClass('slideUp');
5936 ft.removeClass('slideUp');
5937 ft.addClass('slideDown');
5958 * @class Roo.bootstrap.NavSidebar
5959 * @extends Roo.bootstrap.Navbar
5960 * Bootstrap Sidebar class
5963 * Create a new Sidebar
5964 * @param {Object} config The config object
5968 Roo.bootstrap.NavSidebar = function(config){
5969 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5972 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5974 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5976 getAutoCreate : function(){
5981 cls: 'sidebar sidebar-nav'
6003 * @class Roo.bootstrap.NavGroup
6004 * @extends Roo.bootstrap.Component
6005 * Bootstrap NavGroup class
6006 * @cfg {String} align (left|right)
6007 * @cfg {Boolean} inverse
6008 * @cfg {String} type (nav|pills|tab) default nav
6009 * @cfg {String} navId - reference Id for navbar.
6010 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6013 * Create a new nav group
6014 * @param {Object} config The config object
6017 Roo.bootstrap.NavGroup = function(config){
6018 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6021 Roo.bootstrap.NavGroup.register(this);
6025 * Fires when the active item changes
6026 * @param {Roo.bootstrap.NavGroup} this
6027 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6028 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6035 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6047 getAutoCreate : function()
6049 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6055 if (Roo.bootstrap.version == 4) {
6056 if (['tabs','pills'].indexOf(this.type) != -1) {
6057 cfg.cls += ' nav-' + this.type;
6059 // trying to remove so header bar can right align top?
6060 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6061 // do not use on header bar...
6062 cfg.cls += ' navbar-nav';
6067 if (['tabs','pills'].indexOf(this.type) != -1) {
6068 cfg.cls += ' nav-' + this.type
6070 if (this.type !== 'nav') {
6071 Roo.log('nav type must be nav/tabs/pills')
6073 cfg.cls += ' navbar-nav'
6077 if (this.parent() && this.parent().sidebar) {
6080 cls: 'dashboard-menu sidebar-menu'
6086 if (this.form === true) {
6089 cls: 'navbar-form form-inline'
6091 //nav navbar-right ml-md-auto
6092 if (this.align === 'right') {
6093 cfg.cls += ' navbar-right ml-md-auto';
6095 cfg.cls += ' navbar-left';
6099 if (this.align === 'right') {
6100 cfg.cls += ' navbar-right ml-md-auto';
6102 cfg.cls += ' mr-auto';
6106 cfg.cls += ' navbar-inverse';
6114 * sets the active Navigation item
6115 * @param {Roo.bootstrap.NavItem} the new current navitem
6117 setActiveItem : function(item)
6120 Roo.each(this.navItems, function(v){
6125 v.setActive(false, true);
6132 item.setActive(true, true);
6133 this.fireEvent('changed', this, item, prev);
6138 * gets the active Navigation item
6139 * @return {Roo.bootstrap.NavItem} the current navitem
6141 getActive : function()
6145 Roo.each(this.navItems, function(v){
6156 indexOfNav : function()
6160 Roo.each(this.navItems, function(v,i){
6171 * adds a Navigation item
6172 * @param {Roo.bootstrap.NavItem} the navitem to add
6174 addItem : function(cfg)
6176 if (this.form && Roo.bootstrap.version == 4) {
6179 var cn = new Roo.bootstrap.NavItem(cfg);
6181 cn.parentId = this.id;
6182 cn.onRender(this.el, null);
6186 * register a Navigation item
6187 * @param {Roo.bootstrap.NavItem} the navitem to add
6189 register : function(item)
6191 this.navItems.push( item);
6192 item.navId = this.navId;
6197 * clear all the Navigation item
6200 clearAll : function()
6203 this.el.dom.innerHTML = '';
6206 getNavItem: function(tabId)
6209 Roo.each(this.navItems, function(e) {
6210 if (e.tabId == tabId) {
6220 setActiveNext : function()
6222 var i = this.indexOfNav(this.getActive());
6223 if (i > this.navItems.length) {
6226 this.setActiveItem(this.navItems[i+1]);
6228 setActivePrev : function()
6230 var i = this.indexOfNav(this.getActive());
6234 this.setActiveItem(this.navItems[i-1]);
6236 clearWasActive : function(except) {
6237 Roo.each(this.navItems, function(e) {
6238 if (e.tabId != except.tabId && e.was_active) {
6239 e.was_active = false;
6246 getWasActive : function ()
6249 Roo.each(this.navItems, function(e) {
6264 Roo.apply(Roo.bootstrap.NavGroup, {
6268 * register a Navigation Group
6269 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6271 register : function(navgrp)
6273 this.groups[navgrp.navId] = navgrp;
6277 * fetch a Navigation Group based on the navigation ID
6278 * @param {string} the navgroup to add
6279 * @returns {Roo.bootstrap.NavGroup} the navgroup
6281 get: function(navId) {
6282 if (typeof(this.groups[navId]) == 'undefined') {
6284 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6286 return this.groups[navId] ;
6301 * @class Roo.bootstrap.NavItem
6302 * @extends Roo.bootstrap.Component
6303 * Bootstrap Navbar.NavItem class
6304 * @cfg {String} href link to
6305 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6306 * @cfg {Boolean} button_outline show and outlined button
6307 * @cfg {String} html content of button
6308 * @cfg {String} badge text inside badge
6309 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6310 * @cfg {String} glyphicon DEPRICATED - use fa
6311 * @cfg {String} icon DEPRICATED - use fa
6312 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6313 * @cfg {Boolean} active Is item active
6314 * @cfg {Boolean} disabled Is item disabled
6315 * @cfg {String} linkcls Link Class
6316 * @cfg {Boolean} preventDefault (true | false) default false
6317 * @cfg {String} tabId the tab that this item activates.
6318 * @cfg {String} tagtype (a|span) render as a href or span?
6319 * @cfg {Boolean} animateRef (true|false) link to element default false
6322 * Create a new Navbar Item
6323 * @param {Object} config The config object
6325 Roo.bootstrap.NavItem = function(config){
6326 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6331 * The raw click event for the entire grid.
6332 * @param {Roo.EventObject} e
6337 * Fires when the active item active state changes
6338 * @param {Roo.bootstrap.NavItem} this
6339 * @param {boolean} state the new state
6345 * Fires when scroll to element
6346 * @param {Roo.bootstrap.NavItem} this
6347 * @param {Object} options
6348 * @param {Roo.EventObject} e
6356 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6365 preventDefault : false,
6373 button_outline : false,
6377 getAutoCreate : function(){
6384 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6387 cfg.cls += ' active' ;
6389 if (this.disabled) {
6390 cfg.cls += ' disabled';
6394 if (this.button_weight.length) {
6395 cfg.tag = this.href ? 'a' : 'button';
6396 cfg.html = this.html || '';
6397 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6399 cfg.href = this.href;
6402 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6404 cfg.cls += " nav-html";
6407 // menu .. should add dropdown-menu class - so no need for carat..
6409 if (this.badge !== '') {
6411 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6416 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6420 href : this.href || "#",
6421 html: this.html || '',
6425 if (this.tagtype == 'a') {
6426 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6430 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6431 } else if (this.fa) {
6432 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6433 } else if(this.glyphicon) {
6434 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6436 cfg.cn[0].cls += " nav-html";
6440 cfg.cn[0].html += " <span class='caret'></span>";
6444 if (this.badge !== '') {
6445 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6453 onRender : function(ct, position)
6455 // Roo.log("Call onRender: " + this.xtype);
6456 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6460 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6461 this.navLink = this.el.select('.nav-link',true).first();
6462 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6467 initEvents: function()
6469 if (typeof (this.menu) != 'undefined') {
6470 this.menu.parentType = this.xtype;
6471 this.menu.triggerEl = this.el;
6472 this.menu = this.addxtype(Roo.apply({}, this.menu));
6475 this.el.on('click', this.onClick, this);
6477 //if(this.tagtype == 'span'){
6478 // this.el.select('span',true).on('click', this.onClick, this);
6481 // at this point parent should be available..
6482 this.parent().register(this);
6485 onClick : function(e)
6487 if (e.getTarget('.dropdown-menu-item')) {
6488 // did you click on a menu itemm.... - then don't trigger onclick..
6493 this.preventDefault ||
6496 Roo.log("NavItem - prevent Default?");
6500 if (this.disabled) {
6504 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6505 if (tg && tg.transition) {
6506 Roo.log("waiting for the transitionend");
6512 //Roo.log("fire event clicked");
6513 if(this.fireEvent('click', this, e) === false){
6517 if(this.tagtype == 'span'){
6521 //Roo.log(this.href);
6522 var ael = this.el.select('a',true).first();
6525 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6526 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6527 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6528 return; // ignore... - it's a 'hash' to another page.
6530 Roo.log("NavItem - prevent Default?");
6532 this.scrollToElement(e);
6536 var p = this.parent();
6538 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6539 if (typeof(p.setActiveItem) !== 'undefined') {
6540 p.setActiveItem(this);
6544 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6545 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6546 // remove the collapsed menu expand...
6547 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6551 isActive: function () {
6554 setActive : function(state, fire, is_was_active)
6556 if (this.active && !state && this.navId) {
6557 this.was_active = true;
6558 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6560 nv.clearWasActive(this);
6564 this.active = state;
6567 this.el.removeClass('active');
6568 this.navLink ? this.navLink.removeClass('active') : false;
6569 } else if (!this.el.hasClass('active')) {
6571 this.el.addClass('active');
6572 if (Roo.bootstrap.version == 4 && this.navLink ) {
6573 this.navLink.addClass('active');
6578 this.fireEvent('changed', this, state);
6581 // show a panel if it's registered and related..
6583 if (!this.navId || !this.tabId || !state || is_was_active) {
6587 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6591 var pan = tg.getPanelByName(this.tabId);
6595 // if we can not flip to new panel - go back to old nav highlight..
6596 if (false == tg.showPanel(pan)) {
6597 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6599 var onav = nv.getWasActive();
6601 onav.setActive(true, false, true);
6610 // this should not be here...
6611 setDisabled : function(state)
6613 this.disabled = state;
6615 this.el.removeClass('disabled');
6616 } else if (!this.el.hasClass('disabled')) {
6617 this.el.addClass('disabled');
6623 * Fetch the element to display the tooltip on.
6624 * @return {Roo.Element} defaults to this.el
6626 tooltipEl : function()
6628 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6631 scrollToElement : function(e)
6633 var c = document.body;
6636 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6638 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6639 c = document.documentElement;
6642 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6648 var o = target.calcOffsetsTo(c);
6655 this.fireEvent('scrollto', this, options, e);
6657 Roo.get(c).scrollTo('top', options.value, true);
6662 * Set the HTML (text content) of the item
6663 * @param {string} html content for the nav item
6665 setHtml : function(html)
6668 this.htmlEl.dom.innerHTML = html;
6680 * <span> icon </span>
6681 * <span> text </span>
6682 * <span>badge </span>
6686 * @class Roo.bootstrap.NavSidebarItem
6687 * @extends Roo.bootstrap.NavItem
6688 * Bootstrap Navbar.NavSidebarItem class
6689 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6690 * {Boolean} open is the menu open
6691 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6692 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6693 * {String} buttonSize (sm|md|lg)the extra classes for the button
6694 * {Boolean} showArrow show arrow next to the text (default true)
6696 * Create a new Navbar Button
6697 * @param {Object} config The config object
6699 Roo.bootstrap.NavSidebarItem = function(config){
6700 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6705 * The raw click event for the entire grid.
6706 * @param {Roo.EventObject} e
6711 * Fires when the active item active state changes
6712 * @param {Roo.bootstrap.NavSidebarItem} this
6713 * @param {boolean} state the new state
6721 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6723 badgeWeight : 'default',
6729 buttonWeight : 'default',
6735 getAutoCreate : function(){
6740 href : this.href || '#',
6746 if(this.buttonView){
6749 href : this.href || '#',
6750 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6763 cfg.cls += ' active';
6766 if (this.disabled) {
6767 cfg.cls += ' disabled';
6770 cfg.cls += ' open x-open';
6773 if (this.glyphicon || this.icon) {
6774 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6775 a.cn.push({ tag : 'i', cls : c }) ;
6778 if(!this.buttonView){
6781 html : this.html || ''
6788 if (this.badge !== '') {
6789 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6795 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6798 a.cls += ' dropdown-toggle treeview' ;
6804 initEvents : function()
6806 if (typeof (this.menu) != 'undefined') {
6807 this.menu.parentType = this.xtype;
6808 this.menu.triggerEl = this.el;
6809 this.menu = this.addxtype(Roo.apply({}, this.menu));
6812 this.el.on('click', this.onClick, this);
6814 if(this.badge !== ''){
6815 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6820 onClick : function(e)
6827 if(this.preventDefault){
6831 this.fireEvent('click', this, e);
6834 disable : function()
6836 this.setDisabled(true);
6841 this.setDisabled(false);
6844 setDisabled : function(state)
6846 if(this.disabled == state){
6850 this.disabled = state;
6853 this.el.addClass('disabled');
6857 this.el.removeClass('disabled');
6862 setActive : function(state)
6864 if(this.active == state){
6868 this.active = state;
6871 this.el.addClass('active');
6875 this.el.removeClass('active');
6880 isActive: function ()
6885 setBadge : function(str)
6891 this.badgeEl.dom.innerHTML = str;
6906 Roo.namespace('Roo.bootstrap.breadcrumb');
6910 * @class Roo.bootstrap.breadcrumb.Nav
6911 * @extends Roo.bootstrap.Component
6912 * Bootstrap Breadcrumb Nav Class
6914 * @children Roo.bootstrap.breadcrumb.Item
6917 * Create a new breadcrumb.Nav
6918 * @param {Object} config The config object
6922 Roo.bootstrap.breadcrumb.Nav = function(config){
6923 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6928 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6930 getAutoCreate : function()
6947 initEvents: function()
6949 this.olEl = this.el.select('ol',true).first();
6951 getChildContainer : function()
6967 * @class Roo.bootstrap.breadcrumb.Nav
6968 * @extends Roo.bootstrap.Component
6969 * Bootstrap Breadcrumb Nav Class
6971 * @children Roo.bootstrap.breadcrumb.Component
6972 * @cfg {String} html the content of the link.
6973 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6974 * @cfg {Boolean} active is it active
6978 * Create a new breadcrumb.Nav
6979 * @param {Object} config The config object
6982 Roo.bootstrap.breadcrumb.Item = function(config){
6983 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6988 * The img click event for the img.
6989 * @param {Roo.EventObject} e
6996 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7001 getAutoCreate : function()
7006 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7008 if (this.href !== false) {
7015 cfg.html = this.html;
7021 initEvents: function()
7024 this.el.select('a', true).first().on('click',this.onClick, this)
7028 onClick : function(e)
7031 this.fireEvent('click',this, e);
7044 * @class Roo.bootstrap.Row
7045 * @extends Roo.bootstrap.Component
7046 * Bootstrap Row class (contains columns...)
7050 * @param {Object} config The config object
7053 Roo.bootstrap.Row = function(config){
7054 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7057 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7059 getAutoCreate : function(){
7078 * @class Roo.bootstrap.Pagination
7079 * @extends Roo.bootstrap.Component
7080 * Bootstrap Pagination class
7081 * @cfg {String} size xs | sm | md | lg
7082 * @cfg {Boolean} inverse false | true
7085 * Create a new Pagination
7086 * @param {Object} config The config object
7089 Roo.bootstrap.Pagination = function(config){
7090 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7093 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7099 getAutoCreate : function(){
7105 cfg.cls += ' inverse';
7111 cfg.cls += " " + this.cls;
7129 * @class Roo.bootstrap.PaginationItem
7130 * @extends Roo.bootstrap.Component
7131 * Bootstrap PaginationItem class
7132 * @cfg {String} html text
7133 * @cfg {String} href the link
7134 * @cfg {Boolean} preventDefault (true | false) default true
7135 * @cfg {Boolean} active (true | false) default false
7136 * @cfg {Boolean} disabled default false
7140 * Create a new PaginationItem
7141 * @param {Object} config The config object
7145 Roo.bootstrap.PaginationItem = function(config){
7146 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7151 * The raw click event for the entire grid.
7152 * @param {Roo.EventObject} e
7158 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7162 preventDefault: true,
7167 getAutoCreate : function(){
7173 href : this.href ? this.href : '#',
7174 html : this.html ? this.html : ''
7184 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7188 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7194 initEvents: function() {
7196 this.el.on('click', this.onClick, this);
7199 onClick : function(e)
7201 Roo.log('PaginationItem on click ');
7202 if(this.preventDefault){
7210 this.fireEvent('click', this, e);
7226 * @class Roo.bootstrap.Slider
7227 * @extends Roo.bootstrap.Component
7228 * Bootstrap Slider class
7231 * Create a new Slider
7232 * @param {Object} config The config object
7235 Roo.bootstrap.Slider = function(config){
7236 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7239 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7241 getAutoCreate : function(){
7245 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7249 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7261 * Ext JS Library 1.1.1
7262 * Copyright(c) 2006-2007, Ext JS, LLC.
7264 * Originally Released Under LGPL - original licence link has changed is not relivant.
7267 * <script type="text/javascript">
7272 * @class Roo.grid.ColumnModel
7273 * @extends Roo.util.Observable
7274 * This is the default implementation of a ColumnModel used by the Grid. It defines
7275 * the columns in the grid.
7278 var colModel = new Roo.grid.ColumnModel([
7279 {header: "Ticker", width: 60, sortable: true, locked: true},
7280 {header: "Company Name", width: 150, sortable: true},
7281 {header: "Market Cap.", width: 100, sortable: true},
7282 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7283 {header: "Employees", width: 100, sortable: true, resizable: false}
7288 * The config options listed for this class are options which may appear in each
7289 * individual column definition.
7290 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7292 * @param {Object} config An Array of column config objects. See this class's
7293 * config objects for details.
7295 Roo.grid.ColumnModel = function(config){
7297 * The config passed into the constructor
7299 this.config = config;
7302 // if no id, create one
7303 // if the column does not have a dataIndex mapping,
7304 // map it to the order it is in the config
7305 for(var i = 0, len = config.length; i < len; i++){
7307 if(typeof c.dataIndex == "undefined"){
7310 if(typeof c.renderer == "string"){
7311 c.renderer = Roo.util.Format[c.renderer];
7313 if(typeof c.id == "undefined"){
7316 if(c.editor && c.editor.xtype){
7317 c.editor = Roo.factory(c.editor, Roo.grid);
7319 if(c.editor && c.editor.isFormField){
7320 c.editor = new Roo.grid.GridEditor(c.editor);
7322 this.lookup[c.id] = c;
7326 * The width of columns which have no width specified (defaults to 100)
7329 this.defaultWidth = 100;
7332 * Default sortable of columns which have no sortable specified (defaults to false)
7335 this.defaultSortable = false;
7339 * @event widthchange
7340 * Fires when the width of a column changes.
7341 * @param {ColumnModel} this
7342 * @param {Number} columnIndex The column index
7343 * @param {Number} newWidth The new width
7345 "widthchange": true,
7347 * @event headerchange
7348 * Fires when the text of a header changes.
7349 * @param {ColumnModel} this
7350 * @param {Number} columnIndex The column index
7351 * @param {Number} newText The new header text
7353 "headerchange": true,
7355 * @event hiddenchange
7356 * Fires when a column is hidden or "unhidden".
7357 * @param {ColumnModel} this
7358 * @param {Number} columnIndex The column index
7359 * @param {Boolean} hidden true if hidden, false otherwise
7361 "hiddenchange": true,
7363 * @event columnmoved
7364 * Fires when a column is moved.
7365 * @param {ColumnModel} this
7366 * @param {Number} oldIndex
7367 * @param {Number} newIndex
7369 "columnmoved" : true,
7371 * @event columlockchange
7372 * Fires when a column's locked state is changed
7373 * @param {ColumnModel} this
7374 * @param {Number} colIndex
7375 * @param {Boolean} locked true if locked
7377 "columnlockchange" : true
7379 Roo.grid.ColumnModel.superclass.constructor.call(this);
7381 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7383 * @cfg {String} header The header text to display in the Grid view.
7386 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7387 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7388 * specified, the column's index is used as an index into the Record's data Array.
7391 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7392 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7395 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7396 * Defaults to the value of the {@link #defaultSortable} property.
7397 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7400 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7403 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7406 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7409 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7412 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7413 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7414 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7415 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7418 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7421 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7424 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7427 * @cfg {String} cursor (Optional)
7430 * @cfg {String} tooltip (Optional)
7433 * @cfg {Number} xs (Optional)
7436 * @cfg {Number} sm (Optional)
7439 * @cfg {Number} md (Optional)
7442 * @cfg {Number} lg (Optional)
7445 * Returns the id of the column at the specified index.
7446 * @param {Number} index The column index
7447 * @return {String} the id
7449 getColumnId : function(index){
7450 return this.config[index].id;
7454 * Returns the column for a specified id.
7455 * @param {String} id The column id
7456 * @return {Object} the column
7458 getColumnById : function(id){
7459 return this.lookup[id];
7464 * Returns the column for a specified dataIndex.
7465 * @param {String} dataIndex The column dataIndex
7466 * @return {Object|Boolean} the column or false if not found
7468 getColumnByDataIndex: function(dataIndex){
7469 var index = this.findColumnIndex(dataIndex);
7470 return index > -1 ? this.config[index] : false;
7474 * Returns the index for a specified column id.
7475 * @param {String} id The column id
7476 * @return {Number} the index, or -1 if not found
7478 getIndexById : function(id){
7479 for(var i = 0, len = this.config.length; i < len; i++){
7480 if(this.config[i].id == id){
7488 * Returns the index for a specified column dataIndex.
7489 * @param {String} dataIndex The column dataIndex
7490 * @return {Number} the index, or -1 if not found
7493 findColumnIndex : function(dataIndex){
7494 for(var i = 0, len = this.config.length; i < len; i++){
7495 if(this.config[i].dataIndex == dataIndex){
7503 moveColumn : function(oldIndex, newIndex){
7504 var c = this.config[oldIndex];
7505 this.config.splice(oldIndex, 1);
7506 this.config.splice(newIndex, 0, c);
7507 this.dataMap = null;
7508 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7511 isLocked : function(colIndex){
7512 return this.config[colIndex].locked === true;
7515 setLocked : function(colIndex, value, suppressEvent){
7516 if(this.isLocked(colIndex) == value){
7519 this.config[colIndex].locked = value;
7521 this.fireEvent("columnlockchange", this, colIndex, value);
7525 getTotalLockedWidth : function(){
7527 for(var i = 0; i < this.config.length; i++){
7528 if(this.isLocked(i) && !this.isHidden(i)){
7529 this.totalWidth += this.getColumnWidth(i);
7535 getLockedCount : function(){
7536 for(var i = 0, len = this.config.length; i < len; i++){
7537 if(!this.isLocked(i)){
7542 return this.config.length;
7546 * Returns the number of columns.
7549 getColumnCount : function(visibleOnly){
7550 if(visibleOnly === true){
7552 for(var i = 0, len = this.config.length; i < len; i++){
7553 if(!this.isHidden(i)){
7559 return this.config.length;
7563 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7564 * @param {Function} fn
7565 * @param {Object} scope (optional)
7566 * @return {Array} result
7568 getColumnsBy : function(fn, scope){
7570 for(var i = 0, len = this.config.length; i < len; i++){
7571 var c = this.config[i];
7572 if(fn.call(scope||this, c, i) === true){
7580 * Returns true if the specified column is sortable.
7581 * @param {Number} col The column index
7584 isSortable : function(col){
7585 if(typeof this.config[col].sortable == "undefined"){
7586 return this.defaultSortable;
7588 return this.config[col].sortable;
7592 * Returns the rendering (formatting) function defined for the column.
7593 * @param {Number} col The column index.
7594 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7596 getRenderer : function(col){
7597 if(!this.config[col].renderer){
7598 return Roo.grid.ColumnModel.defaultRenderer;
7600 return this.config[col].renderer;
7604 * Sets the rendering (formatting) function for a column.
7605 * @param {Number} col The column index
7606 * @param {Function} fn The function to use to process the cell's raw data
7607 * to return HTML markup for the grid view. The render function is called with
7608 * the following parameters:<ul>
7609 * <li>Data value.</li>
7610 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7611 * <li>css A CSS style string to apply to the table cell.</li>
7612 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7613 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7614 * <li>Row index</li>
7615 * <li>Column index</li>
7616 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7618 setRenderer : function(col, fn){
7619 this.config[col].renderer = fn;
7623 * Returns the width for the specified column.
7624 * @param {Number} col The column index
7627 getColumnWidth : function(col){
7628 return this.config[col].width * 1 || this.defaultWidth;
7632 * Sets the width for a column.
7633 * @param {Number} col The column index
7634 * @param {Number} width The new width
7636 setColumnWidth : function(col, width, suppressEvent){
7637 this.config[col].width = width;
7638 this.totalWidth = null;
7640 this.fireEvent("widthchange", this, col, width);
7645 * Returns the total width of all columns.
7646 * @param {Boolean} includeHidden True to include hidden column widths
7649 getTotalWidth : function(includeHidden){
7650 if(!this.totalWidth){
7651 this.totalWidth = 0;
7652 for(var i = 0, len = this.config.length; i < len; i++){
7653 if(includeHidden || !this.isHidden(i)){
7654 this.totalWidth += this.getColumnWidth(i);
7658 return this.totalWidth;
7662 * Returns the header for the specified column.
7663 * @param {Number} col The column index
7666 getColumnHeader : function(col){
7667 return this.config[col].header;
7671 * Sets the header for a column.
7672 * @param {Number} col The column index
7673 * @param {String} header The new header
7675 setColumnHeader : function(col, header){
7676 this.config[col].header = header;
7677 this.fireEvent("headerchange", this, col, header);
7681 * Returns the tooltip for the specified column.
7682 * @param {Number} col The column index
7685 getColumnTooltip : function(col){
7686 return this.config[col].tooltip;
7689 * Sets the tooltip for a column.
7690 * @param {Number} col The column index
7691 * @param {String} tooltip The new tooltip
7693 setColumnTooltip : function(col, tooltip){
7694 this.config[col].tooltip = tooltip;
7698 * Returns the dataIndex for the specified column.
7699 * @param {Number} col The column index
7702 getDataIndex : function(col){
7703 return this.config[col].dataIndex;
7707 * Sets the dataIndex for a column.
7708 * @param {Number} col The column index
7709 * @param {Number} dataIndex The new dataIndex
7711 setDataIndex : function(col, dataIndex){
7712 this.config[col].dataIndex = dataIndex;
7718 * Returns true if the cell is editable.
7719 * @param {Number} colIndex The column index
7720 * @param {Number} rowIndex The row index - this is nto actually used..?
7723 isCellEditable : function(colIndex, rowIndex){
7724 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7728 * Returns the editor defined for the cell/column.
7729 * return false or null to disable editing.
7730 * @param {Number} colIndex The column index
7731 * @param {Number} rowIndex The row index
7734 getCellEditor : function(colIndex, rowIndex){
7735 return this.config[colIndex].editor;
7739 * Sets if a column is editable.
7740 * @param {Number} col The column index
7741 * @param {Boolean} editable True if the column is editable
7743 setEditable : function(col, editable){
7744 this.config[col].editable = editable;
7749 * Returns true if the column is hidden.
7750 * @param {Number} colIndex The column index
7753 isHidden : function(colIndex){
7754 return this.config[colIndex].hidden;
7759 * Returns true if the column width cannot be changed
7761 isFixed : function(colIndex){
7762 return this.config[colIndex].fixed;
7766 * Returns true if the column can be resized
7769 isResizable : function(colIndex){
7770 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7773 * Sets if a column is hidden.
7774 * @param {Number} colIndex The column index
7775 * @param {Boolean} hidden True if the column is hidden
7777 setHidden : function(colIndex, hidden){
7778 this.config[colIndex].hidden = hidden;
7779 this.totalWidth = null;
7780 this.fireEvent("hiddenchange", this, colIndex, hidden);
7784 * Sets the editor for a column.
7785 * @param {Number} col The column index
7786 * @param {Object} editor The editor object
7788 setEditor : function(col, editor){
7789 this.config[col].editor = editor;
7793 Roo.grid.ColumnModel.defaultRenderer = function(value)
7795 if(typeof value == "object") {
7798 if(typeof value == "string" && value.length < 1){
7802 return String.format("{0}", value);
7805 // Alias for backwards compatibility
7806 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7809 * Ext JS Library 1.1.1
7810 * Copyright(c) 2006-2007, Ext JS, LLC.
7812 * Originally Released Under LGPL - original licence link has changed is not relivant.
7815 * <script type="text/javascript">
7819 * @class Roo.LoadMask
7820 * A simple utility class for generically masking elements while loading data. If the element being masked has
7821 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7822 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7823 * element's UpdateManager load indicator and will be destroyed after the initial load.
7825 * Create a new LoadMask
7826 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7827 * @param {Object} config The config object
7829 Roo.LoadMask = function(el, config){
7830 this.el = Roo.get(el);
7831 Roo.apply(this, config);
7833 this.store.on('beforeload', this.onBeforeLoad, this);
7834 this.store.on('load', this.onLoad, this);
7835 this.store.on('loadexception', this.onLoadException, this);
7836 this.removeMask = false;
7838 var um = this.el.getUpdateManager();
7839 um.showLoadIndicator = false; // disable the default indicator
7840 um.on('beforeupdate', this.onBeforeLoad, this);
7841 um.on('update', this.onLoad, this);
7842 um.on('failure', this.onLoad, this);
7843 this.removeMask = true;
7847 Roo.LoadMask.prototype = {
7849 * @cfg {Boolean} removeMask
7850 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7851 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7855 * The text to display in a centered loading message box (defaults to 'Loading...')
7859 * @cfg {String} msgCls
7860 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7862 msgCls : 'x-mask-loading',
7865 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7871 * Disables the mask to prevent it from being displayed
7873 disable : function(){
7874 this.disabled = true;
7878 * Enables the mask so that it can be displayed
7880 enable : function(){
7881 this.disabled = false;
7884 onLoadException : function()
7888 if (typeof(arguments[3]) != 'undefined') {
7889 Roo.MessageBox.alert("Error loading",arguments[3]);
7893 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7894 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7901 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7906 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7910 onBeforeLoad : function(){
7912 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7917 destroy : function(){
7919 this.store.un('beforeload', this.onBeforeLoad, this);
7920 this.store.un('load', this.onLoad, this);
7921 this.store.un('loadexception', this.onLoadException, this);
7923 var um = this.el.getUpdateManager();
7924 um.un('beforeupdate', this.onBeforeLoad, this);
7925 um.un('update', this.onLoad, this);
7926 um.un('failure', this.onLoad, this);
7937 * @class Roo.bootstrap.Table
7938 * @extends Roo.bootstrap.Component
7939 * Bootstrap Table class
7940 * @cfg {String} cls table class
7941 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7942 * @cfg {String} bgcolor Specifies the background color for a table
7943 * @cfg {Number} border Specifies whether the table cells should have borders or not
7944 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7945 * @cfg {Number} cellspacing Specifies the space between cells
7946 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7947 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7948 * @cfg {String} sortable Specifies that the table should be sortable
7949 * @cfg {String} summary Specifies a summary of the content of a table
7950 * @cfg {Number} width Specifies the width of a table
7951 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7953 * @cfg {boolean} striped Should the rows be alternative striped
7954 * @cfg {boolean} bordered Add borders to the table
7955 * @cfg {boolean} hover Add hover highlighting
7956 * @cfg {boolean} condensed Format condensed
7957 * @cfg {boolean} responsive Format condensed
7958 * @cfg {Boolean} loadMask (true|false) default false
7959 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7960 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7961 * @cfg {Boolean} rowSelection (true|false) default false
7962 * @cfg {Boolean} cellSelection (true|false) default false
7963 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7964 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7965 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7966 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7970 * Create a new Table
7971 * @param {Object} config The config object
7974 Roo.bootstrap.Table = function(config){
7975 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7980 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7981 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7982 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7983 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7985 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7987 this.sm.grid = this;
7988 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7989 this.sm = this.selModel;
7990 this.sm.xmodule = this.xmodule || false;
7993 if (this.cm && typeof(this.cm.config) == 'undefined') {
7994 this.colModel = new Roo.grid.ColumnModel(this.cm);
7995 this.cm = this.colModel;
7996 this.cm.xmodule = this.xmodule || false;
7999 this.store= Roo.factory(this.store, Roo.data);
8000 this.ds = this.store;
8001 this.ds.xmodule = this.xmodule || false;
8004 if (this.footer && this.store) {
8005 this.footer.dataSource = this.ds;
8006 this.footer = Roo.factory(this.footer);
8013 * Fires when a cell is clicked
8014 * @param {Roo.bootstrap.Table} this
8015 * @param {Roo.Element} el
8016 * @param {Number} rowIndex
8017 * @param {Number} columnIndex
8018 * @param {Roo.EventObject} e
8022 * @event celldblclick
8023 * Fires when a cell is double clicked
8024 * @param {Roo.bootstrap.Table} this
8025 * @param {Roo.Element} el
8026 * @param {Number} rowIndex
8027 * @param {Number} columnIndex
8028 * @param {Roo.EventObject} e
8030 "celldblclick" : true,
8033 * Fires when a row is clicked
8034 * @param {Roo.bootstrap.Table} this
8035 * @param {Roo.Element} el
8036 * @param {Number} rowIndex
8037 * @param {Roo.EventObject} e
8041 * @event rowdblclick
8042 * Fires when a row is double clicked
8043 * @param {Roo.bootstrap.Table} this
8044 * @param {Roo.Element} el
8045 * @param {Number} rowIndex
8046 * @param {Roo.EventObject} e
8048 "rowdblclick" : true,
8051 * Fires when a mouseover occur
8052 * @param {Roo.bootstrap.Table} this
8053 * @param {Roo.Element} el
8054 * @param {Number} rowIndex
8055 * @param {Number} columnIndex
8056 * @param {Roo.EventObject} e
8061 * Fires when a mouseout occur
8062 * @param {Roo.bootstrap.Table} this
8063 * @param {Roo.Element} el
8064 * @param {Number} rowIndex
8065 * @param {Number} columnIndex
8066 * @param {Roo.EventObject} e
8071 * Fires when a row is rendered, so you can change add a style to it.
8072 * @param {Roo.bootstrap.Table} this
8073 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8077 * @event rowsrendered
8078 * Fires when all the rows have been rendered
8079 * @param {Roo.bootstrap.Table} this
8081 'rowsrendered' : true,
8083 * @event contextmenu
8084 * The raw contextmenu event for the entire grid.
8085 * @param {Roo.EventObject} e
8087 "contextmenu" : true,
8089 * @event rowcontextmenu
8090 * Fires when a row is right clicked
8091 * @param {Roo.bootstrap.Table} this
8092 * @param {Number} rowIndex
8093 * @param {Roo.EventObject} e
8095 "rowcontextmenu" : true,
8097 * @event cellcontextmenu
8098 * Fires when a cell is right clicked
8099 * @param {Roo.bootstrap.Table} this
8100 * @param {Number} rowIndex
8101 * @param {Number} cellIndex
8102 * @param {Roo.EventObject} e
8104 "cellcontextmenu" : true,
8106 * @event headercontextmenu
8107 * Fires when a header is right clicked
8108 * @param {Roo.bootstrap.Table} this
8109 * @param {Number} columnIndex
8110 * @param {Roo.EventObject} e
8112 "headercontextmenu" : true
8116 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8142 rowSelection : false,
8143 cellSelection : false,
8146 // Roo.Element - the tbody
8148 // Roo.Element - thead element
8151 container: false, // used by gridpanel...
8157 auto_hide_footer : false,
8159 getAutoCreate : function()
8161 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8168 if (this.scrollBody) {
8169 cfg.cls += ' table-body-fixed';
8172 cfg.cls += ' table-striped';
8176 cfg.cls += ' table-hover';
8178 if (this.bordered) {
8179 cfg.cls += ' table-bordered';
8181 if (this.condensed) {
8182 cfg.cls += ' table-condensed';
8184 if (this.responsive) {
8185 cfg.cls += ' table-responsive';
8189 cfg.cls+= ' ' +this.cls;
8192 // this lot should be simplifed...
8205 ].forEach(function(k) {
8213 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8216 if(this.store || this.cm){
8217 if(this.headerShow){
8218 cfg.cn.push(this.renderHeader());
8221 cfg.cn.push(this.renderBody());
8223 if(this.footerShow){
8224 cfg.cn.push(this.renderFooter());
8226 // where does this come from?
8227 //cfg.cls+= ' TableGrid';
8230 return { cn : [ cfg ] };
8233 initEvents : function()
8235 if(!this.store || !this.cm){
8238 if (this.selModel) {
8239 this.selModel.initEvents();
8243 //Roo.log('initEvents with ds!!!!');
8245 this.mainBody = this.el.select('tbody', true).first();
8246 this.mainHead = this.el.select('thead', true).first();
8247 this.mainFoot = this.el.select('tfoot', true).first();
8253 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8254 e.on('click', _this.sort, _this);
8257 this.mainBody.on("click", this.onClick, this);
8258 this.mainBody.on("dblclick", this.onDblClick, this);
8260 // why is this done????? = it breaks dialogs??
8261 //this.parent().el.setStyle('position', 'relative');
8265 this.footer.parentId = this.id;
8266 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8269 this.el.select('tfoot tr td').first().addClass('hide');
8274 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8277 this.store.on('load', this.onLoad, this);
8278 this.store.on('beforeload', this.onBeforeLoad, this);
8279 this.store.on('update', this.onUpdate, this);
8280 this.store.on('add', this.onAdd, this);
8281 this.store.on("clear", this.clear, this);
8283 this.el.on("contextmenu", this.onContextMenu, this);
8285 this.mainBody.on('scroll', this.onBodyScroll, this);
8287 this.cm.on("headerchange", this.onHeaderChange, this);
8289 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8293 onContextMenu : function(e, t)
8295 this.processEvent("contextmenu", e);
8298 processEvent : function(name, e)
8300 if (name != 'touchstart' ) {
8301 this.fireEvent(name, e);
8304 var t = e.getTarget();
8306 var cell = Roo.get(t);
8312 if(cell.findParent('tfoot', false, true)){
8316 if(cell.findParent('thead', false, true)){
8318 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8319 cell = Roo.get(t).findParent('th', false, true);
8321 Roo.log("failed to find th in thead?");
8322 Roo.log(e.getTarget());
8327 var cellIndex = cell.dom.cellIndex;
8329 var ename = name == 'touchstart' ? 'click' : name;
8330 this.fireEvent("header" + ename, this, cellIndex, e);
8335 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8336 cell = Roo.get(t).findParent('td', false, true);
8338 Roo.log("failed to find th in tbody?");
8339 Roo.log(e.getTarget());
8344 var row = cell.findParent('tr', false, true);
8345 var cellIndex = cell.dom.cellIndex;
8346 var rowIndex = row.dom.rowIndex - 1;
8350 this.fireEvent("row" + name, this, rowIndex, e);
8354 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8360 onMouseover : function(e, el)
8362 var cell = Roo.get(el);
8368 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8369 cell = cell.findParent('td', false, true);
8372 var row = cell.findParent('tr', false, true);
8373 var cellIndex = cell.dom.cellIndex;
8374 var rowIndex = row.dom.rowIndex - 1; // start from 0
8376 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8380 onMouseout : 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('mouseout', this, cell, rowIndex, cellIndex, e);
8400 onClick : function(e, el)
8402 var cell = Roo.get(el);
8404 if(!cell || (!this.cellSelection && !this.rowSelection)){
8408 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8409 cell = cell.findParent('td', false, true);
8412 if(!cell || typeof(cell) == 'undefined'){
8416 var row = cell.findParent('tr', false, true);
8418 if(!row || typeof(row) == 'undefined'){
8422 var cellIndex = cell.dom.cellIndex;
8423 var rowIndex = this.getRowIndex(row);
8425 // why??? - should these not be based on SelectionModel?
8426 if(this.cellSelection){
8427 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8430 if(this.rowSelection){
8431 this.fireEvent('rowclick', this, row, rowIndex, e);
8437 onDblClick : function(e,el)
8439 var cell = Roo.get(el);
8441 if(!cell || (!this.cellSelection && !this.rowSelection)){
8445 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8446 cell = cell.findParent('td', false, true);
8449 if(!cell || typeof(cell) == 'undefined'){
8453 var row = cell.findParent('tr', false, true);
8455 if(!row || typeof(row) == 'undefined'){
8459 var cellIndex = cell.dom.cellIndex;
8460 var rowIndex = this.getRowIndex(row);
8462 if(this.cellSelection){
8463 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8466 if(this.rowSelection){
8467 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8471 sort : function(e,el)
8473 var col = Roo.get(el);
8475 if(!col.hasClass('sortable')){
8479 var sort = col.attr('sort');
8482 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8486 this.store.sortInfo = {field : sort, direction : dir};
8489 Roo.log("calling footer first");
8490 this.footer.onClick('first');
8493 this.store.load({ params : { start : 0 } });
8497 renderHeader : function()
8505 this.totalWidth = 0;
8507 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8509 var config = cm.config[i];
8513 cls : 'x-hcol-' + i,
8515 html: cm.getColumnHeader(i)
8520 if(typeof(config.sortable) != 'undefined' && config.sortable){
8522 c.html = '<i class="glyphicon"></i>' + c.html;
8525 // could use BS4 hidden-..-down
8527 if(typeof(config.lgHeader) != 'undefined'){
8528 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8531 if(typeof(config.mdHeader) != 'undefined'){
8532 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8535 if(typeof(config.smHeader) != 'undefined'){
8536 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8539 if(typeof(config.xsHeader) != 'undefined'){
8540 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8547 if(typeof(config.tooltip) != 'undefined'){
8548 c.tooltip = config.tooltip;
8551 if(typeof(config.colspan) != 'undefined'){
8552 c.colspan = config.colspan;
8555 if(typeof(config.hidden) != 'undefined' && config.hidden){
8556 c.style += ' display:none;';
8559 if(typeof(config.dataIndex) != 'undefined'){
8560 c.sort = config.dataIndex;
8565 if(typeof(config.align) != 'undefined' && config.align.length){
8566 c.style += ' text-align:' + config.align + ';';
8569 if(typeof(config.width) != 'undefined'){
8570 c.style += ' width:' + config.width + 'px;';
8571 this.totalWidth += config.width;
8573 this.totalWidth += 100; // assume minimum of 100 per column?
8576 if(typeof(config.cls) != 'undefined'){
8577 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8580 ['xs','sm','md','lg'].map(function(size){
8582 if(typeof(config[size]) == 'undefined'){
8586 if (!config[size]) { // 0 = hidden
8587 // BS 4 '0' is treated as hide that column and below.
8588 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8592 c.cls += ' col-' + size + '-' + config[size] + (
8593 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8605 renderBody : function()
8615 colspan : this.cm.getColumnCount()
8625 renderFooter : function()
8635 colspan : this.cm.getColumnCount()
8649 // Roo.log('ds onload');
8654 var ds = this.store;
8656 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8657 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8658 if (_this.store.sortInfo) {
8660 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8661 e.select('i', true).addClass(['glyphicon-arrow-up']);
8664 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8665 e.select('i', true).addClass(['glyphicon-arrow-down']);
8670 var tbody = this.mainBody;
8672 if(ds.getCount() > 0){
8673 ds.data.each(function(d,rowIndex){
8674 var row = this.renderRow(cm, ds, rowIndex);
8676 tbody.createChild(row);
8680 if(row.cellObjects.length){
8681 Roo.each(row.cellObjects, function(r){
8682 _this.renderCellObject(r);
8689 var tfoot = this.el.select('tfoot', true).first();
8691 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8693 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8695 var total = this.ds.getTotalCount();
8697 if(this.footer.pageSize < total){
8698 this.mainFoot.show();
8702 Roo.each(this.el.select('tbody td', true).elements, function(e){
8703 e.on('mouseover', _this.onMouseover, _this);
8706 Roo.each(this.el.select('tbody td', true).elements, function(e){
8707 e.on('mouseout', _this.onMouseout, _this);
8709 this.fireEvent('rowsrendered', this);
8715 onUpdate : function(ds,record)
8717 this.refreshRow(record);
8721 onRemove : function(ds, record, index, isUpdate){
8722 if(isUpdate !== true){
8723 this.fireEvent("beforerowremoved", this, index, record);
8725 var bt = this.mainBody.dom;
8727 var rows = this.el.select('tbody > tr', true).elements;
8729 if(typeof(rows[index]) != 'undefined'){
8730 bt.removeChild(rows[index].dom);
8733 // if(bt.rows[index]){
8734 // bt.removeChild(bt.rows[index]);
8737 if(isUpdate !== true){
8738 //this.stripeRows(index);
8739 //this.syncRowHeights(index, index);
8741 this.fireEvent("rowremoved", this, index, record);
8745 onAdd : function(ds, records, rowIndex)
8747 //Roo.log('on Add called');
8748 // - note this does not handle multiple adding very well..
8749 var bt = this.mainBody.dom;
8750 for (var i =0 ; i < records.length;i++) {
8751 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8752 //Roo.log(records[i]);
8753 //Roo.log(this.store.getAt(rowIndex+i));
8754 this.insertRow(this.store, rowIndex + i, false);
8761 refreshRow : function(record){
8762 var ds = this.store, index;
8763 if(typeof record == 'number'){
8765 record = ds.getAt(index);
8767 index = ds.indexOf(record);
8769 return; // should not happen - but seems to
8772 this.insertRow(ds, index, true);
8774 this.onRemove(ds, record, index+1, true);
8776 //this.syncRowHeights(index, index);
8778 this.fireEvent("rowupdated", this, index, record);
8781 insertRow : function(dm, rowIndex, isUpdate){
8784 this.fireEvent("beforerowsinserted", this, rowIndex);
8786 //var s = this.getScrollState();
8787 var row = this.renderRow(this.cm, this.store, rowIndex);
8788 // insert before rowIndex..
8789 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8793 if(row.cellObjects.length){
8794 Roo.each(row.cellObjects, function(r){
8795 _this.renderCellObject(r);
8800 this.fireEvent("rowsinserted", this, rowIndex);
8801 //this.syncRowHeights(firstRow, lastRow);
8802 //this.stripeRows(firstRow);
8809 getRowDom : function(rowIndex)
8811 var rows = this.el.select('tbody > tr', true).elements;
8813 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8816 // returns the object tree for a tr..
8819 renderRow : function(cm, ds, rowIndex)
8821 var d = ds.getAt(rowIndex);
8825 cls : 'x-row-' + rowIndex,
8829 var cellObjects = [];
8831 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8832 var config = cm.config[i];
8834 var renderer = cm.getRenderer(i);
8838 if(typeof(renderer) !== 'undefined'){
8839 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8841 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8842 // and are rendered into the cells after the row is rendered - using the id for the element.
8844 if(typeof(value) === 'object'){
8854 rowIndex : rowIndex,
8859 this.fireEvent('rowclass', this, rowcfg);
8863 cls : rowcfg.rowClass + ' x-col-' + i,
8865 html: (typeof(value) === 'object') ? '' : value
8872 if(typeof(config.colspan) != 'undefined'){
8873 td.colspan = config.colspan;
8876 if(typeof(config.hidden) != 'undefined' && config.hidden){
8877 td.style += ' display:none;';
8880 if(typeof(config.align) != 'undefined' && config.align.length){
8881 td.style += ' text-align:' + config.align + ';';
8883 if(typeof(config.valign) != 'undefined' && config.valign.length){
8884 td.style += ' vertical-align:' + config.valign + ';';
8887 if(typeof(config.width) != 'undefined'){
8888 td.style += ' width:' + config.width + 'px;';
8891 if(typeof(config.cursor) != 'undefined'){
8892 td.style += ' cursor:' + config.cursor + ';';
8895 if(typeof(config.cls) != 'undefined'){
8896 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8899 ['xs','sm','md','lg'].map(function(size){
8901 if(typeof(config[size]) == 'undefined'){
8907 if (!config[size]) { // 0 = hidden
8908 // BS 4 '0' is treated as hide that column and below.
8909 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8913 td.cls += ' col-' + size + '-' + config[size] + (
8914 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8924 row.cellObjects = cellObjects;
8932 onBeforeLoad : function()
8941 this.el.select('tbody', true).first().dom.innerHTML = '';
8944 * Show or hide a row.
8945 * @param {Number} rowIndex to show or hide
8946 * @param {Boolean} state hide
8948 setRowVisibility : function(rowIndex, state)
8950 var bt = this.mainBody.dom;
8952 var rows = this.el.select('tbody > tr', true).elements;
8954 if(typeof(rows[rowIndex]) == 'undefined'){
8957 rows[rowIndex].dom.style.display = state ? '' : 'none';
8961 getSelectionModel : function(){
8963 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8965 return this.selModel;
8968 * Render the Roo.bootstrap object from renderder
8970 renderCellObject : function(r)
8974 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8976 var t = r.cfg.render(r.container);
8979 Roo.each(r.cfg.cn, function(c){
8981 container: t.getChildContainer(),
8984 _this.renderCellObject(child);
8989 getRowIndex : function(row)
8993 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9004 * Returns the grid's underlying element = used by panel.Grid
9005 * @return {Element} The element
9007 getGridEl : function(){
9011 * Forces a resize - used by panel.Grid
9012 * @return {Element} The element
9014 autoSize : function()
9016 //var ctr = Roo.get(this.container.dom.parentElement);
9017 var ctr = Roo.get(this.el.dom);
9019 var thd = this.getGridEl().select('thead',true).first();
9020 var tbd = this.getGridEl().select('tbody', true).first();
9021 var tfd = this.getGridEl().select('tfoot', true).first();
9023 var cw = ctr.getWidth();
9024 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9028 tbd.setWidth(ctr.getWidth());
9029 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9030 // this needs fixing for various usage - currently only hydra job advers I think..
9032 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9034 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9037 cw = Math.max(cw, this.totalWidth);
9038 this.getGridEl().select('tbody tr',true).setWidth(cw);
9040 // resize 'expandable coloumn?
9042 return; // we doe not have a view in this design..
9045 onBodyScroll: function()
9047 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9049 this.mainHead.setStyle({
9050 'position' : 'relative',
9051 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9057 var scrollHeight = this.mainBody.dom.scrollHeight;
9059 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9061 var height = this.mainBody.getHeight();
9063 if(scrollHeight - height == scrollTop) {
9065 var total = this.ds.getTotalCount();
9067 if(this.footer.cursor + this.footer.pageSize < total){
9069 this.footer.ds.load({
9071 start : this.footer.cursor + this.footer.pageSize,
9072 limit : this.footer.pageSize
9082 onHeaderChange : function()
9084 var header = this.renderHeader();
9085 var table = this.el.select('table', true).first();
9087 this.mainHead.remove();
9088 this.mainHead = table.createChild(header, this.mainBody, false);
9091 onHiddenChange : function(colModel, colIndex, hidden)
9093 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9094 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9096 this.CSS.updateRule(thSelector, "display", "");
9097 this.CSS.updateRule(tdSelector, "display", "");
9100 this.CSS.updateRule(thSelector, "display", "none");
9101 this.CSS.updateRule(tdSelector, "display", "none");
9104 this.onHeaderChange();
9108 setColumnWidth: function(col_index, width)
9110 // width = "md-2 xs-2..."
9111 if(!this.colModel.config[col_index]) {
9115 var w = width.split(" ");
9117 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9119 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9122 for(var j = 0; j < w.length; j++) {
9128 var size_cls = w[j].split("-");
9130 if(!Number.isInteger(size_cls[1] * 1)) {
9134 if(!this.colModel.config[col_index][size_cls[0]]) {
9138 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9142 h_row[0].classList.replace(
9143 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9144 "col-"+size_cls[0]+"-"+size_cls[1]
9147 for(var i = 0; i < rows.length; i++) {
9149 var size_cls = w[j].split("-");
9151 if(!Number.isInteger(size_cls[1] * 1)) {
9155 if(!this.colModel.config[col_index][size_cls[0]]) {
9159 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9163 rows[i].classList.replace(
9164 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9165 "col-"+size_cls[0]+"-"+size_cls[1]
9169 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9184 * @class Roo.bootstrap.TableCell
9185 * @extends Roo.bootstrap.Component
9186 * Bootstrap TableCell class
9187 * @cfg {String} html cell contain text
9188 * @cfg {String} cls cell class
9189 * @cfg {String} tag cell tag (td|th) default td
9190 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9191 * @cfg {String} align Aligns the content in a cell
9192 * @cfg {String} axis Categorizes cells
9193 * @cfg {String} bgcolor Specifies the background color of a cell
9194 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9195 * @cfg {Number} colspan Specifies the number of columns a cell should span
9196 * @cfg {String} headers Specifies one or more header cells a cell is related to
9197 * @cfg {Number} height Sets the height of a cell
9198 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9199 * @cfg {Number} rowspan Sets the number of rows a cell should span
9200 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9201 * @cfg {String} valign Vertical aligns the content in a cell
9202 * @cfg {Number} width Specifies the width of a cell
9205 * Create a new TableCell
9206 * @param {Object} config The config object
9209 Roo.bootstrap.TableCell = function(config){
9210 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9213 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9233 getAutoCreate : function(){
9234 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9254 cfg.align=this.align
9260 cfg.bgcolor=this.bgcolor
9263 cfg.charoff=this.charoff
9266 cfg.colspan=this.colspan
9269 cfg.headers=this.headers
9272 cfg.height=this.height
9275 cfg.nowrap=this.nowrap
9278 cfg.rowspan=this.rowspan
9281 cfg.scope=this.scope
9284 cfg.valign=this.valign
9287 cfg.width=this.width
9306 * @class Roo.bootstrap.TableRow
9307 * @extends Roo.bootstrap.Component
9308 * Bootstrap TableRow class
9309 * @cfg {String} cls row class
9310 * @cfg {String} align Aligns the content in a table row
9311 * @cfg {String} bgcolor Specifies a background color for a table row
9312 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9313 * @cfg {String} valign Vertical aligns the content in a table row
9316 * Create a new TableRow
9317 * @param {Object} config The config object
9320 Roo.bootstrap.TableRow = function(config){
9321 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9324 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9332 getAutoCreate : function(){
9333 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9343 cfg.align = this.align;
9346 cfg.bgcolor = this.bgcolor;
9349 cfg.charoff = this.charoff;
9352 cfg.valign = this.valign;
9370 * @class Roo.bootstrap.TableBody
9371 * @extends Roo.bootstrap.Component
9372 * Bootstrap TableBody class
9373 * @cfg {String} cls element class
9374 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9375 * @cfg {String} align Aligns the content inside the element
9376 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9377 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9380 * Create a new TableBody
9381 * @param {Object} config The config object
9384 Roo.bootstrap.TableBody = function(config){
9385 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9388 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9396 getAutoCreate : function(){
9397 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9411 cfg.align = this.align;
9414 cfg.charoff = this.charoff;
9417 cfg.valign = this.valign;
9424 // initEvents : function()
9431 // this.store = Roo.factory(this.store, Roo.data);
9432 // this.store.on('load', this.onLoad, this);
9434 // this.store.load();
9438 // onLoad: function ()
9440 // this.fireEvent('load', this);
9450 * Ext JS Library 1.1.1
9451 * Copyright(c) 2006-2007, Ext JS, LLC.
9453 * Originally Released Under LGPL - original licence link has changed is not relivant.
9456 * <script type="text/javascript">
9459 // as we use this in bootstrap.
9460 Roo.namespace('Roo.form');
9462 * @class Roo.form.Action
9463 * Internal Class used to handle form actions
9465 * @param {Roo.form.BasicForm} el The form element or its id
9466 * @param {Object} config Configuration options
9471 // define the action interface
9472 Roo.form.Action = function(form, options){
9474 this.options = options || {};
9477 * Client Validation Failed
9480 Roo.form.Action.CLIENT_INVALID = 'client';
9482 * Server Validation Failed
9485 Roo.form.Action.SERVER_INVALID = 'server';
9487 * Connect to Server Failed
9490 Roo.form.Action.CONNECT_FAILURE = 'connect';
9492 * Reading Data from Server Failed
9495 Roo.form.Action.LOAD_FAILURE = 'load';
9497 Roo.form.Action.prototype = {
9499 failureType : undefined,
9500 response : undefined,
9504 run : function(options){
9509 success : function(response){
9514 handleResponse : function(response){
9518 // default connection failure
9519 failure : function(response){
9521 this.response = response;
9522 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9523 this.form.afterAction(this, false);
9526 processResponse : function(response){
9527 this.response = response;
9528 if(!response.responseText){
9531 this.result = this.handleResponse(response);
9535 // utility functions used internally
9536 getUrl : function(appendParams){
9537 var url = this.options.url || this.form.url || this.form.el.dom.action;
9539 var p = this.getParams();
9541 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9547 getMethod : function(){
9548 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9551 getParams : function(){
9552 var bp = this.form.baseParams;
9553 var p = this.options.params;
9555 if(typeof p == "object"){
9556 p = Roo.urlEncode(Roo.applyIf(p, bp));
9557 }else if(typeof p == 'string' && bp){
9558 p += '&' + Roo.urlEncode(bp);
9561 p = Roo.urlEncode(bp);
9566 createCallback : function(){
9568 success: this.success,
9569 failure: this.failure,
9571 timeout: (this.form.timeout*1000),
9572 upload: this.form.fileUpload ? this.success : undefined
9577 Roo.form.Action.Submit = function(form, options){
9578 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9581 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9584 haveProgress : false,
9585 uploadComplete : false,
9587 // uploadProgress indicator.
9588 uploadProgress : function()
9590 if (!this.form.progressUrl) {
9594 if (!this.haveProgress) {
9595 Roo.MessageBox.progress("Uploading", "Uploading");
9597 if (this.uploadComplete) {
9598 Roo.MessageBox.hide();
9602 this.haveProgress = true;
9604 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9606 var c = new Roo.data.Connection();
9608 url : this.form.progressUrl,
9613 success : function(req){
9614 //console.log(data);
9618 rdata = Roo.decode(req.responseText)
9620 Roo.log("Invalid data from server..");
9624 if (!rdata || !rdata.success) {
9626 Roo.MessageBox.alert(Roo.encode(rdata));
9629 var data = rdata.data;
9631 if (this.uploadComplete) {
9632 Roo.MessageBox.hide();
9637 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9638 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9641 this.uploadProgress.defer(2000,this);
9644 failure: function(data) {
9645 Roo.log('progress url failed ');
9656 // run get Values on the form, so it syncs any secondary forms.
9657 this.form.getValues();
9659 var o = this.options;
9660 var method = this.getMethod();
9661 var isPost = method == 'POST';
9662 if(o.clientValidation === false || this.form.isValid()){
9664 if (this.form.progressUrl) {
9665 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9666 (new Date() * 1) + '' + Math.random());
9671 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9672 form:this.form.el.dom,
9673 url:this.getUrl(!isPost),
9675 params:isPost ? this.getParams() : null,
9676 isUpload: this.form.fileUpload,
9677 formData : this.form.formData
9680 this.uploadProgress();
9682 }else if (o.clientValidation !== false){ // client validation failed
9683 this.failureType = Roo.form.Action.CLIENT_INVALID;
9684 this.form.afterAction(this, false);
9688 success : function(response)
9690 this.uploadComplete= true;
9691 if (this.haveProgress) {
9692 Roo.MessageBox.hide();
9696 var result = this.processResponse(response);
9697 if(result === true || result.success){
9698 this.form.afterAction(this, true);
9702 this.form.markInvalid(result.errors);
9703 this.failureType = Roo.form.Action.SERVER_INVALID;
9705 this.form.afterAction(this, false);
9707 failure : function(response)
9709 this.uploadComplete= true;
9710 if (this.haveProgress) {
9711 Roo.MessageBox.hide();
9714 this.response = response;
9715 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9716 this.form.afterAction(this, false);
9719 handleResponse : function(response){
9720 if(this.form.errorReader){
9721 var rs = this.form.errorReader.read(response);
9724 for(var i = 0, len = rs.records.length; i < len; i++) {
9725 var r = rs.records[i];
9729 if(errors.length < 1){
9733 success : rs.success,
9739 ret = Roo.decode(response.responseText);
9743 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9753 Roo.form.Action.Load = function(form, options){
9754 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9755 this.reader = this.form.reader;
9758 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9763 Roo.Ajax.request(Roo.apply(
9764 this.createCallback(), {
9765 method:this.getMethod(),
9766 url:this.getUrl(false),
9767 params:this.getParams()
9771 success : function(response){
9773 var result = this.processResponse(response);
9774 if(result === true || !result.success || !result.data){
9775 this.failureType = Roo.form.Action.LOAD_FAILURE;
9776 this.form.afterAction(this, false);
9779 this.form.clearInvalid();
9780 this.form.setValues(result.data);
9781 this.form.afterAction(this, true);
9784 handleResponse : function(response){
9785 if(this.form.reader){
9786 var rs = this.form.reader.read(response);
9787 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9789 success : rs.success,
9793 return Roo.decode(response.responseText);
9797 Roo.form.Action.ACTION_TYPES = {
9798 'load' : Roo.form.Action.Load,
9799 'submit' : Roo.form.Action.Submit
9808 * @class Roo.bootstrap.Form
9809 * @extends Roo.bootstrap.Component
9810 * Bootstrap Form class
9811 * @cfg {String} method GET | POST (default POST)
9812 * @cfg {String} labelAlign top | left (default top)
9813 * @cfg {String} align left | right - for navbars
9814 * @cfg {Boolean} loadMask load mask when submit (default true)
9819 * @param {Object} config The config object
9823 Roo.bootstrap.Form = function(config){
9825 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9827 Roo.bootstrap.Form.popover.apply();
9831 * @event clientvalidation
9832 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9833 * @param {Form} this
9834 * @param {Boolean} valid true if the form has passed client-side validation
9836 clientvalidation: true,
9838 * @event beforeaction
9839 * Fires before any action is performed. Return false to cancel the action.
9840 * @param {Form} this
9841 * @param {Action} action The action to be performed
9845 * @event actionfailed
9846 * Fires when an action fails.
9847 * @param {Form} this
9848 * @param {Action} action The action that failed
9850 actionfailed : true,
9852 * @event actioncomplete
9853 * Fires when an action is completed.
9854 * @param {Form} this
9855 * @param {Action} action The action that completed
9857 actioncomplete : true
9861 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9864 * @cfg {String} method
9865 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9870 * The URL to use for form actions if one isn't supplied in the action options.
9873 * @cfg {Boolean} fileUpload
9874 * Set to true if this form is a file upload.
9878 * @cfg {Object} baseParams
9879 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9883 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9887 * @cfg {Sting} align (left|right) for navbar forms
9892 activeAction : null,
9895 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9896 * element by passing it or its id or mask the form itself by passing in true.
9899 waitMsgTarget : false,
9904 * @cfg {Boolean} errorMask (true|false) default false
9909 * @cfg {Number} maskOffset Default 100
9914 * @cfg {Boolean} maskBody
9918 getAutoCreate : function(){
9922 method : this.method || 'POST',
9923 id : this.id || Roo.id(),
9926 if (this.parent().xtype.match(/^Nav/)) {
9927 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9931 if (this.labelAlign == 'left' ) {
9932 cfg.cls += ' form-horizontal';
9938 initEvents : function()
9940 this.el.on('submit', this.onSubmit, this);
9941 // this was added as random key presses on the form where triggering form submit.
9942 this.el.on('keypress', function(e) {
9943 if (e.getCharCode() != 13) {
9946 // we might need to allow it for textareas.. and some other items.
9947 // check e.getTarget().
9949 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9953 Roo.log("keypress blocked");
9961 onSubmit : function(e){
9966 * Returns true if client-side validation on the form is successful.
9969 isValid : function(){
9970 var items = this.getItems();
9974 items.each(function(f){
9980 Roo.log('invalid field: ' + f.name);
9984 if(!target && f.el.isVisible(true)){
9990 if(this.errorMask && !valid){
9991 Roo.bootstrap.Form.popover.mask(this, target);
9998 * Returns true if any fields in this form have changed since their original load.
10001 isDirty : function(){
10003 var items = this.getItems();
10004 items.each(function(f){
10014 * Performs a predefined action (submit or load) or custom actions you define on this form.
10015 * @param {String} actionName The name of the action type
10016 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10017 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10018 * accept other config options):
10020 Property Type Description
10021 ---------------- --------------- ----------------------------------------------------------------------------------
10022 url String The url for the action (defaults to the form's url)
10023 method String The form method to use (defaults to the form's method, or POST if not defined)
10024 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10025 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10026 validate the form on the client (defaults to false)
10028 * @return {BasicForm} this
10030 doAction : function(action, options){
10031 if(typeof action == 'string'){
10032 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10034 if(this.fireEvent('beforeaction', this, action) !== false){
10035 this.beforeAction(action);
10036 action.run.defer(100, action);
10042 beforeAction : function(action){
10043 var o = action.options;
10048 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10050 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10053 // not really supported yet.. ??
10055 //if(this.waitMsgTarget === true){
10056 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10057 //}else if(this.waitMsgTarget){
10058 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10059 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10061 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10067 afterAction : function(action, success){
10068 this.activeAction = null;
10069 var o = action.options;
10074 Roo.get(document.body).unmask();
10080 //if(this.waitMsgTarget === true){
10081 // this.el.unmask();
10082 //}else if(this.waitMsgTarget){
10083 // this.waitMsgTarget.unmask();
10085 // Roo.MessageBox.updateProgress(1);
10086 // Roo.MessageBox.hide();
10093 Roo.callback(o.success, o.scope, [this, action]);
10094 this.fireEvent('actioncomplete', this, action);
10098 // failure condition..
10099 // we have a scenario where updates need confirming.
10100 // eg. if a locking scenario exists..
10101 // we look for { errors : { needs_confirm : true }} in the response.
10103 (typeof(action.result) != 'undefined') &&
10104 (typeof(action.result.errors) != 'undefined') &&
10105 (typeof(action.result.errors.needs_confirm) != 'undefined')
10108 Roo.log("not supported yet");
10111 Roo.MessageBox.confirm(
10112 "Change requires confirmation",
10113 action.result.errorMsg,
10118 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10128 Roo.callback(o.failure, o.scope, [this, action]);
10129 // show an error message if no failed handler is set..
10130 if (!this.hasListener('actionfailed')) {
10131 Roo.log("need to add dialog support");
10133 Roo.MessageBox.alert("Error",
10134 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10135 action.result.errorMsg :
10136 "Saving Failed, please check your entries or try again"
10141 this.fireEvent('actionfailed', this, action);
10146 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10147 * @param {String} id The value to search for
10150 findField : function(id){
10151 var items = this.getItems();
10152 var field = items.get(id);
10154 items.each(function(f){
10155 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10162 return field || null;
10165 * Mark fields in this form invalid in bulk.
10166 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10167 * @return {BasicForm} this
10169 markInvalid : function(errors){
10170 if(errors instanceof Array){
10171 for(var i = 0, len = errors.length; i < len; i++){
10172 var fieldError = errors[i];
10173 var f = this.findField(fieldError.id);
10175 f.markInvalid(fieldError.msg);
10181 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10182 field.markInvalid(errors[id]);
10186 //Roo.each(this.childForms || [], function (f) {
10187 // f.markInvalid(errors);
10194 * Set values for fields in this form in bulk.
10195 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10196 * @return {BasicForm} this
10198 setValues : function(values){
10199 if(values instanceof Array){ // array of objects
10200 for(var i = 0, len = values.length; i < len; i++){
10202 var f = this.findField(v.id);
10204 f.setValue(v.value);
10205 if(this.trackResetOnLoad){
10206 f.originalValue = f.getValue();
10210 }else{ // object hash
10213 if(typeof values[id] != 'function' && (field = this.findField(id))){
10215 if (field.setFromData &&
10216 field.valueField &&
10217 field.displayField &&
10218 // combos' with local stores can
10219 // be queried via setValue()
10220 // to set their value..
10221 (field.store && !field.store.isLocal)
10225 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10226 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10227 field.setFromData(sd);
10229 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10231 field.setFromData(values);
10234 field.setValue(values[id]);
10238 if(this.trackResetOnLoad){
10239 field.originalValue = field.getValue();
10245 //Roo.each(this.childForms || [], function (f) {
10246 // f.setValues(values);
10253 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10254 * they are returned as an array.
10255 * @param {Boolean} asString
10258 getValues : function(asString){
10259 //if (this.childForms) {
10260 // copy values from the child forms
10261 // Roo.each(this.childForms, function (f) {
10262 // this.setValues(f.getValues());
10268 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10269 if(asString === true){
10272 return Roo.urlDecode(fs);
10276 * Returns the fields in this form as an object with key/value pairs.
10277 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10280 getFieldValues : function(with_hidden)
10282 var items = this.getItems();
10284 items.each(function(f){
10286 if (!f.getName()) {
10290 var v = f.getValue();
10292 if (f.inputType =='radio') {
10293 if (typeof(ret[f.getName()]) == 'undefined') {
10294 ret[f.getName()] = ''; // empty..
10297 if (!f.el.dom.checked) {
10301 v = f.el.dom.value;
10305 if(f.xtype == 'MoneyField'){
10306 ret[f.currencyName] = f.getCurrency();
10309 // not sure if this supported any more..
10310 if ((typeof(v) == 'object') && f.getRawValue) {
10311 v = f.getRawValue() ; // dates..
10313 // combo boxes where name != hiddenName...
10314 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10315 ret[f.name] = f.getRawValue();
10317 ret[f.getName()] = v;
10324 * Clears all invalid messages in this form.
10325 * @return {BasicForm} this
10327 clearInvalid : function(){
10328 var items = this.getItems();
10330 items.each(function(f){
10338 * Resets this form.
10339 * @return {BasicForm} this
10341 reset : function(){
10342 var items = this.getItems();
10343 items.each(function(f){
10347 Roo.each(this.childForms || [], function (f) {
10355 getItems : function()
10357 var r=new Roo.util.MixedCollection(false, function(o){
10358 return o.id || (o.id = Roo.id());
10360 var iter = function(el) {
10367 Roo.each(el.items,function(e) {
10376 hideFields : function(items)
10378 Roo.each(items, function(i){
10380 var f = this.findField(i);
10391 showFields : function(items)
10393 Roo.each(items, function(i){
10395 var f = this.findField(i);
10408 Roo.apply(Roo.bootstrap.Form, {
10424 intervalID : false,
10430 if(this.isApplied){
10435 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10436 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10437 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10438 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10441 this.maskEl.top.enableDisplayMode("block");
10442 this.maskEl.left.enableDisplayMode("block");
10443 this.maskEl.bottom.enableDisplayMode("block");
10444 this.maskEl.right.enableDisplayMode("block");
10446 this.toolTip = new Roo.bootstrap.Tooltip({
10447 cls : 'roo-form-error-popover',
10449 'left' : ['r-l', [-2,0], 'right'],
10450 'right' : ['l-r', [2,0], 'left'],
10451 'bottom' : ['tl-bl', [0,2], 'top'],
10452 'top' : [ 'bl-tl', [0,-2], 'bottom']
10456 this.toolTip.render(Roo.get(document.body));
10458 this.toolTip.el.enableDisplayMode("block");
10460 Roo.get(document.body).on('click', function(){
10464 Roo.get(document.body).on('touchstart', function(){
10468 this.isApplied = true
10471 mask : function(form, target)
10475 this.target = target;
10477 if(!this.form.errorMask || !target.el){
10481 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10483 Roo.log(scrollable);
10485 var ot = this.target.el.calcOffsetsTo(scrollable);
10487 var scrollTo = ot[1] - this.form.maskOffset;
10489 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10491 scrollable.scrollTo('top', scrollTo);
10493 var box = this.target.el.getBox();
10495 var zIndex = Roo.bootstrap.Modal.zIndex++;
10498 this.maskEl.top.setStyle('position', 'absolute');
10499 this.maskEl.top.setStyle('z-index', zIndex);
10500 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10501 this.maskEl.top.setLeft(0);
10502 this.maskEl.top.setTop(0);
10503 this.maskEl.top.show();
10505 this.maskEl.left.setStyle('position', 'absolute');
10506 this.maskEl.left.setStyle('z-index', zIndex);
10507 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10508 this.maskEl.left.setLeft(0);
10509 this.maskEl.left.setTop(box.y - this.padding);
10510 this.maskEl.left.show();
10512 this.maskEl.bottom.setStyle('position', 'absolute');
10513 this.maskEl.bottom.setStyle('z-index', zIndex);
10514 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10515 this.maskEl.bottom.setLeft(0);
10516 this.maskEl.bottom.setTop(box.bottom + this.padding);
10517 this.maskEl.bottom.show();
10519 this.maskEl.right.setStyle('position', 'absolute');
10520 this.maskEl.right.setStyle('z-index', zIndex);
10521 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10522 this.maskEl.right.setLeft(box.right + this.padding);
10523 this.maskEl.right.setTop(box.y - this.padding);
10524 this.maskEl.right.show();
10526 this.toolTip.bindEl = this.target.el;
10528 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10530 var tip = this.target.blankText;
10532 if(this.target.getValue() !== '' ) {
10534 if (this.target.invalidText.length) {
10535 tip = this.target.invalidText;
10536 } else if (this.target.regexText.length){
10537 tip = this.target.regexText;
10541 this.toolTip.show(tip);
10543 this.intervalID = window.setInterval(function() {
10544 Roo.bootstrap.Form.popover.unmask();
10547 window.onwheel = function(){ return false;};
10549 (function(){ this.isMasked = true; }).defer(500, this);
10553 unmask : function()
10555 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10559 this.maskEl.top.setStyle('position', 'absolute');
10560 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10561 this.maskEl.top.hide();
10563 this.maskEl.left.setStyle('position', 'absolute');
10564 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10565 this.maskEl.left.hide();
10567 this.maskEl.bottom.setStyle('position', 'absolute');
10568 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10569 this.maskEl.bottom.hide();
10571 this.maskEl.right.setStyle('position', 'absolute');
10572 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10573 this.maskEl.right.hide();
10575 this.toolTip.hide();
10577 this.toolTip.el.hide();
10579 window.onwheel = function(){ return true;};
10581 if(this.intervalID){
10582 window.clearInterval(this.intervalID);
10583 this.intervalID = false;
10586 this.isMasked = false;
10596 * Ext JS Library 1.1.1
10597 * Copyright(c) 2006-2007, Ext JS, LLC.
10599 * Originally Released Under LGPL - original licence link has changed is not relivant.
10602 * <script type="text/javascript">
10605 * @class Roo.form.VTypes
10606 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10609 Roo.form.VTypes = function(){
10610 // closure these in so they are only created once.
10611 var alpha = /^[a-zA-Z_]+$/;
10612 var alphanum = /^[a-zA-Z0-9_]+$/;
10613 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10614 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10616 // All these messages and functions are configurable
10619 * The function used to validate email addresses
10620 * @param {String} value The email address
10622 'email' : function(v){
10623 return email.test(v);
10626 * The error text to display when the email validation function returns false
10629 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10631 * The keystroke filter mask to be applied on email input
10634 'emailMask' : /[a-z0-9_\.\-@]/i,
10637 * The function used to validate URLs
10638 * @param {String} value The URL
10640 'url' : function(v){
10641 return url.test(v);
10644 * The error text to display when the url validation function returns false
10647 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10650 * The function used to validate alpha values
10651 * @param {String} value The value
10653 'alpha' : function(v){
10654 return alpha.test(v);
10657 * The error text to display when the alpha validation function returns false
10660 'alphaText' : 'This field should only contain letters and _',
10662 * The keystroke filter mask to be applied on alpha input
10665 'alphaMask' : /[a-z_]/i,
10668 * The function used to validate alphanumeric values
10669 * @param {String} value The value
10671 'alphanum' : function(v){
10672 return alphanum.test(v);
10675 * The error text to display when the alphanumeric validation function returns false
10678 'alphanumText' : 'This field should only contain letters, numbers and _',
10680 * The keystroke filter mask to be applied on alphanumeric input
10683 'alphanumMask' : /[a-z0-9_]/i
10693 * @class Roo.bootstrap.Input
10694 * @extends Roo.bootstrap.Component
10695 * Bootstrap Input class
10696 * @cfg {Boolean} disabled is it disabled
10697 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10698 * @cfg {String} name name of the input
10699 * @cfg {string} fieldLabel - the label associated
10700 * @cfg {string} placeholder - placeholder to put in text.
10701 * @cfg {string} before - input group add on before
10702 * @cfg {string} after - input group add on after
10703 * @cfg {string} size - (lg|sm) or leave empty..
10704 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10705 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10706 * @cfg {Number} md colspan out of 12 for computer-sized screens
10707 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10708 * @cfg {string} value default value of the input
10709 * @cfg {Number} labelWidth set the width of label
10710 * @cfg {Number} labellg set the width of label (1-12)
10711 * @cfg {Number} labelmd set the width of label (1-12)
10712 * @cfg {Number} labelsm set the width of label (1-12)
10713 * @cfg {Number} labelxs set the width of label (1-12)
10714 * @cfg {String} labelAlign (top|left)
10715 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10716 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10717 * @cfg {String} indicatorpos (left|right) default left
10718 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10719 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10720 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10722 * @cfg {String} align (left|center|right) Default left
10723 * @cfg {Boolean} forceFeedback (true|false) Default false
10726 * Create a new Input
10727 * @param {Object} config The config object
10730 Roo.bootstrap.Input = function(config){
10732 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10737 * Fires when this field receives input focus.
10738 * @param {Roo.form.Field} this
10743 * Fires when this field loses input focus.
10744 * @param {Roo.form.Field} this
10748 * @event specialkey
10749 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10750 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10751 * @param {Roo.form.Field} this
10752 * @param {Roo.EventObject} e The event object
10757 * Fires just before the field blurs if the field value has changed.
10758 * @param {Roo.form.Field} this
10759 * @param {Mixed} newValue The new value
10760 * @param {Mixed} oldValue The original value
10765 * Fires after the field has been marked as invalid.
10766 * @param {Roo.form.Field} this
10767 * @param {String} msg The validation message
10772 * Fires after the field has been validated with no errors.
10773 * @param {Roo.form.Field} this
10778 * Fires after the key up
10779 * @param {Roo.form.Field} this
10780 * @param {Roo.EventObject} e The event Object
10785 * Fires after the user pastes into input
10786 * @param {Roo.form.Field} this
10787 * @param {Roo.EventObject} e The event Object
10793 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10795 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10796 automatic validation (defaults to "keyup").
10798 validationEvent : "keyup",
10800 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10802 validateOnBlur : true,
10804 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10806 validationDelay : 250,
10808 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10810 focusClass : "x-form-focus", // not needed???
10814 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10816 invalidClass : "has-warning",
10819 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10821 validClass : "has-success",
10824 * @cfg {Boolean} hasFeedback (true|false) default true
10826 hasFeedback : true,
10829 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10831 invalidFeedbackClass : "glyphicon-warning-sign",
10834 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10836 validFeedbackClass : "glyphicon-ok",
10839 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10841 selectOnFocus : false,
10844 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10848 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10853 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10855 disableKeyFilter : false,
10858 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10862 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10866 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10868 blankText : "Please complete this mandatory field",
10871 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10875 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10877 maxLength : Number.MAX_VALUE,
10879 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10881 minLengthText : "The minimum length for this field is {0}",
10883 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10885 maxLengthText : "The maximum length for this field is {0}",
10889 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10890 * If available, this function will be called only after the basic validators all return true, and will be passed the
10891 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10895 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10896 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10897 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10901 * @cfg {String} regexText -- Depricated - use Invalid Text
10906 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10912 autocomplete: false,
10916 inputType : 'text',
10919 placeholder: false,
10924 preventMark: false,
10925 isFormField : true,
10928 labelAlign : false,
10931 formatedValue : false,
10932 forceFeedback : false,
10934 indicatorpos : 'left',
10944 parentLabelAlign : function()
10947 while (parent.parent()) {
10948 parent = parent.parent();
10949 if (typeof(parent.labelAlign) !='undefined') {
10950 return parent.labelAlign;
10957 getAutoCreate : function()
10959 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10965 if(this.inputType != 'hidden'){
10966 cfg.cls = 'form-group' //input-group
10972 type : this.inputType,
10973 value : this.value,
10974 cls : 'form-control',
10975 placeholder : this.placeholder || '',
10976 autocomplete : this.autocomplete || 'new-password'
10978 if (this.inputType == 'file') {
10979 input.style = 'overflow:hidden'; // why not in CSS?
10982 if(this.capture.length){
10983 input.capture = this.capture;
10986 if(this.accept.length){
10987 input.accept = this.accept + "/*";
10991 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10994 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10995 input.maxLength = this.maxLength;
10998 if (this.disabled) {
10999 input.disabled=true;
11002 if (this.readOnly) {
11003 input.readonly=true;
11007 input.name = this.name;
11011 input.cls += ' input-' + this.size;
11015 ['xs','sm','md','lg'].map(function(size){
11016 if (settings[size]) {
11017 cfg.cls += ' col-' + size + '-' + settings[size];
11021 var inputblock = input;
11025 cls: 'glyphicon form-control-feedback'
11028 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11031 cls : 'has-feedback',
11039 if (this.before || this.after) {
11042 cls : 'input-group',
11046 if (this.before && typeof(this.before) == 'string') {
11048 inputblock.cn.push({
11050 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11054 if (this.before && typeof(this.before) == 'object') {
11055 this.before = Roo.factory(this.before);
11057 inputblock.cn.push({
11059 cls : 'roo-input-before input-group-prepend input-group-' +
11060 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11064 inputblock.cn.push(input);
11066 if (this.after && typeof(this.after) == 'string') {
11067 inputblock.cn.push({
11069 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11073 if (this.after && typeof(this.after) == 'object') {
11074 this.after = Roo.factory(this.after);
11076 inputblock.cn.push({
11078 cls : 'roo-input-after input-group-append input-group-' +
11079 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11083 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11084 inputblock.cls += ' has-feedback';
11085 inputblock.cn.push(feedback);
11090 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11091 tooltip : 'This field is required'
11093 if (this.allowBlank ) {
11094 indicator.style = this.allowBlank ? ' display:none' : '';
11096 if (align ==='left' && this.fieldLabel.length) {
11098 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11105 cls : 'control-label col-form-label',
11106 html : this.fieldLabel
11117 var labelCfg = cfg.cn[1];
11118 var contentCfg = cfg.cn[2];
11120 if(this.indicatorpos == 'right'){
11125 cls : 'control-label col-form-label',
11129 html : this.fieldLabel
11143 labelCfg = cfg.cn[0];
11144 contentCfg = cfg.cn[1];
11148 if(this.labelWidth > 12){
11149 labelCfg.style = "width: " + this.labelWidth + 'px';
11152 if(this.labelWidth < 13 && this.labelmd == 0){
11153 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11156 if(this.labellg > 0){
11157 labelCfg.cls += ' col-lg-' + this.labellg;
11158 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11161 if(this.labelmd > 0){
11162 labelCfg.cls += ' col-md-' + this.labelmd;
11163 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11166 if(this.labelsm > 0){
11167 labelCfg.cls += ' col-sm-' + this.labelsm;
11168 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11171 if(this.labelxs > 0){
11172 labelCfg.cls += ' col-xs-' + this.labelxs;
11173 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11177 } else if ( this.fieldLabel.length) {
11184 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11185 tooltip : 'This field is required',
11186 style : this.allowBlank ? ' display:none' : ''
11190 //cls : 'input-group-addon',
11191 html : this.fieldLabel
11199 if(this.indicatorpos == 'right'){
11204 //cls : 'input-group-addon',
11205 html : this.fieldLabel
11210 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11211 tooltip : 'This field is required',
11212 style : this.allowBlank ? ' display:none' : ''
11232 if (this.parentType === 'Navbar' && this.parent().bar) {
11233 cfg.cls += ' navbar-form';
11236 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11237 // on BS4 we do this only if not form
11238 cfg.cls += ' navbar-form';
11246 * return the real input element.
11248 inputEl: function ()
11250 return this.el.select('input.form-control',true).first();
11253 tooltipEl : function()
11255 return this.inputEl();
11258 indicatorEl : function()
11260 if (Roo.bootstrap.version == 4) {
11261 return false; // not enabled in v4 yet.
11264 var indicator = this.el.select('i.roo-required-indicator',true).first();
11274 setDisabled : function(v)
11276 var i = this.inputEl().dom;
11278 i.removeAttribute('disabled');
11282 i.setAttribute('disabled','true');
11284 initEvents : function()
11287 this.inputEl().on("keydown" , this.fireKey, this);
11288 this.inputEl().on("focus", this.onFocus, this);
11289 this.inputEl().on("blur", this.onBlur, this);
11291 this.inputEl().relayEvent('keyup', this);
11292 this.inputEl().relayEvent('paste', this);
11294 this.indicator = this.indicatorEl();
11296 if(this.indicator){
11297 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11300 // reference to original value for reset
11301 this.originalValue = this.getValue();
11302 //Roo.form.TextField.superclass.initEvents.call(this);
11303 if(this.validationEvent == 'keyup'){
11304 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11305 this.inputEl().on('keyup', this.filterValidation, this);
11307 else if(this.validationEvent !== false){
11308 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11311 if(this.selectOnFocus){
11312 this.on("focus", this.preFocus, this);
11315 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11316 this.inputEl().on("keypress", this.filterKeys, this);
11318 this.inputEl().relayEvent('keypress', this);
11321 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11322 this.el.on("click", this.autoSize, this);
11325 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11326 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11329 if (typeof(this.before) == 'object') {
11330 this.before.render(this.el.select('.roo-input-before',true).first());
11332 if (typeof(this.after) == 'object') {
11333 this.after.render(this.el.select('.roo-input-after',true).first());
11336 this.inputEl().on('change', this.onChange, this);
11339 filterValidation : function(e){
11340 if(!e.isNavKeyPress()){
11341 this.validationTask.delay(this.validationDelay);
11345 * Validates the field value
11346 * @return {Boolean} True if the value is valid, else false
11348 validate : function(){
11349 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11350 if(this.disabled || this.validateValue(this.getRawValue())){
11355 this.markInvalid();
11361 * Validates a value according to the field's validation rules and marks the field as invalid
11362 * if the validation fails
11363 * @param {Mixed} value The value to validate
11364 * @return {Boolean} True if the value is valid, else false
11366 validateValue : function(value)
11368 if(this.getVisibilityEl().hasClass('hidden')){
11372 if(value.length < 1) { // if it's blank
11373 if(this.allowBlank){
11379 if(value.length < this.minLength){
11382 if(value.length > this.maxLength){
11386 var vt = Roo.form.VTypes;
11387 if(!vt[this.vtype](value, this)){
11391 if(typeof this.validator == "function"){
11392 var msg = this.validator(value);
11396 if (typeof(msg) == 'string') {
11397 this.invalidText = msg;
11401 if(this.regex && !this.regex.test(value)){
11409 fireKey : function(e){
11410 //Roo.log('field ' + e.getKey());
11411 if(e.isNavKeyPress()){
11412 this.fireEvent("specialkey", this, e);
11415 focus : function (selectText){
11417 this.inputEl().focus();
11418 if(selectText === true){
11419 this.inputEl().dom.select();
11425 onFocus : function(){
11426 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11427 // this.el.addClass(this.focusClass);
11429 if(!this.hasFocus){
11430 this.hasFocus = true;
11431 this.startValue = this.getValue();
11432 this.fireEvent("focus", this);
11436 beforeBlur : Roo.emptyFn,
11440 onBlur : function(){
11442 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11443 //this.el.removeClass(this.focusClass);
11445 this.hasFocus = false;
11446 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11449 var v = this.getValue();
11450 if(String(v) !== String(this.startValue)){
11451 this.fireEvent('change', this, v, this.startValue);
11453 this.fireEvent("blur", this);
11456 onChange : function(e)
11458 var v = this.getValue();
11459 if(String(v) !== String(this.startValue)){
11460 this.fireEvent('change', this, v, this.startValue);
11466 * Resets the current field value to the originally loaded value and clears any validation messages
11468 reset : function(){
11469 this.setValue(this.originalValue);
11473 * Returns the name of the field
11474 * @return {Mixed} name The name field
11476 getName: function(){
11480 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11481 * @return {Mixed} value The field value
11483 getValue : function(){
11485 var v = this.inputEl().getValue();
11490 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11491 * @return {Mixed} value The field value
11493 getRawValue : function(){
11494 var v = this.inputEl().getValue();
11500 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11501 * @param {Mixed} value The value to set
11503 setRawValue : function(v){
11504 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11507 selectText : function(start, end){
11508 var v = this.getRawValue();
11510 start = start === undefined ? 0 : start;
11511 end = end === undefined ? v.length : end;
11512 var d = this.inputEl().dom;
11513 if(d.setSelectionRange){
11514 d.setSelectionRange(start, end);
11515 }else if(d.createTextRange){
11516 var range = d.createTextRange();
11517 range.moveStart("character", start);
11518 range.moveEnd("character", v.length-end);
11525 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11526 * @param {Mixed} value The value to set
11528 setValue : function(v){
11531 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11537 processValue : function(value){
11538 if(this.stripCharsRe){
11539 var newValue = value.replace(this.stripCharsRe, '');
11540 if(newValue !== value){
11541 this.setRawValue(newValue);
11548 preFocus : function(){
11550 if(this.selectOnFocus){
11551 this.inputEl().dom.select();
11554 filterKeys : function(e){
11555 var k = e.getKey();
11556 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11559 var c = e.getCharCode(), cc = String.fromCharCode(c);
11560 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11563 if(!this.maskRe.test(cc)){
11568 * Clear any invalid styles/messages for this field
11570 clearInvalid : function(){
11572 if(!this.el || this.preventMark){ // not rendered
11577 this.el.removeClass([this.invalidClass, 'is-invalid']);
11579 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11581 var feedback = this.el.select('.form-control-feedback', true).first();
11584 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11589 if(this.indicator){
11590 this.indicator.removeClass('visible');
11591 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11594 this.fireEvent('valid', this);
11598 * Mark this field as valid
11600 markValid : function()
11602 if(!this.el || this.preventMark){ // not rendered...
11606 this.el.removeClass([this.invalidClass, this.validClass]);
11607 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11609 var feedback = this.el.select('.form-control-feedback', true).first();
11612 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11615 if(this.indicator){
11616 this.indicator.removeClass('visible');
11617 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11625 if(this.allowBlank && !this.getRawValue().length){
11628 if (Roo.bootstrap.version == 3) {
11629 this.el.addClass(this.validClass);
11631 this.inputEl().addClass('is-valid');
11634 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11636 var feedback = this.el.select('.form-control-feedback', true).first();
11639 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11640 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11645 this.fireEvent('valid', this);
11649 * Mark this field as invalid
11650 * @param {String} msg The validation message
11652 markInvalid : function(msg)
11654 if(!this.el || this.preventMark){ // not rendered
11658 this.el.removeClass([this.invalidClass, this.validClass]);
11659 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11661 var feedback = this.el.select('.form-control-feedback', true).first();
11664 this.el.select('.form-control-feedback', true).first().removeClass(
11665 [this.invalidFeedbackClass, this.validFeedbackClass]);
11672 if(this.allowBlank && !this.getRawValue().length){
11676 if(this.indicator){
11677 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11678 this.indicator.addClass('visible');
11680 if (Roo.bootstrap.version == 3) {
11681 this.el.addClass(this.invalidClass);
11683 this.inputEl().addClass('is-invalid');
11688 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11690 var feedback = this.el.select('.form-control-feedback', true).first();
11693 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11695 if(this.getValue().length || this.forceFeedback){
11696 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11703 this.fireEvent('invalid', this, msg);
11706 SafariOnKeyDown : function(event)
11708 // this is a workaround for a password hang bug on chrome/ webkit.
11709 if (this.inputEl().dom.type != 'password') {
11713 var isSelectAll = false;
11715 if(this.inputEl().dom.selectionEnd > 0){
11716 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11718 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11719 event.preventDefault();
11724 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11726 event.preventDefault();
11727 // this is very hacky as keydown always get's upper case.
11729 var cc = String.fromCharCode(event.getCharCode());
11730 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11734 adjustWidth : function(tag, w){
11735 tag = tag.toLowerCase();
11736 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11737 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11738 if(tag == 'input'){
11741 if(tag == 'textarea'){
11744 }else if(Roo.isOpera){
11745 if(tag == 'input'){
11748 if(tag == 'textarea'){
11756 setFieldLabel : function(v)
11758 if(!this.rendered){
11762 if(this.indicatorEl()){
11763 var ar = this.el.select('label > span',true);
11765 if (ar.elements.length) {
11766 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11767 this.fieldLabel = v;
11771 var br = this.el.select('label',true);
11773 if(br.elements.length) {
11774 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11775 this.fieldLabel = v;
11779 Roo.log('Cannot Found any of label > span || label in input');
11783 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11784 this.fieldLabel = v;
11799 * @class Roo.bootstrap.TextArea
11800 * @extends Roo.bootstrap.Input
11801 * Bootstrap TextArea class
11802 * @cfg {Number} cols Specifies the visible width of a text area
11803 * @cfg {Number} rows Specifies the visible number of lines in a text area
11804 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11805 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11806 * @cfg {string} html text
11809 * Create a new TextArea
11810 * @param {Object} config The config object
11813 Roo.bootstrap.TextArea = function(config){
11814 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11818 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11828 getAutoCreate : function(){
11830 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11836 if(this.inputType != 'hidden'){
11837 cfg.cls = 'form-group' //input-group
11845 value : this.value || '',
11846 html: this.html || '',
11847 cls : 'form-control',
11848 placeholder : this.placeholder || ''
11852 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11853 input.maxLength = this.maxLength;
11857 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11861 input.cols = this.cols;
11864 if (this.readOnly) {
11865 input.readonly = true;
11869 input.name = this.name;
11873 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11877 ['xs','sm','md','lg'].map(function(size){
11878 if (settings[size]) {
11879 cfg.cls += ' col-' + size + '-' + settings[size];
11883 var inputblock = input;
11885 if(this.hasFeedback && !this.allowBlank){
11889 cls: 'glyphicon form-control-feedback'
11893 cls : 'has-feedback',
11902 if (this.before || this.after) {
11905 cls : 'input-group',
11909 inputblock.cn.push({
11911 cls : 'input-group-addon',
11916 inputblock.cn.push(input);
11918 if(this.hasFeedback && !this.allowBlank){
11919 inputblock.cls += ' has-feedback';
11920 inputblock.cn.push(feedback);
11924 inputblock.cn.push({
11926 cls : 'input-group-addon',
11933 if (align ==='left' && this.fieldLabel.length) {
11938 cls : 'control-label',
11939 html : this.fieldLabel
11950 if(this.labelWidth > 12){
11951 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11954 if(this.labelWidth < 13 && this.labelmd == 0){
11955 this.labelmd = this.labelWidth;
11958 if(this.labellg > 0){
11959 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11960 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11963 if(this.labelmd > 0){
11964 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11965 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11968 if(this.labelsm > 0){
11969 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11970 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11973 if(this.labelxs > 0){
11974 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11975 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11978 } else if ( this.fieldLabel.length) {
11983 //cls : 'input-group-addon',
11984 html : this.fieldLabel
12002 if (this.disabled) {
12003 input.disabled=true;
12010 * return the real textarea element.
12012 inputEl: function ()
12014 return this.el.select('textarea.form-control',true).first();
12018 * Clear any invalid styles/messages for this field
12020 clearInvalid : function()
12023 if(!this.el || this.preventMark){ // not rendered
12027 var label = this.el.select('label', true).first();
12028 var icon = this.el.select('i.fa-star', true).first();
12033 this.el.removeClass( this.validClass);
12034 this.inputEl().removeClass('is-invalid');
12036 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12038 var feedback = this.el.select('.form-control-feedback', true).first();
12041 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12046 this.fireEvent('valid', this);
12050 * Mark this field as valid
12052 markValid : function()
12054 if(!this.el || this.preventMark){ // not rendered
12058 this.el.removeClass([this.invalidClass, this.validClass]);
12059 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12061 var feedback = this.el.select('.form-control-feedback', true).first();
12064 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12067 if(this.disabled || this.allowBlank){
12071 var label = this.el.select('label', true).first();
12072 var icon = this.el.select('i.fa-star', true).first();
12077 if (Roo.bootstrap.version == 3) {
12078 this.el.addClass(this.validClass);
12080 this.inputEl().addClass('is-valid');
12084 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12086 var feedback = this.el.select('.form-control-feedback', true).first();
12089 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12090 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12095 this.fireEvent('valid', this);
12099 * Mark this field as invalid
12100 * @param {String} msg The validation message
12102 markInvalid : function(msg)
12104 if(!this.el || this.preventMark){ // not rendered
12108 this.el.removeClass([this.invalidClass, this.validClass]);
12109 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12111 var feedback = this.el.select('.form-control-feedback', true).first();
12114 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12117 if(this.disabled || this.allowBlank){
12121 var label = this.el.select('label', true).first();
12122 var icon = this.el.select('i.fa-star', true).first();
12124 if(!this.getValue().length && label && !icon){
12125 this.el.createChild({
12127 cls : 'text-danger fa fa-lg fa-star',
12128 tooltip : 'This field is required',
12129 style : 'margin-right:5px;'
12133 if (Roo.bootstrap.version == 3) {
12134 this.el.addClass(this.invalidClass);
12136 this.inputEl().addClass('is-invalid');
12139 // fixme ... this may be depricated need to test..
12140 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12142 var feedback = this.el.select('.form-control-feedback', true).first();
12145 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12147 if(this.getValue().length || this.forceFeedback){
12148 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12155 this.fireEvent('invalid', this, msg);
12163 * trigger field - base class for combo..
12168 * @class Roo.bootstrap.TriggerField
12169 * @extends Roo.bootstrap.Input
12170 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12171 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12172 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12173 * for which you can provide a custom implementation. For example:
12175 var trigger = new Roo.bootstrap.TriggerField();
12176 trigger.onTriggerClick = myTriggerFn;
12177 trigger.applyTo('my-field');
12180 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12181 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12182 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12183 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12184 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12187 * Create a new TriggerField.
12188 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12189 * to the base TextField)
12191 Roo.bootstrap.TriggerField = function(config){
12192 this.mimicing = false;
12193 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12196 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12198 * @cfg {String} triggerClass A CSS class to apply to the trigger
12201 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12206 * @cfg {Boolean} removable (true|false) special filter default false
12210 /** @cfg {Boolean} grow @hide */
12211 /** @cfg {Number} growMin @hide */
12212 /** @cfg {Number} growMax @hide */
12218 autoSize: Roo.emptyFn,
12222 deferHeight : true,
12225 actionMode : 'wrap',
12230 getAutoCreate : function(){
12232 var align = this.labelAlign || this.parentLabelAlign();
12237 cls: 'form-group' //input-group
12244 type : this.inputType,
12245 cls : 'form-control',
12246 autocomplete: 'new-password',
12247 placeholder : this.placeholder || ''
12251 input.name = this.name;
12254 input.cls += ' input-' + this.size;
12257 if (this.disabled) {
12258 input.disabled=true;
12261 var inputblock = input;
12263 if(this.hasFeedback && !this.allowBlank){
12267 cls: 'glyphicon form-control-feedback'
12270 if(this.removable && !this.editable ){
12272 cls : 'has-feedback',
12278 cls : 'roo-combo-removable-btn close'
12285 cls : 'has-feedback',
12294 if(this.removable && !this.editable ){
12296 cls : 'roo-removable',
12302 cls : 'roo-combo-removable-btn close'
12309 if (this.before || this.after) {
12312 cls : 'input-group',
12316 inputblock.cn.push({
12318 cls : 'input-group-addon input-group-prepend input-group-text',
12323 inputblock.cn.push(input);
12325 if(this.hasFeedback && !this.allowBlank){
12326 inputblock.cls += ' has-feedback';
12327 inputblock.cn.push(feedback);
12331 inputblock.cn.push({
12333 cls : 'input-group-addon input-group-append input-group-text',
12342 var ibwrap = inputblock;
12347 cls: 'roo-select2-choices',
12351 cls: 'roo-select2-search-field',
12363 cls: 'roo-select2-container input-group',
12368 cls: 'form-hidden-field'
12374 if(!this.multiple && this.showToggleBtn){
12380 if (this.caret != false) {
12383 cls: 'fa fa-' + this.caret
12390 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12392 Roo.bootstrap.version == 3 ? caret : '',
12395 cls: 'combobox-clear',
12409 combobox.cls += ' roo-select2-container-multi';
12413 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12414 tooltip : 'This field is required'
12416 if (Roo.bootstrap.version == 4) {
12419 style : 'display:none'
12424 if (align ==='left' && this.fieldLabel.length) {
12426 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12433 cls : 'control-label',
12434 html : this.fieldLabel
12446 var labelCfg = cfg.cn[1];
12447 var contentCfg = cfg.cn[2];
12449 if(this.indicatorpos == 'right'){
12454 cls : 'control-label',
12458 html : this.fieldLabel
12472 labelCfg = cfg.cn[0];
12473 contentCfg = cfg.cn[1];
12476 if(this.labelWidth > 12){
12477 labelCfg.style = "width: " + this.labelWidth + 'px';
12480 if(this.labelWidth < 13 && this.labelmd == 0){
12481 this.labelmd = this.labelWidth;
12484 if(this.labellg > 0){
12485 labelCfg.cls += ' col-lg-' + this.labellg;
12486 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12489 if(this.labelmd > 0){
12490 labelCfg.cls += ' col-md-' + this.labelmd;
12491 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12494 if(this.labelsm > 0){
12495 labelCfg.cls += ' col-sm-' + this.labelsm;
12496 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12499 if(this.labelxs > 0){
12500 labelCfg.cls += ' col-xs-' + this.labelxs;
12501 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12504 } else if ( this.fieldLabel.length) {
12505 // Roo.log(" label");
12510 //cls : 'input-group-addon',
12511 html : this.fieldLabel
12519 if(this.indicatorpos == 'right'){
12527 html : this.fieldLabel
12541 // Roo.log(" no label && no align");
12548 ['xs','sm','md','lg'].map(function(size){
12549 if (settings[size]) {
12550 cfg.cls += ' col-' + size + '-' + settings[size];
12561 onResize : function(w, h){
12562 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12563 // if(typeof w == 'number'){
12564 // var x = w - this.trigger.getWidth();
12565 // this.inputEl().setWidth(this.adjustWidth('input', x));
12566 // this.trigger.setStyle('left', x+'px');
12571 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12574 getResizeEl : function(){
12575 return this.inputEl();
12579 getPositionEl : function(){
12580 return this.inputEl();
12584 alignErrorIcon : function(){
12585 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12589 initEvents : function(){
12593 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12594 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12595 if(!this.multiple && this.showToggleBtn){
12596 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12597 if(this.hideTrigger){
12598 this.trigger.setDisplayed(false);
12600 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12604 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12607 if(this.removable && !this.editable && !this.tickable){
12608 var close = this.closeTriggerEl();
12611 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12612 close.on('click', this.removeBtnClick, this, close);
12616 //this.trigger.addClassOnOver('x-form-trigger-over');
12617 //this.trigger.addClassOnClick('x-form-trigger-click');
12620 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12624 closeTriggerEl : function()
12626 var close = this.el.select('.roo-combo-removable-btn', true).first();
12627 return close ? close : false;
12630 removeBtnClick : function(e, h, el)
12632 e.preventDefault();
12634 if(this.fireEvent("remove", this) !== false){
12636 this.fireEvent("afterremove", this)
12640 createList : function()
12642 this.list = Roo.get(document.body).createChild({
12643 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12644 cls: 'typeahead typeahead-long dropdown-menu shadow',
12645 style: 'display:none'
12648 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12653 initTrigger : function(){
12658 onDestroy : function(){
12660 this.trigger.removeAllListeners();
12661 // this.trigger.remove();
12664 // this.wrap.remove();
12666 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12670 onFocus : function(){
12671 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12673 if(!this.mimicing){
12674 this.wrap.addClass('x-trigger-wrap-focus');
12675 this.mimicing = true;
12676 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12677 if(this.monitorTab){
12678 this.el.on("keydown", this.checkTab, this);
12685 checkTab : function(e){
12686 if(e.getKey() == e.TAB){
12687 this.triggerBlur();
12692 onBlur : function(){
12697 mimicBlur : function(e, t){
12699 if(!this.wrap.contains(t) && this.validateBlur()){
12700 this.triggerBlur();
12706 triggerBlur : function(){
12707 this.mimicing = false;
12708 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12709 if(this.monitorTab){
12710 this.el.un("keydown", this.checkTab, this);
12712 //this.wrap.removeClass('x-trigger-wrap-focus');
12713 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12717 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12718 validateBlur : function(e, t){
12723 onDisable : function(){
12724 this.inputEl().dom.disabled = true;
12725 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12727 // this.wrap.addClass('x-item-disabled');
12732 onEnable : function(){
12733 this.inputEl().dom.disabled = false;
12734 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12736 // this.el.removeClass('x-item-disabled');
12741 onShow : function(){
12742 var ae = this.getActionEl();
12745 ae.dom.style.display = '';
12746 ae.dom.style.visibility = 'visible';
12752 onHide : function(){
12753 var ae = this.getActionEl();
12754 ae.dom.style.display = 'none';
12758 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12759 * by an implementing function.
12761 * @param {EventObject} e
12763 onTriggerClick : Roo.emptyFn
12771 * @class Roo.bootstrap.CardUploader
12772 * @extends Roo.bootstrap.Button
12773 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12774 * @cfg {Number} errorTimeout default 3000
12775 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12776 * @cfg {Array} html The button text.
12780 * Create a new CardUploader
12781 * @param {Object} config The config object
12784 Roo.bootstrap.CardUploader = function(config){
12788 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12791 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12799 * When a image is clicked on - and needs to display a slideshow or similar..
12800 * @param {Roo.bootstrap.Card} this
12801 * @param {Object} The image information data
12807 * When a the download link is clicked
12808 * @param {Roo.bootstrap.Card} this
12809 * @param {Object} The image information data contains
12816 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12819 errorTimeout : 3000,
12823 fileCollection : false,
12826 getAutoCreate : function()
12830 cls :'form-group' ,
12835 //cls : 'input-group-addon',
12836 html : this.fieldLabel
12844 value : this.value,
12845 cls : 'd-none form-control'
12850 multiple : 'multiple',
12852 cls : 'd-none roo-card-upload-selector'
12856 cls : 'roo-card-uploader-button-container w-100 mb-2'
12859 cls : 'card-columns roo-card-uploader-container'
12869 getChildContainer : function() /// what children are added to.
12871 return this.containerEl;
12874 getButtonContainer : function() /// what children are added to.
12876 return this.el.select(".roo-card-uploader-button-container").first();
12879 initEvents : function()
12882 Roo.bootstrap.Input.prototype.initEvents.call(this);
12886 xns: Roo.bootstrap,
12889 container_method : 'getButtonContainer' ,
12890 html : this.html, // fix changable?
12893 'click' : function(btn, e) {
12902 this.urlAPI = (window.createObjectURL && window) ||
12903 (window.URL && URL.revokeObjectURL && URL) ||
12904 (window.webkitURL && webkitURL);
12909 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12911 this.selectorEl.on('change', this.onFileSelected, this);
12914 this.images.forEach(function(img) {
12917 this.images = false;
12919 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12925 onClick : function(e)
12927 e.preventDefault();
12929 this.selectorEl.dom.click();
12933 onFileSelected : function(e)
12935 e.preventDefault();
12937 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12941 Roo.each(this.selectorEl.dom.files, function(file){
12942 this.addFile(file);
12951 addFile : function(file)
12954 if(typeof(file) === 'string'){
12955 throw "Add file by name?"; // should not happen
12959 if(!file || !this.urlAPI){
12969 var url = _this.urlAPI.createObjectURL( file);
12972 id : Roo.bootstrap.CardUploader.ID--,
12973 is_uploaded : false,
12977 mimetype : file.type,
12985 * addCard - add an Attachment to the uploader
12986 * @param data - the data about the image to upload
12990 title : "Title of file",
12991 is_uploaded : false,
12992 src : "http://.....",
12993 srcfile : { the File upload object },
12994 mimetype : file.type,
12997 .. any other data...
13003 addCard : function (data)
13005 // hidden input element?
13006 // if the file is not an image...
13007 //then we need to use something other that and header_image
13012 xns : Roo.bootstrap,
13013 xtype : 'CardFooter',
13016 xns : Roo.bootstrap,
13022 xns : Roo.bootstrap,
13024 html : String.format("<small>{0}</small>", data.title),
13025 cls : 'col-10 text-left',
13030 click : function() {
13032 t.fireEvent( "download", t, data );
13038 xns : Roo.bootstrap,
13040 style: 'max-height: 28px; ',
13046 click : function() {
13047 t.removeCard(data.id)
13059 var cn = this.addxtype(
13062 xns : Roo.bootstrap,
13065 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13066 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13067 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13072 initEvents : function() {
13073 Roo.bootstrap.Card.prototype.initEvents.call(this);
13075 this.imgEl = this.el.select('.card-img-top').first();
13077 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13078 this.imgEl.set({ 'pointer' : 'cursor' });
13081 this.getCardFooter().addClass('p-1');
13088 // dont' really need ot update items.
13089 // this.items.push(cn);
13090 this.fileCollection.add(cn);
13092 if (!data.srcfile) {
13093 this.updateInput();
13098 var reader = new FileReader();
13099 reader.addEventListener("load", function() {
13100 data.srcdata = reader.result;
13103 reader.readAsDataURL(data.srcfile);
13108 removeCard : function(id)
13111 var card = this.fileCollection.get(id);
13112 card.data.is_deleted = 1;
13113 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13114 //this.fileCollection.remove(card);
13115 //this.items = this.items.filter(function(e) { return e != card });
13116 // dont' really need ot update items.
13117 card.el.dom.parentNode.removeChild(card.el.dom);
13118 this.updateInput();
13124 this.fileCollection.each(function(card) {
13125 if (card.el.dom && card.el.dom.parentNode) {
13126 card.el.dom.parentNode.removeChild(card.el.dom);
13129 this.fileCollection.clear();
13130 this.updateInput();
13133 updateInput : function()
13136 this.fileCollection.each(function(e) {
13140 this.inputEl().dom.value = JSON.stringify(data);
13150 Roo.bootstrap.CardUploader.ID = -1;/*
13152 * Ext JS Library 1.1.1
13153 * Copyright(c) 2006-2007, Ext JS, LLC.
13155 * Originally Released Under LGPL - original licence link has changed is not relivant.
13158 * <script type="text/javascript">
13163 * @class Roo.data.SortTypes
13165 * Defines the default sorting (casting?) comparison functions used when sorting data.
13167 Roo.data.SortTypes = {
13169 * Default sort that does nothing
13170 * @param {Mixed} s The value being converted
13171 * @return {Mixed} The comparison value
13173 none : function(s){
13178 * The regular expression used to strip tags
13182 stripTagsRE : /<\/?[^>]+>/gi,
13185 * Strips all HTML tags to sort on text only
13186 * @param {Mixed} s The value being converted
13187 * @return {String} The comparison value
13189 asText : function(s){
13190 return String(s).replace(this.stripTagsRE, "");
13194 * Strips all HTML tags to sort on text only - Case insensitive
13195 * @param {Mixed} s The value being converted
13196 * @return {String} The comparison value
13198 asUCText : function(s){
13199 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13203 * Case insensitive string
13204 * @param {Mixed} s The value being converted
13205 * @return {String} The comparison value
13207 asUCString : function(s) {
13208 return String(s).toUpperCase();
13213 * @param {Mixed} s The value being converted
13214 * @return {Number} The comparison value
13216 asDate : function(s) {
13220 if(s instanceof Date){
13221 return s.getTime();
13223 return Date.parse(String(s));
13228 * @param {Mixed} s The value being converted
13229 * @return {Float} The comparison value
13231 asFloat : function(s) {
13232 var val = parseFloat(String(s).replace(/,/g, ""));
13241 * @param {Mixed} s The value being converted
13242 * @return {Number} The comparison value
13244 asInt : function(s) {
13245 var val = parseInt(String(s).replace(/,/g, ""));
13253 * Ext JS Library 1.1.1
13254 * Copyright(c) 2006-2007, Ext JS, LLC.
13256 * Originally Released Under LGPL - original licence link has changed is not relivant.
13259 * <script type="text/javascript">
13263 * @class Roo.data.Record
13264 * Instances of this class encapsulate both record <em>definition</em> information, and record
13265 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13266 * to access Records cached in an {@link Roo.data.Store} object.<br>
13268 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13269 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13272 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13274 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13275 * {@link #create}. The parameters are the same.
13276 * @param {Array} data An associative Array of data values keyed by the field name.
13277 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13278 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13279 * not specified an integer id is generated.
13281 Roo.data.Record = function(data, id){
13282 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13287 * Generate a constructor for a specific record layout.
13288 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13289 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13290 * Each field definition object may contain the following properties: <ul>
13291 * <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,
13292 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13293 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13294 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13295 * is being used, then this is a string containing the javascript expression to reference the data relative to
13296 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13297 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13298 * this may be omitted.</p></li>
13299 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13300 * <ul><li>auto (Default, implies no conversion)</li>
13305 * <li>date</li></ul></p></li>
13306 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13307 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13308 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13309 * by the Reader into an object that will be stored in the Record. It is passed the
13310 * following parameters:<ul>
13311 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13313 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13315 * <br>usage:<br><pre><code>
13316 var TopicRecord = Roo.data.Record.create(
13317 {name: 'title', mapping: 'topic_title'},
13318 {name: 'author', mapping: 'username'},
13319 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13320 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13321 {name: 'lastPoster', mapping: 'user2'},
13322 {name: 'excerpt', mapping: 'post_text'}
13325 var myNewRecord = new TopicRecord({
13326 title: 'Do my job please',
13329 lastPost: new Date(),
13330 lastPoster: 'Animal',
13331 excerpt: 'No way dude!'
13333 myStore.add(myNewRecord);
13338 Roo.data.Record.create = function(o){
13339 var f = function(){
13340 f.superclass.constructor.apply(this, arguments);
13342 Roo.extend(f, Roo.data.Record);
13343 var p = f.prototype;
13344 p.fields = new Roo.util.MixedCollection(false, function(field){
13347 for(var i = 0, len = o.length; i < len; i++){
13348 p.fields.add(new Roo.data.Field(o[i]));
13350 f.getField = function(name){
13351 return p.fields.get(name);
13356 Roo.data.Record.AUTO_ID = 1000;
13357 Roo.data.Record.EDIT = 'edit';
13358 Roo.data.Record.REJECT = 'reject';
13359 Roo.data.Record.COMMIT = 'commit';
13361 Roo.data.Record.prototype = {
13363 * Readonly flag - true if this record has been modified.
13372 join : function(store){
13373 this.store = store;
13377 * Set the named field to the specified value.
13378 * @param {String} name The name of the field to set.
13379 * @param {Object} value The value to set the field to.
13381 set : function(name, value){
13382 if(this.data[name] == value){
13386 if(!this.modified){
13387 this.modified = {};
13389 if(typeof this.modified[name] == 'undefined'){
13390 this.modified[name] = this.data[name];
13392 this.data[name] = value;
13393 if(!this.editing && this.store){
13394 this.store.afterEdit(this);
13399 * Get the value of the named field.
13400 * @param {String} name The name of the field to get the value of.
13401 * @return {Object} The value of the field.
13403 get : function(name){
13404 return this.data[name];
13408 beginEdit : function(){
13409 this.editing = true;
13410 this.modified = {};
13414 cancelEdit : function(){
13415 this.editing = false;
13416 delete this.modified;
13420 endEdit : function(){
13421 this.editing = false;
13422 if(this.dirty && this.store){
13423 this.store.afterEdit(this);
13428 * Usually called by the {@link Roo.data.Store} which owns the Record.
13429 * Rejects all changes made to the Record since either creation, or the last commit operation.
13430 * Modified fields are reverted to their original values.
13432 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13433 * of reject operations.
13435 reject : function(){
13436 var m = this.modified;
13438 if(typeof m[n] != "function"){
13439 this.data[n] = m[n];
13442 this.dirty = false;
13443 delete this.modified;
13444 this.editing = false;
13446 this.store.afterReject(this);
13451 * Usually called by the {@link Roo.data.Store} which owns the Record.
13452 * Commits all changes made to the Record since either creation, or the last commit operation.
13454 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13455 * of commit operations.
13457 commit : function(){
13458 this.dirty = false;
13459 delete this.modified;
13460 this.editing = false;
13462 this.store.afterCommit(this);
13467 hasError : function(){
13468 return this.error != null;
13472 clearError : function(){
13477 * Creates a copy of this record.
13478 * @param {String} id (optional) A new record id if you don't want to use this record's id
13481 copy : function(newId) {
13482 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13486 * Ext JS Library 1.1.1
13487 * Copyright(c) 2006-2007, Ext JS, LLC.
13489 * Originally Released Under LGPL - original licence link has changed is not relivant.
13492 * <script type="text/javascript">
13498 * @class Roo.data.Store
13499 * @extends Roo.util.Observable
13500 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13501 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13503 * 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
13504 * has no knowledge of the format of the data returned by the Proxy.<br>
13506 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13507 * instances from the data object. These records are cached and made available through accessor functions.
13509 * Creates a new Store.
13510 * @param {Object} config A config object containing the objects needed for the Store to access data,
13511 * and read the data into Records.
13513 Roo.data.Store = function(config){
13514 this.data = new Roo.util.MixedCollection(false);
13515 this.data.getKey = function(o){
13518 this.baseParams = {};
13520 this.paramNames = {
13525 "multisort" : "_multisort"
13528 if(config && config.data){
13529 this.inlineData = config.data;
13530 delete config.data;
13533 Roo.apply(this, config);
13535 if(this.reader){ // reader passed
13536 this.reader = Roo.factory(this.reader, Roo.data);
13537 this.reader.xmodule = this.xmodule || false;
13538 if(!this.recordType){
13539 this.recordType = this.reader.recordType;
13541 if(this.reader.onMetaChange){
13542 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13546 if(this.recordType){
13547 this.fields = this.recordType.prototype.fields;
13549 this.modified = [];
13553 * @event datachanged
13554 * Fires when the data cache has changed, and a widget which is using this Store
13555 * as a Record cache should refresh its view.
13556 * @param {Store} this
13558 datachanged : true,
13560 * @event metachange
13561 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13562 * @param {Store} this
13563 * @param {Object} meta The JSON metadata
13568 * Fires when Records have been added to the Store
13569 * @param {Store} this
13570 * @param {Roo.data.Record[]} records The array of Records added
13571 * @param {Number} index The index at which the record(s) were added
13576 * Fires when a Record has been removed from the Store
13577 * @param {Store} this
13578 * @param {Roo.data.Record} record The Record that was removed
13579 * @param {Number} index The index at which the record was removed
13584 * Fires when a Record has been updated
13585 * @param {Store} this
13586 * @param {Roo.data.Record} record The Record that was updated
13587 * @param {String} operation The update operation being performed. Value may be one of:
13589 Roo.data.Record.EDIT
13590 Roo.data.Record.REJECT
13591 Roo.data.Record.COMMIT
13597 * Fires when the data cache has been cleared.
13598 * @param {Store} this
13602 * @event beforeload
13603 * Fires before a request is made for a new data object. If the beforeload handler returns false
13604 * the load action will be canceled.
13605 * @param {Store} this
13606 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13610 * @event beforeloadadd
13611 * Fires after a new set of Records has been loaded.
13612 * @param {Store} this
13613 * @param {Roo.data.Record[]} records The Records that were loaded
13614 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13616 beforeloadadd : true,
13619 * Fires after a new set of Records has been loaded, before they are added to the store.
13620 * @param {Store} this
13621 * @param {Roo.data.Record[]} records The Records that were loaded
13622 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13623 * @params {Object} return from reader
13627 * @event loadexception
13628 * Fires if an exception occurs in the Proxy during loading.
13629 * Called with the signature of the Proxy's "loadexception" event.
13630 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13633 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13634 * @param {Object} load options
13635 * @param {Object} jsonData from your request (normally this contains the Exception)
13637 loadexception : true
13641 this.proxy = Roo.factory(this.proxy, Roo.data);
13642 this.proxy.xmodule = this.xmodule || false;
13643 this.relayEvents(this.proxy, ["loadexception"]);
13645 this.sortToggle = {};
13646 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13648 Roo.data.Store.superclass.constructor.call(this);
13650 if(this.inlineData){
13651 this.loadData(this.inlineData);
13652 delete this.inlineData;
13656 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13658 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13659 * without a remote query - used by combo/forms at present.
13663 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13666 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13669 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13670 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13673 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13674 * on any HTTP request
13677 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13680 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13684 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13685 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13687 remoteSort : false,
13690 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13691 * loaded or when a record is removed. (defaults to false).
13693 pruneModifiedRecords : false,
13696 lastOptions : null,
13699 * Add Records to the Store and fires the add event.
13700 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13702 add : function(records){
13703 records = [].concat(records);
13704 for(var i = 0, len = records.length; i < len; i++){
13705 records[i].join(this);
13707 var index = this.data.length;
13708 this.data.addAll(records);
13709 this.fireEvent("add", this, records, index);
13713 * Remove a Record from the Store and fires the remove event.
13714 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13716 remove : function(record){
13717 var index = this.data.indexOf(record);
13718 this.data.removeAt(index);
13720 if(this.pruneModifiedRecords){
13721 this.modified.remove(record);
13723 this.fireEvent("remove", this, record, index);
13727 * Remove all Records from the Store and fires the clear event.
13729 removeAll : function(){
13731 if(this.pruneModifiedRecords){
13732 this.modified = [];
13734 this.fireEvent("clear", this);
13738 * Inserts Records to the Store at the given index and fires the add event.
13739 * @param {Number} index The start index at which to insert the passed Records.
13740 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13742 insert : function(index, records){
13743 records = [].concat(records);
13744 for(var i = 0, len = records.length; i < len; i++){
13745 this.data.insert(index, records[i]);
13746 records[i].join(this);
13748 this.fireEvent("add", this, records, index);
13752 * Get the index within the cache of the passed Record.
13753 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13754 * @return {Number} The index of the passed Record. Returns -1 if not found.
13756 indexOf : function(record){
13757 return this.data.indexOf(record);
13761 * Get the index within the cache of the Record with the passed id.
13762 * @param {String} id The id of the Record to find.
13763 * @return {Number} The index of the Record. Returns -1 if not found.
13765 indexOfId : function(id){
13766 return this.data.indexOfKey(id);
13770 * Get the Record with the specified id.
13771 * @param {String} id The id of the Record to find.
13772 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13774 getById : function(id){
13775 return this.data.key(id);
13779 * Get the Record at the specified index.
13780 * @param {Number} index The index of the Record to find.
13781 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13783 getAt : function(index){
13784 return this.data.itemAt(index);
13788 * Returns a range of Records between specified indices.
13789 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13790 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13791 * @return {Roo.data.Record[]} An array of Records
13793 getRange : function(start, end){
13794 return this.data.getRange(start, end);
13798 storeOptions : function(o){
13799 o = Roo.apply({}, o);
13802 this.lastOptions = o;
13806 * Loads the Record cache from the configured Proxy using the configured Reader.
13808 * If using remote paging, then the first load call must specify the <em>start</em>
13809 * and <em>limit</em> properties in the options.params property to establish the initial
13810 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13812 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13813 * and this call will return before the new data has been loaded. Perform any post-processing
13814 * in a callback function, or in a "load" event handler.</strong>
13816 * @param {Object} options An object containing properties which control loading options:<ul>
13817 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13818 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13819 * passed the following arguments:<ul>
13820 * <li>r : Roo.data.Record[]</li>
13821 * <li>options: Options object from the load call</li>
13822 * <li>success: Boolean success indicator</li></ul></li>
13823 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13824 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13827 load : function(options){
13828 options = options || {};
13829 if(this.fireEvent("beforeload", this, options) !== false){
13830 this.storeOptions(options);
13831 var p = Roo.apply(options.params || {}, this.baseParams);
13832 // if meta was not loaded from remote source.. try requesting it.
13833 if (!this.reader.metaFromRemote) {
13834 p._requestMeta = 1;
13836 if(this.sortInfo && this.remoteSort){
13837 var pn = this.paramNames;
13838 p[pn["sort"]] = this.sortInfo.field;
13839 p[pn["dir"]] = this.sortInfo.direction;
13841 if (this.multiSort) {
13842 var pn = this.paramNames;
13843 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13846 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13851 * Reloads the Record cache from the configured Proxy using the configured Reader and
13852 * the options from the last load operation performed.
13853 * @param {Object} options (optional) An object containing properties which may override the options
13854 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13855 * the most recently used options are reused).
13857 reload : function(options){
13858 this.load(Roo.applyIf(options||{}, this.lastOptions));
13862 // Called as a callback by the Reader during a load operation.
13863 loadRecords : function(o, options, success){
13864 if(!o || success === false){
13865 if(success !== false){
13866 this.fireEvent("load", this, [], options, o);
13868 if(options.callback){
13869 options.callback.call(options.scope || this, [], options, false);
13873 // if data returned failure - throw an exception.
13874 if (o.success === false) {
13875 // show a message if no listener is registered.
13876 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13877 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13879 // loadmask wil be hooked into this..
13880 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13883 var r = o.records, t = o.totalRecords || r.length;
13885 this.fireEvent("beforeloadadd", this, r, options, o);
13887 if(!options || options.add !== true){
13888 if(this.pruneModifiedRecords){
13889 this.modified = [];
13891 for(var i = 0, len = r.length; i < len; i++){
13895 this.data = this.snapshot;
13896 delete this.snapshot;
13899 this.data.addAll(r);
13900 this.totalLength = t;
13902 this.fireEvent("datachanged", this);
13904 this.totalLength = Math.max(t, this.data.length+r.length);
13908 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13910 var e = new Roo.data.Record({});
13912 e.set(this.parent.displayField, this.parent.emptyTitle);
13913 e.set(this.parent.valueField, '');
13918 this.fireEvent("load", this, r, options, o);
13919 if(options.callback){
13920 options.callback.call(options.scope || this, r, options, true);
13926 * Loads data from a passed data block. A Reader which understands the format of the data
13927 * must have been configured in the constructor.
13928 * @param {Object} data The data block from which to read the Records. The format of the data expected
13929 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13930 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13932 loadData : function(o, append){
13933 var r = this.reader.readRecords(o);
13934 this.loadRecords(r, {add: append}, true);
13938 * using 'cn' the nested child reader read the child array into it's child stores.
13939 * @param {Object} rec The record with a 'children array
13941 loadDataFromChildren : function(rec)
13943 this.loadData(this.reader.toLoadData(rec));
13948 * Gets the number of cached records.
13950 * <em>If using paging, this may not be the total size of the dataset. If the data object
13951 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13952 * the data set size</em>
13954 getCount : function(){
13955 return this.data.length || 0;
13959 * Gets the total number of records in the dataset as returned by the server.
13961 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13962 * the dataset size</em>
13964 getTotalCount : function(){
13965 return this.totalLength || 0;
13969 * Returns the sort state of the Store as an object with two properties:
13971 field {String} The name of the field by which the Records are sorted
13972 direction {String} The sort order, "ASC" or "DESC"
13975 getSortState : function(){
13976 return this.sortInfo;
13980 applySort : function(){
13981 if(this.sortInfo && !this.remoteSort){
13982 var s = this.sortInfo, f = s.field;
13983 var st = this.fields.get(f).sortType;
13984 var fn = function(r1, r2){
13985 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13986 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13988 this.data.sort(s.direction, fn);
13989 if(this.snapshot && this.snapshot != this.data){
13990 this.snapshot.sort(s.direction, fn);
13996 * Sets the default sort column and order to be used by the next load operation.
13997 * @param {String} fieldName The name of the field to sort by.
13998 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14000 setDefaultSort : function(field, dir){
14001 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14005 * Sort the Records.
14006 * If remote sorting is used, the sort is performed on the server, and the cache is
14007 * reloaded. If local sorting is used, the cache is sorted internally.
14008 * @param {String} fieldName The name of the field to sort by.
14009 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14011 sort : function(fieldName, dir){
14012 var f = this.fields.get(fieldName);
14014 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14016 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14017 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14022 this.sortToggle[f.name] = dir;
14023 this.sortInfo = {field: f.name, direction: dir};
14024 if(!this.remoteSort){
14026 this.fireEvent("datachanged", this);
14028 this.load(this.lastOptions);
14033 * Calls the specified function for each of the Records in the cache.
14034 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14035 * Returning <em>false</em> aborts and exits the iteration.
14036 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14038 each : function(fn, scope){
14039 this.data.each(fn, scope);
14043 * Gets all records modified since the last commit. Modified records are persisted across load operations
14044 * (e.g., during paging).
14045 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14047 getModifiedRecords : function(){
14048 return this.modified;
14052 createFilterFn : function(property, value, anyMatch){
14053 if(!value.exec){ // not a regex
14054 value = String(value);
14055 if(value.length == 0){
14058 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14060 return function(r){
14061 return value.test(r.data[property]);
14066 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14067 * @param {String} property A field on your records
14068 * @param {Number} start The record index to start at (defaults to 0)
14069 * @param {Number} end The last record index to include (defaults to length - 1)
14070 * @return {Number} The sum
14072 sum : function(property, start, end){
14073 var rs = this.data.items, v = 0;
14074 start = start || 0;
14075 end = (end || end === 0) ? end : rs.length-1;
14077 for(var i = start; i <= end; i++){
14078 v += (rs[i].data[property] || 0);
14084 * Filter the records by a specified property.
14085 * @param {String} field A field on your records
14086 * @param {String/RegExp} value Either a string that the field
14087 * should start with or a RegExp to test against the field
14088 * @param {Boolean} anyMatch True to match any part not just the beginning
14090 filter : function(property, value, anyMatch){
14091 var fn = this.createFilterFn(property, value, anyMatch);
14092 return fn ? this.filterBy(fn) : this.clearFilter();
14096 * Filter by a function. The specified function will be called with each
14097 * record in this data source. If the function returns true the record is included,
14098 * otherwise it is filtered.
14099 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14100 * @param {Object} scope (optional) The scope of the function (defaults to this)
14102 filterBy : function(fn, scope){
14103 this.snapshot = this.snapshot || this.data;
14104 this.data = this.queryBy(fn, scope||this);
14105 this.fireEvent("datachanged", this);
14109 * Query the records by a specified property.
14110 * @param {String} field A field on your records
14111 * @param {String/RegExp} value Either a string that the field
14112 * should start with or a RegExp to test against the field
14113 * @param {Boolean} anyMatch True to match any part not just the beginning
14114 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14116 query : function(property, value, anyMatch){
14117 var fn = this.createFilterFn(property, value, anyMatch);
14118 return fn ? this.queryBy(fn) : this.data.clone();
14122 * Query by a function. The specified function will be called with each
14123 * record in this data source. If the function returns true the record is included
14125 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14126 * @param {Object} scope (optional) The scope of the function (defaults to this)
14127 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14129 queryBy : function(fn, scope){
14130 var data = this.snapshot || this.data;
14131 return data.filterBy(fn, scope||this);
14135 * Collects unique values for a particular dataIndex from this store.
14136 * @param {String} dataIndex The property to collect
14137 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14138 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14139 * @return {Array} An array of the unique values
14141 collect : function(dataIndex, allowNull, bypassFilter){
14142 var d = (bypassFilter === true && this.snapshot) ?
14143 this.snapshot.items : this.data.items;
14144 var v, sv, r = [], l = {};
14145 for(var i = 0, len = d.length; i < len; i++){
14146 v = d[i].data[dataIndex];
14148 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14157 * Revert to a view of the Record cache with no filtering applied.
14158 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14160 clearFilter : function(suppressEvent){
14161 if(this.snapshot && this.snapshot != this.data){
14162 this.data = this.snapshot;
14163 delete this.snapshot;
14164 if(suppressEvent !== true){
14165 this.fireEvent("datachanged", this);
14171 afterEdit : function(record){
14172 if(this.modified.indexOf(record) == -1){
14173 this.modified.push(record);
14175 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14179 afterReject : function(record){
14180 this.modified.remove(record);
14181 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14185 afterCommit : function(record){
14186 this.modified.remove(record);
14187 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14191 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14192 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14194 commitChanges : function(){
14195 var m = this.modified.slice(0);
14196 this.modified = [];
14197 for(var i = 0, len = m.length; i < len; i++){
14203 * Cancel outstanding changes on all changed records.
14205 rejectChanges : function(){
14206 var m = this.modified.slice(0);
14207 this.modified = [];
14208 for(var i = 0, len = m.length; i < len; i++){
14213 onMetaChange : function(meta, rtype, o){
14214 this.recordType = rtype;
14215 this.fields = rtype.prototype.fields;
14216 delete this.snapshot;
14217 this.sortInfo = meta.sortInfo || this.sortInfo;
14218 this.modified = [];
14219 this.fireEvent('metachange', this, this.reader.meta);
14222 moveIndex : function(data, type)
14224 var index = this.indexOf(data);
14226 var newIndex = index + type;
14230 this.insert(newIndex, data);
14235 * Ext JS Library 1.1.1
14236 * Copyright(c) 2006-2007, Ext JS, LLC.
14238 * Originally Released Under LGPL - original licence link has changed is not relivant.
14241 * <script type="text/javascript">
14245 * @class Roo.data.SimpleStore
14246 * @extends Roo.data.Store
14247 * Small helper class to make creating Stores from Array data easier.
14248 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14249 * @cfg {Array} fields An array of field definition objects, or field name strings.
14250 * @cfg {Object} an existing reader (eg. copied from another store)
14251 * @cfg {Array} data The multi-dimensional array of data
14253 * @param {Object} config
14255 Roo.data.SimpleStore = function(config)
14257 Roo.data.SimpleStore.superclass.constructor.call(this, {
14259 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14262 Roo.data.Record.create(config.fields)
14264 proxy : new Roo.data.MemoryProxy(config.data)
14268 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14270 * Ext JS Library 1.1.1
14271 * Copyright(c) 2006-2007, Ext JS, LLC.
14273 * Originally Released Under LGPL - original licence link has changed is not relivant.
14276 * <script type="text/javascript">
14281 * @extends Roo.data.Store
14282 * @class Roo.data.JsonStore
14283 * Small helper class to make creating Stores for JSON data easier. <br/>
14285 var store = new Roo.data.JsonStore({
14286 url: 'get-images.php',
14288 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14291 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14292 * JsonReader and HttpProxy (unless inline data is provided).</b>
14293 * @cfg {Array} fields An array of field definition objects, or field name strings.
14295 * @param {Object} config
14297 Roo.data.JsonStore = function(c){
14298 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14299 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14300 reader: new Roo.data.JsonReader(c, c.fields)
14303 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14305 * Ext JS Library 1.1.1
14306 * Copyright(c) 2006-2007, Ext JS, LLC.
14308 * Originally Released Under LGPL - original licence link has changed is not relivant.
14311 * <script type="text/javascript">
14315 Roo.data.Field = function(config){
14316 if(typeof config == "string"){
14317 config = {name: config};
14319 Roo.apply(this, config);
14322 this.type = "auto";
14325 var st = Roo.data.SortTypes;
14326 // named sortTypes are supported, here we look them up
14327 if(typeof this.sortType == "string"){
14328 this.sortType = st[this.sortType];
14331 // set default sortType for strings and dates
14332 if(!this.sortType){
14335 this.sortType = st.asUCString;
14338 this.sortType = st.asDate;
14341 this.sortType = st.none;
14346 var stripRe = /[\$,%]/g;
14348 // prebuilt conversion function for this field, instead of
14349 // switching every time we're reading a value
14351 var cv, dateFormat = this.dateFormat;
14356 cv = function(v){ return v; };
14359 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14363 return v !== undefined && v !== null && v !== '' ?
14364 parseInt(String(v).replace(stripRe, ""), 10) : '';
14369 return v !== undefined && v !== null && v !== '' ?
14370 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14375 cv = function(v){ return v === true || v === "true" || v == 1; };
14382 if(v instanceof Date){
14386 if(dateFormat == "timestamp"){
14387 return new Date(v*1000);
14389 return Date.parseDate(v, dateFormat);
14391 var parsed = Date.parse(v);
14392 return parsed ? new Date(parsed) : null;
14401 Roo.data.Field.prototype = {
14409 * Ext JS Library 1.1.1
14410 * Copyright(c) 2006-2007, Ext JS, LLC.
14412 * Originally Released Under LGPL - original licence link has changed is not relivant.
14415 * <script type="text/javascript">
14418 // Base class for reading structured data from a data source. This class is intended to be
14419 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14422 * @class Roo.data.DataReader
14423 * Base class for reading structured data from a data source. This class is intended to be
14424 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14427 Roo.data.DataReader = function(meta, recordType){
14431 this.recordType = recordType instanceof Array ?
14432 Roo.data.Record.create(recordType) : recordType;
14435 Roo.data.DataReader.prototype = {
14438 readerType : 'Data',
14440 * Create an empty record
14441 * @param {Object} data (optional) - overlay some values
14442 * @return {Roo.data.Record} record created.
14444 newRow : function(d) {
14446 this.recordType.prototype.fields.each(function(c) {
14448 case 'int' : da[c.name] = 0; break;
14449 case 'date' : da[c.name] = new Date(); break;
14450 case 'float' : da[c.name] = 0.0; break;
14451 case 'boolean' : da[c.name] = false; break;
14452 default : da[c.name] = ""; break;
14456 return new this.recordType(Roo.apply(da, d));
14462 * Ext JS Library 1.1.1
14463 * Copyright(c) 2006-2007, Ext JS, LLC.
14465 * Originally Released Under LGPL - original licence link has changed is not relivant.
14468 * <script type="text/javascript">
14472 * @class Roo.data.DataProxy
14473 * @extends Roo.data.Observable
14474 * This class is an abstract base class for implementations which provide retrieval of
14475 * unformatted data objects.<br>
14477 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14478 * (of the appropriate type which knows how to parse the data object) to provide a block of
14479 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14481 * Custom implementations must implement the load method as described in
14482 * {@link Roo.data.HttpProxy#load}.
14484 Roo.data.DataProxy = function(){
14487 * @event beforeload
14488 * Fires before a network request is made to retrieve a data object.
14489 * @param {Object} This DataProxy object.
14490 * @param {Object} params The params parameter to the load function.
14495 * Fires before the load method's callback is called.
14496 * @param {Object} This DataProxy object.
14497 * @param {Object} o The data object.
14498 * @param {Object} arg The callback argument object passed to the load function.
14502 * @event loadexception
14503 * Fires if an Exception occurs during data retrieval.
14504 * @param {Object} This DataProxy object.
14505 * @param {Object} o The data object.
14506 * @param {Object} arg The callback argument object passed to the load function.
14507 * @param {Object} e The Exception.
14509 loadexception : true
14511 Roo.data.DataProxy.superclass.constructor.call(this);
14514 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14517 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14521 * Ext JS Library 1.1.1
14522 * Copyright(c) 2006-2007, Ext JS, LLC.
14524 * Originally Released Under LGPL - original licence link has changed is not relivant.
14527 * <script type="text/javascript">
14530 * @class Roo.data.MemoryProxy
14531 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14532 * to the Reader when its load method is called.
14534 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14536 Roo.data.MemoryProxy = function(data){
14540 Roo.data.MemoryProxy.superclass.constructor.call(this);
14544 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14547 * Load data from the requested source (in this case an in-memory
14548 * data object passed to the constructor), read the data object into
14549 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14550 * process that block using the passed callback.
14551 * @param {Object} params This parameter is not used by the MemoryProxy class.
14552 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14553 * object into a block of Roo.data.Records.
14554 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14555 * The function must be passed <ul>
14556 * <li>The Record block object</li>
14557 * <li>The "arg" argument from the load function</li>
14558 * <li>A boolean success indicator</li>
14560 * @param {Object} scope The scope in which to call the callback
14561 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14563 load : function(params, reader, callback, scope, arg){
14564 params = params || {};
14567 result = reader.readRecords(params.data ? params.data :this.data);
14569 this.fireEvent("loadexception", this, arg, null, e);
14570 callback.call(scope, null, arg, false);
14573 callback.call(scope, result, arg, true);
14577 update : function(params, records){
14582 * Ext JS Library 1.1.1
14583 * Copyright(c) 2006-2007, Ext JS, LLC.
14585 * Originally Released Under LGPL - original licence link has changed is not relivant.
14588 * <script type="text/javascript">
14591 * @class Roo.data.HttpProxy
14592 * @extends Roo.data.DataProxy
14593 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14594 * configured to reference a certain URL.<br><br>
14596 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14597 * from which the running page was served.<br><br>
14599 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14601 * Be aware that to enable the browser to parse an XML document, the server must set
14602 * the Content-Type header in the HTTP response to "text/xml".
14604 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14605 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14606 * will be used to make the request.
14608 Roo.data.HttpProxy = function(conn){
14609 Roo.data.HttpProxy.superclass.constructor.call(this);
14610 // is conn a conn config or a real conn?
14612 this.useAjax = !conn || !conn.events;
14616 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14617 // thse are take from connection...
14620 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14623 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14624 * extra parameters to each request made by this object. (defaults to undefined)
14627 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14628 * to each request made by this object. (defaults to undefined)
14631 * @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)
14634 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14637 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14643 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14647 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14648 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14649 * a finer-grained basis than the DataProxy events.
14651 getConnection : function(){
14652 return this.useAjax ? Roo.Ajax : this.conn;
14656 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14657 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14658 * process that block using the passed callback.
14659 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14660 * for the request to the remote server.
14661 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14662 * object into a block of Roo.data.Records.
14663 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14664 * The function must be passed <ul>
14665 * <li>The Record block object</li>
14666 * <li>The "arg" argument from the load function</li>
14667 * <li>A boolean success indicator</li>
14669 * @param {Object} scope The scope in which to call the callback
14670 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14672 load : function(params, reader, callback, scope, arg){
14673 if(this.fireEvent("beforeload", this, params) !== false){
14675 params : params || {},
14677 callback : callback,
14682 callback : this.loadResponse,
14686 Roo.applyIf(o, this.conn);
14687 if(this.activeRequest){
14688 Roo.Ajax.abort(this.activeRequest);
14690 this.activeRequest = Roo.Ajax.request(o);
14692 this.conn.request(o);
14695 callback.call(scope||this, null, arg, false);
14700 loadResponse : function(o, success, response){
14701 delete this.activeRequest;
14703 this.fireEvent("loadexception", this, o, response);
14704 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14709 result = o.reader.read(response);
14711 this.fireEvent("loadexception", this, o, response, e);
14712 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14716 this.fireEvent("load", this, o, o.request.arg);
14717 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14721 update : function(dataSet){
14726 updateResponse : function(dataSet){
14731 * Ext JS Library 1.1.1
14732 * Copyright(c) 2006-2007, Ext JS, LLC.
14734 * Originally Released Under LGPL - original licence link has changed is not relivant.
14737 * <script type="text/javascript">
14741 * @class Roo.data.ScriptTagProxy
14742 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14743 * other than the originating domain of the running page.<br><br>
14745 * <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
14746 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14748 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14749 * source code that is used as the source inside a <script> tag.<br><br>
14751 * In order for the browser to process the returned data, the server must wrap the data object
14752 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14753 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14754 * depending on whether the callback name was passed:
14757 boolean scriptTag = false;
14758 String cb = request.getParameter("callback");
14761 response.setContentType("text/javascript");
14763 response.setContentType("application/x-json");
14765 Writer out = response.getWriter();
14767 out.write(cb + "(");
14769 out.print(dataBlock.toJsonString());
14776 * @param {Object} config A configuration object.
14778 Roo.data.ScriptTagProxy = function(config){
14779 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14780 Roo.apply(this, config);
14781 this.head = document.getElementsByTagName("head")[0];
14784 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14786 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14788 * @cfg {String} url The URL from which to request the data object.
14791 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14795 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14796 * the server the name of the callback function set up by the load call to process the returned data object.
14797 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14798 * javascript output which calls this named function passing the data object as its only parameter.
14800 callbackParam : "callback",
14802 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14803 * name to the request.
14808 * Load data from the configured URL, read the data object into
14809 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14810 * process that block using the passed callback.
14811 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14812 * for the request to the remote server.
14813 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14814 * object into a block of Roo.data.Records.
14815 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14816 * The function must be passed <ul>
14817 * <li>The Record block object</li>
14818 * <li>The "arg" argument from the load function</li>
14819 * <li>A boolean success indicator</li>
14821 * @param {Object} scope The scope in which to call the callback
14822 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14824 load : function(params, reader, callback, scope, arg){
14825 if(this.fireEvent("beforeload", this, params) !== false){
14827 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14829 var url = this.url;
14830 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14832 url += "&_dc=" + (new Date().getTime());
14834 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14837 cb : "stcCallback"+transId,
14838 scriptId : "stcScript"+transId,
14842 callback : callback,
14848 window[trans.cb] = function(o){
14849 conn.handleResponse(o, trans);
14852 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14854 if(this.autoAbort !== false){
14858 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14860 var script = document.createElement("script");
14861 script.setAttribute("src", url);
14862 script.setAttribute("type", "text/javascript");
14863 script.setAttribute("id", trans.scriptId);
14864 this.head.appendChild(script);
14866 this.trans = trans;
14868 callback.call(scope||this, null, arg, false);
14873 isLoading : function(){
14874 return this.trans ? true : false;
14878 * Abort the current server request.
14880 abort : function(){
14881 if(this.isLoading()){
14882 this.destroyTrans(this.trans);
14887 destroyTrans : function(trans, isLoaded){
14888 this.head.removeChild(document.getElementById(trans.scriptId));
14889 clearTimeout(trans.timeoutId);
14891 window[trans.cb] = undefined;
14893 delete window[trans.cb];
14896 // if hasn't been loaded, wait for load to remove it to prevent script error
14897 window[trans.cb] = function(){
14898 window[trans.cb] = undefined;
14900 delete window[trans.cb];
14907 handleResponse : function(o, trans){
14908 this.trans = false;
14909 this.destroyTrans(trans, true);
14912 result = trans.reader.readRecords(o);
14914 this.fireEvent("loadexception", this, o, trans.arg, e);
14915 trans.callback.call(trans.scope||window, null, trans.arg, false);
14918 this.fireEvent("load", this, o, trans.arg);
14919 trans.callback.call(trans.scope||window, result, trans.arg, true);
14923 handleFailure : function(trans){
14924 this.trans = false;
14925 this.destroyTrans(trans, false);
14926 this.fireEvent("loadexception", this, null, trans.arg);
14927 trans.callback.call(trans.scope||window, null, trans.arg, false);
14931 * Ext JS Library 1.1.1
14932 * Copyright(c) 2006-2007, Ext JS, LLC.
14934 * Originally Released Under LGPL - original licence link has changed is not relivant.
14937 * <script type="text/javascript">
14941 * @class Roo.data.JsonReader
14942 * @extends Roo.data.DataReader
14943 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14944 * based on mappings in a provided Roo.data.Record constructor.
14946 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14947 * in the reply previously.
14952 var RecordDef = Roo.data.Record.create([
14953 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14954 {name: 'occupation'} // This field will use "occupation" as the mapping.
14956 var myReader = new Roo.data.JsonReader({
14957 totalProperty: "results", // The property which contains the total dataset size (optional)
14958 root: "rows", // The property which contains an Array of row objects
14959 id: "id" // The property within each row object that provides an ID for the record (optional)
14963 * This would consume a JSON file like this:
14965 { 'results': 2, 'rows': [
14966 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14967 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14970 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14971 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14972 * paged from the remote server.
14973 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14974 * @cfg {String} root name of the property which contains the Array of row objects.
14975 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14976 * @cfg {Array} fields Array of field definition objects
14978 * Create a new JsonReader
14979 * @param {Object} meta Metadata configuration options
14980 * @param {Object} recordType Either an Array of field definition objects,
14981 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14983 Roo.data.JsonReader = function(meta, recordType){
14986 // set some defaults:
14987 Roo.applyIf(meta, {
14988 totalProperty: 'total',
14989 successProperty : 'success',
14994 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14996 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14998 readerType : 'Json',
15001 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15002 * Used by Store query builder to append _requestMeta to params.
15005 metaFromRemote : false,
15007 * This method is only used by a DataProxy which has retrieved data from a remote server.
15008 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15009 * @return {Object} data A data block which is used by an Roo.data.Store object as
15010 * a cache of Roo.data.Records.
15012 read : function(response){
15013 var json = response.responseText;
15015 var o = /* eval:var:o */ eval("("+json+")");
15017 throw {message: "JsonReader.read: Json object not found"};
15023 this.metaFromRemote = true;
15024 this.meta = o.metaData;
15025 this.recordType = Roo.data.Record.create(o.metaData.fields);
15026 this.onMetaChange(this.meta, this.recordType, o);
15028 return this.readRecords(o);
15031 // private function a store will implement
15032 onMetaChange : function(meta, recordType, o){
15039 simpleAccess: function(obj, subsc) {
15046 getJsonAccessor: function(){
15048 return function(expr) {
15050 return(re.test(expr))
15051 ? new Function("obj", "return obj." + expr)
15056 return Roo.emptyFn;
15061 * Create a data block containing Roo.data.Records from an XML document.
15062 * @param {Object} o An object which contains an Array of row objects in the property specified
15063 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15064 * which contains the total size of the dataset.
15065 * @return {Object} data A data block which is used by an Roo.data.Store object as
15066 * a cache of Roo.data.Records.
15068 readRecords : function(o){
15070 * After any data loads, the raw JSON data is available for further custom processing.
15074 var s = this.meta, Record = this.recordType,
15075 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15077 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15079 if(s.totalProperty) {
15080 this.getTotal = this.getJsonAccessor(s.totalProperty);
15082 if(s.successProperty) {
15083 this.getSuccess = this.getJsonAccessor(s.successProperty);
15085 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15087 var g = this.getJsonAccessor(s.id);
15088 this.getId = function(rec) {
15090 return (r === undefined || r === "") ? null : r;
15093 this.getId = function(){return null;};
15096 for(var jj = 0; jj < fl; jj++){
15098 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15099 this.ef[jj] = this.getJsonAccessor(map);
15103 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15104 if(s.totalProperty){
15105 var vt = parseInt(this.getTotal(o), 10);
15110 if(s.successProperty){
15111 var vs = this.getSuccess(o);
15112 if(vs === false || vs === 'false'){
15117 for(var i = 0; i < c; i++){
15120 var id = this.getId(n);
15121 for(var j = 0; j < fl; j++){
15123 var v = this.ef[j](n);
15125 Roo.log('missing convert for ' + f.name);
15129 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15131 var record = new Record(values, id);
15133 records[i] = record;
15139 totalRecords : totalRecords
15142 // used when loading children.. @see loadDataFromChildren
15143 toLoadData: function(rec)
15145 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15146 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15147 return { data : data, total : data.length };
15152 * Ext JS Library 1.1.1
15153 * Copyright(c) 2006-2007, Ext JS, LLC.
15155 * Originally Released Under LGPL - original licence link has changed is not relivant.
15158 * <script type="text/javascript">
15162 * @class Roo.data.ArrayReader
15163 * @extends Roo.data.DataReader
15164 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15165 * Each element of that Array represents a row of data fields. The
15166 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15167 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15171 var RecordDef = Roo.data.Record.create([
15172 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15173 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15175 var myReader = new Roo.data.ArrayReader({
15176 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15180 * This would consume an Array like this:
15182 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15186 * Create a new JsonReader
15187 * @param {Object} meta Metadata configuration options.
15188 * @param {Object|Array} recordType Either an Array of field definition objects
15190 * @cfg {Array} fields Array of field definition objects
15191 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15192 * as specified to {@link Roo.data.Record#create},
15193 * or an {@link Roo.data.Record} object
15196 * created using {@link Roo.data.Record#create}.
15198 Roo.data.ArrayReader = function(meta, recordType)
15200 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15203 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15206 * Create a data block containing Roo.data.Records from an XML document.
15207 * @param {Object} o An Array of row objects which represents the dataset.
15208 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15209 * a cache of Roo.data.Records.
15211 readRecords : function(o)
15213 var sid = this.meta ? this.meta.id : null;
15214 var recordType = this.recordType, fields = recordType.prototype.fields;
15217 for(var i = 0; i < root.length; i++){
15220 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15221 for(var j = 0, jlen = fields.length; j < jlen; j++){
15222 var f = fields.items[j];
15223 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15224 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15226 values[f.name] = v;
15228 var record = new recordType(values, id);
15230 records[records.length] = record;
15234 totalRecords : records.length
15237 // used when loading children.. @see loadDataFromChildren
15238 toLoadData: function(rec)
15240 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15241 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15252 * @class Roo.bootstrap.ComboBox
15253 * @extends Roo.bootstrap.TriggerField
15254 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15255 * @cfg {Boolean} append (true|false) default false
15256 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15257 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15258 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15259 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15260 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15261 * @cfg {Boolean} animate default true
15262 * @cfg {Boolean} emptyResultText only for touch device
15263 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15264 * @cfg {String} emptyTitle default ''
15265 * @cfg {Number} width fixed with? experimental
15267 * Create a new ComboBox.
15268 * @param {Object} config Configuration options
15270 Roo.bootstrap.ComboBox = function(config){
15271 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15275 * Fires when the dropdown list is expanded
15276 * @param {Roo.bootstrap.ComboBox} combo This combo box
15281 * Fires when the dropdown list is collapsed
15282 * @param {Roo.bootstrap.ComboBox} combo This combo box
15286 * @event beforeselect
15287 * Fires before a list item is selected. Return false to cancel the selection.
15288 * @param {Roo.bootstrap.ComboBox} combo This combo box
15289 * @param {Roo.data.Record} record The data record returned from the underlying store
15290 * @param {Number} index The index of the selected item in the dropdown list
15292 'beforeselect' : true,
15295 * Fires when a list item is selected
15296 * @param {Roo.bootstrap.ComboBox} combo This combo box
15297 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15298 * @param {Number} index The index of the selected item in the dropdown list
15302 * @event beforequery
15303 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15304 * The event object passed has these properties:
15305 * @param {Roo.bootstrap.ComboBox} combo This combo box
15306 * @param {String} query The query
15307 * @param {Boolean} forceAll true to force "all" query
15308 * @param {Boolean} cancel true to cancel the query
15309 * @param {Object} e The query event object
15311 'beforequery': true,
15314 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15315 * @param {Roo.bootstrap.ComboBox} combo This combo box
15320 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15321 * @param {Roo.bootstrap.ComboBox} combo This combo box
15322 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15327 * Fires when the remove value from the combobox array
15328 * @param {Roo.bootstrap.ComboBox} combo This combo box
15332 * @event afterremove
15333 * Fires when the remove value from the combobox array
15334 * @param {Roo.bootstrap.ComboBox} combo This combo box
15336 'afterremove' : true,
15338 * @event specialfilter
15339 * Fires when specialfilter
15340 * @param {Roo.bootstrap.ComboBox} combo This combo box
15342 'specialfilter' : true,
15345 * Fires when tick the element
15346 * @param {Roo.bootstrap.ComboBox} combo This combo box
15350 * @event touchviewdisplay
15351 * Fires when touch view require special display (default is using displayField)
15352 * @param {Roo.bootstrap.ComboBox} combo This combo box
15353 * @param {Object} cfg set html .
15355 'touchviewdisplay' : true
15360 this.tickItems = [];
15362 this.selectedIndex = -1;
15363 if(this.mode == 'local'){
15364 if(config.queryDelay === undefined){
15365 this.queryDelay = 10;
15367 if(config.minChars === undefined){
15373 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15376 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15377 * rendering into an Roo.Editor, defaults to false)
15380 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15381 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15384 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15387 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15388 * the dropdown list (defaults to undefined, with no header element)
15392 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15396 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15398 listWidth: undefined,
15400 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15401 * mode = 'remote' or 'text' if mode = 'local')
15403 displayField: undefined,
15406 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15407 * mode = 'remote' or 'value' if mode = 'local').
15408 * Note: use of a valueField requires the user make a selection
15409 * in order for a value to be mapped.
15411 valueField: undefined,
15413 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15418 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15419 * field's data value (defaults to the underlying DOM element's name)
15421 hiddenName: undefined,
15423 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15427 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15429 selectedClass: 'active',
15432 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15436 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15437 * anchor positions (defaults to 'tl-bl')
15439 listAlign: 'tl-bl?',
15441 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15445 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15446 * query specified by the allQuery config option (defaults to 'query')
15448 triggerAction: 'query',
15450 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15451 * (defaults to 4, does not apply if editable = false)
15455 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15456 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15460 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15461 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15465 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15466 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15470 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15471 * when editable = true (defaults to false)
15473 selectOnFocus:false,
15475 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15477 queryParam: 'query',
15479 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15480 * when mode = 'remote' (defaults to 'Loading...')
15482 loadingText: 'Loading...',
15484 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15488 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15492 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15493 * traditional select (defaults to true)
15497 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15501 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15505 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15506 * listWidth has a higher value)
15510 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15511 * allow the user to set arbitrary text into the field (defaults to false)
15513 forceSelection:false,
15515 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15516 * if typeAhead = true (defaults to 250)
15518 typeAheadDelay : 250,
15520 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15521 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15523 valueNotFoundText : undefined,
15525 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15527 blockFocus : false,
15530 * @cfg {Boolean} disableClear Disable showing of clear button.
15532 disableClear : false,
15534 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15536 alwaysQuery : false,
15539 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15544 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15546 invalidClass : "has-warning",
15549 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15551 validClass : "has-success",
15554 * @cfg {Boolean} specialFilter (true|false) special filter default false
15556 specialFilter : false,
15559 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15561 mobileTouchView : true,
15564 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15566 useNativeIOS : false,
15569 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15571 mobile_restrict_height : false,
15573 ios_options : false,
15585 btnPosition : 'right',
15586 triggerList : true,
15587 showToggleBtn : true,
15589 emptyResultText: 'Empty',
15590 triggerText : 'Select',
15594 // element that contains real text value.. (when hidden is used..)
15596 getAutoCreate : function()
15601 * Render classic select for iso
15604 if(Roo.isIOS && this.useNativeIOS){
15605 cfg = this.getAutoCreateNativeIOS();
15613 if(Roo.isTouch && this.mobileTouchView){
15614 cfg = this.getAutoCreateTouchView();
15621 if(!this.tickable){
15622 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15627 * ComboBox with tickable selections
15630 var align = this.labelAlign || this.parentLabelAlign();
15633 cls : 'form-group roo-combobox-tickable' //input-group
15636 var btn_text_select = '';
15637 var btn_text_done = '';
15638 var btn_text_cancel = '';
15640 if (this.btn_text_show) {
15641 btn_text_select = 'Select';
15642 btn_text_done = 'Done';
15643 btn_text_cancel = 'Cancel';
15648 cls : 'tickable-buttons',
15653 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15654 //html : this.triggerText
15655 html: btn_text_select
15661 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15663 html: btn_text_done
15669 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15671 html: btn_text_cancel
15677 buttons.cn.unshift({
15679 cls: 'roo-select2-search-field-input'
15685 Roo.each(buttons.cn, function(c){
15687 c.cls += ' btn-' + _this.size;
15690 if (_this.disabled) {
15697 style : 'display: contents',
15702 cls: 'form-hidden-field'
15706 cls: 'roo-select2-choices',
15710 cls: 'roo-select2-search-field',
15721 cls: 'roo-select2-container input-group roo-select2-container-multi',
15727 // cls: 'typeahead typeahead-long dropdown-menu',
15728 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15733 if(this.hasFeedback && !this.allowBlank){
15737 cls: 'glyphicon form-control-feedback'
15740 combobox.cn.push(feedback);
15747 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15748 tooltip : 'This field is required'
15750 if (Roo.bootstrap.version == 4) {
15753 style : 'display:none'
15756 if (align ==='left' && this.fieldLabel.length) {
15758 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15765 cls : 'control-label col-form-label',
15766 html : this.fieldLabel
15778 var labelCfg = cfg.cn[1];
15779 var contentCfg = cfg.cn[2];
15782 if(this.indicatorpos == 'right'){
15788 cls : 'control-label col-form-label',
15792 html : this.fieldLabel
15808 labelCfg = cfg.cn[0];
15809 contentCfg = cfg.cn[1];
15813 if(this.labelWidth > 12){
15814 labelCfg.style = "width: " + this.labelWidth + 'px';
15816 if(this.width * 1 > 0){
15817 contentCfg.style = "width: " + this.width + 'px';
15819 if(this.labelWidth < 13 && this.labelmd == 0){
15820 this.labelmd = this.labelWidth;
15823 if(this.labellg > 0){
15824 labelCfg.cls += ' col-lg-' + this.labellg;
15825 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15828 if(this.labelmd > 0){
15829 labelCfg.cls += ' col-md-' + this.labelmd;
15830 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15833 if(this.labelsm > 0){
15834 labelCfg.cls += ' col-sm-' + this.labelsm;
15835 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15838 if(this.labelxs > 0){
15839 labelCfg.cls += ' col-xs-' + this.labelxs;
15840 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15844 } else if ( this.fieldLabel.length) {
15845 // Roo.log(" label");
15850 //cls : 'input-group-addon',
15851 html : this.fieldLabel
15856 if(this.indicatorpos == 'right'){
15860 //cls : 'input-group-addon',
15861 html : this.fieldLabel
15871 // Roo.log(" no label && no align");
15878 ['xs','sm','md','lg'].map(function(size){
15879 if (settings[size]) {
15880 cfg.cls += ' col-' + size + '-' + settings[size];
15888 _initEventsCalled : false,
15891 initEvents: function()
15893 if (this._initEventsCalled) { // as we call render... prevent looping...
15896 this._initEventsCalled = true;
15899 throw "can not find store for combo";
15902 this.indicator = this.indicatorEl();
15904 this.store = Roo.factory(this.store, Roo.data);
15905 this.store.parent = this;
15907 // if we are building from html. then this element is so complex, that we can not really
15908 // use the rendered HTML.
15909 // so we have to trash and replace the previous code.
15910 if (Roo.XComponent.build_from_html) {
15911 // remove this element....
15912 var e = this.el.dom, k=0;
15913 while (e ) { e = e.previousSibling; ++k;}
15918 this.rendered = false;
15920 this.render(this.parent().getChildContainer(true), k);
15923 if(Roo.isIOS && this.useNativeIOS){
15924 this.initIOSView();
15932 if(Roo.isTouch && this.mobileTouchView){
15933 this.initTouchView();
15938 this.initTickableEvents();
15942 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15944 if(this.hiddenName){
15946 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15948 this.hiddenField.dom.value =
15949 this.hiddenValue !== undefined ? this.hiddenValue :
15950 this.value !== undefined ? this.value : '';
15952 // prevent input submission
15953 this.el.dom.removeAttribute('name');
15954 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15959 // this.el.dom.setAttribute('autocomplete', 'off');
15962 var cls = 'x-combo-list';
15964 //this.list = new Roo.Layer({
15965 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15971 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15972 _this.list.setWidth(lw);
15975 this.list.on('mouseover', this.onViewOver, this);
15976 this.list.on('mousemove', this.onViewMove, this);
15977 this.list.on('scroll', this.onViewScroll, this);
15980 this.list.swallowEvent('mousewheel');
15981 this.assetHeight = 0;
15984 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15985 this.assetHeight += this.header.getHeight();
15988 this.innerList = this.list.createChild({cls:cls+'-inner'});
15989 this.innerList.on('mouseover', this.onViewOver, this);
15990 this.innerList.on('mousemove', this.onViewMove, this);
15991 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15993 if(this.allowBlank && !this.pageSize && !this.disableClear){
15994 this.footer = this.list.createChild({cls:cls+'-ft'});
15995 this.pageTb = new Roo.Toolbar(this.footer);
15999 this.footer = this.list.createChild({cls:cls+'-ft'});
16000 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16001 {pageSize: this.pageSize});
16005 if (this.pageTb && this.allowBlank && !this.disableClear) {
16007 this.pageTb.add(new Roo.Toolbar.Fill(), {
16008 cls: 'x-btn-icon x-btn-clear',
16010 handler: function()
16013 _this.clearValue();
16014 _this.onSelect(false, -1);
16019 this.assetHeight += this.footer.getHeight();
16024 this.tpl = Roo.bootstrap.version == 4 ?
16025 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16026 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16029 this.view = new Roo.View(this.list, this.tpl, {
16030 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16032 //this.view.wrapEl.setDisplayed(false);
16033 this.view.on('click', this.onViewClick, this);
16036 this.store.on('beforeload', this.onBeforeLoad, this);
16037 this.store.on('load', this.onLoad, this);
16038 this.store.on('loadexception', this.onLoadException, this);
16040 if(this.resizable){
16041 this.resizer = new Roo.Resizable(this.list, {
16042 pinned:true, handles:'se'
16044 this.resizer.on('resize', function(r, w, h){
16045 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16046 this.listWidth = w;
16047 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16048 this.restrictHeight();
16050 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16053 if(!this.editable){
16054 this.editable = true;
16055 this.setEditable(false);
16060 if (typeof(this.events.add.listeners) != 'undefined') {
16062 this.addicon = this.wrap.createChild(
16063 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16065 this.addicon.on('click', function(e) {
16066 this.fireEvent('add', this);
16069 if (typeof(this.events.edit.listeners) != 'undefined') {
16071 this.editicon = this.wrap.createChild(
16072 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16073 if (this.addicon) {
16074 this.editicon.setStyle('margin-left', '40px');
16076 this.editicon.on('click', function(e) {
16078 // we fire even if inothing is selected..
16079 this.fireEvent('edit', this, this.lastData );
16085 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16086 "up" : function(e){
16087 this.inKeyMode = true;
16091 "down" : function(e){
16092 if(!this.isExpanded()){
16093 this.onTriggerClick();
16095 this.inKeyMode = true;
16100 "enter" : function(e){
16101 // this.onViewClick();
16105 if(this.fireEvent("specialkey", this, e)){
16106 this.onViewClick(false);
16112 "esc" : function(e){
16116 "tab" : function(e){
16119 if(this.fireEvent("specialkey", this, e)){
16120 this.onViewClick(false);
16128 doRelay : function(foo, bar, hname){
16129 if(hname == 'down' || this.scope.isExpanded()){
16130 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16139 this.queryDelay = Math.max(this.queryDelay || 10,
16140 this.mode == 'local' ? 10 : 250);
16143 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16145 if(this.typeAhead){
16146 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16148 if(this.editable !== false){
16149 this.inputEl().on("keyup", this.onKeyUp, this);
16151 if(this.forceSelection){
16152 this.inputEl().on('blur', this.doForce, this);
16156 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16157 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16161 initTickableEvents: function()
16165 if(this.hiddenName){
16167 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16169 this.hiddenField.dom.value =
16170 this.hiddenValue !== undefined ? this.hiddenValue :
16171 this.value !== undefined ? this.value : '';
16173 // prevent input submission
16174 this.el.dom.removeAttribute('name');
16175 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16180 // this.list = this.el.select('ul.dropdown-menu',true).first();
16182 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16183 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16184 if(this.triggerList){
16185 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16188 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16189 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16191 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16192 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16194 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16195 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16197 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16198 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16199 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16202 this.cancelBtn.hide();
16207 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16208 _this.list.setWidth(lw);
16211 this.list.on('mouseover', this.onViewOver, this);
16212 this.list.on('mousemove', this.onViewMove, this);
16214 this.list.on('scroll', this.onViewScroll, this);
16217 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16218 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16221 this.view = new Roo.View(this.list, this.tpl, {
16226 selectedClass: this.selectedClass
16229 //this.view.wrapEl.setDisplayed(false);
16230 this.view.on('click', this.onViewClick, this);
16234 this.store.on('beforeload', this.onBeforeLoad, this);
16235 this.store.on('load', this.onLoad, this);
16236 this.store.on('loadexception', this.onLoadException, this);
16239 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16240 "up" : function(e){
16241 this.inKeyMode = true;
16245 "down" : function(e){
16246 this.inKeyMode = true;
16250 "enter" : function(e){
16251 if(this.fireEvent("specialkey", this, e)){
16252 this.onViewClick(false);
16258 "esc" : function(e){
16259 this.onTickableFooterButtonClick(e, false, false);
16262 "tab" : function(e){
16263 this.fireEvent("specialkey", this, e);
16265 this.onTickableFooterButtonClick(e, false, false);
16272 doRelay : function(e, fn, key){
16273 if(this.scope.isExpanded()){
16274 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16283 this.queryDelay = Math.max(this.queryDelay || 10,
16284 this.mode == 'local' ? 10 : 250);
16287 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16289 if(this.typeAhead){
16290 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16293 if(this.editable !== false){
16294 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16297 this.indicator = this.indicatorEl();
16299 if(this.indicator){
16300 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16301 this.indicator.hide();
16306 onDestroy : function(){
16308 this.view.setStore(null);
16309 this.view.el.removeAllListeners();
16310 this.view.el.remove();
16311 this.view.purgeListeners();
16314 this.list.dom.innerHTML = '';
16318 this.store.un('beforeload', this.onBeforeLoad, this);
16319 this.store.un('load', this.onLoad, this);
16320 this.store.un('loadexception', this.onLoadException, this);
16322 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16326 fireKey : function(e){
16327 if(e.isNavKeyPress() && !this.list.isVisible()){
16328 this.fireEvent("specialkey", this, e);
16333 onResize: function(w, h)
16337 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16339 // if(typeof w != 'number'){
16340 // // we do not handle it!?!?
16343 // var tw = this.trigger.getWidth();
16344 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16345 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16347 // this.inputEl().setWidth( this.adjustWidth('input', x));
16349 // //this.trigger.setStyle('left', x+'px');
16351 // if(this.list && this.listWidth === undefined){
16352 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16353 // this.list.setWidth(lw);
16354 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16362 * Allow or prevent the user from directly editing the field text. If false is passed,
16363 * the user will only be able to select from the items defined in the dropdown list. This method
16364 * is the runtime equivalent of setting the 'editable' config option at config time.
16365 * @param {Boolean} value True to allow the user to directly edit the field text
16367 setEditable : function(value){
16368 if(value == this.editable){
16371 this.editable = value;
16373 this.inputEl().dom.setAttribute('readOnly', true);
16374 this.inputEl().on('mousedown', this.onTriggerClick, this);
16375 this.inputEl().addClass('x-combo-noedit');
16377 this.inputEl().dom.setAttribute('readOnly', false);
16378 this.inputEl().un('mousedown', this.onTriggerClick, this);
16379 this.inputEl().removeClass('x-combo-noedit');
16385 onBeforeLoad : function(combo,opts){
16386 if(!this.hasFocus){
16390 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16392 this.restrictHeight();
16393 this.selectedIndex = -1;
16397 onLoad : function(){
16399 this.hasQuery = false;
16401 if(!this.hasFocus){
16405 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16406 this.loading.hide();
16409 if(this.store.getCount() > 0){
16412 this.restrictHeight();
16413 if(this.lastQuery == this.allQuery){
16414 if(this.editable && !this.tickable){
16415 this.inputEl().dom.select();
16419 !this.selectByValue(this.value, true) &&
16422 !this.store.lastOptions ||
16423 typeof(this.store.lastOptions.add) == 'undefined' ||
16424 this.store.lastOptions.add != true
16427 this.select(0, true);
16430 if(this.autoFocus){
16433 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16434 this.taTask.delay(this.typeAheadDelay);
16438 this.onEmptyResults();
16444 onLoadException : function()
16446 this.hasQuery = false;
16448 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16449 this.loading.hide();
16452 if(this.tickable && this.editable){
16457 // only causes errors at present
16458 //Roo.log(this.store.reader.jsonData);
16459 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16461 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16467 onTypeAhead : function(){
16468 if(this.store.getCount() > 0){
16469 var r = this.store.getAt(0);
16470 var newValue = r.data[this.displayField];
16471 var len = newValue.length;
16472 var selStart = this.getRawValue().length;
16474 if(selStart != len){
16475 this.setRawValue(newValue);
16476 this.selectText(selStart, newValue.length);
16482 onSelect : function(record, index){
16484 if(this.fireEvent('beforeselect', this, record, index) !== false){
16486 this.setFromData(index > -1 ? record.data : false);
16489 this.fireEvent('select', this, record, index);
16494 * Returns the currently selected field value or empty string if no value is set.
16495 * @return {String} value The selected value
16497 getValue : function()
16499 if(Roo.isIOS && this.useNativeIOS){
16500 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16504 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16507 if(this.valueField){
16508 return typeof this.value != 'undefined' ? this.value : '';
16510 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16514 getRawValue : function()
16516 if(Roo.isIOS && this.useNativeIOS){
16517 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16520 var v = this.inputEl().getValue();
16526 * Clears any text/value currently set in the field
16528 clearValue : function(){
16530 if(this.hiddenField){
16531 this.hiddenField.dom.value = '';
16534 this.setRawValue('');
16535 this.lastSelectionText = '';
16536 this.lastData = false;
16538 var close = this.closeTriggerEl();
16549 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16550 * will be displayed in the field. If the value does not match the data value of an existing item,
16551 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16552 * Otherwise the field will be blank (although the value will still be set).
16553 * @param {String} value The value to match
16555 setValue : function(v)
16557 if(Roo.isIOS && this.useNativeIOS){
16558 this.setIOSValue(v);
16568 if(this.valueField){
16569 var r = this.findRecord(this.valueField, v);
16571 text = r.data[this.displayField];
16572 }else if(this.valueNotFoundText !== undefined){
16573 text = this.valueNotFoundText;
16576 this.lastSelectionText = text;
16577 if(this.hiddenField){
16578 this.hiddenField.dom.value = v;
16580 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16583 var close = this.closeTriggerEl();
16586 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16592 * @property {Object} the last set data for the element
16597 * Sets the value of the field based on a object which is related to the record format for the store.
16598 * @param {Object} value the value to set as. or false on reset?
16600 setFromData : function(o){
16607 var dv = ''; // display value
16608 var vv = ''; // value value..
16610 if (this.displayField) {
16611 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16613 // this is an error condition!!!
16614 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16617 if(this.valueField){
16618 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16621 var close = this.closeTriggerEl();
16624 if(dv.length || vv * 1 > 0){
16626 this.blockFocus=true;
16632 if(this.hiddenField){
16633 this.hiddenField.dom.value = vv;
16635 this.lastSelectionText = dv;
16636 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16640 // no hidden field.. - we store the value in 'value', but still display
16641 // display field!!!!
16642 this.lastSelectionText = dv;
16643 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16650 reset : function(){
16651 // overridden so that last data is reset..
16658 this.setValue(this.originalValue);
16659 //this.clearInvalid();
16660 this.lastData = false;
16662 this.view.clearSelections();
16668 findRecord : function(prop, value){
16670 if(this.store.getCount() > 0){
16671 this.store.each(function(r){
16672 if(r.data[prop] == value){
16682 getName: function()
16684 // returns hidden if it's set..
16685 if (!this.rendered) {return ''};
16686 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16690 onViewMove : function(e, t){
16691 this.inKeyMode = false;
16695 onViewOver : function(e, t){
16696 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16699 var item = this.view.findItemFromChild(t);
16702 var index = this.view.indexOf(item);
16703 this.select(index, false);
16708 onViewClick : function(view, doFocus, el, e)
16710 var index = this.view.getSelectedIndexes()[0];
16712 var r = this.store.getAt(index);
16716 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16723 Roo.each(this.tickItems, function(v,k){
16725 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16727 _this.tickItems.splice(k, 1);
16729 if(typeof(e) == 'undefined' && view == false){
16730 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16742 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16743 this.tickItems.push(r.data);
16746 if(typeof(e) == 'undefined' && view == false){
16747 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16754 this.onSelect(r, index);
16756 if(doFocus !== false && !this.blockFocus){
16757 this.inputEl().focus();
16762 restrictHeight : function(){
16763 //this.innerList.dom.style.height = '';
16764 //var inner = this.innerList.dom;
16765 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16766 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16767 //this.list.beginUpdate();
16768 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16769 this.list.alignTo(this.inputEl(), this.listAlign);
16770 this.list.alignTo(this.inputEl(), this.listAlign);
16771 //this.list.endUpdate();
16775 onEmptyResults : function(){
16777 if(this.tickable && this.editable){
16778 this.hasFocus = false;
16779 this.restrictHeight();
16787 * Returns true if the dropdown list is expanded, else false.
16789 isExpanded : function(){
16790 return this.list.isVisible();
16794 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16795 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16796 * @param {String} value The data value of the item to select
16797 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16798 * selected item if it is not currently in view (defaults to true)
16799 * @return {Boolean} True if the value matched an item in the list, else false
16801 selectByValue : function(v, scrollIntoView){
16802 if(v !== undefined && v !== null){
16803 var r = this.findRecord(this.valueField || this.displayField, v);
16805 this.select(this.store.indexOf(r), scrollIntoView);
16813 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16814 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16815 * @param {Number} index The zero-based index of the list item to select
16816 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16817 * selected item if it is not currently in view (defaults to true)
16819 select : function(index, scrollIntoView){
16820 this.selectedIndex = index;
16821 this.view.select(index);
16822 if(scrollIntoView !== false){
16823 var el = this.view.getNode(index);
16825 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16828 this.list.scrollChildIntoView(el, false);
16834 selectNext : function(){
16835 var ct = this.store.getCount();
16837 if(this.selectedIndex == -1){
16839 }else if(this.selectedIndex < ct-1){
16840 this.select(this.selectedIndex+1);
16846 selectPrev : function(){
16847 var ct = this.store.getCount();
16849 if(this.selectedIndex == -1){
16851 }else if(this.selectedIndex != 0){
16852 this.select(this.selectedIndex-1);
16858 onKeyUp : function(e){
16859 if(this.editable !== false && !e.isSpecialKey()){
16860 this.lastKey = e.getKey();
16861 this.dqTask.delay(this.queryDelay);
16866 validateBlur : function(){
16867 return !this.list || !this.list.isVisible();
16871 initQuery : function(){
16873 var v = this.getRawValue();
16875 if(this.tickable && this.editable){
16876 v = this.tickableInputEl().getValue();
16883 doForce : function(){
16884 if(this.inputEl().dom.value.length > 0){
16885 this.inputEl().dom.value =
16886 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16892 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16893 * query allowing the query action to be canceled if needed.
16894 * @param {String} query The SQL query to execute
16895 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16896 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16897 * saved in the current store (defaults to false)
16899 doQuery : function(q, forceAll){
16901 if(q === undefined || q === null){
16906 forceAll: forceAll,
16910 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16915 forceAll = qe.forceAll;
16916 if(forceAll === true || (q.length >= this.minChars)){
16918 this.hasQuery = true;
16920 if(this.lastQuery != q || this.alwaysQuery){
16921 this.lastQuery = q;
16922 if(this.mode == 'local'){
16923 this.selectedIndex = -1;
16925 this.store.clearFilter();
16928 if(this.specialFilter){
16929 this.fireEvent('specialfilter', this);
16934 this.store.filter(this.displayField, q);
16937 this.store.fireEvent("datachanged", this.store);
16944 this.store.baseParams[this.queryParam] = q;
16946 var options = {params : this.getParams(q)};
16949 options.add = true;
16950 options.params.start = this.page * this.pageSize;
16953 this.store.load(options);
16956 * this code will make the page width larger, at the beginning, the list not align correctly,
16957 * we should expand the list on onLoad
16958 * so command out it
16963 this.selectedIndex = -1;
16968 this.loadNext = false;
16972 getParams : function(q){
16974 //p[this.queryParam] = q;
16978 p.limit = this.pageSize;
16984 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16986 collapse : function(){
16987 if(!this.isExpanded()){
16993 this.hasFocus = false;
16997 this.cancelBtn.hide();
16998 this.trigger.show();
17001 this.tickableInputEl().dom.value = '';
17002 this.tickableInputEl().blur();
17007 Roo.get(document).un('mousedown', this.collapseIf, this);
17008 Roo.get(document).un('mousewheel', this.collapseIf, this);
17009 if (!this.editable) {
17010 Roo.get(document).un('keydown', this.listKeyPress, this);
17012 this.fireEvent('collapse', this);
17018 collapseIf : function(e){
17019 var in_combo = e.within(this.el);
17020 var in_list = e.within(this.list);
17021 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17023 if (in_combo || in_list || is_list) {
17024 //e.stopPropagation();
17029 this.onTickableFooterButtonClick(e, false, false);
17037 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17039 expand : function(){
17041 if(this.isExpanded() || !this.hasFocus){
17045 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17046 this.list.setWidth(lw);
17052 this.restrictHeight();
17056 this.tickItems = Roo.apply([], this.item);
17059 this.cancelBtn.show();
17060 this.trigger.hide();
17063 this.tickableInputEl().focus();
17068 Roo.get(document).on('mousedown', this.collapseIf, this);
17069 Roo.get(document).on('mousewheel', this.collapseIf, this);
17070 if (!this.editable) {
17071 Roo.get(document).on('keydown', this.listKeyPress, this);
17074 this.fireEvent('expand', this);
17078 // Implements the default empty TriggerField.onTriggerClick function
17079 onTriggerClick : function(e)
17081 Roo.log('trigger click');
17083 if(this.disabled || !this.triggerList){
17088 this.loadNext = false;
17090 if(this.isExpanded()){
17092 if (!this.blockFocus) {
17093 this.inputEl().focus();
17097 this.hasFocus = true;
17098 if(this.triggerAction == 'all') {
17099 this.doQuery(this.allQuery, true);
17101 this.doQuery(this.getRawValue());
17103 if (!this.blockFocus) {
17104 this.inputEl().focus();
17109 onTickableTriggerClick : function(e)
17116 this.loadNext = false;
17117 this.hasFocus = true;
17119 if(this.triggerAction == 'all') {
17120 this.doQuery(this.allQuery, true);
17122 this.doQuery(this.getRawValue());
17126 onSearchFieldClick : function(e)
17128 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17129 this.onTickableFooterButtonClick(e, false, false);
17133 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17138 this.loadNext = false;
17139 this.hasFocus = true;
17141 if(this.triggerAction == 'all') {
17142 this.doQuery(this.allQuery, true);
17144 this.doQuery(this.getRawValue());
17148 listKeyPress : function(e)
17150 //Roo.log('listkeypress');
17151 // scroll to first matching element based on key pres..
17152 if (e.isSpecialKey()) {
17155 var k = String.fromCharCode(e.getKey()).toUpperCase();
17158 var csel = this.view.getSelectedNodes();
17159 var cselitem = false;
17161 var ix = this.view.indexOf(csel[0]);
17162 cselitem = this.store.getAt(ix);
17163 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17169 this.store.each(function(v) {
17171 // start at existing selection.
17172 if (cselitem.id == v.id) {
17178 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17179 match = this.store.indexOf(v);
17185 if (match === false) {
17186 return true; // no more action?
17189 this.view.select(match);
17190 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17191 sn.scrollIntoView(sn.dom.parentNode, false);
17194 onViewScroll : function(e, t){
17196 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){
17200 this.hasQuery = true;
17202 this.loading = this.list.select('.loading', true).first();
17204 if(this.loading === null){
17205 this.list.createChild({
17207 cls: 'loading roo-select2-more-results roo-select2-active',
17208 html: 'Loading more results...'
17211 this.loading = this.list.select('.loading', true).first();
17213 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17215 this.loading.hide();
17218 this.loading.show();
17223 this.loadNext = true;
17225 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17230 addItem : function(o)
17232 var dv = ''; // display value
17234 if (this.displayField) {
17235 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17237 // this is an error condition!!!
17238 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17245 var choice = this.choices.createChild({
17247 cls: 'roo-select2-search-choice',
17256 cls: 'roo-select2-search-choice-close fa fa-times',
17261 }, this.searchField);
17263 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17265 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17273 this.inputEl().dom.value = '';
17278 onRemoveItem : function(e, _self, o)
17280 e.preventDefault();
17282 this.lastItem = Roo.apply([], this.item);
17284 var index = this.item.indexOf(o.data) * 1;
17287 Roo.log('not this item?!');
17291 this.item.splice(index, 1);
17296 this.fireEvent('remove', this, e);
17302 syncValue : function()
17304 if(!this.item.length){
17311 Roo.each(this.item, function(i){
17312 if(_this.valueField){
17313 value.push(i[_this.valueField]);
17320 this.value = value.join(',');
17322 if(this.hiddenField){
17323 this.hiddenField.dom.value = this.value;
17326 this.store.fireEvent("datachanged", this.store);
17331 clearItem : function()
17333 if(!this.multiple){
17339 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17347 if(this.tickable && !Roo.isTouch){
17348 this.view.refresh();
17352 inputEl: function ()
17354 if(Roo.isIOS && this.useNativeIOS){
17355 return this.el.select('select.roo-ios-select', true).first();
17358 if(Roo.isTouch && this.mobileTouchView){
17359 return this.el.select('input.form-control',true).first();
17363 return this.searchField;
17366 return this.el.select('input.form-control',true).first();
17369 onTickableFooterButtonClick : function(e, btn, el)
17371 e.preventDefault();
17373 this.lastItem = Roo.apply([], this.item);
17375 if(btn && btn.name == 'cancel'){
17376 this.tickItems = Roo.apply([], this.item);
17385 Roo.each(this.tickItems, function(o){
17393 validate : function()
17395 if(this.getVisibilityEl().hasClass('hidden')){
17399 var v = this.getRawValue();
17402 v = this.getValue();
17405 if(this.disabled || this.allowBlank || v.length){
17410 this.markInvalid();
17414 tickableInputEl : function()
17416 if(!this.tickable || !this.editable){
17417 return this.inputEl();
17420 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17424 getAutoCreateTouchView : function()
17429 cls: 'form-group' //input-group
17435 type : this.inputType,
17436 cls : 'form-control x-combo-noedit',
17437 autocomplete: 'new-password',
17438 placeholder : this.placeholder || '',
17443 input.name = this.name;
17447 input.cls += ' input-' + this.size;
17450 if (this.disabled) {
17451 input.disabled = true;
17455 cls : 'roo-combobox-wrap',
17462 inputblock.cls += ' input-group';
17464 inputblock.cn.unshift({
17466 cls : 'input-group-addon input-group-prepend input-group-text',
17471 if(this.removable && !this.multiple){
17472 inputblock.cls += ' roo-removable';
17474 inputblock.cn.push({
17477 cls : 'roo-combo-removable-btn close'
17481 if(this.hasFeedback && !this.allowBlank){
17483 inputblock.cls += ' has-feedback';
17485 inputblock.cn.push({
17487 cls: 'glyphicon form-control-feedback'
17494 inputblock.cls += (this.before) ? '' : ' input-group';
17496 inputblock.cn.push({
17498 cls : 'input-group-addon input-group-append input-group-text',
17504 var ibwrap = inputblock;
17509 cls: 'roo-select2-choices',
17513 cls: 'roo-select2-search-field',
17526 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17531 cls: 'form-hidden-field'
17537 if(!this.multiple && this.showToggleBtn){
17543 if (this.caret != false) {
17546 cls: 'fa fa-' + this.caret
17553 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17555 Roo.bootstrap.version == 3 ? caret : '',
17558 cls: 'combobox-clear',
17572 combobox.cls += ' roo-select2-container-multi';
17575 var align = this.labelAlign || this.parentLabelAlign();
17577 if (align ==='left' && this.fieldLabel.length) {
17582 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17583 tooltip : 'This field is required'
17587 cls : 'control-label col-form-label',
17588 html : this.fieldLabel
17592 cls : 'roo-combobox-wrap ',
17599 var labelCfg = cfg.cn[1];
17600 var contentCfg = cfg.cn[2];
17603 if(this.indicatorpos == 'right'){
17608 cls : 'control-label col-form-label',
17612 html : this.fieldLabel
17616 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17617 tooltip : 'This field is required'
17622 cls : "roo-combobox-wrap ",
17630 labelCfg = cfg.cn[0];
17631 contentCfg = cfg.cn[1];
17636 if(this.labelWidth > 12){
17637 labelCfg.style = "width: " + this.labelWidth + 'px';
17640 if(this.labelWidth < 13 && this.labelmd == 0){
17641 this.labelmd = this.labelWidth;
17644 if(this.labellg > 0){
17645 labelCfg.cls += ' col-lg-' + this.labellg;
17646 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17649 if(this.labelmd > 0){
17650 labelCfg.cls += ' col-md-' + this.labelmd;
17651 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17654 if(this.labelsm > 0){
17655 labelCfg.cls += ' col-sm-' + this.labelsm;
17656 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17659 if(this.labelxs > 0){
17660 labelCfg.cls += ' col-xs-' + this.labelxs;
17661 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17665 } else if ( this.fieldLabel.length) {
17669 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17670 tooltip : 'This field is required'
17674 cls : 'control-label',
17675 html : this.fieldLabel
17686 if(this.indicatorpos == 'right'){
17690 cls : 'control-label',
17691 html : this.fieldLabel,
17695 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17696 tooltip : 'This field is required'
17713 var settings = this;
17715 ['xs','sm','md','lg'].map(function(size){
17716 if (settings[size]) {
17717 cfg.cls += ' col-' + size + '-' + settings[size];
17724 initTouchView : function()
17726 this.renderTouchView();
17728 this.touchViewEl.on('scroll', function(){
17729 this.el.dom.scrollTop = 0;
17732 this.originalValue = this.getValue();
17734 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17736 this.inputEl().on("click", this.showTouchView, this);
17737 if (this.triggerEl) {
17738 this.triggerEl.on("click", this.showTouchView, this);
17742 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17743 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17745 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17747 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17748 this.store.on('load', this.onTouchViewLoad, this);
17749 this.store.on('loadexception', this.onTouchViewLoadException, this);
17751 if(this.hiddenName){
17753 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17755 this.hiddenField.dom.value =
17756 this.hiddenValue !== undefined ? this.hiddenValue :
17757 this.value !== undefined ? this.value : '';
17759 this.el.dom.removeAttribute('name');
17760 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17764 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17765 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17768 if(this.removable && !this.multiple){
17769 var close = this.closeTriggerEl();
17771 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17772 close.on('click', this.removeBtnClick, this, close);
17776 * fix the bug in Safari iOS8
17778 this.inputEl().on("focus", function(e){
17779 document.activeElement.blur();
17782 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17789 renderTouchView : function()
17791 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17792 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17794 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17795 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17797 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17798 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17799 this.touchViewBodyEl.setStyle('overflow', 'auto');
17801 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17802 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17804 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17805 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17809 showTouchView : function()
17815 this.touchViewHeaderEl.hide();
17817 if(this.modalTitle.length){
17818 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17819 this.touchViewHeaderEl.show();
17822 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17823 this.touchViewEl.show();
17825 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17827 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17828 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17830 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17832 if(this.modalTitle.length){
17833 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17836 this.touchViewBodyEl.setHeight(bodyHeight);
17840 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17842 this.touchViewEl.addClass(['in','show']);
17845 if(this._touchViewMask){
17846 Roo.get(document.body).addClass("x-body-masked");
17847 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17848 this._touchViewMask.setStyle('z-index', 10000);
17849 this._touchViewMask.addClass('show');
17852 this.doTouchViewQuery();
17856 hideTouchView : function()
17858 this.touchViewEl.removeClass(['in','show']);
17862 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17864 this.touchViewEl.setStyle('display', 'none');
17867 if(this._touchViewMask){
17868 this._touchViewMask.removeClass('show');
17869 Roo.get(document.body).removeClass("x-body-masked");
17873 setTouchViewValue : function()
17880 Roo.each(this.tickItems, function(o){
17885 this.hideTouchView();
17888 doTouchViewQuery : function()
17897 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17901 if(!this.alwaysQuery || this.mode == 'local'){
17902 this.onTouchViewLoad();
17909 onTouchViewBeforeLoad : function(combo,opts)
17915 onTouchViewLoad : function()
17917 if(this.store.getCount() < 1){
17918 this.onTouchViewEmptyResults();
17922 this.clearTouchView();
17924 var rawValue = this.getRawValue();
17926 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17928 this.tickItems = [];
17930 this.store.data.each(function(d, rowIndex){
17931 var row = this.touchViewListGroup.createChild(template);
17933 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17934 row.addClass(d.data.cls);
17937 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17940 html : d.data[this.displayField]
17943 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17944 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17947 row.removeClass('selected');
17948 if(!this.multiple && this.valueField &&
17949 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17952 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17953 row.addClass('selected');
17956 if(this.multiple && this.valueField &&
17957 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17961 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17962 this.tickItems.push(d.data);
17965 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17969 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17971 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17973 if(this.modalTitle.length){
17974 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17977 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17979 if(this.mobile_restrict_height && listHeight < bodyHeight){
17980 this.touchViewBodyEl.setHeight(listHeight);
17985 if(firstChecked && listHeight > bodyHeight){
17986 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17991 onTouchViewLoadException : function()
17993 this.hideTouchView();
17996 onTouchViewEmptyResults : function()
17998 this.clearTouchView();
18000 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18002 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18006 clearTouchView : function()
18008 this.touchViewListGroup.dom.innerHTML = '';
18011 onTouchViewClick : function(e, el, o)
18013 e.preventDefault();
18016 var rowIndex = o.rowIndex;
18018 var r = this.store.getAt(rowIndex);
18020 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18022 if(!this.multiple){
18023 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18024 c.dom.removeAttribute('checked');
18027 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18029 this.setFromData(r.data);
18031 var close = this.closeTriggerEl();
18037 this.hideTouchView();
18039 this.fireEvent('select', this, r, rowIndex);
18044 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18045 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18046 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18050 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18051 this.addItem(r.data);
18052 this.tickItems.push(r.data);
18056 getAutoCreateNativeIOS : function()
18059 cls: 'form-group' //input-group,
18064 cls : 'roo-ios-select'
18068 combobox.name = this.name;
18071 if (this.disabled) {
18072 combobox.disabled = true;
18075 var settings = this;
18077 ['xs','sm','md','lg'].map(function(size){
18078 if (settings[size]) {
18079 cfg.cls += ' col-' + size + '-' + settings[size];
18089 initIOSView : function()
18091 this.store.on('load', this.onIOSViewLoad, this);
18096 onIOSViewLoad : function()
18098 if(this.store.getCount() < 1){
18102 this.clearIOSView();
18104 if(this.allowBlank) {
18106 var default_text = '-- SELECT --';
18108 if(this.placeholder.length){
18109 default_text = this.placeholder;
18112 if(this.emptyTitle.length){
18113 default_text += ' - ' + this.emptyTitle + ' -';
18116 var opt = this.inputEl().createChild({
18119 html : default_text
18123 o[this.valueField] = 0;
18124 o[this.displayField] = default_text;
18126 this.ios_options.push({
18133 this.store.data.each(function(d, rowIndex){
18137 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18138 html = d.data[this.displayField];
18143 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18144 value = d.data[this.valueField];
18153 if(this.value == d.data[this.valueField]){
18154 option['selected'] = true;
18157 var opt = this.inputEl().createChild(option);
18159 this.ios_options.push({
18166 this.inputEl().on('change', function(){
18167 this.fireEvent('select', this);
18172 clearIOSView: function()
18174 this.inputEl().dom.innerHTML = '';
18176 this.ios_options = [];
18179 setIOSValue: function(v)
18183 if(!this.ios_options){
18187 Roo.each(this.ios_options, function(opts){
18189 opts.el.dom.removeAttribute('selected');
18191 if(opts.data[this.valueField] != v){
18195 opts.el.dom.setAttribute('selected', true);
18201 * @cfg {Boolean} grow
18205 * @cfg {Number} growMin
18209 * @cfg {Number} growMax
18218 Roo.apply(Roo.bootstrap.ComboBox, {
18222 cls: 'modal-header',
18244 cls: 'list-group-item',
18248 cls: 'roo-combobox-list-group-item-value'
18252 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18266 listItemCheckbox : {
18268 cls: 'list-group-item',
18272 cls: 'roo-combobox-list-group-item-value'
18276 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18292 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18297 cls: 'modal-footer',
18305 cls: 'col-xs-6 text-left',
18308 cls: 'btn btn-danger roo-touch-view-cancel',
18314 cls: 'col-xs-6 text-right',
18317 cls: 'btn btn-success roo-touch-view-ok',
18328 Roo.apply(Roo.bootstrap.ComboBox, {
18330 touchViewTemplate : {
18332 cls: 'modal fade roo-combobox-touch-view',
18336 cls: 'modal-dialog',
18337 style : 'position:fixed', // we have to fix position....
18341 cls: 'modal-content',
18343 Roo.bootstrap.ComboBox.header,
18344 Roo.bootstrap.ComboBox.body,
18345 Roo.bootstrap.ComboBox.footer
18354 * Ext JS Library 1.1.1
18355 * Copyright(c) 2006-2007, Ext JS, LLC.
18357 * Originally Released Under LGPL - original licence link has changed is not relivant.
18360 * <script type="text/javascript">
18365 * @extends Roo.util.Observable
18366 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18367 * This class also supports single and multi selection modes. <br>
18368 * Create a data model bound view:
18370 var store = new Roo.data.Store(...);
18372 var view = new Roo.View({
18374 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18376 singleSelect: true,
18377 selectedClass: "ydataview-selected",
18381 // listen for node click?
18382 view.on("click", function(vw, index, node, e){
18383 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18387 dataModel.load("foobar.xml");
18389 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18391 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18392 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18394 * Note: old style constructor is still suported (container, template, config)
18397 * Create a new View
18398 * @param {Object} config The config object
18401 Roo.View = function(config, depreciated_tpl, depreciated_config){
18403 this.parent = false;
18405 if (typeof(depreciated_tpl) == 'undefined') {
18406 // new way.. - universal constructor.
18407 Roo.apply(this, config);
18408 this.el = Roo.get(this.el);
18411 this.el = Roo.get(config);
18412 this.tpl = depreciated_tpl;
18413 Roo.apply(this, depreciated_config);
18415 this.wrapEl = this.el.wrap().wrap();
18416 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18419 if(typeof(this.tpl) == "string"){
18420 this.tpl = new Roo.Template(this.tpl);
18422 // support xtype ctors..
18423 this.tpl = new Roo.factory(this.tpl, Roo);
18427 this.tpl.compile();
18432 * @event beforeclick
18433 * Fires before a click is processed. Returns false to cancel the default action.
18434 * @param {Roo.View} this
18435 * @param {Number} index The index of the target node
18436 * @param {HTMLElement} node The target node
18437 * @param {Roo.EventObject} e The raw event object
18439 "beforeclick" : true,
18442 * Fires when a template node is clicked.
18443 * @param {Roo.View} this
18444 * @param {Number} index The index of the target node
18445 * @param {HTMLElement} node The target node
18446 * @param {Roo.EventObject} e The raw event object
18451 * Fires when a template node is double clicked.
18452 * @param {Roo.View} this
18453 * @param {Number} index The index of the target node
18454 * @param {HTMLElement} node The target node
18455 * @param {Roo.EventObject} e The raw event object
18459 * @event contextmenu
18460 * Fires when a template node is right clicked.
18461 * @param {Roo.View} this
18462 * @param {Number} index The index of the target node
18463 * @param {HTMLElement} node The target node
18464 * @param {Roo.EventObject} e The raw event object
18466 "contextmenu" : true,
18468 * @event selectionchange
18469 * Fires when the selected nodes change.
18470 * @param {Roo.View} this
18471 * @param {Array} selections Array of the selected nodes
18473 "selectionchange" : true,
18476 * @event beforeselect
18477 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18478 * @param {Roo.View} this
18479 * @param {HTMLElement} node The node to be selected
18480 * @param {Array} selections Array of currently selected nodes
18482 "beforeselect" : true,
18484 * @event preparedata
18485 * Fires on every row to render, to allow you to change the data.
18486 * @param {Roo.View} this
18487 * @param {Object} data to be rendered (change this)
18489 "preparedata" : true
18497 "click": this.onClick,
18498 "dblclick": this.onDblClick,
18499 "contextmenu": this.onContextMenu,
18503 this.selections = [];
18505 this.cmp = new Roo.CompositeElementLite([]);
18507 this.store = Roo.factory(this.store, Roo.data);
18508 this.setStore(this.store, true);
18511 if ( this.footer && this.footer.xtype) {
18513 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18515 this.footer.dataSource = this.store;
18516 this.footer.container = fctr;
18517 this.footer = Roo.factory(this.footer, Roo);
18518 fctr.insertFirst(this.el);
18520 // this is a bit insane - as the paging toolbar seems to detach the el..
18521 // dom.parentNode.parentNode.parentNode
18522 // they get detached?
18526 Roo.View.superclass.constructor.call(this);
18531 Roo.extend(Roo.View, Roo.util.Observable, {
18534 * @cfg {Roo.data.Store} store Data store to load data from.
18539 * @cfg {String|Roo.Element} el The container element.
18544 * @cfg {String|Roo.Template} tpl The template used by this View
18548 * @cfg {String} dataName the named area of the template to use as the data area
18549 * Works with domtemplates roo-name="name"
18553 * @cfg {String} selectedClass The css class to add to selected nodes
18555 selectedClass : "x-view-selected",
18557 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18562 * @cfg {String} text to display on mask (default Loading)
18566 * @cfg {Boolean} multiSelect Allow multiple selection
18568 multiSelect : false,
18570 * @cfg {Boolean} singleSelect Allow single selection
18572 singleSelect: false,
18575 * @cfg {Boolean} toggleSelect - selecting
18577 toggleSelect : false,
18580 * @cfg {Boolean} tickable - selecting
18585 * Returns the element this view is bound to.
18586 * @return {Roo.Element}
18588 getEl : function(){
18589 return this.wrapEl;
18595 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18597 refresh : function(){
18598 //Roo.log('refresh');
18601 // if we are using something like 'domtemplate', then
18602 // the what gets used is:
18603 // t.applySubtemplate(NAME, data, wrapping data..)
18604 // the outer template then get' applied with
18605 // the store 'extra data'
18606 // and the body get's added to the
18607 // roo-name="data" node?
18608 // <span class='roo-tpl-{name}'></span> ?????
18612 this.clearSelections();
18613 this.el.update("");
18615 var records = this.store.getRange();
18616 if(records.length < 1) {
18618 // is this valid?? = should it render a template??
18620 this.el.update(this.emptyText);
18624 if (this.dataName) {
18625 this.el.update(t.apply(this.store.meta)); //????
18626 el = this.el.child('.roo-tpl-' + this.dataName);
18629 for(var i = 0, len = records.length; i < len; i++){
18630 var data = this.prepareData(records[i].data, i, records[i]);
18631 this.fireEvent("preparedata", this, data, i, records[i]);
18633 var d = Roo.apply({}, data);
18636 Roo.apply(d, {'roo-id' : Roo.id()});
18640 Roo.each(this.parent.item, function(item){
18641 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18644 Roo.apply(d, {'roo-data-checked' : 'checked'});
18648 html[html.length] = Roo.util.Format.trim(
18650 t.applySubtemplate(this.dataName, d, this.store.meta) :
18657 el.update(html.join(""));
18658 this.nodes = el.dom.childNodes;
18659 this.updateIndexes(0);
18664 * Function to override to reformat the data that is sent to
18665 * the template for each node.
18666 * DEPRICATED - use the preparedata event handler.
18667 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18668 * a JSON object for an UpdateManager bound view).
18670 prepareData : function(data, index, record)
18672 this.fireEvent("preparedata", this, data, index, record);
18676 onUpdate : function(ds, record){
18677 // Roo.log('on update');
18678 this.clearSelections();
18679 var index = this.store.indexOf(record);
18680 var n = this.nodes[index];
18681 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18682 n.parentNode.removeChild(n);
18683 this.updateIndexes(index, index);
18689 onAdd : function(ds, records, index)
18691 //Roo.log(['on Add', ds, records, index] );
18692 this.clearSelections();
18693 if(this.nodes.length == 0){
18697 var n = this.nodes[index];
18698 for(var i = 0, len = records.length; i < len; i++){
18699 var d = this.prepareData(records[i].data, i, records[i]);
18701 this.tpl.insertBefore(n, d);
18704 this.tpl.append(this.el, d);
18707 this.updateIndexes(index);
18710 onRemove : function(ds, record, index){
18711 // Roo.log('onRemove');
18712 this.clearSelections();
18713 var el = this.dataName ?
18714 this.el.child('.roo-tpl-' + this.dataName) :
18717 el.dom.removeChild(this.nodes[index]);
18718 this.updateIndexes(index);
18722 * Refresh an individual node.
18723 * @param {Number} index
18725 refreshNode : function(index){
18726 this.onUpdate(this.store, this.store.getAt(index));
18729 updateIndexes : function(startIndex, endIndex){
18730 var ns = this.nodes;
18731 startIndex = startIndex || 0;
18732 endIndex = endIndex || ns.length - 1;
18733 for(var i = startIndex; i <= endIndex; i++){
18734 ns[i].nodeIndex = i;
18739 * Changes the data store this view uses and refresh the view.
18740 * @param {Store} store
18742 setStore : function(store, initial){
18743 if(!initial && this.store){
18744 this.store.un("datachanged", this.refresh);
18745 this.store.un("add", this.onAdd);
18746 this.store.un("remove", this.onRemove);
18747 this.store.un("update", this.onUpdate);
18748 this.store.un("clear", this.refresh);
18749 this.store.un("beforeload", this.onBeforeLoad);
18750 this.store.un("load", this.onLoad);
18751 this.store.un("loadexception", this.onLoad);
18755 store.on("datachanged", this.refresh, this);
18756 store.on("add", this.onAdd, this);
18757 store.on("remove", this.onRemove, this);
18758 store.on("update", this.onUpdate, this);
18759 store.on("clear", this.refresh, this);
18760 store.on("beforeload", this.onBeforeLoad, this);
18761 store.on("load", this.onLoad, this);
18762 store.on("loadexception", this.onLoad, this);
18770 * onbeforeLoad - masks the loading area.
18773 onBeforeLoad : function(store,opts)
18775 //Roo.log('onBeforeLoad');
18777 this.el.update("");
18779 this.el.mask(this.mask ? this.mask : "Loading" );
18781 onLoad : function ()
18788 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18789 * @param {HTMLElement} node
18790 * @return {HTMLElement} The template node
18792 findItemFromChild : function(node){
18793 var el = this.dataName ?
18794 this.el.child('.roo-tpl-' + this.dataName,true) :
18797 if(!node || node.parentNode == el){
18800 var p = node.parentNode;
18801 while(p && p != el){
18802 if(p.parentNode == el){
18811 onClick : function(e){
18812 var item = this.findItemFromChild(e.getTarget());
18814 var index = this.indexOf(item);
18815 if(this.onItemClick(item, index, e) !== false){
18816 this.fireEvent("click", this, index, item, e);
18819 this.clearSelections();
18824 onContextMenu : function(e){
18825 var item = this.findItemFromChild(e.getTarget());
18827 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18832 onDblClick : function(e){
18833 var item = this.findItemFromChild(e.getTarget());
18835 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18839 onItemClick : function(item, index, e)
18841 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18844 if (this.toggleSelect) {
18845 var m = this.isSelected(item) ? 'unselect' : 'select';
18848 _t[m](item, true, false);
18851 if(this.multiSelect || this.singleSelect){
18852 if(this.multiSelect && e.shiftKey && this.lastSelection){
18853 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18855 this.select(item, this.multiSelect && e.ctrlKey);
18856 this.lastSelection = item;
18859 if(!this.tickable){
18860 e.preventDefault();
18868 * Get the number of selected nodes.
18871 getSelectionCount : function(){
18872 return this.selections.length;
18876 * Get the currently selected nodes.
18877 * @return {Array} An array of HTMLElements
18879 getSelectedNodes : function(){
18880 return this.selections;
18884 * Get the indexes of the selected nodes.
18887 getSelectedIndexes : function(){
18888 var indexes = [], s = this.selections;
18889 for(var i = 0, len = s.length; i < len; i++){
18890 indexes.push(s[i].nodeIndex);
18896 * Clear all selections
18897 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18899 clearSelections : function(suppressEvent){
18900 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18901 this.cmp.elements = this.selections;
18902 this.cmp.removeClass(this.selectedClass);
18903 this.selections = [];
18904 if(!suppressEvent){
18905 this.fireEvent("selectionchange", this, this.selections);
18911 * Returns true if the passed node is selected
18912 * @param {HTMLElement/Number} node The node or node index
18913 * @return {Boolean}
18915 isSelected : function(node){
18916 var s = this.selections;
18920 node = this.getNode(node);
18921 return s.indexOf(node) !== -1;
18926 * @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
18927 * @param {Boolean} keepExisting (optional) true to keep existing selections
18928 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18930 select : function(nodeInfo, keepExisting, suppressEvent){
18931 if(nodeInfo instanceof Array){
18933 this.clearSelections(true);
18935 for(var i = 0, len = nodeInfo.length; i < len; i++){
18936 this.select(nodeInfo[i], true, true);
18940 var node = this.getNode(nodeInfo);
18941 if(!node || this.isSelected(node)){
18942 return; // already selected.
18945 this.clearSelections(true);
18948 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18949 Roo.fly(node).addClass(this.selectedClass);
18950 this.selections.push(node);
18951 if(!suppressEvent){
18952 this.fireEvent("selectionchange", this, this.selections);
18960 * @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
18961 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18962 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18964 unselect : function(nodeInfo, keepExisting, suppressEvent)
18966 if(nodeInfo instanceof Array){
18967 Roo.each(this.selections, function(s) {
18968 this.unselect(s, nodeInfo);
18972 var node = this.getNode(nodeInfo);
18973 if(!node || !this.isSelected(node)){
18974 //Roo.log("not selected");
18975 return; // not selected.
18979 Roo.each(this.selections, function(s) {
18981 Roo.fly(node).removeClass(this.selectedClass);
18988 this.selections= ns;
18989 this.fireEvent("selectionchange", this, this.selections);
18993 * Gets a template node.
18994 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18995 * @return {HTMLElement} The node or null if it wasn't found
18997 getNode : function(nodeInfo){
18998 if(typeof nodeInfo == "string"){
18999 return document.getElementById(nodeInfo);
19000 }else if(typeof nodeInfo == "number"){
19001 return this.nodes[nodeInfo];
19007 * Gets a range template nodes.
19008 * @param {Number} startIndex
19009 * @param {Number} endIndex
19010 * @return {Array} An array of nodes
19012 getNodes : function(start, end){
19013 var ns = this.nodes;
19014 start = start || 0;
19015 end = typeof end == "undefined" ? ns.length - 1 : end;
19018 for(var i = start; i <= end; i++){
19022 for(var i = start; i >= end; i--){
19030 * Finds the index of the passed node
19031 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19032 * @return {Number} The index of the node or -1
19034 indexOf : function(node){
19035 node = this.getNode(node);
19036 if(typeof node.nodeIndex == "number"){
19037 return node.nodeIndex;
19039 var ns = this.nodes;
19040 for(var i = 0, len = ns.length; i < len; i++){
19051 * based on jquery fullcalendar
19055 Roo.bootstrap = Roo.bootstrap || {};
19057 * @class Roo.bootstrap.Calendar
19058 * @extends Roo.bootstrap.Component
19059 * Bootstrap Calendar class
19060 * @cfg {Boolean} loadMask (true|false) default false
19061 * @cfg {Object} header generate the user specific header of the calendar, default false
19064 * Create a new Container
19065 * @param {Object} config The config object
19070 Roo.bootstrap.Calendar = function(config){
19071 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19075 * Fires when a date is selected
19076 * @param {DatePicker} this
19077 * @param {Date} date The selected date
19081 * @event monthchange
19082 * Fires when the displayed month changes
19083 * @param {DatePicker} this
19084 * @param {Date} date The selected month
19086 'monthchange': true,
19088 * @event evententer
19089 * Fires when mouse over an event
19090 * @param {Calendar} this
19091 * @param {event} Event
19093 'evententer': true,
19095 * @event eventleave
19096 * Fires when the mouse leaves an
19097 * @param {Calendar} this
19100 'eventleave': true,
19102 * @event eventclick
19103 * Fires when the mouse click an
19104 * @param {Calendar} this
19113 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19116 * @cfg {Number} startDay
19117 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19125 getAutoCreate : function(){
19128 var fc_button = function(name, corner, style, content ) {
19129 return Roo.apply({},{
19131 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19133 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19136 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19147 style : 'width:100%',
19154 cls : 'fc-header-left',
19156 fc_button('prev', 'left', 'arrow', '‹' ),
19157 fc_button('next', 'right', 'arrow', '›' ),
19158 { tag: 'span', cls: 'fc-header-space' },
19159 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19167 cls : 'fc-header-center',
19171 cls: 'fc-header-title',
19174 html : 'month / year'
19182 cls : 'fc-header-right',
19184 /* fc_button('month', 'left', '', 'month' ),
19185 fc_button('week', '', '', 'week' ),
19186 fc_button('day', 'right', '', 'day' )
19198 header = this.header;
19201 var cal_heads = function() {
19203 // fixme - handle this.
19205 for (var i =0; i < Date.dayNames.length; i++) {
19206 var d = Date.dayNames[i];
19209 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19210 html : d.substring(0,3)
19214 ret[0].cls += ' fc-first';
19215 ret[6].cls += ' fc-last';
19218 var cal_cell = function(n) {
19221 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19226 cls: 'fc-day-number',
19230 cls: 'fc-day-content',
19234 style: 'position: relative;' // height: 17px;
19246 var cal_rows = function() {
19249 for (var r = 0; r < 6; r++) {
19256 for (var i =0; i < Date.dayNames.length; i++) {
19257 var d = Date.dayNames[i];
19258 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19261 row.cn[0].cls+=' fc-first';
19262 row.cn[0].cn[0].style = 'min-height:90px';
19263 row.cn[6].cls+=' fc-last';
19267 ret[0].cls += ' fc-first';
19268 ret[4].cls += ' fc-prev-last';
19269 ret[5].cls += ' fc-last';
19276 cls: 'fc-border-separate',
19277 style : 'width:100%',
19285 cls : 'fc-first fc-last',
19303 cls : 'fc-content',
19304 style : "position: relative;",
19307 cls : 'fc-view fc-view-month fc-grid',
19308 style : 'position: relative',
19309 unselectable : 'on',
19312 cls : 'fc-event-container',
19313 style : 'position:absolute;z-index:8;top:0;left:0;'
19331 initEvents : function()
19334 throw "can not find store for calendar";
19340 style: "text-align:center",
19344 style: "background-color:white;width:50%;margin:250 auto",
19348 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19359 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19361 var size = this.el.select('.fc-content', true).first().getSize();
19362 this.maskEl.setSize(size.width, size.height);
19363 this.maskEl.enableDisplayMode("block");
19364 if(!this.loadMask){
19365 this.maskEl.hide();
19368 this.store = Roo.factory(this.store, Roo.data);
19369 this.store.on('load', this.onLoad, this);
19370 this.store.on('beforeload', this.onBeforeLoad, this);
19374 this.cells = this.el.select('.fc-day',true);
19375 //Roo.log(this.cells);
19376 this.textNodes = this.el.query('.fc-day-number');
19377 this.cells.addClassOnOver('fc-state-hover');
19379 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19380 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19381 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19382 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19384 this.on('monthchange', this.onMonthChange, this);
19386 this.update(new Date().clearTime());
19389 resize : function() {
19390 var sz = this.el.getSize();
19392 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19393 this.el.select('.fc-day-content div',true).setHeight(34);
19398 showPrevMonth : function(e){
19399 this.update(this.activeDate.add("mo", -1));
19401 showToday : function(e){
19402 this.update(new Date().clearTime());
19405 showNextMonth : function(e){
19406 this.update(this.activeDate.add("mo", 1));
19410 showPrevYear : function(){
19411 this.update(this.activeDate.add("y", -1));
19415 showNextYear : function(){
19416 this.update(this.activeDate.add("y", 1));
19421 update : function(date)
19423 var vd = this.activeDate;
19424 this.activeDate = date;
19425 // if(vd && this.el){
19426 // var t = date.getTime();
19427 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19428 // Roo.log('using add remove');
19430 // this.fireEvent('monthchange', this, date);
19432 // this.cells.removeClass("fc-state-highlight");
19433 // this.cells.each(function(c){
19434 // if(c.dateValue == t){
19435 // c.addClass("fc-state-highlight");
19436 // setTimeout(function(){
19437 // try{c.dom.firstChild.focus();}catch(e){}
19447 var days = date.getDaysInMonth();
19449 var firstOfMonth = date.getFirstDateOfMonth();
19450 var startingPos = firstOfMonth.getDay()-this.startDay;
19452 if(startingPos < this.startDay){
19456 var pm = date.add(Date.MONTH, -1);
19457 var prevStart = pm.getDaysInMonth()-startingPos;
19459 this.cells = this.el.select('.fc-day',true);
19460 this.textNodes = this.el.query('.fc-day-number');
19461 this.cells.addClassOnOver('fc-state-hover');
19463 var cells = this.cells.elements;
19464 var textEls = this.textNodes;
19466 Roo.each(cells, function(cell){
19467 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19470 days += startingPos;
19472 // convert everything to numbers so it's fast
19473 var day = 86400000;
19474 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19477 //Roo.log(prevStart);
19479 var today = new Date().clearTime().getTime();
19480 var sel = date.clearTime().getTime();
19481 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19482 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19483 var ddMatch = this.disabledDatesRE;
19484 var ddText = this.disabledDatesText;
19485 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19486 var ddaysText = this.disabledDaysText;
19487 var format = this.format;
19489 var setCellClass = function(cal, cell){
19493 //Roo.log('set Cell Class');
19495 var t = d.getTime();
19499 cell.dateValue = t;
19501 cell.className += " fc-today";
19502 cell.className += " fc-state-highlight";
19503 cell.title = cal.todayText;
19506 // disable highlight in other month..
19507 //cell.className += " fc-state-highlight";
19512 cell.className = " fc-state-disabled";
19513 cell.title = cal.minText;
19517 cell.className = " fc-state-disabled";
19518 cell.title = cal.maxText;
19522 if(ddays.indexOf(d.getDay()) != -1){
19523 cell.title = ddaysText;
19524 cell.className = " fc-state-disabled";
19527 if(ddMatch && format){
19528 var fvalue = d.dateFormat(format);
19529 if(ddMatch.test(fvalue)){
19530 cell.title = ddText.replace("%0", fvalue);
19531 cell.className = " fc-state-disabled";
19535 if (!cell.initialClassName) {
19536 cell.initialClassName = cell.dom.className;
19539 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19544 for(; i < startingPos; i++) {
19545 textEls[i].innerHTML = (++prevStart);
19546 d.setDate(d.getDate()+1);
19548 cells[i].className = "fc-past fc-other-month";
19549 setCellClass(this, cells[i]);
19554 for(; i < days; i++){
19555 intDay = i - startingPos + 1;
19556 textEls[i].innerHTML = (intDay);
19557 d.setDate(d.getDate()+1);
19559 cells[i].className = ''; // "x-date-active";
19560 setCellClass(this, cells[i]);
19564 for(; i < 42; i++) {
19565 textEls[i].innerHTML = (++extraDays);
19566 d.setDate(d.getDate()+1);
19568 cells[i].className = "fc-future fc-other-month";
19569 setCellClass(this, cells[i]);
19572 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19574 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19576 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19577 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19579 if(totalRows != 6){
19580 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19581 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19584 this.fireEvent('monthchange', this, date);
19588 if(!this.internalRender){
19589 var main = this.el.dom.firstChild;
19590 var w = main.offsetWidth;
19591 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19592 Roo.fly(main).setWidth(w);
19593 this.internalRender = true;
19594 // opera does not respect the auto grow header center column
19595 // then, after it gets a width opera refuses to recalculate
19596 // without a second pass
19597 if(Roo.isOpera && !this.secondPass){
19598 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19599 this.secondPass = true;
19600 this.update.defer(10, this, [date]);
19607 findCell : function(dt) {
19608 dt = dt.clearTime().getTime();
19610 this.cells.each(function(c){
19611 //Roo.log("check " +c.dateValue + '?=' + dt);
19612 if(c.dateValue == dt){
19622 findCells : function(ev) {
19623 var s = ev.start.clone().clearTime().getTime();
19625 var e= ev.end.clone().clearTime().getTime();
19628 this.cells.each(function(c){
19629 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19631 if(c.dateValue > e){
19634 if(c.dateValue < s){
19643 // findBestRow: function(cells)
19647 // for (var i =0 ; i < cells.length;i++) {
19648 // ret = Math.max(cells[i].rows || 0,ret);
19655 addItem : function(ev)
19657 // look for vertical location slot in
19658 var cells = this.findCells(ev);
19660 // ev.row = this.findBestRow(cells);
19662 // work out the location.
19666 for(var i =0; i < cells.length; i++) {
19668 cells[i].row = cells[0].row;
19671 cells[i].row = cells[i].row + 1;
19681 if (crow.start.getY() == cells[i].getY()) {
19683 crow.end = cells[i];
19700 cells[0].events.push(ev);
19702 this.calevents.push(ev);
19705 clearEvents: function() {
19707 if(!this.calevents){
19711 Roo.each(this.cells.elements, function(c){
19717 Roo.each(this.calevents, function(e) {
19718 Roo.each(e.els, function(el) {
19719 el.un('mouseenter' ,this.onEventEnter, this);
19720 el.un('mouseleave' ,this.onEventLeave, this);
19725 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19731 renderEvents: function()
19735 this.cells.each(function(c) {
19744 if(c.row != c.events.length){
19745 r = 4 - (4 - (c.row - c.events.length));
19748 c.events = ev.slice(0, r);
19749 c.more = ev.slice(r);
19751 if(c.more.length && c.more.length == 1){
19752 c.events.push(c.more.pop());
19755 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19759 this.cells.each(function(c) {
19761 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19764 for (var e = 0; e < c.events.length; e++){
19765 var ev = c.events[e];
19766 var rows = ev.rows;
19768 for(var i = 0; i < rows.length; i++) {
19770 // how many rows should it span..
19773 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19774 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19776 unselectable : "on",
19779 cls: 'fc-event-inner',
19783 // cls: 'fc-event-time',
19784 // html : cells.length > 1 ? '' : ev.time
19788 cls: 'fc-event-title',
19789 html : String.format('{0}', ev.title)
19796 cls: 'ui-resizable-handle ui-resizable-e',
19797 html : '  '
19804 cfg.cls += ' fc-event-start';
19806 if ((i+1) == rows.length) {
19807 cfg.cls += ' fc-event-end';
19810 var ctr = _this.el.select('.fc-event-container',true).first();
19811 var cg = ctr.createChild(cfg);
19813 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19814 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19816 var r = (c.more.length) ? 1 : 0;
19817 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19818 cg.setWidth(ebox.right - sbox.x -2);
19820 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19821 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19822 cg.on('click', _this.onEventClick, _this, ev);
19833 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19834 style : 'position: absolute',
19835 unselectable : "on",
19838 cls: 'fc-event-inner',
19842 cls: 'fc-event-title',
19850 cls: 'ui-resizable-handle ui-resizable-e',
19851 html : '  '
19857 var ctr = _this.el.select('.fc-event-container',true).first();
19858 var cg = ctr.createChild(cfg);
19860 var sbox = c.select('.fc-day-content',true).first().getBox();
19861 var ebox = c.select('.fc-day-content',true).first().getBox();
19863 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19864 cg.setWidth(ebox.right - sbox.x -2);
19866 cg.on('click', _this.onMoreEventClick, _this, c.more);
19876 onEventEnter: function (e, el,event,d) {
19877 this.fireEvent('evententer', this, el, event);
19880 onEventLeave: function (e, el,event,d) {
19881 this.fireEvent('eventleave', this, el, event);
19884 onEventClick: function (e, el,event,d) {
19885 this.fireEvent('eventclick', this, el, event);
19888 onMonthChange: function () {
19892 onMoreEventClick: function(e, el, more)
19896 this.calpopover.placement = 'right';
19897 this.calpopover.setTitle('More');
19899 this.calpopover.setContent('');
19901 var ctr = this.calpopover.el.select('.popover-content', true).first();
19903 Roo.each(more, function(m){
19905 cls : 'fc-event-hori fc-event-draggable',
19908 var cg = ctr.createChild(cfg);
19910 cg.on('click', _this.onEventClick, _this, m);
19913 this.calpopover.show(el);
19918 onLoad: function ()
19920 this.calevents = [];
19923 if(this.store.getCount() > 0){
19924 this.store.data.each(function(d){
19927 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19928 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19929 time : d.data.start_time,
19930 title : d.data.title,
19931 description : d.data.description,
19932 venue : d.data.venue
19937 this.renderEvents();
19939 if(this.calevents.length && this.loadMask){
19940 this.maskEl.hide();
19944 onBeforeLoad: function()
19946 this.clearEvents();
19948 this.maskEl.show();
19962 * @class Roo.bootstrap.Popover
19963 * @extends Roo.bootstrap.Component
19964 * Bootstrap Popover class
19965 * @cfg {String} html contents of the popover (or false to use children..)
19966 * @cfg {String} title of popover (or false to hide)
19967 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19968 * @cfg {String} trigger click || hover (or false to trigger manually)
19969 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19970 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19971 * - if false and it has a 'parent' then it will be automatically added to that element
19972 * - if string - Roo.get will be called
19973 * @cfg {Number} delay - delay before showing
19976 * Create a new Popover
19977 * @param {Object} config The config object
19980 Roo.bootstrap.Popover = function(config){
19981 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19987 * After the popover show
19989 * @param {Roo.bootstrap.Popover} this
19994 * After the popover hide
19996 * @param {Roo.bootstrap.Popover} this
20002 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20007 placement : 'right',
20008 trigger : 'hover', // hover
20014 can_build_overlaid : false,
20016 maskEl : false, // the mask element
20019 alignEl : false, // when show is called with an element - this get's stored.
20021 getChildContainer : function()
20023 return this.contentEl;
20026 getPopoverHeader : function()
20028 this.title = true; // flag not to hide it..
20029 this.headerEl.addClass('p-0');
20030 return this.headerEl
20034 getAutoCreate : function(){
20037 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20038 style: 'display:block',
20044 cls : 'popover-inner ',
20048 cls: 'popover-title popover-header',
20049 html : this.title === false ? '' : this.title
20052 cls : 'popover-content popover-body ' + (this.cls || ''),
20053 html : this.html || ''
20064 * @param {string} the title
20066 setTitle: function(str)
20070 this.headerEl.dom.innerHTML = str;
20075 * @param {string} the body content
20077 setContent: function(str)
20080 if (this.contentEl) {
20081 this.contentEl.dom.innerHTML = str;
20085 // as it get's added to the bottom of the page.
20086 onRender : function(ct, position)
20088 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20093 var cfg = Roo.apply({}, this.getAutoCreate());
20097 cfg.cls += ' ' + this.cls;
20100 cfg.style = this.style;
20102 //Roo.log("adding to ");
20103 this.el = Roo.get(document.body).createChild(cfg, position);
20104 // Roo.log(this.el);
20107 this.contentEl = this.el.select('.popover-content',true).first();
20108 this.headerEl = this.el.select('.popover-title',true).first();
20111 if(typeof(this.items) != 'undefined'){
20112 var items = this.items;
20115 for(var i =0;i < items.length;i++) {
20116 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20120 this.items = nitems;
20122 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20123 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20130 resizeMask : function()
20132 this.maskEl.setSize(
20133 Roo.lib.Dom.getViewWidth(true),
20134 Roo.lib.Dom.getViewHeight(true)
20138 initEvents : function()
20142 Roo.bootstrap.Popover.register(this);
20145 this.arrowEl = this.el.select('.arrow',true).first();
20146 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20147 this.el.enableDisplayMode('block');
20151 if (this.over === false && !this.parent()) {
20154 if (this.triggers === false) {
20159 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20160 var triggers = this.trigger ? this.trigger.split(' ') : [];
20161 Roo.each(triggers, function(trigger) {
20163 if (trigger == 'click') {
20164 on_el.on('click', this.toggle, this);
20165 } else if (trigger != 'manual') {
20166 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20167 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20169 on_el.on(eventIn ,this.enter, this);
20170 on_el.on(eventOut, this.leave, this);
20180 toggle : function () {
20181 this.hoverState == 'in' ? this.leave() : this.enter();
20184 enter : function () {
20186 clearTimeout(this.timeout);
20188 this.hoverState = 'in';
20190 if (!this.delay || !this.delay.show) {
20195 this.timeout = setTimeout(function () {
20196 if (_t.hoverState == 'in') {
20199 }, this.delay.show)
20202 leave : function() {
20203 clearTimeout(this.timeout);
20205 this.hoverState = 'out';
20207 if (!this.delay || !this.delay.hide) {
20212 this.timeout = setTimeout(function () {
20213 if (_t.hoverState == 'out') {
20216 }, this.delay.hide)
20220 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20221 * @param {string} (left|right|top|bottom) position
20223 show : function (on_el, placement)
20225 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20226 on_el = on_el || false; // default to false
20229 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20230 on_el = this.parent().el;
20231 } else if (this.over) {
20232 on_el = Roo.get(this.over);
20237 this.alignEl = Roo.get( on_el );
20240 this.render(document.body);
20246 if (this.title === false) {
20247 this.headerEl.hide();
20252 this.el.dom.style.display = 'block';
20255 if (this.alignEl) {
20256 this.updatePosition(this.placement, true);
20259 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20260 var es = this.el.getSize();
20261 var x = Roo.lib.Dom.getViewWidth()/2;
20262 var y = Roo.lib.Dom.getViewHeight()/2;
20263 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20268 //var arrow = this.el.select('.arrow',true).first();
20269 //arrow.set(align[2],
20271 this.el.addClass('in');
20275 this.hoverState = 'in';
20278 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20279 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20280 this.maskEl.dom.style.display = 'block';
20281 this.maskEl.addClass('show');
20283 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20285 this.fireEvent('show', this);
20289 * fire this manually after loading a grid in the table for example
20290 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20291 * @param {Boolean} try and move it if we cant get right position.
20293 updatePosition : function(placement, try_move)
20295 // allow for calling with no parameters
20296 placement = placement ? placement : this.placement;
20297 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20299 this.el.removeClass([
20300 'fade','top','bottom', 'left', 'right','in',
20301 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20303 this.el.addClass(placement + ' bs-popover-' + placement);
20305 if (!this.alignEl ) {
20309 switch (placement) {
20311 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20312 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20313 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20314 //normal display... or moved up/down.
20315 this.el.setXY(offset);
20316 var xy = this.alignEl.getAnchorXY('tr', false);
20318 this.arrowEl.setXY(xy);
20321 // continue through...
20322 return this.updatePosition('left', false);
20326 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20327 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20328 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20329 //normal display... or moved up/down.
20330 this.el.setXY(offset);
20331 var xy = this.alignEl.getAnchorXY('tl', false);
20332 xy[0]-=10;xy[1]+=5; // << fix me
20333 this.arrowEl.setXY(xy);
20337 return this.updatePosition('right', false);
20340 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20341 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20342 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20343 //normal display... or moved up/down.
20344 this.el.setXY(offset);
20345 var xy = this.alignEl.getAnchorXY('t', false);
20346 xy[1]-=10; // << fix me
20347 this.arrowEl.setXY(xy);
20351 return this.updatePosition('bottom', false);
20354 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20355 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20356 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20357 //normal display... or moved up/down.
20358 this.el.setXY(offset);
20359 var xy = this.alignEl.getAnchorXY('b', false);
20360 xy[1]+=2; // << fix me
20361 this.arrowEl.setXY(xy);
20365 return this.updatePosition('top', false);
20376 this.el.setXY([0,0]);
20377 this.el.removeClass('in');
20379 this.hoverState = null;
20380 this.maskEl.hide(); // always..
20381 this.fireEvent('hide', this);
20387 Roo.apply(Roo.bootstrap.Popover, {
20390 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20391 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20392 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20393 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20398 clickHander : false,
20401 onMouseDown : function(e)
20403 if (!e.getTarget(".roo-popover")) {
20411 register : function(popup)
20413 if (!Roo.bootstrap.Popover.clickHandler) {
20414 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20416 // hide other popups.
20418 this.popups.push(popup);
20420 hideAll : function()
20422 this.popups.forEach(function(p) {
20430 * Card header - holder for the card header elements.
20435 * @class Roo.bootstrap.PopoverNav
20436 * @extends Roo.bootstrap.NavGroup
20437 * Bootstrap Popover header navigation class
20439 * Create a new Popover Header Navigation
20440 * @param {Object} config The config object
20443 Roo.bootstrap.PopoverNav = function(config){
20444 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20447 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20450 container_method : 'getPopoverHeader'
20468 * @class Roo.bootstrap.Progress
20469 * @extends Roo.bootstrap.Component
20470 * Bootstrap Progress class
20471 * @cfg {Boolean} striped striped of the progress bar
20472 * @cfg {Boolean} active animated of the progress bar
20476 * Create a new Progress
20477 * @param {Object} config The config object
20480 Roo.bootstrap.Progress = function(config){
20481 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20484 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20489 getAutoCreate : function(){
20497 cfg.cls += ' progress-striped';
20501 cfg.cls += ' active';
20520 * @class Roo.bootstrap.ProgressBar
20521 * @extends Roo.bootstrap.Component
20522 * Bootstrap ProgressBar class
20523 * @cfg {Number} aria_valuenow aria-value now
20524 * @cfg {Number} aria_valuemin aria-value min
20525 * @cfg {Number} aria_valuemax aria-value max
20526 * @cfg {String} label label for the progress bar
20527 * @cfg {String} panel (success | info | warning | danger )
20528 * @cfg {String} role role of the progress bar
20529 * @cfg {String} sr_only text
20533 * Create a new ProgressBar
20534 * @param {Object} config The config object
20537 Roo.bootstrap.ProgressBar = function(config){
20538 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20541 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20545 aria_valuemax : 100,
20551 getAutoCreate : function()
20556 cls: 'progress-bar',
20557 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20569 cfg.role = this.role;
20572 if(this.aria_valuenow){
20573 cfg['aria-valuenow'] = this.aria_valuenow;
20576 if(this.aria_valuemin){
20577 cfg['aria-valuemin'] = this.aria_valuemin;
20580 if(this.aria_valuemax){
20581 cfg['aria-valuemax'] = this.aria_valuemax;
20584 if(this.label && !this.sr_only){
20585 cfg.html = this.label;
20589 cfg.cls += ' progress-bar-' + this.panel;
20595 update : function(aria_valuenow)
20597 this.aria_valuenow = aria_valuenow;
20599 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20614 * @class Roo.bootstrap.TabGroup
20615 * @extends Roo.bootstrap.Column
20616 * Bootstrap Column class
20617 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20618 * @cfg {Boolean} carousel true to make the group behave like a carousel
20619 * @cfg {Boolean} bullets show bullets for the panels
20620 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20621 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20622 * @cfg {Boolean} showarrow (true|false) show arrow default true
20625 * Create a new TabGroup
20626 * @param {Object} config The config object
20629 Roo.bootstrap.TabGroup = function(config){
20630 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20632 this.navId = Roo.id();
20635 Roo.bootstrap.TabGroup.register(this);
20639 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20642 transition : false,
20647 slideOnTouch : false,
20650 getAutoCreate : function()
20652 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20654 cfg.cls += ' tab-content';
20656 if (this.carousel) {
20657 cfg.cls += ' carousel slide';
20660 cls : 'carousel-inner',
20664 if(this.bullets && !Roo.isTouch){
20667 cls : 'carousel-bullets',
20671 if(this.bullets_cls){
20672 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20679 cfg.cn[0].cn.push(bullets);
20682 if(this.showarrow){
20683 cfg.cn[0].cn.push({
20685 class : 'carousel-arrow',
20689 class : 'carousel-prev',
20693 class : 'fa fa-chevron-left'
20699 class : 'carousel-next',
20703 class : 'fa fa-chevron-right'
20716 initEvents: function()
20718 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20719 // this.el.on("touchstart", this.onTouchStart, this);
20722 if(this.autoslide){
20725 this.slideFn = window.setInterval(function() {
20726 _this.showPanelNext();
20730 if(this.showarrow){
20731 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20732 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20738 // onTouchStart : function(e, el, o)
20740 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20744 // this.showPanelNext();
20748 getChildContainer : function()
20750 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20754 * register a Navigation item
20755 * @param {Roo.bootstrap.NavItem} the navitem to add
20757 register : function(item)
20759 this.tabs.push( item);
20760 item.navId = this.navId; // not really needed..
20765 getActivePanel : function()
20768 Roo.each(this.tabs, function(t) {
20778 getPanelByName : function(n)
20781 Roo.each(this.tabs, function(t) {
20782 if (t.tabId == n) {
20790 indexOfPanel : function(p)
20793 Roo.each(this.tabs, function(t,i) {
20794 if (t.tabId == p.tabId) {
20803 * show a specific panel
20804 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20805 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20807 showPanel : function (pan)
20809 if(this.transition || typeof(pan) == 'undefined'){
20810 Roo.log("waiting for the transitionend");
20814 if (typeof(pan) == 'number') {
20815 pan = this.tabs[pan];
20818 if (typeof(pan) == 'string') {
20819 pan = this.getPanelByName(pan);
20822 var cur = this.getActivePanel();
20825 Roo.log('pan or acitve pan is undefined');
20829 if (pan.tabId == this.getActivePanel().tabId) {
20833 if (false === cur.fireEvent('beforedeactivate')) {
20837 if(this.bullets > 0 && !Roo.isTouch){
20838 this.setActiveBullet(this.indexOfPanel(pan));
20841 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20843 //class="carousel-item carousel-item-next carousel-item-left"
20845 this.transition = true;
20846 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20847 var lr = dir == 'next' ? 'left' : 'right';
20848 pan.el.addClass(dir); // or prev
20849 pan.el.addClass('carousel-item-' + dir); // or prev
20850 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20851 cur.el.addClass(lr); // or right
20852 pan.el.addClass(lr);
20853 cur.el.addClass('carousel-item-' +lr); // or right
20854 pan.el.addClass('carousel-item-' +lr);
20858 cur.el.on('transitionend', function() {
20859 Roo.log("trans end?");
20861 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20862 pan.setActive(true);
20864 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20865 cur.setActive(false);
20867 _this.transition = false;
20869 }, this, { single: true } );
20874 cur.setActive(false);
20875 pan.setActive(true);
20880 showPanelNext : function()
20882 var i = this.indexOfPanel(this.getActivePanel());
20884 if (i >= this.tabs.length - 1 && !this.autoslide) {
20888 if (i >= this.tabs.length - 1 && this.autoslide) {
20892 this.showPanel(this.tabs[i+1]);
20895 showPanelPrev : function()
20897 var i = this.indexOfPanel(this.getActivePanel());
20899 if (i < 1 && !this.autoslide) {
20903 if (i < 1 && this.autoslide) {
20904 i = this.tabs.length;
20907 this.showPanel(this.tabs[i-1]);
20911 addBullet: function()
20913 if(!this.bullets || Roo.isTouch){
20916 var ctr = this.el.select('.carousel-bullets',true).first();
20917 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20918 var bullet = ctr.createChild({
20919 cls : 'bullet bullet-' + i
20920 },ctr.dom.lastChild);
20925 bullet.on('click', (function(e, el, o, ii, t){
20927 e.preventDefault();
20929 this.showPanel(ii);
20931 if(this.autoslide && this.slideFn){
20932 clearInterval(this.slideFn);
20933 this.slideFn = window.setInterval(function() {
20934 _this.showPanelNext();
20938 }).createDelegate(this, [i, bullet], true));
20943 setActiveBullet : function(i)
20949 Roo.each(this.el.select('.bullet', true).elements, function(el){
20950 el.removeClass('selected');
20953 var bullet = this.el.select('.bullet-' + i, true).first();
20959 bullet.addClass('selected');
20970 Roo.apply(Roo.bootstrap.TabGroup, {
20974 * register a Navigation Group
20975 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20977 register : function(navgrp)
20979 this.groups[navgrp.navId] = navgrp;
20983 * fetch a Navigation Group based on the navigation ID
20984 * if one does not exist , it will get created.
20985 * @param {string} the navgroup to add
20986 * @returns {Roo.bootstrap.NavGroup} the navgroup
20988 get: function(navId) {
20989 if (typeof(this.groups[navId]) == 'undefined') {
20990 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20992 return this.groups[navId] ;
21007 * @class Roo.bootstrap.TabPanel
21008 * @extends Roo.bootstrap.Component
21009 * Bootstrap TabPanel class
21010 * @cfg {Boolean} active panel active
21011 * @cfg {String} html panel content
21012 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21013 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21014 * @cfg {String} href click to link..
21015 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21019 * Create a new TabPanel
21020 * @param {Object} config The config object
21023 Roo.bootstrap.TabPanel = function(config){
21024 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21028 * Fires when the active status changes
21029 * @param {Roo.bootstrap.TabPanel} this
21030 * @param {Boolean} state the new state
21035 * @event beforedeactivate
21036 * Fires before a tab is de-activated - can be used to do validation on a form.
21037 * @param {Roo.bootstrap.TabPanel} this
21038 * @return {Boolean} false if there is an error
21041 'beforedeactivate': true
21044 this.tabId = this.tabId || Roo.id();
21048 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21055 touchSlide : false,
21056 getAutoCreate : function(){
21061 // item is needed for carousel - not sure if it has any effect otherwise
21062 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21063 html: this.html || ''
21067 cfg.cls += ' active';
21071 cfg.tabId = this.tabId;
21079 initEvents: function()
21081 var p = this.parent();
21083 this.navId = this.navId || p.navId;
21085 if (typeof(this.navId) != 'undefined') {
21086 // not really needed.. but just in case.. parent should be a NavGroup.
21087 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21091 var i = tg.tabs.length - 1;
21093 if(this.active && tg.bullets > 0 && i < tg.bullets){
21094 tg.setActiveBullet(i);
21098 this.el.on('click', this.onClick, this);
21100 if(Roo.isTouch && this.touchSlide){
21101 this.el.on("touchstart", this.onTouchStart, this);
21102 this.el.on("touchmove", this.onTouchMove, this);
21103 this.el.on("touchend", this.onTouchEnd, this);
21108 onRender : function(ct, position)
21110 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21113 setActive : function(state)
21115 Roo.log("panel - set active " + this.tabId + "=" + state);
21117 this.active = state;
21119 this.el.removeClass('active');
21121 } else if (!this.el.hasClass('active')) {
21122 this.el.addClass('active');
21125 this.fireEvent('changed', this, state);
21128 onClick : function(e)
21130 e.preventDefault();
21132 if(!this.href.length){
21136 window.location.href = this.href;
21145 onTouchStart : function(e)
21147 this.swiping = false;
21149 this.startX = e.browserEvent.touches[0].clientX;
21150 this.startY = e.browserEvent.touches[0].clientY;
21153 onTouchMove : function(e)
21155 this.swiping = true;
21157 this.endX = e.browserEvent.touches[0].clientX;
21158 this.endY = e.browserEvent.touches[0].clientY;
21161 onTouchEnd : function(e)
21168 var tabGroup = this.parent();
21170 if(this.endX > this.startX){ // swiping right
21171 tabGroup.showPanelPrev();
21175 if(this.startX > this.endX){ // swiping left
21176 tabGroup.showPanelNext();
21195 * @class Roo.bootstrap.DateField
21196 * @extends Roo.bootstrap.Input
21197 * Bootstrap DateField class
21198 * @cfg {Number} weekStart default 0
21199 * @cfg {String} viewMode default empty, (months|years)
21200 * @cfg {String} minViewMode default empty, (months|years)
21201 * @cfg {Number} startDate default -Infinity
21202 * @cfg {Number} endDate default Infinity
21203 * @cfg {Boolean} todayHighlight default false
21204 * @cfg {Boolean} todayBtn default false
21205 * @cfg {Boolean} calendarWeeks default false
21206 * @cfg {Object} daysOfWeekDisabled default empty
21207 * @cfg {Boolean} singleMode default false (true | false)
21209 * @cfg {Boolean} keyboardNavigation default true
21210 * @cfg {String} language default en
21213 * Create a new DateField
21214 * @param {Object} config The config object
21217 Roo.bootstrap.DateField = function(config){
21218 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21222 * Fires when this field show.
21223 * @param {Roo.bootstrap.DateField} this
21224 * @param {Mixed} date The date value
21229 * Fires when this field hide.
21230 * @param {Roo.bootstrap.DateField} this
21231 * @param {Mixed} date The date value
21236 * Fires when select a date.
21237 * @param {Roo.bootstrap.DateField} this
21238 * @param {Mixed} date The date value
21242 * @event beforeselect
21243 * Fires when before select a date.
21244 * @param {Roo.bootstrap.DateField} this
21245 * @param {Mixed} date The date value
21247 beforeselect : true
21251 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21254 * @cfg {String} format
21255 * The default date format string which can be overriden for localization support. The format must be
21256 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21260 * @cfg {String} altFormats
21261 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21262 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21264 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21272 todayHighlight : false,
21278 keyboardNavigation: true,
21280 calendarWeeks: false,
21282 startDate: -Infinity,
21286 daysOfWeekDisabled: [],
21290 singleMode : false,
21292 UTCDate: function()
21294 return new Date(Date.UTC.apply(Date, arguments));
21297 UTCToday: function()
21299 var today = new Date();
21300 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21303 getDate: function() {
21304 var d = this.getUTCDate();
21305 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21308 getUTCDate: function() {
21312 setDate: function(d) {
21313 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21316 setUTCDate: function(d) {
21318 this.setValue(this.formatDate(this.date));
21321 onRender: function(ct, position)
21324 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21326 this.language = this.language || 'en';
21327 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21328 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21330 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21331 this.format = this.format || 'm/d/y';
21332 this.isInline = false;
21333 this.isInput = true;
21334 this.component = this.el.select('.add-on', true).first() || false;
21335 this.component = (this.component && this.component.length === 0) ? false : this.component;
21336 this.hasInput = this.component && this.inputEl().length;
21338 if (typeof(this.minViewMode === 'string')) {
21339 switch (this.minViewMode) {
21341 this.minViewMode = 1;
21344 this.minViewMode = 2;
21347 this.minViewMode = 0;
21352 if (typeof(this.viewMode === 'string')) {
21353 switch (this.viewMode) {
21366 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21368 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21370 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21372 this.picker().on('mousedown', this.onMousedown, this);
21373 this.picker().on('click', this.onClick, this);
21375 this.picker().addClass('datepicker-dropdown');
21377 this.startViewMode = this.viewMode;
21379 if(this.singleMode){
21380 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21381 v.setVisibilityMode(Roo.Element.DISPLAY);
21385 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21386 v.setStyle('width', '189px');
21390 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21391 if(!this.calendarWeeks){
21396 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21397 v.attr('colspan', function(i, val){
21398 return parseInt(val) + 1;
21403 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21405 this.setStartDate(this.startDate);
21406 this.setEndDate(this.endDate);
21408 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21415 if(this.isInline) {
21420 picker : function()
21422 return this.pickerEl;
21423 // return this.el.select('.datepicker', true).first();
21426 fillDow: function()
21428 var dowCnt = this.weekStart;
21437 if(this.calendarWeeks){
21445 while (dowCnt < this.weekStart + 7) {
21449 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21453 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21456 fillMonths: function()
21459 var months = this.picker().select('>.datepicker-months td', true).first();
21461 months.dom.innerHTML = '';
21467 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21470 months.createChild(month);
21477 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;
21479 if (this.date < this.startDate) {
21480 this.viewDate = new Date(this.startDate);
21481 } else if (this.date > this.endDate) {
21482 this.viewDate = new Date(this.endDate);
21484 this.viewDate = new Date(this.date);
21492 var d = new Date(this.viewDate),
21493 year = d.getUTCFullYear(),
21494 month = d.getUTCMonth(),
21495 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21496 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21497 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21498 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21499 currentDate = this.date && this.date.valueOf(),
21500 today = this.UTCToday();
21502 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21504 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21506 // this.picker.select('>tfoot th.today').
21507 // .text(dates[this.language].today)
21508 // .toggle(this.todayBtn !== false);
21510 this.updateNavArrows();
21513 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21515 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21517 prevMonth.setUTCDate(day);
21519 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21521 var nextMonth = new Date(prevMonth);
21523 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21525 nextMonth = nextMonth.valueOf();
21527 var fillMonths = false;
21529 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21531 while(prevMonth.valueOf() <= nextMonth) {
21534 if (prevMonth.getUTCDay() === this.weekStart) {
21536 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21544 if(this.calendarWeeks){
21545 // ISO 8601: First week contains first thursday.
21546 // ISO also states week starts on Monday, but we can be more abstract here.
21548 // Start of current week: based on weekstart/current date
21549 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21550 // Thursday of this week
21551 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21552 // First Thursday of year, year from thursday
21553 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21554 // Calendar week: ms between thursdays, div ms per day, div 7 days
21555 calWeek = (th - yth) / 864e5 / 7 + 1;
21557 fillMonths.cn.push({
21565 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21567 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21570 if (this.todayHighlight &&
21571 prevMonth.getUTCFullYear() == today.getFullYear() &&
21572 prevMonth.getUTCMonth() == today.getMonth() &&
21573 prevMonth.getUTCDate() == today.getDate()) {
21574 clsName += ' today';
21577 if (currentDate && prevMonth.valueOf() === currentDate) {
21578 clsName += ' active';
21581 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21582 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21583 clsName += ' disabled';
21586 fillMonths.cn.push({
21588 cls: 'day ' + clsName,
21589 html: prevMonth.getDate()
21592 prevMonth.setDate(prevMonth.getDate()+1);
21595 var currentYear = this.date && this.date.getUTCFullYear();
21596 var currentMonth = this.date && this.date.getUTCMonth();
21598 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21600 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21601 v.removeClass('active');
21603 if(currentYear === year && k === currentMonth){
21604 v.addClass('active');
21607 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21608 v.addClass('disabled');
21614 year = parseInt(year/10, 10) * 10;
21616 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21618 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21621 for (var i = -1; i < 11; i++) {
21622 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21624 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21632 showMode: function(dir)
21635 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21638 Roo.each(this.picker().select('>div',true).elements, function(v){
21639 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21642 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21647 if(this.isInline) {
21651 this.picker().removeClass(['bottom', 'top']);
21653 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21655 * place to the top of element!
21659 this.picker().addClass('top');
21660 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21665 this.picker().addClass('bottom');
21667 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21670 parseDate : function(value)
21672 if(!value || value instanceof Date){
21675 var v = Date.parseDate(value, this.format);
21676 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21677 v = Date.parseDate(value, 'Y-m-d');
21679 if(!v && this.altFormats){
21680 if(!this.altFormatsArray){
21681 this.altFormatsArray = this.altFormats.split("|");
21683 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21684 v = Date.parseDate(value, this.altFormatsArray[i]);
21690 formatDate : function(date, fmt)
21692 return (!date || !(date instanceof Date)) ?
21693 date : date.dateFormat(fmt || this.format);
21696 onFocus : function()
21698 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21702 onBlur : function()
21704 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21706 var d = this.inputEl().getValue();
21713 showPopup : function()
21715 this.picker().show();
21719 this.fireEvent('showpopup', this, this.date);
21722 hidePopup : function()
21724 if(this.isInline) {
21727 this.picker().hide();
21728 this.viewMode = this.startViewMode;
21731 this.fireEvent('hidepopup', this, this.date);
21735 onMousedown: function(e)
21737 e.stopPropagation();
21738 e.preventDefault();
21743 Roo.bootstrap.DateField.superclass.keyup.call(this);
21747 setValue: function(v)
21749 if(this.fireEvent('beforeselect', this, v) !== false){
21750 var d = new Date(this.parseDate(v) ).clearTime();
21752 if(isNaN(d.getTime())){
21753 this.date = this.viewDate = '';
21754 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21758 v = this.formatDate(d);
21760 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21762 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21766 this.fireEvent('select', this, this.date);
21770 getValue: function()
21772 return this.formatDate(this.date);
21775 fireKey: function(e)
21777 if (!this.picker().isVisible()){
21778 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21784 var dateChanged = false,
21786 newDate, newViewDate;
21791 e.preventDefault();
21795 if (!this.keyboardNavigation) {
21798 dir = e.keyCode == 37 ? -1 : 1;
21801 newDate = this.moveYear(this.date, dir);
21802 newViewDate = this.moveYear(this.viewDate, dir);
21803 } else if (e.shiftKey){
21804 newDate = this.moveMonth(this.date, dir);
21805 newViewDate = this.moveMonth(this.viewDate, dir);
21807 newDate = new Date(this.date);
21808 newDate.setUTCDate(this.date.getUTCDate() + dir);
21809 newViewDate = new Date(this.viewDate);
21810 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21812 if (this.dateWithinRange(newDate)){
21813 this.date = newDate;
21814 this.viewDate = newViewDate;
21815 this.setValue(this.formatDate(this.date));
21817 e.preventDefault();
21818 dateChanged = true;
21823 if (!this.keyboardNavigation) {
21826 dir = e.keyCode == 38 ? -1 : 1;
21828 newDate = this.moveYear(this.date, dir);
21829 newViewDate = this.moveYear(this.viewDate, dir);
21830 } else if (e.shiftKey){
21831 newDate = this.moveMonth(this.date, dir);
21832 newViewDate = this.moveMonth(this.viewDate, dir);
21834 newDate = new Date(this.date);
21835 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21836 newViewDate = new Date(this.viewDate);
21837 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21839 if (this.dateWithinRange(newDate)){
21840 this.date = newDate;
21841 this.viewDate = newViewDate;
21842 this.setValue(this.formatDate(this.date));
21844 e.preventDefault();
21845 dateChanged = true;
21849 this.setValue(this.formatDate(this.date));
21851 e.preventDefault();
21854 this.setValue(this.formatDate(this.date));
21868 onClick: function(e)
21870 e.stopPropagation();
21871 e.preventDefault();
21873 var target = e.getTarget();
21875 if(target.nodeName.toLowerCase() === 'i'){
21876 target = Roo.get(target).dom.parentNode;
21879 var nodeName = target.nodeName;
21880 var className = target.className;
21881 var html = target.innerHTML;
21882 //Roo.log(nodeName);
21884 switch(nodeName.toLowerCase()) {
21886 switch(className) {
21892 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21893 switch(this.viewMode){
21895 this.viewDate = this.moveMonth(this.viewDate, dir);
21899 this.viewDate = this.moveYear(this.viewDate, dir);
21905 var date = new Date();
21906 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21908 this.setValue(this.formatDate(this.date));
21915 if (className.indexOf('disabled') < 0) {
21916 this.viewDate.setUTCDate(1);
21917 if (className.indexOf('month') > -1) {
21918 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21920 var year = parseInt(html, 10) || 0;
21921 this.viewDate.setUTCFullYear(year);
21925 if(this.singleMode){
21926 this.setValue(this.formatDate(this.viewDate));
21937 //Roo.log(className);
21938 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21939 var day = parseInt(html, 10) || 1;
21940 var year = (this.viewDate || new Date()).getUTCFullYear(),
21941 month = (this.viewDate || new Date()).getUTCMonth();
21943 if (className.indexOf('old') > -1) {
21950 } else if (className.indexOf('new') > -1) {
21958 //Roo.log([year,month,day]);
21959 this.date = this.UTCDate(year, month, day,0,0,0,0);
21960 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21962 //Roo.log(this.formatDate(this.date));
21963 this.setValue(this.formatDate(this.date));
21970 setStartDate: function(startDate)
21972 this.startDate = startDate || -Infinity;
21973 if (this.startDate !== -Infinity) {
21974 this.startDate = this.parseDate(this.startDate);
21977 this.updateNavArrows();
21980 setEndDate: function(endDate)
21982 this.endDate = endDate || Infinity;
21983 if (this.endDate !== Infinity) {
21984 this.endDate = this.parseDate(this.endDate);
21987 this.updateNavArrows();
21990 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21992 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21993 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21994 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21996 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21997 return parseInt(d, 10);
22000 this.updateNavArrows();
22003 updateNavArrows: function()
22005 if(this.singleMode){
22009 var d = new Date(this.viewDate),
22010 year = d.getUTCFullYear(),
22011 month = d.getUTCMonth();
22013 Roo.each(this.picker().select('.prev', true).elements, function(v){
22015 switch (this.viewMode) {
22018 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22024 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22031 Roo.each(this.picker().select('.next', true).elements, function(v){
22033 switch (this.viewMode) {
22036 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22042 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22050 moveMonth: function(date, dir)
22055 var new_date = new Date(date.valueOf()),
22056 day = new_date.getUTCDate(),
22057 month = new_date.getUTCMonth(),
22058 mag = Math.abs(dir),
22060 dir = dir > 0 ? 1 : -1;
22063 // If going back one month, make sure month is not current month
22064 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22066 return new_date.getUTCMonth() == month;
22068 // If going forward one month, make sure month is as expected
22069 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22071 return new_date.getUTCMonth() != new_month;
22073 new_month = month + dir;
22074 new_date.setUTCMonth(new_month);
22075 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22076 if (new_month < 0 || new_month > 11) {
22077 new_month = (new_month + 12) % 12;
22080 // For magnitudes >1, move one month at a time...
22081 for (var i=0; i<mag; i++) {
22082 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22083 new_date = this.moveMonth(new_date, dir);
22085 // ...then reset the day, keeping it in the new month
22086 new_month = new_date.getUTCMonth();
22087 new_date.setUTCDate(day);
22089 return new_month != new_date.getUTCMonth();
22092 // Common date-resetting loop -- if date is beyond end of month, make it
22095 new_date.setUTCDate(--day);
22096 new_date.setUTCMonth(new_month);
22101 moveYear: function(date, dir)
22103 return this.moveMonth(date, dir*12);
22106 dateWithinRange: function(date)
22108 return date >= this.startDate && date <= this.endDate;
22114 this.picker().remove();
22117 validateValue : function(value)
22119 if(this.getVisibilityEl().hasClass('hidden')){
22123 if(value.length < 1) {
22124 if(this.allowBlank){
22130 if(value.length < this.minLength){
22133 if(value.length > this.maxLength){
22137 var vt = Roo.form.VTypes;
22138 if(!vt[this.vtype](value, this)){
22142 if(typeof this.validator == "function"){
22143 var msg = this.validator(value);
22149 if(this.regex && !this.regex.test(value)){
22153 if(typeof(this.parseDate(value)) == 'undefined'){
22157 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22161 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22171 this.date = this.viewDate = '';
22173 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22178 Roo.apply(Roo.bootstrap.DateField, {
22189 html: '<i class="fa fa-arrow-left"/>'
22199 html: '<i class="fa fa-arrow-right"/>'
22241 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22242 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22243 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22244 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22245 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22258 navFnc: 'FullYear',
22263 navFnc: 'FullYear',
22268 Roo.apply(Roo.bootstrap.DateField, {
22272 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22276 cls: 'datepicker-days',
22280 cls: 'table-condensed',
22282 Roo.bootstrap.DateField.head,
22286 Roo.bootstrap.DateField.footer
22293 cls: 'datepicker-months',
22297 cls: 'table-condensed',
22299 Roo.bootstrap.DateField.head,
22300 Roo.bootstrap.DateField.content,
22301 Roo.bootstrap.DateField.footer
22308 cls: 'datepicker-years',
22312 cls: 'table-condensed',
22314 Roo.bootstrap.DateField.head,
22315 Roo.bootstrap.DateField.content,
22316 Roo.bootstrap.DateField.footer
22335 * @class Roo.bootstrap.TimeField
22336 * @extends Roo.bootstrap.Input
22337 * Bootstrap DateField class
22341 * Create a new TimeField
22342 * @param {Object} config The config object
22345 Roo.bootstrap.TimeField = function(config){
22346 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22350 * Fires when this field show.
22351 * @param {Roo.bootstrap.DateField} thisthis
22352 * @param {Mixed} date The date value
22357 * Fires when this field hide.
22358 * @param {Roo.bootstrap.DateField} this
22359 * @param {Mixed} date The date value
22364 * Fires when select a date.
22365 * @param {Roo.bootstrap.DateField} this
22366 * @param {Mixed} date The date value
22372 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22375 * @cfg {String} format
22376 * The default time format string which can be overriden for localization support. The format must be
22377 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22381 getAutoCreate : function()
22383 this.after = '<i class="fa far fa-clock"></i>';
22384 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22388 onRender: function(ct, position)
22391 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22393 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22395 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22397 this.pop = this.picker().select('>.datepicker-time',true).first();
22398 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22400 this.picker().on('mousedown', this.onMousedown, this);
22401 this.picker().on('click', this.onClick, this);
22403 this.picker().addClass('datepicker-dropdown');
22408 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22409 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22410 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22411 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22412 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22413 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22417 fireKey: function(e){
22418 if (!this.picker().isVisible()){
22419 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22425 e.preventDefault();
22433 this.onTogglePeriod();
22436 this.onIncrementMinutes();
22439 this.onDecrementMinutes();
22448 onClick: function(e) {
22449 e.stopPropagation();
22450 e.preventDefault();
22453 picker : function()
22455 return this.pickerEl;
22458 fillTime: function()
22460 var time = this.pop.select('tbody', true).first();
22462 time.dom.innerHTML = '';
22477 cls: 'hours-up fa fas fa-chevron-up'
22497 cls: 'minutes-up fa fas fa-chevron-up'
22518 cls: 'timepicker-hour',
22533 cls: 'timepicker-minute',
22548 cls: 'btn btn-primary period',
22570 cls: 'hours-down fa fas fa-chevron-down'
22590 cls: 'minutes-down fa fas fa-chevron-down'
22608 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22615 var hours = this.time.getHours();
22616 var minutes = this.time.getMinutes();
22629 hours = hours - 12;
22633 hours = '0' + hours;
22637 minutes = '0' + minutes;
22640 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22641 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22642 this.pop.select('button', true).first().dom.innerHTML = period;
22648 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22650 var cls = ['bottom'];
22652 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22659 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22663 //this.picker().setXY(20000,20000);
22664 this.picker().addClass(cls.join('-'));
22668 Roo.each(cls, function(c){
22673 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22674 //_this.picker().setTop(_this.inputEl().getHeight());
22678 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22680 //_this.picker().setTop(0 - _this.picker().getHeight());
22685 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22689 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22697 onFocus : function()
22699 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22703 onBlur : function()
22705 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22711 this.picker().show();
22716 this.fireEvent('show', this, this.date);
22721 this.picker().hide();
22724 this.fireEvent('hide', this, this.date);
22727 setTime : function()
22730 this.setValue(this.time.format(this.format));
22732 this.fireEvent('select', this, this.date);
22737 onMousedown: function(e){
22738 e.stopPropagation();
22739 e.preventDefault();
22742 onIncrementHours: function()
22744 Roo.log('onIncrementHours');
22745 this.time = this.time.add(Date.HOUR, 1);
22750 onDecrementHours: function()
22752 Roo.log('onDecrementHours');
22753 this.time = this.time.add(Date.HOUR, -1);
22757 onIncrementMinutes: function()
22759 Roo.log('onIncrementMinutes');
22760 this.time = this.time.add(Date.MINUTE, 1);
22764 onDecrementMinutes: function()
22766 Roo.log('onDecrementMinutes');
22767 this.time = this.time.add(Date.MINUTE, -1);
22771 onTogglePeriod: function()
22773 Roo.log('onTogglePeriod');
22774 this.time = this.time.add(Date.HOUR, 12);
22782 Roo.apply(Roo.bootstrap.TimeField, {
22786 cls: 'datepicker dropdown-menu',
22790 cls: 'datepicker-time',
22794 cls: 'table-condensed',
22823 cls: 'btn btn-info ok',
22851 * @class Roo.bootstrap.MonthField
22852 * @extends Roo.bootstrap.Input
22853 * Bootstrap MonthField class
22855 * @cfg {String} language default en
22858 * Create a new MonthField
22859 * @param {Object} config The config object
22862 Roo.bootstrap.MonthField = function(config){
22863 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22868 * Fires when this field show.
22869 * @param {Roo.bootstrap.MonthField} this
22870 * @param {Mixed} date The date value
22875 * Fires when this field hide.
22876 * @param {Roo.bootstrap.MonthField} this
22877 * @param {Mixed} date The date value
22882 * Fires when select a date.
22883 * @param {Roo.bootstrap.MonthField} this
22884 * @param {String} oldvalue The old value
22885 * @param {String} newvalue The new value
22891 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22893 onRender: function(ct, position)
22896 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22898 this.language = this.language || 'en';
22899 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22900 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22902 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22903 this.isInline = false;
22904 this.isInput = true;
22905 this.component = this.el.select('.add-on', true).first() || false;
22906 this.component = (this.component && this.component.length === 0) ? false : this.component;
22907 this.hasInput = this.component && this.inputEL().length;
22909 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22911 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22913 this.picker().on('mousedown', this.onMousedown, this);
22914 this.picker().on('click', this.onClick, this);
22916 this.picker().addClass('datepicker-dropdown');
22918 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22919 v.setStyle('width', '189px');
22926 if(this.isInline) {
22932 setValue: function(v, suppressEvent)
22934 var o = this.getValue();
22936 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22940 if(suppressEvent !== true){
22941 this.fireEvent('select', this, o, v);
22946 getValue: function()
22951 onClick: function(e)
22953 e.stopPropagation();
22954 e.preventDefault();
22956 var target = e.getTarget();
22958 if(target.nodeName.toLowerCase() === 'i'){
22959 target = Roo.get(target).dom.parentNode;
22962 var nodeName = target.nodeName;
22963 var className = target.className;
22964 var html = target.innerHTML;
22966 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22970 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22972 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22978 picker : function()
22980 return this.pickerEl;
22983 fillMonths: function()
22986 var months = this.picker().select('>.datepicker-months td', true).first();
22988 months.dom.innerHTML = '';
22994 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22997 months.createChild(month);
23006 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23007 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23010 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23011 e.removeClass('active');
23013 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23014 e.addClass('active');
23021 if(this.isInline) {
23025 this.picker().removeClass(['bottom', 'top']);
23027 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23029 * place to the top of element!
23033 this.picker().addClass('top');
23034 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23039 this.picker().addClass('bottom');
23041 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23044 onFocus : function()
23046 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23050 onBlur : function()
23052 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23054 var d = this.inputEl().getValue();
23063 this.picker().show();
23064 this.picker().select('>.datepicker-months', true).first().show();
23068 this.fireEvent('show', this, this.date);
23073 if(this.isInline) {
23076 this.picker().hide();
23077 this.fireEvent('hide', this, this.date);
23081 onMousedown: function(e)
23083 e.stopPropagation();
23084 e.preventDefault();
23089 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23093 fireKey: function(e)
23095 if (!this.picker().isVisible()){
23096 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23107 e.preventDefault();
23111 dir = e.keyCode == 37 ? -1 : 1;
23113 this.vIndex = this.vIndex + dir;
23115 if(this.vIndex < 0){
23119 if(this.vIndex > 11){
23123 if(isNaN(this.vIndex)){
23127 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23133 dir = e.keyCode == 38 ? -1 : 1;
23135 this.vIndex = this.vIndex + dir * 4;
23137 if(this.vIndex < 0){
23141 if(this.vIndex > 11){
23145 if(isNaN(this.vIndex)){
23149 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23154 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23155 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23159 e.preventDefault();
23162 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23163 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23179 this.picker().remove();
23184 Roo.apply(Roo.bootstrap.MonthField, {
23203 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23204 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23209 Roo.apply(Roo.bootstrap.MonthField, {
23213 cls: 'datepicker dropdown-menu roo-dynamic',
23217 cls: 'datepicker-months',
23221 cls: 'table-condensed',
23223 Roo.bootstrap.DateField.content
23243 * @class Roo.bootstrap.CheckBox
23244 * @extends Roo.bootstrap.Input
23245 * Bootstrap CheckBox class
23247 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23248 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23249 * @cfg {String} boxLabel The text that appears beside the checkbox
23250 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23251 * @cfg {Boolean} checked initnal the element
23252 * @cfg {Boolean} inline inline the element (default false)
23253 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23254 * @cfg {String} tooltip label tooltip
23257 * Create a new CheckBox
23258 * @param {Object} config The config object
23261 Roo.bootstrap.CheckBox = function(config){
23262 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23267 * Fires when the element is checked or unchecked.
23268 * @param {Roo.bootstrap.CheckBox} this This input
23269 * @param {Boolean} checked The new checked value
23274 * Fires when the element is click.
23275 * @param {Roo.bootstrap.CheckBox} this This input
23282 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23284 inputType: 'checkbox',
23293 // checkbox success does not make any sense really..
23298 getAutoCreate : function()
23300 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23306 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23309 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23315 type : this.inputType,
23316 value : this.inputValue,
23317 cls : 'roo-' + this.inputType, //'form-box',
23318 placeholder : this.placeholder || ''
23322 if(this.inputType != 'radio'){
23326 cls : 'roo-hidden-value',
23327 value : this.checked ? this.inputValue : this.valueOff
23332 if (this.weight) { // Validity check?
23333 cfg.cls += " " + this.inputType + "-" + this.weight;
23336 if (this.disabled) {
23337 input.disabled=true;
23341 input.checked = this.checked;
23346 input.name = this.name;
23348 if(this.inputType != 'radio'){
23349 hidden.name = this.name;
23350 input.name = '_hidden_' + this.name;
23355 input.cls += ' input-' + this.size;
23360 ['xs','sm','md','lg'].map(function(size){
23361 if (settings[size]) {
23362 cfg.cls += ' col-' + size + '-' + settings[size];
23366 var inputblock = input;
23368 if (this.before || this.after) {
23371 cls : 'input-group',
23376 inputblock.cn.push({
23378 cls : 'input-group-addon',
23383 inputblock.cn.push(input);
23385 if(this.inputType != 'radio'){
23386 inputblock.cn.push(hidden);
23390 inputblock.cn.push({
23392 cls : 'input-group-addon',
23398 var boxLabelCfg = false;
23404 //'for': id, // box label is handled by onclick - so no for...
23406 html: this.boxLabel
23409 boxLabelCfg.tooltip = this.tooltip;
23415 if (align ==='left' && this.fieldLabel.length) {
23416 // Roo.log("left and has label");
23421 cls : 'control-label',
23422 html : this.fieldLabel
23433 cfg.cn[1].cn.push(boxLabelCfg);
23436 if(this.labelWidth > 12){
23437 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23440 if(this.labelWidth < 13 && this.labelmd == 0){
23441 this.labelmd = this.labelWidth;
23444 if(this.labellg > 0){
23445 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23446 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23449 if(this.labelmd > 0){
23450 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23451 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23454 if(this.labelsm > 0){
23455 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23456 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23459 if(this.labelxs > 0){
23460 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23461 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23464 } else if ( this.fieldLabel.length) {
23465 // Roo.log(" label");
23469 tag: this.boxLabel ? 'span' : 'label',
23471 cls: 'control-label box-input-label',
23472 //cls : 'input-group-addon',
23473 html : this.fieldLabel
23480 cfg.cn.push(boxLabelCfg);
23485 // Roo.log(" no label && no align");
23486 cfg.cn = [ inputblock ] ;
23488 cfg.cn.push(boxLabelCfg);
23496 if(this.inputType != 'radio'){
23497 cfg.cn.push(hidden);
23505 * return the real input element.
23507 inputEl: function ()
23509 return this.el.select('input.roo-' + this.inputType,true).first();
23511 hiddenEl: function ()
23513 return this.el.select('input.roo-hidden-value',true).first();
23516 labelEl: function()
23518 return this.el.select('label.control-label',true).first();
23520 /* depricated... */
23524 return this.labelEl();
23527 boxLabelEl: function()
23529 return this.el.select('label.box-label',true).first();
23532 initEvents : function()
23534 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23536 this.inputEl().on('click', this.onClick, this);
23538 if (this.boxLabel) {
23539 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23542 this.startValue = this.getValue();
23545 Roo.bootstrap.CheckBox.register(this);
23549 onClick : function(e)
23551 if(this.fireEvent('click', this, e) !== false){
23552 this.setChecked(!this.checked);
23557 setChecked : function(state,suppressEvent)
23559 this.startValue = this.getValue();
23561 if(this.inputType == 'radio'){
23563 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23564 e.dom.checked = false;
23567 this.inputEl().dom.checked = true;
23569 this.inputEl().dom.value = this.inputValue;
23571 if(suppressEvent !== true){
23572 this.fireEvent('check', this, true);
23580 this.checked = state;
23582 this.inputEl().dom.checked = state;
23585 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23587 if(suppressEvent !== true){
23588 this.fireEvent('check', this, state);
23594 getValue : function()
23596 if(this.inputType == 'radio'){
23597 return this.getGroupValue();
23600 return this.hiddenEl().dom.value;
23604 getGroupValue : function()
23606 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23610 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23613 setValue : function(v,suppressEvent)
23615 if(this.inputType == 'radio'){
23616 this.setGroupValue(v, suppressEvent);
23620 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23625 setGroupValue : function(v, suppressEvent)
23627 this.startValue = this.getValue();
23629 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23630 e.dom.checked = false;
23632 if(e.dom.value == v){
23633 e.dom.checked = true;
23637 if(suppressEvent !== true){
23638 this.fireEvent('check', this, true);
23646 validate : function()
23648 if(this.getVisibilityEl().hasClass('hidden')){
23654 (this.inputType == 'radio' && this.validateRadio()) ||
23655 (this.inputType == 'checkbox' && this.validateCheckbox())
23661 this.markInvalid();
23665 validateRadio : function()
23667 if(this.getVisibilityEl().hasClass('hidden')){
23671 if(this.allowBlank){
23677 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23678 if(!e.dom.checked){
23690 validateCheckbox : function()
23693 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23694 //return (this.getValue() == this.inputValue) ? true : false;
23697 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23705 for(var i in group){
23706 if(group[i].el.isVisible(true)){
23714 for(var i in group){
23719 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23726 * Mark this field as valid
23728 markValid : function()
23732 this.fireEvent('valid', this);
23734 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23737 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23744 if(this.inputType == 'radio'){
23745 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23746 var fg = e.findParent('.form-group', false, true);
23747 if (Roo.bootstrap.version == 3) {
23748 fg.removeClass([_this.invalidClass, _this.validClass]);
23749 fg.addClass(_this.validClass);
23751 fg.removeClass(['is-valid', 'is-invalid']);
23752 fg.addClass('is-valid');
23760 var fg = this.el.findParent('.form-group', false, true);
23761 if (Roo.bootstrap.version == 3) {
23762 fg.removeClass([this.invalidClass, this.validClass]);
23763 fg.addClass(this.validClass);
23765 fg.removeClass(['is-valid', 'is-invalid']);
23766 fg.addClass('is-valid');
23771 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23777 for(var i in group){
23778 var fg = group[i].el.findParent('.form-group', false, true);
23779 if (Roo.bootstrap.version == 3) {
23780 fg.removeClass([this.invalidClass, this.validClass]);
23781 fg.addClass(this.validClass);
23783 fg.removeClass(['is-valid', 'is-invalid']);
23784 fg.addClass('is-valid');
23790 * Mark this field as invalid
23791 * @param {String} msg The validation message
23793 markInvalid : function(msg)
23795 if(this.allowBlank){
23801 this.fireEvent('invalid', this, msg);
23803 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23806 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23810 label.markInvalid();
23813 if(this.inputType == 'radio'){
23815 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23816 var fg = e.findParent('.form-group', false, true);
23817 if (Roo.bootstrap.version == 3) {
23818 fg.removeClass([_this.invalidClass, _this.validClass]);
23819 fg.addClass(_this.invalidClass);
23821 fg.removeClass(['is-invalid', 'is-valid']);
23822 fg.addClass('is-invalid');
23830 var fg = this.el.findParent('.form-group', false, true);
23831 if (Roo.bootstrap.version == 3) {
23832 fg.removeClass([_this.invalidClass, _this.validClass]);
23833 fg.addClass(_this.invalidClass);
23835 fg.removeClass(['is-invalid', 'is-valid']);
23836 fg.addClass('is-invalid');
23841 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23847 for(var i in group){
23848 var fg = group[i].el.findParent('.form-group', false, true);
23849 if (Roo.bootstrap.version == 3) {
23850 fg.removeClass([_this.invalidClass, _this.validClass]);
23851 fg.addClass(_this.invalidClass);
23853 fg.removeClass(['is-invalid', 'is-valid']);
23854 fg.addClass('is-invalid');
23860 clearInvalid : function()
23862 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23864 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23866 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23868 if (label && label.iconEl) {
23869 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23870 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23874 disable : function()
23876 if(this.inputType != 'radio'){
23877 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23884 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23885 _this.getActionEl().addClass(this.disabledClass);
23886 e.dom.disabled = true;
23890 this.disabled = true;
23891 this.fireEvent("disable", this);
23895 enable : function()
23897 if(this.inputType != 'radio'){
23898 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23905 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23906 _this.getActionEl().removeClass(this.disabledClass);
23907 e.dom.disabled = false;
23911 this.disabled = false;
23912 this.fireEvent("enable", this);
23916 setBoxLabel : function(v)
23921 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23927 Roo.apply(Roo.bootstrap.CheckBox, {
23932 * register a CheckBox Group
23933 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23935 register : function(checkbox)
23937 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23938 this.groups[checkbox.groupId] = {};
23941 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23945 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23949 * fetch a CheckBox Group based on the group ID
23950 * @param {string} the group ID
23951 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23953 get: function(groupId) {
23954 if (typeof(this.groups[groupId]) == 'undefined') {
23958 return this.groups[groupId] ;
23971 * @class Roo.bootstrap.Radio
23972 * @extends Roo.bootstrap.Component
23973 * Bootstrap Radio class
23974 * @cfg {String} boxLabel - the label associated
23975 * @cfg {String} value - the value of radio
23978 * Create a new Radio
23979 * @param {Object} config The config object
23981 Roo.bootstrap.Radio = function(config){
23982 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23986 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23992 getAutoCreate : function()
23996 cls : 'form-group radio',
24001 html : this.boxLabel
24009 initEvents : function()
24011 this.parent().register(this);
24013 this.el.on('click', this.onClick, this);
24017 onClick : function(e)
24019 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24020 this.setChecked(true);
24024 setChecked : function(state, suppressEvent)
24026 this.parent().setValue(this.value, suppressEvent);
24030 setBoxLabel : function(v)
24035 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24050 * @class Roo.bootstrap.SecurePass
24051 * @extends Roo.bootstrap.Input
24052 * Bootstrap SecurePass class
24056 * Create a new SecurePass
24057 * @param {Object} config The config object
24060 Roo.bootstrap.SecurePass = function (config) {
24061 // these go here, so the translation tool can replace them..
24063 PwdEmpty: "Please type a password, and then retype it to confirm.",
24064 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24065 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24066 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24067 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24068 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24069 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24070 TooWeak: "Your password is Too Weak."
24072 this.meterLabel = "Password strength:";
24073 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24074 this.meterClass = [
24075 "roo-password-meter-tooweak",
24076 "roo-password-meter-weak",
24077 "roo-password-meter-medium",
24078 "roo-password-meter-strong",
24079 "roo-password-meter-grey"
24084 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24087 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24089 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24091 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24092 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24093 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24094 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24095 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24096 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24097 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24107 * @cfg {String/Object} Label for the strength meter (defaults to
24108 * 'Password strength:')
24113 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24114 * ['Weak', 'Medium', 'Strong'])
24117 pwdStrengths: false,
24130 initEvents: function ()
24132 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24134 if (this.el.is('input[type=password]') && Roo.isSafari) {
24135 this.el.on('keydown', this.SafariOnKeyDown, this);
24138 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24141 onRender: function (ct, position)
24143 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24144 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24145 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24147 this.trigger.createChild({
24152 cls: 'roo-password-meter-grey col-xs-12',
24155 //width: this.meterWidth + 'px'
24159 cls: 'roo-password-meter-text'
24165 if (this.hideTrigger) {
24166 this.trigger.setDisplayed(false);
24168 this.setSize(this.width || '', this.height || '');
24171 onDestroy: function ()
24173 if (this.trigger) {
24174 this.trigger.removeAllListeners();
24175 this.trigger.remove();
24178 this.wrap.remove();
24180 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24183 checkStrength: function ()
24185 var pwd = this.inputEl().getValue();
24186 if (pwd == this._lastPwd) {
24191 if (this.ClientSideStrongPassword(pwd)) {
24193 } else if (this.ClientSideMediumPassword(pwd)) {
24195 } else if (this.ClientSideWeakPassword(pwd)) {
24201 Roo.log('strength1: ' + strength);
24203 //var pm = this.trigger.child('div/div/div').dom;
24204 var pm = this.trigger.child('div/div');
24205 pm.removeClass(this.meterClass);
24206 pm.addClass(this.meterClass[strength]);
24209 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24211 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24213 this._lastPwd = pwd;
24217 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24219 this._lastPwd = '';
24221 var pm = this.trigger.child('div/div');
24222 pm.removeClass(this.meterClass);
24223 pm.addClass('roo-password-meter-grey');
24226 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24229 this.inputEl().dom.type='password';
24232 validateValue: function (value)
24234 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24237 if (value.length == 0) {
24238 if (this.allowBlank) {
24239 this.clearInvalid();
24243 this.markInvalid(this.errors.PwdEmpty);
24244 this.errorMsg = this.errors.PwdEmpty;
24252 if (!value.match(/[\x21-\x7e]+/)) {
24253 this.markInvalid(this.errors.PwdBadChar);
24254 this.errorMsg = this.errors.PwdBadChar;
24257 if (value.length < 6) {
24258 this.markInvalid(this.errors.PwdShort);
24259 this.errorMsg = this.errors.PwdShort;
24262 if (value.length > 16) {
24263 this.markInvalid(this.errors.PwdLong);
24264 this.errorMsg = this.errors.PwdLong;
24268 if (this.ClientSideStrongPassword(value)) {
24270 } else if (this.ClientSideMediumPassword(value)) {
24272 } else if (this.ClientSideWeakPassword(value)) {
24279 if (strength < 2) {
24280 //this.markInvalid(this.errors.TooWeak);
24281 this.errorMsg = this.errors.TooWeak;
24286 console.log('strength2: ' + strength);
24288 //var pm = this.trigger.child('div/div/div').dom;
24290 var pm = this.trigger.child('div/div');
24291 pm.removeClass(this.meterClass);
24292 pm.addClass(this.meterClass[strength]);
24294 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24296 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24298 this.errorMsg = '';
24302 CharacterSetChecks: function (type)
24305 this.fResult = false;
24308 isctype: function (character, type)
24311 case this.kCapitalLetter:
24312 if (character >= 'A' && character <= 'Z') {
24317 case this.kSmallLetter:
24318 if (character >= 'a' && character <= 'z') {
24324 if (character >= '0' && character <= '9') {
24329 case this.kPunctuation:
24330 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24341 IsLongEnough: function (pwd, size)
24343 return !(pwd == null || isNaN(size) || pwd.length < size);
24346 SpansEnoughCharacterSets: function (word, nb)
24348 if (!this.IsLongEnough(word, nb))
24353 var characterSetChecks = new Array(
24354 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24355 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24358 for (var index = 0; index < word.length; ++index) {
24359 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24360 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24361 characterSetChecks[nCharSet].fResult = true;
24368 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24369 if (characterSetChecks[nCharSet].fResult) {
24374 if (nCharSets < nb) {
24380 ClientSideStrongPassword: function (pwd)
24382 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24385 ClientSideMediumPassword: function (pwd)
24387 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24390 ClientSideWeakPassword: function (pwd)
24392 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24395 })//<script type="text/javascript">
24398 * Based Ext JS Library 1.1.1
24399 * Copyright(c) 2006-2007, Ext JS, LLC.
24405 * @class Roo.HtmlEditorCore
24406 * @extends Roo.Component
24407 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24409 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24412 Roo.HtmlEditorCore = function(config){
24415 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24420 * @event initialize
24421 * Fires when the editor is fully initialized (including the iframe)
24422 * @param {Roo.HtmlEditorCore} this
24427 * Fires when the editor is first receives the focus. Any insertion must wait
24428 * until after this event.
24429 * @param {Roo.HtmlEditorCore} this
24433 * @event beforesync
24434 * Fires before the textarea is updated with content from the editor iframe. Return false
24435 * to cancel the sync.
24436 * @param {Roo.HtmlEditorCore} this
24437 * @param {String} html
24441 * @event beforepush
24442 * Fires before the iframe editor is updated with content from the textarea. Return false
24443 * to cancel the push.
24444 * @param {Roo.HtmlEditorCore} this
24445 * @param {String} html
24450 * Fires when the textarea is updated with content from the editor iframe.
24451 * @param {Roo.HtmlEditorCore} this
24452 * @param {String} html
24457 * Fires when the iframe editor is updated with content from the textarea.
24458 * @param {Roo.HtmlEditorCore} this
24459 * @param {String} html
24464 * @event editorevent
24465 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24466 * @param {Roo.HtmlEditorCore} this
24472 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24474 // defaults : white / black...
24475 this.applyBlacklists();
24482 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24486 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24492 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24497 * @cfg {Number} height (in pixels)
24501 * @cfg {Number} width (in pixels)
24506 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24509 stylesheets: false,
24514 // private properties
24515 validationEvent : false,
24517 initialized : false,
24519 sourceEditMode : false,
24520 onFocus : Roo.emptyFn,
24522 hideMode:'offsets',
24526 // blacklist + whitelisted elements..
24533 * Protected method that will not generally be called directly. It
24534 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24535 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24537 getDocMarkup : function(){
24541 // inherit styels from page...??
24542 if (this.stylesheets === false) {
24544 Roo.get(document.head).select('style').each(function(node) {
24545 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24548 Roo.get(document.head).select('link').each(function(node) {
24549 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24552 } else if (!this.stylesheets.length) {
24554 st = '<style type="text/css">' +
24555 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24558 for (var i in this.stylesheets) {
24559 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24564 st += '<style type="text/css">' +
24565 'IMG { cursor: pointer } ' +
24568 var cls = 'roo-htmleditor-body';
24570 if(this.bodyCls.length){
24571 cls += ' ' + this.bodyCls;
24574 return '<html><head>' + st +
24575 //<style type="text/css">' +
24576 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24578 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24582 onRender : function(ct, position)
24585 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24586 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24589 this.el.dom.style.border = '0 none';
24590 this.el.dom.setAttribute('tabIndex', -1);
24591 this.el.addClass('x-hidden hide');
24595 if(Roo.isIE){ // fix IE 1px bogus margin
24596 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24600 this.frameId = Roo.id();
24604 var iframe = this.owner.wrap.createChild({
24606 cls: 'form-control', // bootstrap..
24608 name: this.frameId,
24609 frameBorder : 'no',
24610 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24615 this.iframe = iframe.dom;
24617 this.assignDocWin();
24619 this.doc.designMode = 'on';
24622 this.doc.write(this.getDocMarkup());
24626 var task = { // must defer to wait for browser to be ready
24628 //console.log("run task?" + this.doc.readyState);
24629 this.assignDocWin();
24630 if(this.doc.body || this.doc.readyState == 'complete'){
24632 this.doc.designMode="on";
24636 Roo.TaskMgr.stop(task);
24637 this.initEditor.defer(10, this);
24644 Roo.TaskMgr.start(task);
24649 onResize : function(w, h)
24651 Roo.log('resize: ' +w + ',' + h );
24652 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24656 if(typeof w == 'number'){
24658 this.iframe.style.width = w + 'px';
24660 if(typeof h == 'number'){
24662 this.iframe.style.height = h + 'px';
24664 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24671 * Toggles the editor between standard and source edit mode.
24672 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24674 toggleSourceEdit : function(sourceEditMode){
24676 this.sourceEditMode = sourceEditMode === true;
24678 if(this.sourceEditMode){
24680 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24683 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24684 //this.iframe.className = '';
24687 //this.setSize(this.owner.wrap.getSize());
24688 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24695 * Protected method that will not generally be called directly. If you need/want
24696 * custom HTML cleanup, this is the method you should override.
24697 * @param {String} html The HTML to be cleaned
24698 * return {String} The cleaned HTML
24700 cleanHtml : function(html){
24701 html = String(html);
24702 if(html.length > 5){
24703 if(Roo.isSafari){ // strip safari nonsense
24704 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24707 if(html == ' '){
24714 * HTML Editor -> Textarea
24715 * Protected method that will not generally be called directly. Syncs the contents
24716 * of the editor iframe with the textarea.
24718 syncValue : function(){
24719 if(this.initialized){
24720 var bd = (this.doc.body || this.doc.documentElement);
24721 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24722 var html = bd.innerHTML;
24724 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24725 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24727 html = '<div style="'+m[0]+'">' + html + '</div>';
24730 html = this.cleanHtml(html);
24731 // fix up the special chars.. normaly like back quotes in word...
24732 // however we do not want to do this with chinese..
24733 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24735 var cc = match.charCodeAt();
24737 // Get the character value, handling surrogate pairs
24738 if (match.length == 2) {
24739 // It's a surrogate pair, calculate the Unicode code point
24740 var high = match.charCodeAt(0) - 0xD800;
24741 var low = match.charCodeAt(1) - 0xDC00;
24742 cc = (high * 0x400) + low + 0x10000;
24744 (cc >= 0x4E00 && cc < 0xA000 ) ||
24745 (cc >= 0x3400 && cc < 0x4E00 ) ||
24746 (cc >= 0xf900 && cc < 0xfb00 )
24751 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24752 return "&#" + cc + ";";
24759 if(this.owner.fireEvent('beforesync', this, html) !== false){
24760 this.el.dom.value = html;
24761 this.owner.fireEvent('sync', this, html);
24767 * Protected method that will not generally be called directly. Pushes the value of the textarea
24768 * into the iframe editor.
24770 pushValue : function(){
24771 if(this.initialized){
24772 var v = this.el.dom.value.trim();
24774 // if(v.length < 1){
24778 if(this.owner.fireEvent('beforepush', this, v) !== false){
24779 var d = (this.doc.body || this.doc.documentElement);
24781 this.cleanUpPaste();
24782 this.el.dom.value = d.innerHTML;
24783 this.owner.fireEvent('push', this, v);
24789 deferFocus : function(){
24790 this.focus.defer(10, this);
24794 focus : function(){
24795 if(this.win && !this.sourceEditMode){
24802 assignDocWin: function()
24804 var iframe = this.iframe;
24807 this.doc = iframe.contentWindow.document;
24808 this.win = iframe.contentWindow;
24810 // if (!Roo.get(this.frameId)) {
24813 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24814 // this.win = Roo.get(this.frameId).dom.contentWindow;
24816 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24820 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24821 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24826 initEditor : function(){
24827 //console.log("INIT EDITOR");
24828 this.assignDocWin();
24832 this.doc.designMode="on";
24834 this.doc.write(this.getDocMarkup());
24837 var dbody = (this.doc.body || this.doc.documentElement);
24838 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24839 // this copies styles from the containing element into thsi one..
24840 // not sure why we need all of this..
24841 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24843 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24844 //ss['background-attachment'] = 'fixed'; // w3c
24845 dbody.bgProperties = 'fixed'; // ie
24846 //Roo.DomHelper.applyStyles(dbody, ss);
24847 Roo.EventManager.on(this.doc, {
24848 //'mousedown': this.onEditorEvent,
24849 'mouseup': this.onEditorEvent,
24850 'dblclick': this.onEditorEvent,
24851 'click': this.onEditorEvent,
24852 'keyup': this.onEditorEvent,
24857 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24859 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24860 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24862 this.initialized = true;
24864 this.owner.fireEvent('initialize', this);
24869 onDestroy : function(){
24875 //for (var i =0; i < this.toolbars.length;i++) {
24876 // // fixme - ask toolbars for heights?
24877 // this.toolbars[i].onDestroy();
24880 //this.wrap.dom.innerHTML = '';
24881 //this.wrap.remove();
24886 onFirstFocus : function(){
24888 this.assignDocWin();
24891 this.activated = true;
24894 if(Roo.isGecko){ // prevent silly gecko errors
24896 var s = this.win.getSelection();
24897 if(!s.focusNode || s.focusNode.nodeType != 3){
24898 var r = s.getRangeAt(0);
24899 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24904 this.execCmd('useCSS', true);
24905 this.execCmd('styleWithCSS', false);
24908 this.owner.fireEvent('activate', this);
24912 adjustFont: function(btn){
24913 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24914 //if(Roo.isSafari){ // safari
24917 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24918 if(Roo.isSafari){ // safari
24919 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24920 v = (v < 10) ? 10 : v;
24921 v = (v > 48) ? 48 : v;
24922 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24927 v = Math.max(1, v+adjust);
24929 this.execCmd('FontSize', v );
24932 onEditorEvent : function(e)
24934 this.owner.fireEvent('editorevent', this, e);
24935 // this.updateToolbar();
24936 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24939 insertTag : function(tg)
24941 // could be a bit smarter... -> wrap the current selected tRoo..
24942 if (tg.toLowerCase() == 'span' ||
24943 tg.toLowerCase() == 'code' ||
24944 tg.toLowerCase() == 'sup' ||
24945 tg.toLowerCase() == 'sub'
24948 range = this.createRange(this.getSelection());
24949 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24950 wrappingNode.appendChild(range.extractContents());
24951 range.insertNode(wrappingNode);
24958 this.execCmd("formatblock", tg);
24962 insertText : function(txt)
24966 var range = this.createRange();
24967 range.deleteContents();
24968 //alert(Sender.getAttribute('label'));
24970 range.insertNode(this.doc.createTextNode(txt));
24976 * Executes a Midas editor command on the editor document and performs necessary focus and
24977 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24978 * @param {String} cmd The Midas command
24979 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24981 relayCmd : function(cmd, value){
24983 this.execCmd(cmd, value);
24984 this.owner.fireEvent('editorevent', this);
24985 //this.updateToolbar();
24986 this.owner.deferFocus();
24990 * Executes a Midas editor command directly on the editor document.
24991 * For visual commands, you should use {@link #relayCmd} instead.
24992 * <b>This should only be called after the editor is initialized.</b>
24993 * @param {String} cmd The Midas command
24994 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24996 execCmd : function(cmd, value){
24997 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25004 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25006 * @param {String} text | dom node..
25008 insertAtCursor : function(text)
25011 if(!this.activated){
25017 var r = this.doc.selection.createRange();
25028 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25032 // from jquery ui (MIT licenced)
25034 var win = this.win;
25036 if (win.getSelection && win.getSelection().getRangeAt) {
25037 range = win.getSelection().getRangeAt(0);
25038 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25039 range.insertNode(node);
25040 } else if (win.document.selection && win.document.selection.createRange) {
25041 // no firefox support
25042 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25043 win.document.selection.createRange().pasteHTML(txt);
25045 // no firefox support
25046 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25047 this.execCmd('InsertHTML', txt);
25056 mozKeyPress : function(e){
25058 var c = e.getCharCode(), cmd;
25061 c = String.fromCharCode(c).toLowerCase();
25075 this.cleanUpPaste.defer(100, this);
25083 e.preventDefault();
25091 fixKeys : function(){ // load time branching for fastest keydown performance
25093 return function(e){
25094 var k = e.getKey(), r;
25097 r = this.doc.selection.createRange();
25100 r.pasteHTML('    ');
25107 r = this.doc.selection.createRange();
25109 var target = r.parentElement();
25110 if(!target || target.tagName.toLowerCase() != 'li'){
25112 r.pasteHTML('<br />');
25118 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25119 this.cleanUpPaste.defer(100, this);
25125 }else if(Roo.isOpera){
25126 return function(e){
25127 var k = e.getKey();
25131 this.execCmd('InsertHTML','    ');
25134 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25135 this.cleanUpPaste.defer(100, this);
25140 }else if(Roo.isSafari){
25141 return function(e){
25142 var k = e.getKey();
25146 this.execCmd('InsertText','\t');
25150 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25151 this.cleanUpPaste.defer(100, this);
25159 getAllAncestors: function()
25161 var p = this.getSelectedNode();
25164 a.push(p); // push blank onto stack..
25165 p = this.getParentElement();
25169 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25173 a.push(this.doc.body);
25177 lastSelNode : false,
25180 getSelection : function()
25182 this.assignDocWin();
25183 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25186 getSelectedNode: function()
25188 // this may only work on Gecko!!!
25190 // should we cache this!!!!
25195 var range = this.createRange(this.getSelection()).cloneRange();
25198 var parent = range.parentElement();
25200 var testRange = range.duplicate();
25201 testRange.moveToElementText(parent);
25202 if (testRange.inRange(range)) {
25205 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25208 parent = parent.parentElement;
25213 // is ancestor a text element.
25214 var ac = range.commonAncestorContainer;
25215 if (ac.nodeType == 3) {
25216 ac = ac.parentNode;
25219 var ar = ac.childNodes;
25222 var other_nodes = [];
25223 var has_other_nodes = false;
25224 for (var i=0;i<ar.length;i++) {
25225 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25228 // fullly contained node.
25230 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25235 // probably selected..
25236 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25237 other_nodes.push(ar[i]);
25241 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25246 has_other_nodes = true;
25248 if (!nodes.length && other_nodes.length) {
25249 nodes= other_nodes;
25251 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25257 createRange: function(sel)
25259 // this has strange effects when using with
25260 // top toolbar - not sure if it's a great idea.
25261 //this.editor.contentWindow.focus();
25262 if (typeof sel != "undefined") {
25264 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25266 return this.doc.createRange();
25269 return this.doc.createRange();
25272 getParentElement: function()
25275 this.assignDocWin();
25276 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25278 var range = this.createRange(sel);
25281 var p = range.commonAncestorContainer;
25282 while (p.nodeType == 3) { // text node
25293 * Range intersection.. the hard stuff...
25297 * [ -- selected range --- ]
25301 * if end is before start or hits it. fail.
25302 * if start is after end or hits it fail.
25304 * if either hits (but other is outside. - then it's not
25310 // @see http://www.thismuchiknow.co.uk/?p=64.
25311 rangeIntersectsNode : function(range, node)
25313 var nodeRange = node.ownerDocument.createRange();
25315 nodeRange.selectNode(node);
25317 nodeRange.selectNodeContents(node);
25320 var rangeStartRange = range.cloneRange();
25321 rangeStartRange.collapse(true);
25323 var rangeEndRange = range.cloneRange();
25324 rangeEndRange.collapse(false);
25326 var nodeStartRange = nodeRange.cloneRange();
25327 nodeStartRange.collapse(true);
25329 var nodeEndRange = nodeRange.cloneRange();
25330 nodeEndRange.collapse(false);
25332 return rangeStartRange.compareBoundaryPoints(
25333 Range.START_TO_START, nodeEndRange) == -1 &&
25334 rangeEndRange.compareBoundaryPoints(
25335 Range.START_TO_START, nodeStartRange) == 1;
25339 rangeCompareNode : function(range, node)
25341 var nodeRange = node.ownerDocument.createRange();
25343 nodeRange.selectNode(node);
25345 nodeRange.selectNodeContents(node);
25349 range.collapse(true);
25351 nodeRange.collapse(true);
25353 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25354 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25356 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25358 var nodeIsBefore = ss == 1;
25359 var nodeIsAfter = ee == -1;
25361 if (nodeIsBefore && nodeIsAfter) {
25364 if (!nodeIsBefore && nodeIsAfter) {
25365 return 1; //right trailed.
25368 if (nodeIsBefore && !nodeIsAfter) {
25369 return 2; // left trailed.
25375 // private? - in a new class?
25376 cleanUpPaste : function()
25378 // cleans up the whole document..
25379 Roo.log('cleanuppaste');
25381 this.cleanUpChildren(this.doc.body);
25382 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25383 if (clean != this.doc.body.innerHTML) {
25384 this.doc.body.innerHTML = clean;
25389 cleanWordChars : function(input) {// change the chars to hex code
25390 var he = Roo.HtmlEditorCore;
25392 var output = input;
25393 Roo.each(he.swapCodes, function(sw) {
25394 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25396 output = output.replace(swapper, sw[1]);
25403 cleanUpChildren : function (n)
25405 if (!n.childNodes.length) {
25408 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25409 this.cleanUpChild(n.childNodes[i]);
25416 cleanUpChild : function (node)
25419 //console.log(node);
25420 if (node.nodeName == "#text") {
25421 // clean up silly Windows -- stuff?
25424 if (node.nodeName == "#comment") {
25425 node.parentNode.removeChild(node);
25426 // clean up silly Windows -- stuff?
25429 var lcname = node.tagName.toLowerCase();
25430 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25431 // whitelist of tags..
25433 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25435 node.parentNode.removeChild(node);
25440 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25442 // spans with no attributes - just remove them..
25443 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25444 remove_keep_children = true;
25447 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25448 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25450 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25451 // remove_keep_children = true;
25454 if (remove_keep_children) {
25455 this.cleanUpChildren(node);
25456 // inserts everything just before this node...
25457 while (node.childNodes.length) {
25458 var cn = node.childNodes[0];
25459 node.removeChild(cn);
25460 node.parentNode.insertBefore(cn, node);
25462 node.parentNode.removeChild(node);
25466 if (!node.attributes || !node.attributes.length) {
25471 this.cleanUpChildren(node);
25475 function cleanAttr(n,v)
25478 if (v.match(/^\./) || v.match(/^\//)) {
25481 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25484 if (v.match(/^#/)) {
25487 if (v.match(/^\{/)) { // allow template editing.
25490 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25491 node.removeAttribute(n);
25495 var cwhite = this.cwhite;
25496 var cblack = this.cblack;
25498 function cleanStyle(n,v)
25500 if (v.match(/expression/)) { //XSS?? should we even bother..
25501 node.removeAttribute(n);
25505 var parts = v.split(/;/);
25508 Roo.each(parts, function(p) {
25509 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25513 var l = p.split(':').shift().replace(/\s+/g,'');
25514 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25516 if ( cwhite.length && cblack.indexOf(l) > -1) {
25517 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25518 //node.removeAttribute(n);
25522 // only allow 'c whitelisted system attributes'
25523 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25524 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25525 //node.removeAttribute(n);
25535 if (clean.length) {
25536 node.setAttribute(n, clean.join(';'));
25538 node.removeAttribute(n);
25544 for (var i = node.attributes.length-1; i > -1 ; i--) {
25545 var a = node.attributes[i];
25548 if (a.name.toLowerCase().substr(0,2)=='on') {
25549 node.removeAttribute(a.name);
25552 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25553 node.removeAttribute(a.name);
25556 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25557 cleanAttr(a.name,a.value); // fixme..
25560 if (a.name == 'style') {
25561 cleanStyle(a.name,a.value);
25564 /// clean up MS crap..
25565 // tecnically this should be a list of valid class'es..
25568 if (a.name == 'class') {
25569 if (a.value.match(/^Mso/)) {
25570 node.removeAttribute('class');
25573 if (a.value.match(/^body$/)) {
25574 node.removeAttribute('class');
25585 this.cleanUpChildren(node);
25591 * Clean up MS wordisms...
25593 cleanWord : function(node)
25596 this.cleanWord(this.doc.body);
25601 node.nodeName == 'SPAN' &&
25602 !node.hasAttributes() &&
25603 node.childNodes.length == 1 &&
25604 node.firstChild.nodeName == "#text"
25606 var textNode = node.firstChild;
25607 node.removeChild(textNode);
25608 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25609 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25611 node.parentNode.insertBefore(textNode, node);
25612 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25613 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25615 node.parentNode.removeChild(node);
25618 if (node.nodeName == "#text") {
25619 // clean up silly Windows -- stuff?
25622 if (node.nodeName == "#comment") {
25623 node.parentNode.removeChild(node);
25624 // clean up silly Windows -- stuff?
25628 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25629 node.parentNode.removeChild(node);
25632 //Roo.log(node.tagName);
25633 // remove - but keep children..
25634 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25635 //Roo.log('-- removed');
25636 while (node.childNodes.length) {
25637 var cn = node.childNodes[0];
25638 node.removeChild(cn);
25639 node.parentNode.insertBefore(cn, node);
25640 // move node to parent - and clean it..
25641 this.cleanWord(cn);
25643 node.parentNode.removeChild(node);
25644 /// no need to iterate chidlren = it's got none..
25645 //this.iterateChildren(node, this.cleanWord);
25649 if (node.className.length) {
25651 var cn = node.className.split(/\W+/);
25653 Roo.each(cn, function(cls) {
25654 if (cls.match(/Mso[a-zA-Z]+/)) {
25659 node.className = cna.length ? cna.join(' ') : '';
25661 node.removeAttribute("class");
25665 if (node.hasAttribute("lang")) {
25666 node.removeAttribute("lang");
25669 if (node.hasAttribute("style")) {
25671 var styles = node.getAttribute("style").split(";");
25673 Roo.each(styles, function(s) {
25674 if (!s.match(/:/)) {
25677 var kv = s.split(":");
25678 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25681 // what ever is left... we allow.
25684 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25685 if (!nstyle.length) {
25686 node.removeAttribute('style');
25689 this.iterateChildren(node, this.cleanWord);
25695 * iterateChildren of a Node, calling fn each time, using this as the scole..
25696 * @param {DomNode} node node to iterate children of.
25697 * @param {Function} fn method of this class to call on each item.
25699 iterateChildren : function(node, fn)
25701 if (!node.childNodes.length) {
25704 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25705 fn.call(this, node.childNodes[i])
25711 * cleanTableWidths.
25713 * Quite often pasting from word etc.. results in tables with column and widths.
25714 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25717 cleanTableWidths : function(node)
25722 this.cleanTableWidths(this.doc.body);
25727 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25730 Roo.log(node.tagName);
25731 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25732 this.iterateChildren(node, this.cleanTableWidths);
25735 if (node.hasAttribute('width')) {
25736 node.removeAttribute('width');
25740 if (node.hasAttribute("style")) {
25743 var styles = node.getAttribute("style").split(";");
25745 Roo.each(styles, function(s) {
25746 if (!s.match(/:/)) {
25749 var kv = s.split(":");
25750 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25753 // what ever is left... we allow.
25756 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25757 if (!nstyle.length) {
25758 node.removeAttribute('style');
25762 this.iterateChildren(node, this.cleanTableWidths);
25770 domToHTML : function(currentElement, depth, nopadtext) {
25772 depth = depth || 0;
25773 nopadtext = nopadtext || false;
25775 if (!currentElement) {
25776 return this.domToHTML(this.doc.body);
25779 //Roo.log(currentElement);
25781 var allText = false;
25782 var nodeName = currentElement.nodeName;
25783 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25785 if (nodeName == '#text') {
25787 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25792 if (nodeName != 'BODY') {
25795 // Prints the node tagName, such as <A>, <IMG>, etc
25798 for(i = 0; i < currentElement.attributes.length;i++) {
25800 var aname = currentElement.attributes.item(i).name;
25801 if (!currentElement.attributes.item(i).value.length) {
25804 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25807 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25816 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25819 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25824 // Traverse the tree
25826 var currentElementChild = currentElement.childNodes.item(i);
25827 var allText = true;
25828 var innerHTML = '';
25830 while (currentElementChild) {
25831 // Formatting code (indent the tree so it looks nice on the screen)
25832 var nopad = nopadtext;
25833 if (lastnode == 'SPAN') {
25837 if (currentElementChild.nodeName == '#text') {
25838 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25839 toadd = nopadtext ? toadd : toadd.trim();
25840 if (!nopad && toadd.length > 80) {
25841 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25843 innerHTML += toadd;
25846 currentElementChild = currentElement.childNodes.item(i);
25852 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25854 // Recursively traverse the tree structure of the child node
25855 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25856 lastnode = currentElementChild.nodeName;
25858 currentElementChild=currentElement.childNodes.item(i);
25864 // The remaining code is mostly for formatting the tree
25865 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25870 ret+= "</"+tagName+">";
25876 applyBlacklists : function()
25878 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25879 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25883 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25884 if (b.indexOf(tag) > -1) {
25887 this.white.push(tag);
25891 Roo.each(w, function(tag) {
25892 if (b.indexOf(tag) > -1) {
25895 if (this.white.indexOf(tag) > -1) {
25898 this.white.push(tag);
25903 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25904 if (w.indexOf(tag) > -1) {
25907 this.black.push(tag);
25911 Roo.each(b, function(tag) {
25912 if (w.indexOf(tag) > -1) {
25915 if (this.black.indexOf(tag) > -1) {
25918 this.black.push(tag);
25923 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25924 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25928 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25929 if (b.indexOf(tag) > -1) {
25932 this.cwhite.push(tag);
25936 Roo.each(w, function(tag) {
25937 if (b.indexOf(tag) > -1) {
25940 if (this.cwhite.indexOf(tag) > -1) {
25943 this.cwhite.push(tag);
25948 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25949 if (w.indexOf(tag) > -1) {
25952 this.cblack.push(tag);
25956 Roo.each(b, function(tag) {
25957 if (w.indexOf(tag) > -1) {
25960 if (this.cblack.indexOf(tag) > -1) {
25963 this.cblack.push(tag);
25968 setStylesheets : function(stylesheets)
25970 if(typeof(stylesheets) == 'string'){
25971 Roo.get(this.iframe.contentDocument.head).createChild({
25973 rel : 'stylesheet',
25982 Roo.each(stylesheets, function(s) {
25987 Roo.get(_this.iframe.contentDocument.head).createChild({
25989 rel : 'stylesheet',
25998 removeStylesheets : function()
26002 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26007 setStyle : function(style)
26009 Roo.get(this.iframe.contentDocument.head).createChild({
26018 // hide stuff that is not compatible
26032 * @event specialkey
26036 * @cfg {String} fieldClass @hide
26039 * @cfg {String} focusClass @hide
26042 * @cfg {String} autoCreate @hide
26045 * @cfg {String} inputType @hide
26048 * @cfg {String} invalidClass @hide
26051 * @cfg {String} invalidText @hide
26054 * @cfg {String} msgFx @hide
26057 * @cfg {String} validateOnBlur @hide
26061 Roo.HtmlEditorCore.white = [
26062 'area', 'br', 'img', 'input', 'hr', 'wbr',
26064 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26065 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26066 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26067 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26068 'table', 'ul', 'xmp',
26070 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26073 'dir', 'menu', 'ol', 'ul', 'dl',
26079 Roo.HtmlEditorCore.black = [
26080 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26082 'base', 'basefont', 'bgsound', 'blink', 'body',
26083 'frame', 'frameset', 'head', 'html', 'ilayer',
26084 'iframe', 'layer', 'link', 'meta', 'object',
26085 'script', 'style' ,'title', 'xml' // clean later..
26087 Roo.HtmlEditorCore.clean = [
26088 'script', 'style', 'title', 'xml'
26090 Roo.HtmlEditorCore.remove = [
26095 Roo.HtmlEditorCore.ablack = [
26099 Roo.HtmlEditorCore.aclean = [
26100 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26104 Roo.HtmlEditorCore.pwhite= [
26105 'http', 'https', 'mailto'
26108 // white listed style attributes.
26109 Roo.HtmlEditorCore.cwhite= [
26110 // 'text-align', /// default is to allow most things..
26116 // black listed style attributes.
26117 Roo.HtmlEditorCore.cblack= [
26118 // 'font-size' -- this can be set by the project
26122 Roo.HtmlEditorCore.swapCodes =[
26123 [ 8211, "–" ],
26124 [ 8212, "—" ],
26141 * @class Roo.bootstrap.HtmlEditor
26142 * @extends Roo.bootstrap.TextArea
26143 * Bootstrap HtmlEditor class
26146 * Create a new HtmlEditor
26147 * @param {Object} config The config object
26150 Roo.bootstrap.HtmlEditor = function(config){
26151 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26152 if (!this.toolbars) {
26153 this.toolbars = [];
26156 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26159 * @event initialize
26160 * Fires when the editor is fully initialized (including the iframe)
26161 * @param {HtmlEditor} this
26166 * Fires when the editor is first receives the focus. Any insertion must wait
26167 * until after this event.
26168 * @param {HtmlEditor} this
26172 * @event beforesync
26173 * Fires before the textarea is updated with content from the editor iframe. Return false
26174 * to cancel the sync.
26175 * @param {HtmlEditor} this
26176 * @param {String} html
26180 * @event beforepush
26181 * Fires before the iframe editor is updated with content from the textarea. Return false
26182 * to cancel the push.
26183 * @param {HtmlEditor} this
26184 * @param {String} html
26189 * Fires when the textarea is updated with content from the editor iframe.
26190 * @param {HtmlEditor} this
26191 * @param {String} html
26196 * Fires when the iframe editor is updated with content from the textarea.
26197 * @param {HtmlEditor} this
26198 * @param {String} html
26202 * @event editmodechange
26203 * Fires when the editor switches edit modes
26204 * @param {HtmlEditor} this
26205 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26207 editmodechange: true,
26209 * @event editorevent
26210 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26211 * @param {HtmlEditor} this
26215 * @event firstfocus
26216 * Fires when on first focus - needed by toolbars..
26217 * @param {HtmlEditor} this
26222 * Auto save the htmlEditor value as a file into Events
26223 * @param {HtmlEditor} this
26227 * @event savedpreview
26228 * preview the saved version of htmlEditor
26229 * @param {HtmlEditor} this
26236 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26240 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26245 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26250 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26255 * @cfg {Number} height (in pixels)
26259 * @cfg {Number} width (in pixels)
26264 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26267 stylesheets: false,
26272 // private properties
26273 validationEvent : false,
26275 initialized : false,
26278 onFocus : Roo.emptyFn,
26280 hideMode:'offsets',
26282 tbContainer : false,
26286 toolbarContainer :function() {
26287 return this.wrap.select('.x-html-editor-tb',true).first();
26291 * Protected method that will not generally be called directly. It
26292 * is called when the editor creates its toolbar. Override this method if you need to
26293 * add custom toolbar buttons.
26294 * @param {HtmlEditor} editor
26296 createToolbar : function(){
26297 Roo.log('renewing');
26298 Roo.log("create toolbars");
26300 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26301 this.toolbars[0].render(this.toolbarContainer());
26305 // if (!editor.toolbars || !editor.toolbars.length) {
26306 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26309 // for (var i =0 ; i < editor.toolbars.length;i++) {
26310 // editor.toolbars[i] = Roo.factory(
26311 // typeof(editor.toolbars[i]) == 'string' ?
26312 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26313 // Roo.bootstrap.HtmlEditor);
26314 // editor.toolbars[i].init(editor);
26320 onRender : function(ct, position)
26322 // Roo.log("Call onRender: " + this.xtype);
26324 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26326 this.wrap = this.inputEl().wrap({
26327 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26330 this.editorcore.onRender(ct, position);
26332 if (this.resizable) {
26333 this.resizeEl = new Roo.Resizable(this.wrap, {
26337 minHeight : this.height,
26338 height: this.height,
26339 handles : this.resizable,
26342 resize : function(r, w, h) {
26343 _t.onResize(w,h); // -something
26349 this.createToolbar(this);
26352 if(!this.width && this.resizable){
26353 this.setSize(this.wrap.getSize());
26355 if (this.resizeEl) {
26356 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26357 // should trigger onReize..
26363 onResize : function(w, h)
26365 Roo.log('resize: ' +w + ',' + h );
26366 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26370 if(this.inputEl() ){
26371 if(typeof w == 'number'){
26372 var aw = w - this.wrap.getFrameWidth('lr');
26373 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26376 if(typeof h == 'number'){
26377 var tbh = -11; // fixme it needs to tool bar size!
26378 for (var i =0; i < this.toolbars.length;i++) {
26379 // fixme - ask toolbars for heights?
26380 tbh += this.toolbars[i].el.getHeight();
26381 //if (this.toolbars[i].footer) {
26382 // tbh += this.toolbars[i].footer.el.getHeight();
26390 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26391 ah -= 5; // knock a few pixes off for look..
26392 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26396 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26397 this.editorcore.onResize(ew,eh);
26402 * Toggles the editor between standard and source edit mode.
26403 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26405 toggleSourceEdit : function(sourceEditMode)
26407 this.editorcore.toggleSourceEdit(sourceEditMode);
26409 if(this.editorcore.sourceEditMode){
26410 Roo.log('editor - showing textarea');
26413 // Roo.log(this.syncValue());
26415 this.inputEl().removeClass(['hide', 'x-hidden']);
26416 this.inputEl().dom.removeAttribute('tabIndex');
26417 this.inputEl().focus();
26419 Roo.log('editor - hiding textarea');
26421 // Roo.log(this.pushValue());
26424 this.inputEl().addClass(['hide', 'x-hidden']);
26425 this.inputEl().dom.setAttribute('tabIndex', -1);
26426 //this.deferFocus();
26429 if(this.resizable){
26430 this.setSize(this.wrap.getSize());
26433 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26436 // private (for BoxComponent)
26437 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26439 // private (for BoxComponent)
26440 getResizeEl : function(){
26444 // private (for BoxComponent)
26445 getPositionEl : function(){
26450 initEvents : function(){
26451 this.originalValue = this.getValue();
26455 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26458 // markInvalid : Roo.emptyFn,
26460 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26463 // clearInvalid : Roo.emptyFn,
26465 setValue : function(v){
26466 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26467 this.editorcore.pushValue();
26472 deferFocus : function(){
26473 this.focus.defer(10, this);
26477 focus : function(){
26478 this.editorcore.focus();
26484 onDestroy : function(){
26490 for (var i =0; i < this.toolbars.length;i++) {
26491 // fixme - ask toolbars for heights?
26492 this.toolbars[i].onDestroy();
26495 this.wrap.dom.innerHTML = '';
26496 this.wrap.remove();
26501 onFirstFocus : function(){
26502 //Roo.log("onFirstFocus");
26503 this.editorcore.onFirstFocus();
26504 for (var i =0; i < this.toolbars.length;i++) {
26505 this.toolbars[i].onFirstFocus();
26511 syncValue : function()
26513 this.editorcore.syncValue();
26516 pushValue : function()
26518 this.editorcore.pushValue();
26522 // hide stuff that is not compatible
26536 * @event specialkey
26540 * @cfg {String} fieldClass @hide
26543 * @cfg {String} focusClass @hide
26546 * @cfg {String} autoCreate @hide
26549 * @cfg {String} inputType @hide
26553 * @cfg {String} invalidText @hide
26556 * @cfg {String} msgFx @hide
26559 * @cfg {String} validateOnBlur @hide
26568 Roo.namespace('Roo.bootstrap.htmleditor');
26570 * @class Roo.bootstrap.HtmlEditorToolbar1
26576 new Roo.bootstrap.HtmlEditor({
26579 new Roo.bootstrap.HtmlEditorToolbar1({
26580 disable : { fonts: 1 , format: 1, ..., ... , ...],
26586 * @cfg {Object} disable List of elements to disable..
26587 * @cfg {Array} btns List of additional buttons.
26591 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26594 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26597 Roo.apply(this, config);
26599 // default disabled, based on 'good practice'..
26600 this.disable = this.disable || {};
26601 Roo.applyIf(this.disable, {
26604 specialElements : true
26606 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26608 this.editor = config.editor;
26609 this.editorcore = config.editor.editorcore;
26611 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26613 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26614 // dont call parent... till later.
26616 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26621 editorcore : false,
26626 "h1","h2","h3","h4","h5","h6",
26628 "abbr", "acronym", "address", "cite", "samp", "var",
26632 onRender : function(ct, position)
26634 // Roo.log("Call onRender: " + this.xtype);
26636 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26638 this.el.dom.style.marginBottom = '0';
26640 var editorcore = this.editorcore;
26641 var editor= this.editor;
26644 var btn = function(id,cmd , toggle, handler, html){
26646 var event = toggle ? 'toggle' : 'click';
26651 xns: Roo.bootstrap,
26655 enableToggle:toggle !== false,
26657 pressed : toggle ? false : null,
26660 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26661 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26667 // var cb_box = function...
26672 xns: Roo.bootstrap,
26677 xns: Roo.bootstrap,
26681 Roo.each(this.formats, function(f) {
26682 style.menu.items.push({
26684 xns: Roo.bootstrap,
26685 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26690 editorcore.insertTag(this.tagname);
26697 children.push(style);
26699 btn('bold',false,true);
26700 btn('italic',false,true);
26701 btn('align-left', 'justifyleft',true);
26702 btn('align-center', 'justifycenter',true);
26703 btn('align-right' , 'justifyright',true);
26704 btn('link', false, false, function(btn) {
26705 //Roo.log("create link?");
26706 var url = prompt(this.createLinkText, this.defaultLinkValue);
26707 if(url && url != 'http:/'+'/'){
26708 this.editorcore.relayCmd('createlink', url);
26711 btn('list','insertunorderedlist',true);
26712 btn('pencil', false,true, function(btn){
26714 this.toggleSourceEdit(btn.pressed);
26717 if (this.editor.btns.length > 0) {
26718 for (var i = 0; i<this.editor.btns.length; i++) {
26719 children.push(this.editor.btns[i]);
26727 xns: Roo.bootstrap,
26732 xns: Roo.bootstrap,
26737 cog.menu.items.push({
26739 xns: Roo.bootstrap,
26740 html : Clean styles,
26745 editorcore.insertTag(this.tagname);
26754 this.xtype = 'NavSimplebar';
26756 for(var i=0;i< children.length;i++) {
26758 this.buttons.add(this.addxtypeChild(children[i]));
26762 editor.on('editorevent', this.updateToolbar, this);
26764 onBtnClick : function(id)
26766 this.editorcore.relayCmd(id);
26767 this.editorcore.focus();
26771 * Protected method that will not generally be called directly. It triggers
26772 * a toolbar update by reading the markup state of the current selection in the editor.
26774 updateToolbar: function(){
26776 if(!this.editorcore.activated){
26777 this.editor.onFirstFocus(); // is this neeed?
26781 var btns = this.buttons;
26782 var doc = this.editorcore.doc;
26783 btns.get('bold').setActive(doc.queryCommandState('bold'));
26784 btns.get('italic').setActive(doc.queryCommandState('italic'));
26785 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26787 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26788 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26789 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26791 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26792 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26795 var ans = this.editorcore.getAllAncestors();
26796 if (this.formatCombo) {
26799 var store = this.formatCombo.store;
26800 this.formatCombo.setValue("");
26801 for (var i =0; i < ans.length;i++) {
26802 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26804 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26812 // hides menus... - so this cant be on a menu...
26813 Roo.bootstrap.MenuMgr.hideAll();
26815 Roo.bootstrap.MenuMgr.hideAll();
26816 //this.editorsyncValue();
26818 onFirstFocus: function() {
26819 this.buttons.each(function(item){
26823 toggleSourceEdit : function(sourceEditMode){
26826 if(sourceEditMode){
26827 Roo.log("disabling buttons");
26828 this.buttons.each( function(item){
26829 if(item.cmd != 'pencil'){
26835 Roo.log("enabling buttons");
26836 if(this.editorcore.initialized){
26837 this.buttons.each( function(item){
26843 Roo.log("calling toggole on editor");
26844 // tell the editor that it's been pressed..
26845 this.editor.toggleSourceEdit(sourceEditMode);
26859 * @class Roo.bootstrap.Markdown
26860 * @extends Roo.bootstrap.TextArea
26861 * Bootstrap Showdown editable area
26862 * @cfg {string} content
26865 * Create a new Showdown
26868 Roo.bootstrap.Markdown = function(config){
26869 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26873 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26877 initEvents : function()
26880 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26881 this.markdownEl = this.el.createChild({
26882 cls : 'roo-markdown-area'
26884 this.inputEl().addClass('d-none');
26885 if (this.getValue() == '') {
26886 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26889 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26891 this.markdownEl.on('click', this.toggleTextEdit, this);
26892 this.on('blur', this.toggleTextEdit, this);
26893 this.on('specialkey', this.resizeTextArea, this);
26896 toggleTextEdit : function()
26898 var sh = this.markdownEl.getHeight();
26899 this.inputEl().addClass('d-none');
26900 this.markdownEl.addClass('d-none');
26901 if (!this.editing) {
26903 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26904 this.inputEl().removeClass('d-none');
26905 this.inputEl().focus();
26906 this.editing = true;
26909 // show showdown...
26910 this.updateMarkdown();
26911 this.markdownEl.removeClass('d-none');
26912 this.editing = false;
26915 updateMarkdown : function()
26917 if (this.getValue() == '') {
26918 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26922 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26925 resizeTextArea: function () {
26928 Roo.log([sh, this.getValue().split("\n").length * 30]);
26929 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26931 setValue : function(val)
26933 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26934 if (!this.editing) {
26935 this.updateMarkdown();
26941 if (!this.editing) {
26942 this.toggleTextEdit();
26950 * @class Roo.bootstrap.Table.AbstractSelectionModel
26951 * @extends Roo.util.Observable
26952 * Abstract base class for grid SelectionModels. It provides the interface that should be
26953 * implemented by descendant classes. This class should not be directly instantiated.
26956 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26957 this.locked = false;
26958 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26962 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26963 /** @ignore Called by the grid automatically. Do not call directly. */
26964 init : function(grid){
26970 * Locks the selections.
26973 this.locked = true;
26977 * Unlocks the selections.
26979 unlock : function(){
26980 this.locked = false;
26984 * Returns true if the selections are locked.
26985 * @return {Boolean}
26987 isLocked : function(){
26988 return this.locked;
26992 initEvents : function ()
26998 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26999 * @class Roo.bootstrap.Table.RowSelectionModel
27000 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27001 * It supports multiple selections and keyboard selection/navigation.
27003 * @param {Object} config
27006 Roo.bootstrap.Table.RowSelectionModel = function(config){
27007 Roo.apply(this, config);
27008 this.selections = new Roo.util.MixedCollection(false, function(o){
27013 this.lastActive = false;
27017 * @event selectionchange
27018 * Fires when the selection changes
27019 * @param {SelectionModel} this
27021 "selectionchange" : true,
27023 * @event afterselectionchange
27024 * Fires after the selection changes (eg. by key press or clicking)
27025 * @param {SelectionModel} this
27027 "afterselectionchange" : true,
27029 * @event beforerowselect
27030 * Fires when a row is selected being selected, return false to cancel.
27031 * @param {SelectionModel} this
27032 * @param {Number} rowIndex The selected index
27033 * @param {Boolean} keepExisting False if other selections will be cleared
27035 "beforerowselect" : true,
27038 * Fires when a row is selected.
27039 * @param {SelectionModel} this
27040 * @param {Number} rowIndex The selected index
27041 * @param {Roo.data.Record} r The record
27043 "rowselect" : true,
27045 * @event rowdeselect
27046 * Fires when a row is deselected.
27047 * @param {SelectionModel} this
27048 * @param {Number} rowIndex The selected index
27050 "rowdeselect" : true
27052 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27053 this.locked = false;
27056 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27058 * @cfg {Boolean} singleSelect
27059 * True to allow selection of only one row at a time (defaults to false)
27061 singleSelect : false,
27064 initEvents : function()
27067 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27068 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27069 //}else{ // allow click to work like normal
27070 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27072 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27073 this.grid.on("rowclick", this.handleMouseDown, this);
27075 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27076 "up" : function(e){
27078 this.selectPrevious(e.shiftKey);
27079 }else if(this.last !== false && this.lastActive !== false){
27080 var last = this.last;
27081 this.selectRange(this.last, this.lastActive-1);
27082 this.grid.getView().focusRow(this.lastActive);
27083 if(last !== false){
27087 this.selectFirstRow();
27089 this.fireEvent("afterselectionchange", this);
27091 "down" : function(e){
27093 this.selectNext(e.shiftKey);
27094 }else if(this.last !== false && this.lastActive !== false){
27095 var last = this.last;
27096 this.selectRange(this.last, this.lastActive+1);
27097 this.grid.getView().focusRow(this.lastActive);
27098 if(last !== false){
27102 this.selectFirstRow();
27104 this.fireEvent("afterselectionchange", this);
27108 this.grid.store.on('load', function(){
27109 this.selections.clear();
27112 var view = this.grid.view;
27113 view.on("refresh", this.onRefresh, this);
27114 view.on("rowupdated", this.onRowUpdated, this);
27115 view.on("rowremoved", this.onRemove, this);
27120 onRefresh : function()
27122 var ds = this.grid.store, i, v = this.grid.view;
27123 var s = this.selections;
27124 s.each(function(r){
27125 if((i = ds.indexOfId(r.id)) != -1){
27134 onRemove : function(v, index, r){
27135 this.selections.remove(r);
27139 onRowUpdated : function(v, index, r){
27140 if(this.isSelected(r)){
27141 v.onRowSelect(index);
27147 * @param {Array} records The records to select
27148 * @param {Boolean} keepExisting (optional) True to keep existing selections
27150 selectRecords : function(records, keepExisting)
27153 this.clearSelections();
27155 var ds = this.grid.store;
27156 for(var i = 0, len = records.length; i < len; i++){
27157 this.selectRow(ds.indexOf(records[i]), true);
27162 * Gets the number of selected rows.
27165 getCount : function(){
27166 return this.selections.length;
27170 * Selects the first row in the grid.
27172 selectFirstRow : function(){
27177 * Select the last row.
27178 * @param {Boolean} keepExisting (optional) True to keep existing selections
27180 selectLastRow : function(keepExisting){
27181 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27182 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27186 * Selects the row immediately following the last selected row.
27187 * @param {Boolean} keepExisting (optional) True to keep existing selections
27189 selectNext : function(keepExisting)
27191 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27192 this.selectRow(this.last+1, keepExisting);
27193 this.grid.getView().focusRow(this.last);
27198 * Selects the row that precedes the last selected row.
27199 * @param {Boolean} keepExisting (optional) True to keep existing selections
27201 selectPrevious : function(keepExisting){
27203 this.selectRow(this.last-1, keepExisting);
27204 this.grid.getView().focusRow(this.last);
27209 * Returns the selected records
27210 * @return {Array} Array of selected records
27212 getSelections : function(){
27213 return [].concat(this.selections.items);
27217 * Returns the first selected record.
27220 getSelected : function(){
27221 return this.selections.itemAt(0);
27226 * Clears all selections.
27228 clearSelections : function(fast)
27234 var ds = this.grid.store;
27235 var s = this.selections;
27236 s.each(function(r){
27237 this.deselectRow(ds.indexOfId(r.id));
27241 this.selections.clear();
27248 * Selects all rows.
27250 selectAll : function(){
27254 this.selections.clear();
27255 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27256 this.selectRow(i, true);
27261 * Returns True if there is a selection.
27262 * @return {Boolean}
27264 hasSelection : function(){
27265 return this.selections.length > 0;
27269 * Returns True if the specified row is selected.
27270 * @param {Number/Record} record The record or index of the record to check
27271 * @return {Boolean}
27273 isSelected : function(index){
27274 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27275 return (r && this.selections.key(r.id) ? true : false);
27279 * Returns True if the specified record id is selected.
27280 * @param {String} id The id of record to check
27281 * @return {Boolean}
27283 isIdSelected : function(id){
27284 return (this.selections.key(id) ? true : false);
27289 handleMouseDBClick : function(e, t){
27293 handleMouseDown : function(e, t)
27295 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27296 if(this.isLocked() || rowIndex < 0 ){
27299 if(e.shiftKey && this.last !== false){
27300 var last = this.last;
27301 this.selectRange(last, rowIndex, e.ctrlKey);
27302 this.last = last; // reset the last
27306 var isSelected = this.isSelected(rowIndex);
27307 //Roo.log("select row:" + rowIndex);
27309 this.deselectRow(rowIndex);
27311 this.selectRow(rowIndex, true);
27315 if(e.button !== 0 && isSelected){
27316 alert('rowIndex 2: ' + rowIndex);
27317 view.focusRow(rowIndex);
27318 }else if(e.ctrlKey && isSelected){
27319 this.deselectRow(rowIndex);
27320 }else if(!isSelected){
27321 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27322 view.focusRow(rowIndex);
27326 this.fireEvent("afterselectionchange", this);
27329 handleDragableRowClick : function(grid, rowIndex, e)
27331 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27332 this.selectRow(rowIndex, false);
27333 grid.view.focusRow(rowIndex);
27334 this.fireEvent("afterselectionchange", this);
27339 * Selects multiple rows.
27340 * @param {Array} rows Array of the indexes of the row to select
27341 * @param {Boolean} keepExisting (optional) True to keep existing selections
27343 selectRows : function(rows, keepExisting){
27345 this.clearSelections();
27347 for(var i = 0, len = rows.length; i < len; i++){
27348 this.selectRow(rows[i], true);
27353 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27354 * @param {Number} startRow The index of the first row in the range
27355 * @param {Number} endRow The index of the last row in the range
27356 * @param {Boolean} keepExisting (optional) True to retain existing selections
27358 selectRange : function(startRow, endRow, keepExisting){
27363 this.clearSelections();
27365 if(startRow <= endRow){
27366 for(var i = startRow; i <= endRow; i++){
27367 this.selectRow(i, true);
27370 for(var i = startRow; i >= endRow; i--){
27371 this.selectRow(i, true);
27377 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
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
27381 deselectRange : function(startRow, endRow, preventViewNotify){
27385 for(var i = startRow; i <= endRow; i++){
27386 this.deselectRow(i, preventViewNotify);
27392 * @param {Number} row The index of the row to select
27393 * @param {Boolean} keepExisting (optional) True to keep existing selections
27395 selectRow : function(index, keepExisting, preventViewNotify)
27397 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27400 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27401 if(!keepExisting || this.singleSelect){
27402 this.clearSelections();
27405 var r = this.grid.store.getAt(index);
27406 //console.log('selectRow - record id :' + r.id);
27408 this.selections.add(r);
27409 this.last = this.lastActive = index;
27410 if(!preventViewNotify){
27411 var proxy = new Roo.Element(
27412 this.grid.getRowDom(index)
27414 proxy.addClass('bg-info info');
27416 this.fireEvent("rowselect", this, index, r);
27417 this.fireEvent("selectionchange", this);
27423 * @param {Number} row The index of the row to deselect
27425 deselectRow : function(index, preventViewNotify)
27430 if(this.last == index){
27433 if(this.lastActive == index){
27434 this.lastActive = false;
27437 var r = this.grid.store.getAt(index);
27442 this.selections.remove(r);
27443 //.console.log('deselectRow - record id :' + r.id);
27444 if(!preventViewNotify){
27446 var proxy = new Roo.Element(
27447 this.grid.getRowDom(index)
27449 proxy.removeClass('bg-info info');
27451 this.fireEvent("rowdeselect", this, index);
27452 this.fireEvent("selectionchange", this);
27456 restoreLast : function(){
27458 this.last = this._last;
27463 acceptsNav : function(row, col, cm){
27464 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27468 onEditorKey : function(field, e){
27469 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27474 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27476 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27478 }else if(k == e.ENTER && !e.ctrlKey){
27482 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27484 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27486 }else if(k == e.ESC){
27490 g.startEditing(newCell[0], newCell[1]);
27496 * Ext JS Library 1.1.1
27497 * Copyright(c) 2006-2007, Ext JS, LLC.
27499 * Originally Released Under LGPL - original licence link has changed is not relivant.
27502 * <script type="text/javascript">
27506 * @class Roo.bootstrap.PagingToolbar
27507 * @extends Roo.bootstrap.NavSimplebar
27508 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27510 * Create a new PagingToolbar
27511 * @param {Object} config The config object
27512 * @param {Roo.data.Store} store
27514 Roo.bootstrap.PagingToolbar = function(config)
27516 // old args format still supported... - xtype is prefered..
27517 // created from xtype...
27519 this.ds = config.dataSource;
27521 if (config.store && !this.ds) {
27522 this.store= Roo.factory(config.store, Roo.data);
27523 this.ds = this.store;
27524 this.ds.xmodule = this.xmodule || false;
27527 this.toolbarItems = [];
27528 if (config.items) {
27529 this.toolbarItems = config.items;
27532 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27537 this.bind(this.ds);
27540 if (Roo.bootstrap.version == 4) {
27541 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27543 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27548 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27550 * @cfg {Roo.data.Store} dataSource
27551 * The underlying data store providing the paged data
27554 * @cfg {String/HTMLElement/Element} container
27555 * container The id or element that will contain the toolbar
27558 * @cfg {Boolean} displayInfo
27559 * True to display the displayMsg (defaults to false)
27562 * @cfg {Number} pageSize
27563 * The number of records to display per page (defaults to 20)
27567 * @cfg {String} displayMsg
27568 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27570 displayMsg : 'Displaying {0} - {1} of {2}',
27572 * @cfg {String} emptyMsg
27573 * The message to display when no records are found (defaults to "No data to display")
27575 emptyMsg : 'No data to display',
27577 * Customizable piece of the default paging text (defaults to "Page")
27580 beforePageText : "Page",
27582 * Customizable piece of the default paging text (defaults to "of %0")
27585 afterPageText : "of {0}",
27587 * Customizable piece of the default paging text (defaults to "First Page")
27590 firstText : "First Page",
27592 * Customizable piece of the default paging text (defaults to "Previous Page")
27595 prevText : "Previous Page",
27597 * Customizable piece of the default paging text (defaults to "Next Page")
27600 nextText : "Next Page",
27602 * Customizable piece of the default paging text (defaults to "Last Page")
27605 lastText : "Last Page",
27607 * Customizable piece of the default paging text (defaults to "Refresh")
27610 refreshText : "Refresh",
27614 onRender : function(ct, position)
27616 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27617 this.navgroup.parentId = this.id;
27618 this.navgroup.onRender(this.el, null);
27619 // add the buttons to the navgroup
27621 if(this.displayInfo){
27622 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27623 this.displayEl = this.el.select('.x-paging-info', true).first();
27624 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27625 // this.displayEl = navel.el.select('span',true).first();
27631 Roo.each(_this.buttons, function(e){ // this might need to use render????
27632 Roo.factory(e).render(_this.el);
27636 Roo.each(_this.toolbarItems, function(e) {
27637 _this.navgroup.addItem(e);
27641 this.first = this.navgroup.addItem({
27642 tooltip: this.firstText,
27643 cls: "prev btn-outline-secondary",
27644 html : ' <i class="fa fa-step-backward"></i>',
27646 preventDefault: true,
27647 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27650 this.prev = this.navgroup.addItem({
27651 tooltip: this.prevText,
27652 cls: "prev btn-outline-secondary",
27653 html : ' <i class="fa fa-backward"></i>',
27655 preventDefault: true,
27656 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27658 //this.addSeparator();
27661 var field = this.navgroup.addItem( {
27663 cls : 'x-paging-position btn-outline-secondary',
27665 html : this.beforePageText +
27666 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27667 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27670 this.field = field.el.select('input', true).first();
27671 this.field.on("keydown", this.onPagingKeydown, this);
27672 this.field.on("focus", function(){this.dom.select();});
27675 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27676 //this.field.setHeight(18);
27677 //this.addSeparator();
27678 this.next = this.navgroup.addItem({
27679 tooltip: this.nextText,
27680 cls: "next btn-outline-secondary",
27681 html : ' <i class="fa fa-forward"></i>',
27683 preventDefault: true,
27684 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27686 this.last = this.navgroup.addItem({
27687 tooltip: this.lastText,
27688 html : ' <i class="fa fa-step-forward"></i>',
27689 cls: "next btn-outline-secondary",
27691 preventDefault: true,
27692 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27694 //this.addSeparator();
27695 this.loading = this.navgroup.addItem({
27696 tooltip: this.refreshText,
27697 cls: "btn-outline-secondary",
27698 html : ' <i class="fa fa-refresh"></i>',
27699 preventDefault: true,
27700 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27706 updateInfo : function(){
27707 if(this.displayEl){
27708 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27709 var msg = count == 0 ?
27713 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27715 this.displayEl.update(msg);
27720 onLoad : function(ds, r, o)
27722 this.cursor = o.params && o.params.start ? o.params.start : 0;
27724 var d = this.getPageData(),
27729 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27730 this.field.dom.value = ap;
27731 this.first.setDisabled(ap == 1);
27732 this.prev.setDisabled(ap == 1);
27733 this.next.setDisabled(ap == ps);
27734 this.last.setDisabled(ap == ps);
27735 this.loading.enable();
27740 getPageData : function(){
27741 var total = this.ds.getTotalCount();
27744 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27745 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27750 onLoadError : function(){
27751 this.loading.enable();
27755 onPagingKeydown : function(e){
27756 var k = e.getKey();
27757 var d = this.getPageData();
27759 var v = this.field.dom.value, pageNum;
27760 if(!v || isNaN(pageNum = parseInt(v, 10))){
27761 this.field.dom.value = d.activePage;
27764 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27765 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27768 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))
27770 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27771 this.field.dom.value = pageNum;
27772 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27775 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27777 var v = this.field.dom.value, pageNum;
27778 var increment = (e.shiftKey) ? 10 : 1;
27779 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27782 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27783 this.field.dom.value = d.activePage;
27786 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27788 this.field.dom.value = parseInt(v, 10) + increment;
27789 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27790 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27797 beforeLoad : function(){
27799 this.loading.disable();
27804 onClick : function(which){
27813 ds.load({params:{start: 0, limit: this.pageSize}});
27816 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27819 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27822 var total = ds.getTotalCount();
27823 var extra = total % this.pageSize;
27824 var lastStart = extra ? (total - extra) : total-this.pageSize;
27825 ds.load({params:{start: lastStart, limit: this.pageSize}});
27828 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27834 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27835 * @param {Roo.data.Store} store The data store to unbind
27837 unbind : function(ds){
27838 ds.un("beforeload", this.beforeLoad, this);
27839 ds.un("load", this.onLoad, this);
27840 ds.un("loadexception", this.onLoadError, this);
27841 ds.un("remove", this.updateInfo, this);
27842 ds.un("add", this.updateInfo, this);
27843 this.ds = undefined;
27847 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27848 * @param {Roo.data.Store} store The data store to bind
27850 bind : function(ds){
27851 ds.on("beforeload", this.beforeLoad, this);
27852 ds.on("load", this.onLoad, this);
27853 ds.on("loadexception", this.onLoadError, this);
27854 ds.on("remove", this.updateInfo, this);
27855 ds.on("add", this.updateInfo, this);
27866 * @class Roo.bootstrap.MessageBar
27867 * @extends Roo.bootstrap.Component
27868 * Bootstrap MessageBar class
27869 * @cfg {String} html contents of the MessageBar
27870 * @cfg {String} weight (info | success | warning | danger) default info
27871 * @cfg {String} beforeClass insert the bar before the given class
27872 * @cfg {Boolean} closable (true | false) default false
27873 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27876 * Create a new Element
27877 * @param {Object} config The config object
27880 Roo.bootstrap.MessageBar = function(config){
27881 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27884 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27890 beforeClass: 'bootstrap-sticky-wrap',
27892 getAutoCreate : function(){
27896 cls: 'alert alert-dismissable alert-' + this.weight,
27901 html: this.html || ''
27907 cfg.cls += ' alert-messages-fixed';
27921 onRender : function(ct, position)
27923 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27926 var cfg = Roo.apply({}, this.getAutoCreate());
27930 cfg.cls += ' ' + this.cls;
27933 cfg.style = this.style;
27935 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27937 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27940 this.el.select('>button.close').on('click', this.hide, this);
27946 if (!this.rendered) {
27952 this.fireEvent('show', this);
27958 if (!this.rendered) {
27964 this.fireEvent('hide', this);
27967 update : function()
27969 // var e = this.el.dom.firstChild;
27971 // if(this.closable){
27972 // e = e.nextSibling;
27975 // e.data = this.html || '';
27977 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27993 * @class Roo.bootstrap.Graph
27994 * @extends Roo.bootstrap.Component
27995 * Bootstrap Graph class
27999 @cfg {String} graphtype bar | vbar | pie
28000 @cfg {number} g_x coodinator | centre x (pie)
28001 @cfg {number} g_y coodinator | centre y (pie)
28002 @cfg {number} g_r radius (pie)
28003 @cfg {number} g_height height of the chart (respected by all elements in the set)
28004 @cfg {number} g_width width of the chart (respected by all elements in the set)
28005 @cfg {Object} title The title of the chart
28008 -opts (object) options for the chart
28010 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28011 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28013 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.
28014 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28016 o stretch (boolean)
28018 -opts (object) options for the pie
28021 o startAngle (number)
28022 o endAngle (number)
28026 * Create a new Input
28027 * @param {Object} config The config object
28030 Roo.bootstrap.Graph = function(config){
28031 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28037 * The img click event for the img.
28038 * @param {Roo.EventObject} e
28044 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28055 //g_colors: this.colors,
28062 getAutoCreate : function(){
28073 onRender : function(ct,position){
28076 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28078 if (typeof(Raphael) == 'undefined') {
28079 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28083 this.raphael = Raphael(this.el.dom);
28085 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28086 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28087 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28088 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28090 r.text(160, 10, "Single Series Chart").attr(txtattr);
28091 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28092 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28093 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28095 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28096 r.barchart(330, 10, 300, 220, data1);
28097 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28098 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28101 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28102 // r.barchart(30, 30, 560, 250, xdata, {
28103 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28104 // axis : "0 0 1 1",
28105 // axisxlabels : xdata
28106 // //yvalues : cols,
28109 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28111 // this.load(null,xdata,{
28112 // axis : "0 0 1 1",
28113 // axisxlabels : xdata
28118 load : function(graphtype,xdata,opts)
28120 this.raphael.clear();
28122 graphtype = this.graphtype;
28127 var r = this.raphael,
28128 fin = function () {
28129 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28131 fout = function () {
28132 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28134 pfin = function() {
28135 this.sector.stop();
28136 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28139 this.label[0].stop();
28140 this.label[0].attr({ r: 7.5 });
28141 this.label[1].attr({ "font-weight": 800 });
28144 pfout = function() {
28145 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28148 this.label[0].animate({ r: 5 }, 500, "bounce");
28149 this.label[1].attr({ "font-weight": 400 });
28155 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28158 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28161 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28162 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28164 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28171 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28176 setTitle: function(o)
28181 initEvents: function() {
28184 this.el.on('click', this.onClick, this);
28188 onClick : function(e)
28190 Roo.log('img onclick');
28191 this.fireEvent('click', this, e);
28203 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28206 * @class Roo.bootstrap.dash.NumberBox
28207 * @extends Roo.bootstrap.Component
28208 * Bootstrap NumberBox class
28209 * @cfg {String} headline Box headline
28210 * @cfg {String} content Box content
28211 * @cfg {String} icon Box icon
28212 * @cfg {String} footer Footer text
28213 * @cfg {String} fhref Footer href
28216 * Create a new NumberBox
28217 * @param {Object} config The config object
28221 Roo.bootstrap.dash.NumberBox = function(config){
28222 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28226 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28235 getAutoCreate : function(){
28239 cls : 'small-box ',
28247 cls : 'roo-headline',
28248 html : this.headline
28252 cls : 'roo-content',
28253 html : this.content
28267 cls : 'ion ' + this.icon
28276 cls : 'small-box-footer',
28277 href : this.fhref || '#',
28281 cfg.cn.push(footer);
28288 onRender : function(ct,position){
28289 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28296 setHeadline: function (value)
28298 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28301 setFooter: function (value, href)
28303 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28306 this.el.select('a.small-box-footer',true).first().attr('href', href);
28311 setContent: function (value)
28313 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28316 initEvents: function()
28330 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28333 * @class Roo.bootstrap.dash.TabBox
28334 * @extends Roo.bootstrap.Component
28335 * Bootstrap TabBox class
28336 * @cfg {String} title Title of the TabBox
28337 * @cfg {String} icon Icon of the TabBox
28338 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28339 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28342 * Create a new TabBox
28343 * @param {Object} config The config object
28347 Roo.bootstrap.dash.TabBox = function(config){
28348 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28353 * When a pane is added
28354 * @param {Roo.bootstrap.dash.TabPane} pane
28358 * @event activatepane
28359 * When a pane is activated
28360 * @param {Roo.bootstrap.dash.TabPane} pane
28362 "activatepane" : true
28370 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28375 tabScrollable : false,
28377 getChildContainer : function()
28379 return this.el.select('.tab-content', true).first();
28382 getAutoCreate : function(){
28386 cls: 'pull-left header',
28394 cls: 'fa ' + this.icon
28400 cls: 'nav nav-tabs pull-right',
28406 if(this.tabScrollable){
28413 cls: 'nav nav-tabs pull-right',
28424 cls: 'nav-tabs-custom',
28429 cls: 'tab-content no-padding',
28437 initEvents : function()
28439 //Roo.log('add add pane handler');
28440 this.on('addpane', this.onAddPane, this);
28443 * Updates the box title
28444 * @param {String} html to set the title to.
28446 setTitle : function(value)
28448 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28450 onAddPane : function(pane)
28452 this.panes.push(pane);
28453 //Roo.log('addpane');
28455 // tabs are rendere left to right..
28456 if(!this.showtabs){
28460 var ctr = this.el.select('.nav-tabs', true).first();
28463 var existing = ctr.select('.nav-tab',true);
28464 var qty = existing.getCount();;
28467 var tab = ctr.createChild({
28469 cls : 'nav-tab' + (qty ? '' : ' active'),
28477 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28480 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28482 pane.el.addClass('active');
28487 onTabClick : function(ev,un,ob,pane)
28489 //Roo.log('tab - prev default');
28490 ev.preventDefault();
28493 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28494 pane.tab.addClass('active');
28495 //Roo.log(pane.title);
28496 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28497 // technically we should have a deactivate event.. but maybe add later.
28498 // and it should not de-activate the selected tab...
28499 this.fireEvent('activatepane', pane);
28500 pane.el.addClass('active');
28501 pane.fireEvent('activate');
28506 getActivePane : function()
28509 Roo.each(this.panes, function(p) {
28510 if(p.el.hasClass('active')){
28531 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28533 * @class Roo.bootstrap.TabPane
28534 * @extends Roo.bootstrap.Component
28535 * Bootstrap TabPane class
28536 * @cfg {Boolean} active (false | true) Default false
28537 * @cfg {String} title title of panel
28541 * Create a new TabPane
28542 * @param {Object} config The config object
28545 Roo.bootstrap.dash.TabPane = function(config){
28546 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28552 * When a pane is activated
28553 * @param {Roo.bootstrap.dash.TabPane} pane
28560 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28565 // the tabBox that this is attached to.
28568 getAutoCreate : function()
28576 cfg.cls += ' active';
28581 initEvents : function()
28583 //Roo.log('trigger add pane handler');
28584 this.parent().fireEvent('addpane', this)
28588 * Updates the tab title
28589 * @param {String} html to set the title to.
28591 setTitle: function(str)
28597 this.tab.select('a', true).first().dom.innerHTML = str;
28614 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28617 * @class Roo.bootstrap.menu.Menu
28618 * @extends Roo.bootstrap.Component
28619 * Bootstrap Menu class - container for Menu
28620 * @cfg {String} html Text of the menu
28621 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28622 * @cfg {String} icon Font awesome icon
28623 * @cfg {String} pos Menu align to (top | bottom) default bottom
28627 * Create a new Menu
28628 * @param {Object} config The config object
28632 Roo.bootstrap.menu.Menu = function(config){
28633 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28637 * @event beforeshow
28638 * Fires before this menu is displayed
28639 * @param {Roo.bootstrap.menu.Menu} this
28643 * @event beforehide
28644 * Fires before this menu is hidden
28645 * @param {Roo.bootstrap.menu.Menu} this
28650 * Fires after this menu is displayed
28651 * @param {Roo.bootstrap.menu.Menu} this
28656 * Fires after this menu is hidden
28657 * @param {Roo.bootstrap.menu.Menu} this
28662 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28663 * @param {Roo.bootstrap.menu.Menu} this
28664 * @param {Roo.EventObject} e
28671 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28675 weight : 'default',
28680 getChildContainer : function() {
28681 if(this.isSubMenu){
28685 return this.el.select('ul.dropdown-menu', true).first();
28688 getAutoCreate : function()
28693 cls : 'roo-menu-text',
28701 cls : 'fa ' + this.icon
28712 cls : 'dropdown-button btn btn-' + this.weight,
28717 cls : 'dropdown-toggle btn btn-' + this.weight,
28727 cls : 'dropdown-menu'
28733 if(this.pos == 'top'){
28734 cfg.cls += ' dropup';
28737 if(this.isSubMenu){
28740 cls : 'dropdown-menu'
28747 onRender : function(ct, position)
28749 this.isSubMenu = ct.hasClass('dropdown-submenu');
28751 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28754 initEvents : function()
28756 if(this.isSubMenu){
28760 this.hidden = true;
28762 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28763 this.triggerEl.on('click', this.onTriggerPress, this);
28765 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28766 this.buttonEl.on('click', this.onClick, this);
28772 if(this.isSubMenu){
28776 return this.el.select('ul.dropdown-menu', true).first();
28779 onClick : function(e)
28781 this.fireEvent("click", this, e);
28784 onTriggerPress : function(e)
28786 if (this.isVisible()) {
28793 isVisible : function(){
28794 return !this.hidden;
28799 this.fireEvent("beforeshow", this);
28801 this.hidden = false;
28802 this.el.addClass('open');
28804 Roo.get(document).on("mouseup", this.onMouseUp, this);
28806 this.fireEvent("show", this);
28813 this.fireEvent("beforehide", this);
28815 this.hidden = true;
28816 this.el.removeClass('open');
28818 Roo.get(document).un("mouseup", this.onMouseUp);
28820 this.fireEvent("hide", this);
28823 onMouseUp : function()
28837 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28840 * @class Roo.bootstrap.menu.Item
28841 * @extends Roo.bootstrap.Component
28842 * Bootstrap MenuItem class
28843 * @cfg {Boolean} submenu (true | false) default false
28844 * @cfg {String} html text of the item
28845 * @cfg {String} href the link
28846 * @cfg {Boolean} disable (true | false) default false
28847 * @cfg {Boolean} preventDefault (true | false) default true
28848 * @cfg {String} icon Font awesome icon
28849 * @cfg {String} pos Submenu align to (left | right) default right
28853 * Create a new Item
28854 * @param {Object} config The config object
28858 Roo.bootstrap.menu.Item = function(config){
28859 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28863 * Fires when the mouse is hovering over this menu
28864 * @param {Roo.bootstrap.menu.Item} this
28865 * @param {Roo.EventObject} e
28870 * Fires when the mouse exits this menu
28871 * @param {Roo.bootstrap.menu.Item} this
28872 * @param {Roo.EventObject} e
28878 * The raw click event for the entire grid.
28879 * @param {Roo.EventObject} e
28885 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28890 preventDefault: true,
28895 getAutoCreate : function()
28900 cls : 'roo-menu-item-text',
28908 cls : 'fa ' + this.icon
28917 href : this.href || '#',
28924 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28928 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28930 if(this.pos == 'left'){
28931 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28938 initEvents : function()
28940 this.el.on('mouseover', this.onMouseOver, this);
28941 this.el.on('mouseout', this.onMouseOut, this);
28943 this.el.select('a', true).first().on('click', this.onClick, this);
28947 onClick : function(e)
28949 if(this.preventDefault){
28950 e.preventDefault();
28953 this.fireEvent("click", this, e);
28956 onMouseOver : function(e)
28958 if(this.submenu && this.pos == 'left'){
28959 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28962 this.fireEvent("mouseover", this, e);
28965 onMouseOut : function(e)
28967 this.fireEvent("mouseout", this, e);
28979 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28982 * @class Roo.bootstrap.menu.Separator
28983 * @extends Roo.bootstrap.Component
28984 * Bootstrap Separator class
28987 * Create a new Separator
28988 * @param {Object} config The config object
28992 Roo.bootstrap.menu.Separator = function(config){
28993 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28996 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28998 getAutoCreate : function(){
29001 cls: 'dropdown-divider divider'
29019 * @class Roo.bootstrap.Tooltip
29020 * Bootstrap Tooltip class
29021 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29022 * to determine which dom element triggers the tooltip.
29024 * It needs to add support for additional attributes like tooltip-position
29027 * Create a new Toolti
29028 * @param {Object} config The config object
29031 Roo.bootstrap.Tooltip = function(config){
29032 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29034 this.alignment = Roo.bootstrap.Tooltip.alignment;
29036 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29037 this.alignment = config.alignment;
29042 Roo.apply(Roo.bootstrap.Tooltip, {
29044 * @function init initialize tooltip monitoring.
29048 currentTip : false,
29049 currentRegion : false,
29055 Roo.get(document).on('mouseover', this.enter ,this);
29056 Roo.get(document).on('mouseout', this.leave, this);
29059 this.currentTip = new Roo.bootstrap.Tooltip();
29062 enter : function(ev)
29064 var dom = ev.getTarget();
29066 //Roo.log(['enter',dom]);
29067 var el = Roo.fly(dom);
29068 if (this.currentEl) {
29070 //Roo.log(this.currentEl);
29071 //Roo.log(this.currentEl.contains(dom));
29072 if (this.currentEl == el) {
29075 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29081 if (this.currentTip.el) {
29082 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29086 if(!el || el.dom == document){
29092 if (!el.attr('tooltip')) {
29093 pel = el.findParent("[tooltip]");
29095 bindEl = Roo.get(pel);
29101 // you can not look for children, as if el is the body.. then everythign is the child..
29102 if (!pel && !el.attr('tooltip')) { //
29103 if (!el.select("[tooltip]").elements.length) {
29106 // is the mouse over this child...?
29107 bindEl = el.select("[tooltip]").first();
29108 var xy = ev.getXY();
29109 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29110 //Roo.log("not in region.");
29113 //Roo.log("child element over..");
29116 this.currentEl = el;
29117 this.currentTip.bind(bindEl);
29118 this.currentRegion = Roo.lib.Region.getRegion(dom);
29119 this.currentTip.enter();
29122 leave : function(ev)
29124 var dom = ev.getTarget();
29125 //Roo.log(['leave',dom]);
29126 if (!this.currentEl) {
29131 if (dom != this.currentEl.dom) {
29134 var xy = ev.getXY();
29135 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29138 // only activate leave if mouse cursor is outside... bounding box..
29143 if (this.currentTip) {
29144 this.currentTip.leave();
29146 //Roo.log('clear currentEl');
29147 this.currentEl = false;
29152 'left' : ['r-l', [-2,0], 'right'],
29153 'right' : ['l-r', [2,0], 'left'],
29154 'bottom' : ['t-b', [0,2], 'top'],
29155 'top' : [ 'b-t', [0,-2], 'bottom']
29161 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29166 delay : null, // can be { show : 300 , hide: 500}
29170 hoverState : null, //???
29172 placement : 'bottom',
29176 getAutoCreate : function(){
29183 cls : 'tooltip-arrow arrow'
29186 cls : 'tooltip-inner'
29193 bind : function(el)
29198 initEvents : function()
29200 this.arrowEl = this.el.select('.arrow', true).first();
29201 this.innerEl = this.el.select('.tooltip-inner', true).first();
29204 enter : function () {
29206 if (this.timeout != null) {
29207 clearTimeout(this.timeout);
29210 this.hoverState = 'in';
29211 //Roo.log("enter - show");
29212 if (!this.delay || !this.delay.show) {
29217 this.timeout = setTimeout(function () {
29218 if (_t.hoverState == 'in') {
29221 }, this.delay.show);
29225 clearTimeout(this.timeout);
29227 this.hoverState = 'out';
29228 if (!this.delay || !this.delay.hide) {
29234 this.timeout = setTimeout(function () {
29235 //Roo.log("leave - timeout");
29237 if (_t.hoverState == 'out') {
29239 Roo.bootstrap.Tooltip.currentEl = false;
29244 show : function (msg)
29247 this.render(document.body);
29250 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29252 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29254 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29256 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29257 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29259 var placement = typeof this.placement == 'function' ?
29260 this.placement.call(this, this.el, on_el) :
29263 var autoToken = /\s?auto?\s?/i;
29264 var autoPlace = autoToken.test(placement);
29266 placement = placement.replace(autoToken, '') || 'top';
29270 //this.el.setXY([0,0]);
29272 //this.el.dom.style.display='block';
29274 //this.el.appendTo(on_el);
29276 var p = this.getPosition();
29277 var box = this.el.getBox();
29283 var align = this.alignment[placement];
29285 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29287 if(placement == 'top' || placement == 'bottom'){
29289 placement = 'right';
29292 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29293 placement = 'left';
29296 var scroll = Roo.select('body', true).first().getScroll();
29298 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29302 align = this.alignment[placement];
29304 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29308 var elems = document.getElementsByTagName('div');
29309 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29310 for (var i = 0; i < elems.length; i++) {
29311 var zindex = Number.parseInt(
29312 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29315 if (zindex > highest) {
29322 this.el.dom.style.zIndex = highest;
29324 this.el.alignTo(this.bindEl, align[0],align[1]);
29325 //var arrow = this.el.select('.arrow',true).first();
29326 //arrow.set(align[2],
29328 this.el.addClass(placement);
29329 this.el.addClass("bs-tooltip-"+ placement);
29331 this.el.addClass('in fade show');
29333 this.hoverState = null;
29335 if (this.el.hasClass('fade')) {
29350 //this.el.setXY([0,0]);
29351 this.el.removeClass(['show', 'in']);
29367 * @class Roo.bootstrap.LocationPicker
29368 * @extends Roo.bootstrap.Component
29369 * Bootstrap LocationPicker class
29370 * @cfg {Number} latitude Position when init default 0
29371 * @cfg {Number} longitude Position when init default 0
29372 * @cfg {Number} zoom default 15
29373 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29374 * @cfg {Boolean} mapTypeControl default false
29375 * @cfg {Boolean} disableDoubleClickZoom default false
29376 * @cfg {Boolean} scrollwheel default true
29377 * @cfg {Boolean} streetViewControl default false
29378 * @cfg {Number} radius default 0
29379 * @cfg {String} locationName
29380 * @cfg {Boolean} draggable default true
29381 * @cfg {Boolean} enableAutocomplete default false
29382 * @cfg {Boolean} enableReverseGeocode default true
29383 * @cfg {String} markerTitle
29386 * Create a new LocationPicker
29387 * @param {Object} config The config object
29391 Roo.bootstrap.LocationPicker = function(config){
29393 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29398 * Fires when the picker initialized.
29399 * @param {Roo.bootstrap.LocationPicker} this
29400 * @param {Google Location} location
29404 * @event positionchanged
29405 * Fires when the picker position changed.
29406 * @param {Roo.bootstrap.LocationPicker} this
29407 * @param {Google Location} location
29409 positionchanged : true,
29412 * Fires when the map resize.
29413 * @param {Roo.bootstrap.LocationPicker} this
29418 * Fires when the map show.
29419 * @param {Roo.bootstrap.LocationPicker} this
29424 * Fires when the map hide.
29425 * @param {Roo.bootstrap.LocationPicker} this
29430 * Fires when click the map.
29431 * @param {Roo.bootstrap.LocationPicker} this
29432 * @param {Map event} e
29436 * @event mapRightClick
29437 * Fires when right click the map.
29438 * @param {Roo.bootstrap.LocationPicker} this
29439 * @param {Map event} e
29441 mapRightClick : true,
29443 * @event markerClick
29444 * Fires when click the marker.
29445 * @param {Roo.bootstrap.LocationPicker} this
29446 * @param {Map event} e
29448 markerClick : true,
29450 * @event markerRightClick
29451 * Fires when right click the marker.
29452 * @param {Roo.bootstrap.LocationPicker} this
29453 * @param {Map event} e
29455 markerRightClick : true,
29457 * @event OverlayViewDraw
29458 * Fires when OverlayView Draw
29459 * @param {Roo.bootstrap.LocationPicker} this
29461 OverlayViewDraw : true,
29463 * @event OverlayViewOnAdd
29464 * Fires when OverlayView Draw
29465 * @param {Roo.bootstrap.LocationPicker} this
29467 OverlayViewOnAdd : true,
29469 * @event OverlayViewOnRemove
29470 * Fires when OverlayView Draw
29471 * @param {Roo.bootstrap.LocationPicker} this
29473 OverlayViewOnRemove : true,
29475 * @event OverlayViewShow
29476 * Fires when OverlayView Draw
29477 * @param {Roo.bootstrap.LocationPicker} this
29478 * @param {Pixel} cpx
29480 OverlayViewShow : true,
29482 * @event OverlayViewHide
29483 * Fires when OverlayView Draw
29484 * @param {Roo.bootstrap.LocationPicker} this
29486 OverlayViewHide : true,
29488 * @event loadexception
29489 * Fires when load google lib failed.
29490 * @param {Roo.bootstrap.LocationPicker} this
29492 loadexception : true
29497 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29499 gMapContext: false,
29505 mapTypeControl: false,
29506 disableDoubleClickZoom: false,
29508 streetViewControl: false,
29512 enableAutocomplete: false,
29513 enableReverseGeocode: true,
29516 getAutoCreate: function()
29521 cls: 'roo-location-picker'
29527 initEvents: function(ct, position)
29529 if(!this.el.getWidth() || this.isApplied()){
29533 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29538 initial: function()
29540 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29541 this.fireEvent('loadexception', this);
29545 if(!this.mapTypeId){
29546 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29549 this.gMapContext = this.GMapContext();
29551 this.initOverlayView();
29553 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29557 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29558 _this.setPosition(_this.gMapContext.marker.position);
29561 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29562 _this.fireEvent('mapClick', this, event);
29566 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29567 _this.fireEvent('mapRightClick', this, event);
29571 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29572 _this.fireEvent('markerClick', this, event);
29576 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29577 _this.fireEvent('markerRightClick', this, event);
29581 this.setPosition(this.gMapContext.location);
29583 this.fireEvent('initial', this, this.gMapContext.location);
29586 initOverlayView: function()
29590 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29594 _this.fireEvent('OverlayViewDraw', _this);
29599 _this.fireEvent('OverlayViewOnAdd', _this);
29602 onRemove: function()
29604 _this.fireEvent('OverlayViewOnRemove', _this);
29607 show: function(cpx)
29609 _this.fireEvent('OverlayViewShow', _this, cpx);
29614 _this.fireEvent('OverlayViewHide', _this);
29620 fromLatLngToContainerPixel: function(event)
29622 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29625 isApplied: function()
29627 return this.getGmapContext() == false ? false : true;
29630 getGmapContext: function()
29632 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29635 GMapContext: function()
29637 var position = new google.maps.LatLng(this.latitude, this.longitude);
29639 var _map = new google.maps.Map(this.el.dom, {
29642 mapTypeId: this.mapTypeId,
29643 mapTypeControl: this.mapTypeControl,
29644 disableDoubleClickZoom: this.disableDoubleClickZoom,
29645 scrollwheel: this.scrollwheel,
29646 streetViewControl: this.streetViewControl,
29647 locationName: this.locationName,
29648 draggable: this.draggable,
29649 enableAutocomplete: this.enableAutocomplete,
29650 enableReverseGeocode: this.enableReverseGeocode
29653 var _marker = new google.maps.Marker({
29654 position: position,
29656 title: this.markerTitle,
29657 draggable: this.draggable
29664 location: position,
29665 radius: this.radius,
29666 locationName: this.locationName,
29667 addressComponents: {
29668 formatted_address: null,
29669 addressLine1: null,
29670 addressLine2: null,
29672 streetNumber: null,
29676 stateOrProvince: null
29679 domContainer: this.el.dom,
29680 geodecoder: new google.maps.Geocoder()
29684 drawCircle: function(center, radius, options)
29686 if (this.gMapContext.circle != null) {
29687 this.gMapContext.circle.setMap(null);
29691 options = Roo.apply({}, options, {
29692 strokeColor: "#0000FF",
29693 strokeOpacity: .35,
29695 fillColor: "#0000FF",
29699 options.map = this.gMapContext.map;
29700 options.radius = radius;
29701 options.center = center;
29702 this.gMapContext.circle = new google.maps.Circle(options);
29703 return this.gMapContext.circle;
29709 setPosition: function(location)
29711 this.gMapContext.location = location;
29712 this.gMapContext.marker.setPosition(location);
29713 this.gMapContext.map.panTo(location);
29714 this.drawCircle(location, this.gMapContext.radius, {});
29718 if (this.gMapContext.settings.enableReverseGeocode) {
29719 this.gMapContext.geodecoder.geocode({
29720 latLng: this.gMapContext.location
29721 }, function(results, status) {
29723 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29724 _this.gMapContext.locationName = results[0].formatted_address;
29725 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29727 _this.fireEvent('positionchanged', this, location);
29734 this.fireEvent('positionchanged', this, location);
29739 google.maps.event.trigger(this.gMapContext.map, "resize");
29741 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29743 this.fireEvent('resize', this);
29746 setPositionByLatLng: function(latitude, longitude)
29748 this.setPosition(new google.maps.LatLng(latitude, longitude));
29751 getCurrentPosition: function()
29754 latitude: this.gMapContext.location.lat(),
29755 longitude: this.gMapContext.location.lng()
29759 getAddressName: function()
29761 return this.gMapContext.locationName;
29764 getAddressComponents: function()
29766 return this.gMapContext.addressComponents;
29769 address_component_from_google_geocode: function(address_components)
29773 for (var i = 0; i < address_components.length; i++) {
29774 var component = address_components[i];
29775 if (component.types.indexOf("postal_code") >= 0) {
29776 result.postalCode = component.short_name;
29777 } else if (component.types.indexOf("street_number") >= 0) {
29778 result.streetNumber = component.short_name;
29779 } else if (component.types.indexOf("route") >= 0) {
29780 result.streetName = component.short_name;
29781 } else if (component.types.indexOf("neighborhood") >= 0) {
29782 result.city = component.short_name;
29783 } else if (component.types.indexOf("locality") >= 0) {
29784 result.city = component.short_name;
29785 } else if (component.types.indexOf("sublocality") >= 0) {
29786 result.district = component.short_name;
29787 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29788 result.stateOrProvince = component.short_name;
29789 } else if (component.types.indexOf("country") >= 0) {
29790 result.country = component.short_name;
29794 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29795 result.addressLine2 = "";
29799 setZoomLevel: function(zoom)
29801 this.gMapContext.map.setZoom(zoom);
29814 this.fireEvent('show', this);
29825 this.fireEvent('hide', this);
29830 Roo.apply(Roo.bootstrap.LocationPicker, {
29832 OverlayView : function(map, options)
29834 options = options || {};
29841 * @class Roo.bootstrap.Alert
29842 * @extends Roo.bootstrap.Component
29843 * Bootstrap Alert class - shows an alert area box
29845 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29846 Enter a valid email address
29849 * @cfg {String} title The title of alert
29850 * @cfg {String} html The content of alert
29851 * @cfg {String} weight ( success | info | warning | danger )
29852 * @cfg {String} fa font-awesomeicon
29853 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29854 * @cfg {Boolean} close true to show a x closer
29858 * Create a new alert
29859 * @param {Object} config The config object
29863 Roo.bootstrap.Alert = function(config){
29864 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29868 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29874 faicon: false, // BC
29878 getAutoCreate : function()
29890 style : this.close ? '' : 'display:none'
29894 cls : 'roo-alert-icon'
29899 cls : 'roo-alert-title',
29904 cls : 'roo-alert-text',
29911 cfg.cn[0].cls += ' fa ' + this.faicon;
29914 cfg.cn[0].cls += ' fa ' + this.fa;
29918 cfg.cls += ' alert-' + this.weight;
29924 initEvents: function()
29926 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29927 this.titleEl = this.el.select('.roo-alert-title',true).first();
29928 this.iconEl = this.el.select('.roo-alert-icon',true).first();
29929 if (this.seconds > 0) {
29930 this.hide.defer(this.seconds, this);
29934 setTitle : function(str)
29936 this.titleEl.dom.innerHTML = str;
29939 setText : function(str)
29941 this.titleEl.dom.innerHTML = str;
29944 setWeight : function(weight)
29947 this.el.removeClass('alert-' + this.weight);
29950 this.weight = weight;
29952 this.el.addClass('alert-' + this.weight);
29955 setIcon : function(icon)
29958 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29961 this.faicon = icon;
29963 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
29984 * @class Roo.bootstrap.UploadCropbox
29985 * @extends Roo.bootstrap.Component
29986 * Bootstrap UploadCropbox class
29987 * @cfg {String} emptyText show when image has been loaded
29988 * @cfg {String} rotateNotify show when image too small to rotate
29989 * @cfg {Number} errorTimeout default 3000
29990 * @cfg {Number} minWidth default 300
29991 * @cfg {Number} minHeight default 300
29992 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29993 * @cfg {Boolean} isDocument (true|false) default false
29994 * @cfg {String} url action url
29995 * @cfg {String} paramName default 'imageUpload'
29996 * @cfg {String} method default POST
29997 * @cfg {Boolean} loadMask (true|false) default true
29998 * @cfg {Boolean} loadingText default 'Loading...'
30001 * Create a new UploadCropbox
30002 * @param {Object} config The config object
30005 Roo.bootstrap.UploadCropbox = function(config){
30006 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30010 * @event beforeselectfile
30011 * Fire before select file
30012 * @param {Roo.bootstrap.UploadCropbox} this
30014 "beforeselectfile" : true,
30017 * Fire after initEvent
30018 * @param {Roo.bootstrap.UploadCropbox} this
30023 * Fire after initEvent
30024 * @param {Roo.bootstrap.UploadCropbox} this
30025 * @param {String} data
30030 * Fire when preparing the file data
30031 * @param {Roo.bootstrap.UploadCropbox} this
30032 * @param {Object} file
30037 * Fire when get exception
30038 * @param {Roo.bootstrap.UploadCropbox} this
30039 * @param {XMLHttpRequest} xhr
30041 "exception" : true,
30043 * @event beforeloadcanvas
30044 * Fire before load the canvas
30045 * @param {Roo.bootstrap.UploadCropbox} this
30046 * @param {String} src
30048 "beforeloadcanvas" : true,
30051 * Fire when trash image
30052 * @param {Roo.bootstrap.UploadCropbox} this
30057 * Fire when download the image
30058 * @param {Roo.bootstrap.UploadCropbox} this
30062 * @event footerbuttonclick
30063 * Fire when footerbuttonclick
30064 * @param {Roo.bootstrap.UploadCropbox} this
30065 * @param {String} type
30067 "footerbuttonclick" : true,
30071 * @param {Roo.bootstrap.UploadCropbox} this
30076 * Fire when rotate the image
30077 * @param {Roo.bootstrap.UploadCropbox} this
30078 * @param {String} pos
30083 * Fire when inspect the file
30084 * @param {Roo.bootstrap.UploadCropbox} this
30085 * @param {Object} file
30090 * Fire when xhr upload the file
30091 * @param {Roo.bootstrap.UploadCropbox} this
30092 * @param {Object} data
30097 * Fire when arrange the file data
30098 * @param {Roo.bootstrap.UploadCropbox} this
30099 * @param {Object} formData
30104 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30107 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30109 emptyText : 'Click to upload image',
30110 rotateNotify : 'Image is too small to rotate',
30111 errorTimeout : 3000,
30125 cropType : 'image/jpeg',
30127 canvasLoaded : false,
30128 isDocument : false,
30130 paramName : 'imageUpload',
30132 loadingText : 'Loading...',
30135 getAutoCreate : function()
30139 cls : 'roo-upload-cropbox',
30143 cls : 'roo-upload-cropbox-selector',
30148 cls : 'roo-upload-cropbox-body',
30149 style : 'cursor:pointer',
30153 cls : 'roo-upload-cropbox-preview'
30157 cls : 'roo-upload-cropbox-thumb'
30161 cls : 'roo-upload-cropbox-empty-notify',
30162 html : this.emptyText
30166 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30167 html : this.rotateNotify
30173 cls : 'roo-upload-cropbox-footer',
30176 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30186 onRender : function(ct, position)
30188 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30190 if (this.buttons.length) {
30192 Roo.each(this.buttons, function(bb) {
30194 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30196 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30202 this.maskEl = this.el;
30206 initEvents : function()
30208 this.urlAPI = (window.createObjectURL && window) ||
30209 (window.URL && URL.revokeObjectURL && URL) ||
30210 (window.webkitURL && webkitURL);
30212 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30213 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30215 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30216 this.selectorEl.hide();
30218 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30219 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30221 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30222 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30223 this.thumbEl.hide();
30225 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30226 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30228 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30229 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30230 this.errorEl.hide();
30232 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30233 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30234 this.footerEl.hide();
30236 this.setThumbBoxSize();
30242 this.fireEvent('initial', this);
30249 window.addEventListener("resize", function() { _this.resize(); } );
30251 this.bodyEl.on('click', this.beforeSelectFile, this);
30254 this.bodyEl.on('touchstart', this.onTouchStart, this);
30255 this.bodyEl.on('touchmove', this.onTouchMove, this);
30256 this.bodyEl.on('touchend', this.onTouchEnd, this);
30260 this.bodyEl.on('mousedown', this.onMouseDown, this);
30261 this.bodyEl.on('mousemove', this.onMouseMove, this);
30262 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30263 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30264 Roo.get(document).on('mouseup', this.onMouseUp, this);
30267 this.selectorEl.on('change', this.onFileSelected, this);
30273 this.baseScale = 1;
30275 this.baseRotate = 1;
30276 this.dragable = false;
30277 this.pinching = false;
30280 this.cropData = false;
30281 this.notifyEl.dom.innerHTML = this.emptyText;
30283 this.selectorEl.dom.value = '';
30287 resize : function()
30289 if(this.fireEvent('resize', this) != false){
30290 this.setThumbBoxPosition();
30291 this.setCanvasPosition();
30295 onFooterButtonClick : function(e, el, o, type)
30298 case 'rotate-left' :
30299 this.onRotateLeft(e);
30301 case 'rotate-right' :
30302 this.onRotateRight(e);
30305 this.beforeSelectFile(e);
30320 this.fireEvent('footerbuttonclick', this, type);
30323 beforeSelectFile : function(e)
30325 e.preventDefault();
30327 if(this.fireEvent('beforeselectfile', this) != false){
30328 this.selectorEl.dom.click();
30332 onFileSelected : function(e)
30334 e.preventDefault();
30336 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30340 var file = this.selectorEl.dom.files[0];
30342 if(this.fireEvent('inspect', this, file) != false){
30343 this.prepare(file);
30348 trash : function(e)
30350 this.fireEvent('trash', this);
30353 download : function(e)
30355 this.fireEvent('download', this);
30358 loadCanvas : function(src)
30360 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30364 this.imageEl = document.createElement('img');
30368 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30370 this.imageEl.src = src;
30374 onLoadCanvas : function()
30376 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30377 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30379 this.bodyEl.un('click', this.beforeSelectFile, this);
30381 this.notifyEl.hide();
30382 this.thumbEl.show();
30383 this.footerEl.show();
30385 this.baseRotateLevel();
30387 if(this.isDocument){
30388 this.setThumbBoxSize();
30391 this.setThumbBoxPosition();
30393 this.baseScaleLevel();
30399 this.canvasLoaded = true;
30402 this.maskEl.unmask();
30407 setCanvasPosition : function()
30409 if(!this.canvasEl){
30413 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30414 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30416 this.previewEl.setLeft(pw);
30417 this.previewEl.setTop(ph);
30421 onMouseDown : function(e)
30425 this.dragable = true;
30426 this.pinching = false;
30428 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30429 this.dragable = false;
30433 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30434 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30438 onMouseMove : function(e)
30442 if(!this.canvasLoaded){
30446 if (!this.dragable){
30450 var minX = Math.ceil(this.thumbEl.getLeft(true));
30451 var minY = Math.ceil(this.thumbEl.getTop(true));
30453 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30454 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30456 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30457 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30459 x = x - this.mouseX;
30460 y = y - this.mouseY;
30462 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30463 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30465 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30466 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30468 this.previewEl.setLeft(bgX);
30469 this.previewEl.setTop(bgY);
30471 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30472 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30475 onMouseUp : function(e)
30479 this.dragable = false;
30482 onMouseWheel : function(e)
30486 this.startScale = this.scale;
30488 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30490 if(!this.zoomable()){
30491 this.scale = this.startScale;
30500 zoomable : function()
30502 var minScale = this.thumbEl.getWidth() / this.minWidth;
30504 if(this.minWidth < this.minHeight){
30505 minScale = this.thumbEl.getHeight() / this.minHeight;
30508 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30509 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30513 (this.rotate == 0 || this.rotate == 180) &&
30515 width > this.imageEl.OriginWidth ||
30516 height > this.imageEl.OriginHeight ||
30517 (width < this.minWidth && height < this.minHeight)
30525 (this.rotate == 90 || this.rotate == 270) &&
30527 width > this.imageEl.OriginWidth ||
30528 height > this.imageEl.OriginHeight ||
30529 (width < this.minHeight && height < this.minWidth)
30536 !this.isDocument &&
30537 (this.rotate == 0 || this.rotate == 180) &&
30539 width < this.minWidth ||
30540 width > this.imageEl.OriginWidth ||
30541 height < this.minHeight ||
30542 height > this.imageEl.OriginHeight
30549 !this.isDocument &&
30550 (this.rotate == 90 || this.rotate == 270) &&
30552 width < this.minHeight ||
30553 width > this.imageEl.OriginWidth ||
30554 height < this.minWidth ||
30555 height > this.imageEl.OriginHeight
30565 onRotateLeft : function(e)
30567 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30569 var minScale = this.thumbEl.getWidth() / this.minWidth;
30571 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30572 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30574 this.startScale = this.scale;
30576 while (this.getScaleLevel() < minScale){
30578 this.scale = this.scale + 1;
30580 if(!this.zoomable()){
30585 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30586 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30591 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30598 this.scale = this.startScale;
30600 this.onRotateFail();
30605 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30607 if(this.isDocument){
30608 this.setThumbBoxSize();
30609 this.setThumbBoxPosition();
30610 this.setCanvasPosition();
30615 this.fireEvent('rotate', this, 'left');
30619 onRotateRight : function(e)
30621 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30623 var minScale = this.thumbEl.getWidth() / this.minWidth;
30625 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30626 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30628 this.startScale = this.scale;
30630 while (this.getScaleLevel() < minScale){
30632 this.scale = this.scale + 1;
30634 if(!this.zoomable()){
30639 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30640 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30645 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30652 this.scale = this.startScale;
30654 this.onRotateFail();
30659 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30661 if(this.isDocument){
30662 this.setThumbBoxSize();
30663 this.setThumbBoxPosition();
30664 this.setCanvasPosition();
30669 this.fireEvent('rotate', this, 'right');
30672 onRotateFail : function()
30674 this.errorEl.show(true);
30678 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30683 this.previewEl.dom.innerHTML = '';
30685 var canvasEl = document.createElement("canvas");
30687 var contextEl = canvasEl.getContext("2d");
30689 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30690 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30691 var center = this.imageEl.OriginWidth / 2;
30693 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30694 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30695 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30696 center = this.imageEl.OriginHeight / 2;
30699 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30701 contextEl.translate(center, center);
30702 contextEl.rotate(this.rotate * Math.PI / 180);
30704 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30706 this.canvasEl = document.createElement("canvas");
30708 this.contextEl = this.canvasEl.getContext("2d");
30710 switch (this.rotate) {
30713 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30714 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30716 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30721 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30722 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30724 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30725 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);
30729 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30734 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30735 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30737 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30738 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);
30742 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);
30747 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30748 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30750 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30751 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30755 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);
30762 this.previewEl.appendChild(this.canvasEl);
30764 this.setCanvasPosition();
30769 if(!this.canvasLoaded){
30773 var imageCanvas = document.createElement("canvas");
30775 var imageContext = imageCanvas.getContext("2d");
30777 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30778 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30780 var center = imageCanvas.width / 2;
30782 imageContext.translate(center, center);
30784 imageContext.rotate(this.rotate * Math.PI / 180);
30786 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30788 var canvas = document.createElement("canvas");
30790 var context = canvas.getContext("2d");
30792 canvas.width = this.minWidth;
30793 canvas.height = this.minHeight;
30795 switch (this.rotate) {
30798 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30799 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30801 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30802 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30804 var targetWidth = this.minWidth - 2 * x;
30805 var targetHeight = this.minHeight - 2 * y;
30809 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30810 scale = targetWidth / width;
30813 if(x > 0 && y == 0){
30814 scale = targetHeight / height;
30817 if(x > 0 && y > 0){
30818 scale = targetWidth / width;
30820 if(width < height){
30821 scale = targetHeight / height;
30825 context.scale(scale, scale);
30827 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30828 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30830 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30831 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30833 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30838 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30839 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30841 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30842 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30844 var targetWidth = this.minWidth - 2 * x;
30845 var targetHeight = this.minHeight - 2 * y;
30849 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30850 scale = targetWidth / width;
30853 if(x > 0 && y == 0){
30854 scale = targetHeight / height;
30857 if(x > 0 && y > 0){
30858 scale = targetWidth / width;
30860 if(width < height){
30861 scale = targetHeight / height;
30865 context.scale(scale, scale);
30867 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30868 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30870 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30871 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30873 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30875 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30880 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30881 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30883 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30884 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30886 var targetWidth = this.minWidth - 2 * x;
30887 var targetHeight = this.minHeight - 2 * y;
30891 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30892 scale = targetWidth / width;
30895 if(x > 0 && y == 0){
30896 scale = targetHeight / height;
30899 if(x > 0 && y > 0){
30900 scale = targetWidth / width;
30902 if(width < height){
30903 scale = targetHeight / height;
30907 context.scale(scale, scale);
30909 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30910 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30912 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30913 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30915 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30916 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30918 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30923 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30924 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30926 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30927 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30929 var targetWidth = this.minWidth - 2 * x;
30930 var targetHeight = this.minHeight - 2 * y;
30934 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30935 scale = targetWidth / width;
30938 if(x > 0 && y == 0){
30939 scale = targetHeight / height;
30942 if(x > 0 && y > 0){
30943 scale = targetWidth / width;
30945 if(width < height){
30946 scale = targetHeight / height;
30950 context.scale(scale, scale);
30952 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30953 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30955 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30956 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30958 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30960 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30967 this.cropData = canvas.toDataURL(this.cropType);
30969 if(this.fireEvent('crop', this, this.cropData) !== false){
30970 this.process(this.file, this.cropData);
30977 setThumbBoxSize : function()
30981 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30982 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30983 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30985 this.minWidth = width;
30986 this.minHeight = height;
30988 if(this.rotate == 90 || this.rotate == 270){
30989 this.minWidth = height;
30990 this.minHeight = width;
30995 width = Math.ceil(this.minWidth * height / this.minHeight);
30997 if(this.minWidth > this.minHeight){
30999 height = Math.ceil(this.minHeight * width / this.minWidth);
31002 this.thumbEl.setStyle({
31003 width : width + 'px',
31004 height : height + 'px'
31011 setThumbBoxPosition : function()
31013 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31014 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31016 this.thumbEl.setLeft(x);
31017 this.thumbEl.setTop(y);
31021 baseRotateLevel : function()
31023 this.baseRotate = 1;
31026 typeof(this.exif) != 'undefined' &&
31027 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31028 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31030 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31033 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31037 baseScaleLevel : function()
31041 if(this.isDocument){
31043 if(this.baseRotate == 6 || this.baseRotate == 8){
31045 height = this.thumbEl.getHeight();
31046 this.baseScale = height / this.imageEl.OriginWidth;
31048 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31049 width = this.thumbEl.getWidth();
31050 this.baseScale = width / this.imageEl.OriginHeight;
31056 height = this.thumbEl.getHeight();
31057 this.baseScale = height / this.imageEl.OriginHeight;
31059 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31060 width = this.thumbEl.getWidth();
31061 this.baseScale = width / this.imageEl.OriginWidth;
31067 if(this.baseRotate == 6 || this.baseRotate == 8){
31069 width = this.thumbEl.getHeight();
31070 this.baseScale = width / this.imageEl.OriginHeight;
31072 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31073 height = this.thumbEl.getWidth();
31074 this.baseScale = height / this.imageEl.OriginHeight;
31077 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31078 height = this.thumbEl.getWidth();
31079 this.baseScale = height / this.imageEl.OriginHeight;
31081 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31082 width = this.thumbEl.getHeight();
31083 this.baseScale = width / this.imageEl.OriginWidth;
31090 width = this.thumbEl.getWidth();
31091 this.baseScale = width / this.imageEl.OriginWidth;
31093 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31094 height = this.thumbEl.getHeight();
31095 this.baseScale = height / this.imageEl.OriginHeight;
31098 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31100 height = this.thumbEl.getHeight();
31101 this.baseScale = height / this.imageEl.OriginHeight;
31103 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31104 width = this.thumbEl.getWidth();
31105 this.baseScale = width / this.imageEl.OriginWidth;
31113 getScaleLevel : function()
31115 return this.baseScale * Math.pow(1.1, this.scale);
31118 onTouchStart : function(e)
31120 if(!this.canvasLoaded){
31121 this.beforeSelectFile(e);
31125 var touches = e.browserEvent.touches;
31131 if(touches.length == 1){
31132 this.onMouseDown(e);
31136 if(touches.length != 2){
31142 for(var i = 0, finger; finger = touches[i]; i++){
31143 coords.push(finger.pageX, finger.pageY);
31146 var x = Math.pow(coords[0] - coords[2], 2);
31147 var y = Math.pow(coords[1] - coords[3], 2);
31149 this.startDistance = Math.sqrt(x + y);
31151 this.startScale = this.scale;
31153 this.pinching = true;
31154 this.dragable = false;
31158 onTouchMove : function(e)
31160 if(!this.pinching && !this.dragable){
31164 var touches = e.browserEvent.touches;
31171 this.onMouseMove(e);
31177 for(var i = 0, finger; finger = touches[i]; i++){
31178 coords.push(finger.pageX, finger.pageY);
31181 var x = Math.pow(coords[0] - coords[2], 2);
31182 var y = Math.pow(coords[1] - coords[3], 2);
31184 this.endDistance = Math.sqrt(x + y);
31186 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31188 if(!this.zoomable()){
31189 this.scale = this.startScale;
31197 onTouchEnd : function(e)
31199 this.pinching = false;
31200 this.dragable = false;
31204 process : function(file, crop)
31207 this.maskEl.mask(this.loadingText);
31210 this.xhr = new XMLHttpRequest();
31212 file.xhr = this.xhr;
31214 this.xhr.open(this.method, this.url, true);
31217 "Accept": "application/json",
31218 "Cache-Control": "no-cache",
31219 "X-Requested-With": "XMLHttpRequest"
31222 for (var headerName in headers) {
31223 var headerValue = headers[headerName];
31225 this.xhr.setRequestHeader(headerName, headerValue);
31231 this.xhr.onload = function()
31233 _this.xhrOnLoad(_this.xhr);
31236 this.xhr.onerror = function()
31238 _this.xhrOnError(_this.xhr);
31241 var formData = new FormData();
31243 formData.append('returnHTML', 'NO');
31246 formData.append('crop', crop);
31249 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31250 formData.append(this.paramName, file, file.name);
31253 if(typeof(file.filename) != 'undefined'){
31254 formData.append('filename', file.filename);
31257 if(typeof(file.mimetype) != 'undefined'){
31258 formData.append('mimetype', file.mimetype);
31261 if(this.fireEvent('arrange', this, formData) != false){
31262 this.xhr.send(formData);
31266 xhrOnLoad : function(xhr)
31269 this.maskEl.unmask();
31272 if (xhr.readyState !== 4) {
31273 this.fireEvent('exception', this, xhr);
31277 var response = Roo.decode(xhr.responseText);
31279 if(!response.success){
31280 this.fireEvent('exception', this, xhr);
31284 var response = Roo.decode(xhr.responseText);
31286 this.fireEvent('upload', this, response);
31290 xhrOnError : function()
31293 this.maskEl.unmask();
31296 Roo.log('xhr on error');
31298 var response = Roo.decode(xhr.responseText);
31304 prepare : function(file)
31307 this.maskEl.mask(this.loadingText);
31313 if(typeof(file) === 'string'){
31314 this.loadCanvas(file);
31318 if(!file || !this.urlAPI){
31323 this.cropType = file.type;
31327 if(this.fireEvent('prepare', this, this.file) != false){
31329 var reader = new FileReader();
31331 reader.onload = function (e) {
31332 if (e.target.error) {
31333 Roo.log(e.target.error);
31337 var buffer = e.target.result,
31338 dataView = new DataView(buffer),
31340 maxOffset = dataView.byteLength - 4,
31344 if (dataView.getUint16(0) === 0xffd8) {
31345 while (offset < maxOffset) {
31346 markerBytes = dataView.getUint16(offset);
31348 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31349 markerLength = dataView.getUint16(offset + 2) + 2;
31350 if (offset + markerLength > dataView.byteLength) {
31351 Roo.log('Invalid meta data: Invalid segment size.');
31355 if(markerBytes == 0xffe1){
31356 _this.parseExifData(
31363 offset += markerLength;
31373 var url = _this.urlAPI.createObjectURL(_this.file);
31375 _this.loadCanvas(url);
31380 reader.readAsArrayBuffer(this.file);
31386 parseExifData : function(dataView, offset, length)
31388 var tiffOffset = offset + 10,
31392 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31393 // No Exif data, might be XMP data instead
31397 // Check for the ASCII code for "Exif" (0x45786966):
31398 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31399 // No Exif data, might be XMP data instead
31402 if (tiffOffset + 8 > dataView.byteLength) {
31403 Roo.log('Invalid Exif data: Invalid segment size.');
31406 // Check for the two null bytes:
31407 if (dataView.getUint16(offset + 8) !== 0x0000) {
31408 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31411 // Check the byte alignment:
31412 switch (dataView.getUint16(tiffOffset)) {
31414 littleEndian = true;
31417 littleEndian = false;
31420 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31423 // Check for the TIFF tag marker (0x002A):
31424 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31425 Roo.log('Invalid Exif data: Missing TIFF marker.');
31428 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31429 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31431 this.parseExifTags(
31434 tiffOffset + dirOffset,
31439 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31444 if (dirOffset + 6 > dataView.byteLength) {
31445 Roo.log('Invalid Exif data: Invalid directory offset.');
31448 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31449 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31450 if (dirEndOffset + 4 > dataView.byteLength) {
31451 Roo.log('Invalid Exif data: Invalid directory size.');
31454 for (i = 0; i < tagsNumber; i += 1) {
31458 dirOffset + 2 + 12 * i, // tag offset
31462 // Return the offset to the next directory:
31463 return dataView.getUint32(dirEndOffset, littleEndian);
31466 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31468 var tag = dataView.getUint16(offset, littleEndian);
31470 this.exif[tag] = this.getExifValue(
31474 dataView.getUint16(offset + 2, littleEndian), // tag type
31475 dataView.getUint32(offset + 4, littleEndian), // tag length
31480 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31482 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31491 Roo.log('Invalid Exif data: Invalid tag type.');
31495 tagSize = tagType.size * length;
31496 // Determine if the value is contained in the dataOffset bytes,
31497 // or if the value at the dataOffset is a pointer to the actual data:
31498 dataOffset = tagSize > 4 ?
31499 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31500 if (dataOffset + tagSize > dataView.byteLength) {
31501 Roo.log('Invalid Exif data: Invalid data offset.');
31504 if (length === 1) {
31505 return tagType.getValue(dataView, dataOffset, littleEndian);
31508 for (i = 0; i < length; i += 1) {
31509 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31512 if (tagType.ascii) {
31514 // Concatenate the chars:
31515 for (i = 0; i < values.length; i += 1) {
31517 // Ignore the terminating NULL byte(s):
31518 if (c === '\u0000') {
31530 Roo.apply(Roo.bootstrap.UploadCropbox, {
31532 'Orientation': 0x0112
31536 1: 0, //'top-left',
31538 3: 180, //'bottom-right',
31539 // 4: 'bottom-left',
31541 6: 90, //'right-top',
31542 // 7: 'right-bottom',
31543 8: 270 //'left-bottom'
31547 // byte, 8-bit unsigned int:
31549 getValue: function (dataView, dataOffset) {
31550 return dataView.getUint8(dataOffset);
31554 // ascii, 8-bit byte:
31556 getValue: function (dataView, dataOffset) {
31557 return String.fromCharCode(dataView.getUint8(dataOffset));
31562 // short, 16 bit int:
31564 getValue: function (dataView, dataOffset, littleEndian) {
31565 return dataView.getUint16(dataOffset, littleEndian);
31569 // long, 32 bit int:
31571 getValue: function (dataView, dataOffset, littleEndian) {
31572 return dataView.getUint32(dataOffset, littleEndian);
31576 // rational = two long values, first is numerator, second is denominator:
31578 getValue: function (dataView, dataOffset, littleEndian) {
31579 return dataView.getUint32(dataOffset, littleEndian) /
31580 dataView.getUint32(dataOffset + 4, littleEndian);
31584 // slong, 32 bit signed int:
31586 getValue: function (dataView, dataOffset, littleEndian) {
31587 return dataView.getInt32(dataOffset, littleEndian);
31591 // srational, two slongs, first is numerator, second is denominator:
31593 getValue: function (dataView, dataOffset, littleEndian) {
31594 return dataView.getInt32(dataOffset, littleEndian) /
31595 dataView.getInt32(dataOffset + 4, littleEndian);
31605 cls : 'btn-group roo-upload-cropbox-rotate-left',
31606 action : 'rotate-left',
31610 cls : 'btn btn-default',
31611 html : '<i class="fa fa-undo"></i>'
31617 cls : 'btn-group roo-upload-cropbox-picture',
31618 action : 'picture',
31622 cls : 'btn btn-default',
31623 html : '<i class="fa fa-picture-o"></i>'
31629 cls : 'btn-group roo-upload-cropbox-rotate-right',
31630 action : 'rotate-right',
31634 cls : 'btn btn-default',
31635 html : '<i class="fa fa-repeat"></i>'
31643 cls : 'btn-group roo-upload-cropbox-rotate-left',
31644 action : 'rotate-left',
31648 cls : 'btn btn-default',
31649 html : '<i class="fa fa-undo"></i>'
31655 cls : 'btn-group roo-upload-cropbox-download',
31656 action : 'download',
31660 cls : 'btn btn-default',
31661 html : '<i class="fa fa-download"></i>'
31667 cls : 'btn-group roo-upload-cropbox-crop',
31672 cls : 'btn btn-default',
31673 html : '<i class="fa fa-crop"></i>'
31679 cls : 'btn-group roo-upload-cropbox-trash',
31684 cls : 'btn btn-default',
31685 html : '<i class="fa fa-trash"></i>'
31691 cls : 'btn-group roo-upload-cropbox-rotate-right',
31692 action : 'rotate-right',
31696 cls : 'btn btn-default',
31697 html : '<i class="fa fa-repeat"></i>'
31705 cls : 'btn-group roo-upload-cropbox-rotate-left',
31706 action : 'rotate-left',
31710 cls : 'btn btn-default',
31711 html : '<i class="fa fa-undo"></i>'
31717 cls : 'btn-group roo-upload-cropbox-rotate-right',
31718 action : 'rotate-right',
31722 cls : 'btn btn-default',
31723 html : '<i class="fa fa-repeat"></i>'
31736 * @class Roo.bootstrap.DocumentManager
31737 * @extends Roo.bootstrap.Component
31738 * Bootstrap DocumentManager class
31739 * @cfg {String} paramName default 'imageUpload'
31740 * @cfg {String} toolTipName default 'filename'
31741 * @cfg {String} method default POST
31742 * @cfg {String} url action url
31743 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31744 * @cfg {Boolean} multiple multiple upload default true
31745 * @cfg {Number} thumbSize default 300
31746 * @cfg {String} fieldLabel
31747 * @cfg {Number} labelWidth default 4
31748 * @cfg {String} labelAlign (left|top) default left
31749 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31750 * @cfg {Number} labellg set the width of label (1-12)
31751 * @cfg {Number} labelmd set the width of label (1-12)
31752 * @cfg {Number} labelsm set the width of label (1-12)
31753 * @cfg {Number} labelxs set the width of label (1-12)
31756 * Create a new DocumentManager
31757 * @param {Object} config The config object
31760 Roo.bootstrap.DocumentManager = function(config){
31761 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31764 this.delegates = [];
31769 * Fire when initial the DocumentManager
31770 * @param {Roo.bootstrap.DocumentManager} this
31775 * inspect selected file
31776 * @param {Roo.bootstrap.DocumentManager} this
31777 * @param {File} file
31782 * Fire when xhr load exception
31783 * @param {Roo.bootstrap.DocumentManager} this
31784 * @param {XMLHttpRequest} xhr
31786 "exception" : true,
31788 * @event afterupload
31789 * Fire when xhr load exception
31790 * @param {Roo.bootstrap.DocumentManager} this
31791 * @param {XMLHttpRequest} xhr
31793 "afterupload" : true,
31796 * prepare the form data
31797 * @param {Roo.bootstrap.DocumentManager} this
31798 * @param {Object} formData
31803 * Fire when remove the file
31804 * @param {Roo.bootstrap.DocumentManager} this
31805 * @param {Object} file
31810 * Fire after refresh the file
31811 * @param {Roo.bootstrap.DocumentManager} this
31816 * Fire after click the image
31817 * @param {Roo.bootstrap.DocumentManager} this
31818 * @param {Object} file
31823 * Fire when upload a image and editable set to true
31824 * @param {Roo.bootstrap.DocumentManager} this
31825 * @param {Object} file
31829 * @event beforeselectfile
31830 * Fire before select file
31831 * @param {Roo.bootstrap.DocumentManager} this
31833 "beforeselectfile" : true,
31836 * Fire before process file
31837 * @param {Roo.bootstrap.DocumentManager} this
31838 * @param {Object} file
31842 * @event previewrendered
31843 * Fire when preview rendered
31844 * @param {Roo.bootstrap.DocumentManager} this
31845 * @param {Object} file
31847 "previewrendered" : true,
31850 "previewResize" : true
31855 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31864 paramName : 'imageUpload',
31865 toolTipName : 'filename',
31868 labelAlign : 'left',
31878 getAutoCreate : function()
31880 var managerWidget = {
31882 cls : 'roo-document-manager',
31886 cls : 'roo-document-manager-selector',
31891 cls : 'roo-document-manager-uploader',
31895 cls : 'roo-document-manager-upload-btn',
31896 html : '<i class="fa fa-plus"></i>'
31907 cls : 'column col-md-12',
31912 if(this.fieldLabel.length){
31917 cls : 'column col-md-12',
31918 html : this.fieldLabel
31922 cls : 'column col-md-12',
31927 if(this.labelAlign == 'left'){
31932 html : this.fieldLabel
31941 if(this.labelWidth > 12){
31942 content[0].style = "width: " + this.labelWidth + 'px';
31945 if(this.labelWidth < 13 && this.labelmd == 0){
31946 this.labelmd = this.labelWidth;
31949 if(this.labellg > 0){
31950 content[0].cls += ' col-lg-' + this.labellg;
31951 content[1].cls += ' col-lg-' + (12 - this.labellg);
31954 if(this.labelmd > 0){
31955 content[0].cls += ' col-md-' + this.labelmd;
31956 content[1].cls += ' col-md-' + (12 - this.labelmd);
31959 if(this.labelsm > 0){
31960 content[0].cls += ' col-sm-' + this.labelsm;
31961 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31964 if(this.labelxs > 0){
31965 content[0].cls += ' col-xs-' + this.labelxs;
31966 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31974 cls : 'row clearfix',
31982 initEvents : function()
31984 this.managerEl = this.el.select('.roo-document-manager', true).first();
31985 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31987 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31988 this.selectorEl.hide();
31991 this.selectorEl.attr('multiple', 'multiple');
31994 this.selectorEl.on('change', this.onFileSelected, this);
31996 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31997 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31999 this.uploader.on('click', this.onUploaderClick, this);
32001 this.renderProgressDialog();
32005 window.addEventListener("resize", function() { _this.refresh(); } );
32007 this.fireEvent('initial', this);
32010 renderProgressDialog : function()
32014 this.progressDialog = new Roo.bootstrap.Modal({
32015 cls : 'roo-document-manager-progress-dialog',
32016 allow_close : false,
32027 btnclick : function() {
32028 _this.uploadCancel();
32034 this.progressDialog.render(Roo.get(document.body));
32036 this.progress = new Roo.bootstrap.Progress({
32037 cls : 'roo-document-manager-progress',
32042 this.progress.render(this.progressDialog.getChildContainer());
32044 this.progressBar = new Roo.bootstrap.ProgressBar({
32045 cls : 'roo-document-manager-progress-bar',
32048 aria_valuemax : 12,
32052 this.progressBar.render(this.progress.getChildContainer());
32055 onUploaderClick : function(e)
32057 e.preventDefault();
32059 if(this.fireEvent('beforeselectfile', this) != false){
32060 this.selectorEl.dom.click();
32065 onFileSelected : function(e)
32067 e.preventDefault();
32069 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32073 Roo.each(this.selectorEl.dom.files, function(file){
32074 if(this.fireEvent('inspect', this, file) != false){
32075 this.files.push(file);
32085 this.selectorEl.dom.value = '';
32087 if(!this.files || !this.files.length){
32091 if(this.boxes > 0 && this.files.length > this.boxes){
32092 this.files = this.files.slice(0, this.boxes);
32095 this.uploader.show();
32097 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32098 this.uploader.hide();
32107 Roo.each(this.files, function(file){
32109 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32110 var f = this.renderPreview(file);
32115 if(file.type.indexOf('image') != -1){
32116 this.delegates.push(
32118 _this.process(file);
32119 }).createDelegate(this)
32127 _this.process(file);
32128 }).createDelegate(this)
32133 this.files = files;
32135 this.delegates = this.delegates.concat(docs);
32137 if(!this.delegates.length){
32142 this.progressBar.aria_valuemax = this.delegates.length;
32149 arrange : function()
32151 if(!this.delegates.length){
32152 this.progressDialog.hide();
32157 var delegate = this.delegates.shift();
32159 this.progressDialog.show();
32161 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32163 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32168 refresh : function()
32170 this.uploader.show();
32172 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32173 this.uploader.hide();
32176 Roo.isTouch ? this.closable(false) : this.closable(true);
32178 this.fireEvent('refresh', this);
32181 onRemove : function(e, el, o)
32183 e.preventDefault();
32185 this.fireEvent('remove', this, o);
32189 remove : function(o)
32193 Roo.each(this.files, function(file){
32194 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32203 this.files = files;
32210 Roo.each(this.files, function(file){
32215 file.target.remove();
32224 onClick : function(e, el, o)
32226 e.preventDefault();
32228 this.fireEvent('click', this, o);
32232 closable : function(closable)
32234 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32236 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32248 xhrOnLoad : function(xhr)
32250 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32254 if (xhr.readyState !== 4) {
32256 this.fireEvent('exception', this, xhr);
32260 var response = Roo.decode(xhr.responseText);
32262 if(!response.success){
32264 this.fireEvent('exception', this, xhr);
32268 var file = this.renderPreview(response.data);
32270 this.files.push(file);
32274 this.fireEvent('afterupload', this, xhr);
32278 xhrOnError : function(xhr)
32280 Roo.log('xhr on error');
32282 var response = Roo.decode(xhr.responseText);
32289 process : function(file)
32291 if(this.fireEvent('process', this, file) !== false){
32292 if(this.editable && file.type.indexOf('image') != -1){
32293 this.fireEvent('edit', this, file);
32297 this.uploadStart(file, false);
32304 uploadStart : function(file, crop)
32306 this.xhr = new XMLHttpRequest();
32308 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32313 file.xhr = this.xhr;
32315 this.managerEl.createChild({
32317 cls : 'roo-document-manager-loading',
32321 tooltip : file.name,
32322 cls : 'roo-document-manager-thumb',
32323 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32329 this.xhr.open(this.method, this.url, true);
32332 "Accept": "application/json",
32333 "Cache-Control": "no-cache",
32334 "X-Requested-With": "XMLHttpRequest"
32337 for (var headerName in headers) {
32338 var headerValue = headers[headerName];
32340 this.xhr.setRequestHeader(headerName, headerValue);
32346 this.xhr.onload = function()
32348 _this.xhrOnLoad(_this.xhr);
32351 this.xhr.onerror = function()
32353 _this.xhrOnError(_this.xhr);
32356 var formData = new FormData();
32358 formData.append('returnHTML', 'NO');
32361 formData.append('crop', crop);
32364 formData.append(this.paramName, file, file.name);
32371 if(this.fireEvent('prepare', this, formData, options) != false){
32373 if(options.manually){
32377 this.xhr.send(formData);
32381 this.uploadCancel();
32384 uploadCancel : function()
32390 this.delegates = [];
32392 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32399 renderPreview : function(file)
32401 if(typeof(file.target) != 'undefined' && file.target){
32405 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32407 var previewEl = this.managerEl.createChild({
32409 cls : 'roo-document-manager-preview',
32413 tooltip : file[this.toolTipName],
32414 cls : 'roo-document-manager-thumb',
32415 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32420 html : '<i class="fa fa-times-circle"></i>'
32425 var close = previewEl.select('button.close', true).first();
32427 close.on('click', this.onRemove, this, file);
32429 file.target = previewEl;
32431 var image = previewEl.select('img', true).first();
32435 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32437 image.on('click', this.onClick, this, file);
32439 this.fireEvent('previewrendered', this, file);
32445 onPreviewLoad : function(file, image)
32447 if(typeof(file.target) == 'undefined' || !file.target){
32451 var width = image.dom.naturalWidth || image.dom.width;
32452 var height = image.dom.naturalHeight || image.dom.height;
32454 if(!this.previewResize) {
32458 if(width > height){
32459 file.target.addClass('wide');
32463 file.target.addClass('tall');
32468 uploadFromSource : function(file, crop)
32470 this.xhr = new XMLHttpRequest();
32472 this.managerEl.createChild({
32474 cls : 'roo-document-manager-loading',
32478 tooltip : file.name,
32479 cls : 'roo-document-manager-thumb',
32480 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32486 this.xhr.open(this.method, this.url, true);
32489 "Accept": "application/json",
32490 "Cache-Control": "no-cache",
32491 "X-Requested-With": "XMLHttpRequest"
32494 for (var headerName in headers) {
32495 var headerValue = headers[headerName];
32497 this.xhr.setRequestHeader(headerName, headerValue);
32503 this.xhr.onload = function()
32505 _this.xhrOnLoad(_this.xhr);
32508 this.xhr.onerror = function()
32510 _this.xhrOnError(_this.xhr);
32513 var formData = new FormData();
32515 formData.append('returnHTML', 'NO');
32517 formData.append('crop', crop);
32519 if(typeof(file.filename) != 'undefined'){
32520 formData.append('filename', file.filename);
32523 if(typeof(file.mimetype) != 'undefined'){
32524 formData.append('mimetype', file.mimetype);
32529 if(this.fireEvent('prepare', this, formData) != false){
32530 this.xhr.send(formData);
32540 * @class Roo.bootstrap.DocumentViewer
32541 * @extends Roo.bootstrap.Component
32542 * Bootstrap DocumentViewer class
32543 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32544 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32547 * Create a new DocumentViewer
32548 * @param {Object} config The config object
32551 Roo.bootstrap.DocumentViewer = function(config){
32552 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32557 * Fire after initEvent
32558 * @param {Roo.bootstrap.DocumentViewer} this
32564 * @param {Roo.bootstrap.DocumentViewer} this
32569 * Fire after download button
32570 * @param {Roo.bootstrap.DocumentViewer} this
32575 * Fire after trash button
32576 * @param {Roo.bootstrap.DocumentViewer} this
32583 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32585 showDownload : true,
32589 getAutoCreate : function()
32593 cls : 'roo-document-viewer',
32597 cls : 'roo-document-viewer-body',
32601 cls : 'roo-document-viewer-thumb',
32605 cls : 'roo-document-viewer-image'
32613 cls : 'roo-document-viewer-footer',
32616 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32620 cls : 'btn-group roo-document-viewer-download',
32624 cls : 'btn btn-default',
32625 html : '<i class="fa fa-download"></i>'
32631 cls : 'btn-group roo-document-viewer-trash',
32635 cls : 'btn btn-default',
32636 html : '<i class="fa fa-trash"></i>'
32649 initEvents : function()
32651 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32652 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32654 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32655 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32657 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32658 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32660 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32661 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32663 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32664 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32666 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32667 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32669 this.bodyEl.on('click', this.onClick, this);
32670 this.downloadBtn.on('click', this.onDownload, this);
32671 this.trashBtn.on('click', this.onTrash, this);
32673 this.downloadBtn.hide();
32674 this.trashBtn.hide();
32676 if(this.showDownload){
32677 this.downloadBtn.show();
32680 if(this.showTrash){
32681 this.trashBtn.show();
32684 if(!this.showDownload && !this.showTrash) {
32685 this.footerEl.hide();
32690 initial : function()
32692 this.fireEvent('initial', this);
32696 onClick : function(e)
32698 e.preventDefault();
32700 this.fireEvent('click', this);
32703 onDownload : function(e)
32705 e.preventDefault();
32707 this.fireEvent('download', this);
32710 onTrash : function(e)
32712 e.preventDefault();
32714 this.fireEvent('trash', this);
32726 * @class Roo.bootstrap.NavProgressBar
32727 * @extends Roo.bootstrap.Component
32728 * Bootstrap NavProgressBar class
32731 * Create a new nav progress bar
32732 * @param {Object} config The config object
32735 Roo.bootstrap.NavProgressBar = function(config){
32736 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32738 this.bullets = this.bullets || [];
32740 // Roo.bootstrap.NavProgressBar.register(this);
32744 * Fires when the active item changes
32745 * @param {Roo.bootstrap.NavProgressBar} this
32746 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32747 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32754 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32759 getAutoCreate : function()
32761 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32765 cls : 'roo-navigation-bar-group',
32769 cls : 'roo-navigation-top-bar'
32773 cls : 'roo-navigation-bullets-bar',
32777 cls : 'roo-navigation-bar'
32784 cls : 'roo-navigation-bottom-bar'
32794 initEvents: function()
32799 onRender : function(ct, position)
32801 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32803 if(this.bullets.length){
32804 Roo.each(this.bullets, function(b){
32813 addItem : function(cfg)
32815 var item = new Roo.bootstrap.NavProgressItem(cfg);
32817 item.parentId = this.id;
32818 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32821 var top = new Roo.bootstrap.Element({
32823 cls : 'roo-navigation-bar-text'
32826 var bottom = new Roo.bootstrap.Element({
32828 cls : 'roo-navigation-bar-text'
32831 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32832 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32834 var topText = new Roo.bootstrap.Element({
32836 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32839 var bottomText = new Roo.bootstrap.Element({
32841 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32844 topText.onRender(top.el, null);
32845 bottomText.onRender(bottom.el, null);
32848 item.bottomEl = bottom;
32851 this.barItems.push(item);
32856 getActive : function()
32858 var active = false;
32860 Roo.each(this.barItems, function(v){
32862 if (!v.isActive()) {
32874 setActiveItem : function(item)
32878 Roo.each(this.barItems, function(v){
32879 if (v.rid == item.rid) {
32883 if (v.isActive()) {
32884 v.setActive(false);
32889 item.setActive(true);
32891 this.fireEvent('changed', this, item, prev);
32894 getBarItem: function(rid)
32898 Roo.each(this.barItems, function(e) {
32899 if (e.rid != rid) {
32910 indexOfItem : function(item)
32914 Roo.each(this.barItems, function(v, i){
32916 if (v.rid != item.rid) {
32927 setActiveNext : function()
32929 var i = this.indexOfItem(this.getActive());
32931 if (i > this.barItems.length) {
32935 this.setActiveItem(this.barItems[i+1]);
32938 setActivePrev : function()
32940 var i = this.indexOfItem(this.getActive());
32946 this.setActiveItem(this.barItems[i-1]);
32949 format : function()
32951 if(!this.barItems.length){
32955 var width = 100 / this.barItems.length;
32957 Roo.each(this.barItems, function(i){
32958 i.el.setStyle('width', width + '%');
32959 i.topEl.el.setStyle('width', width + '%');
32960 i.bottomEl.el.setStyle('width', width + '%');
32969 * Nav Progress Item
32974 * @class Roo.bootstrap.NavProgressItem
32975 * @extends Roo.bootstrap.Component
32976 * Bootstrap NavProgressItem class
32977 * @cfg {String} rid the reference id
32978 * @cfg {Boolean} active (true|false) Is item active default false
32979 * @cfg {Boolean} disabled (true|false) Is item active default false
32980 * @cfg {String} html
32981 * @cfg {String} position (top|bottom) text position default bottom
32982 * @cfg {String} icon show icon instead of number
32985 * Create a new NavProgressItem
32986 * @param {Object} config The config object
32988 Roo.bootstrap.NavProgressItem = function(config){
32989 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32994 * The raw click event for the entire grid.
32995 * @param {Roo.bootstrap.NavProgressItem} this
32996 * @param {Roo.EventObject} e
33003 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33009 position : 'bottom',
33012 getAutoCreate : function()
33014 var iconCls = 'roo-navigation-bar-item-icon';
33016 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33020 cls: 'roo-navigation-bar-item',
33030 cfg.cls += ' active';
33033 cfg.cls += ' disabled';
33039 disable : function()
33041 this.setDisabled(true);
33044 enable : function()
33046 this.setDisabled(false);
33049 initEvents: function()
33051 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33053 this.iconEl.on('click', this.onClick, this);
33056 onClick : function(e)
33058 e.preventDefault();
33064 if(this.fireEvent('click', this, e) === false){
33068 this.parent().setActiveItem(this);
33071 isActive: function ()
33073 return this.active;
33076 setActive : function(state)
33078 if(this.active == state){
33082 this.active = state;
33085 this.el.addClass('active');
33089 this.el.removeClass('active');
33094 setDisabled : function(state)
33096 if(this.disabled == state){
33100 this.disabled = state;
33103 this.el.addClass('disabled');
33107 this.el.removeClass('disabled');
33110 tooltipEl : function()
33112 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33125 * @class Roo.bootstrap.FieldLabel
33126 * @extends Roo.bootstrap.Component
33127 * Bootstrap FieldLabel class
33128 * @cfg {String} html contents of the element
33129 * @cfg {String} tag tag of the element default label
33130 * @cfg {String} cls class of the element
33131 * @cfg {String} target label target
33132 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33133 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33134 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33135 * @cfg {String} iconTooltip default "This field is required"
33136 * @cfg {String} indicatorpos (left|right) default left
33139 * Create a new FieldLabel
33140 * @param {Object} config The config object
33143 Roo.bootstrap.FieldLabel = function(config){
33144 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33149 * Fires after the field has been marked as invalid.
33150 * @param {Roo.form.FieldLabel} this
33151 * @param {String} msg The validation message
33156 * Fires after the field has been validated with no errors.
33157 * @param {Roo.form.FieldLabel} this
33163 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33170 invalidClass : 'has-warning',
33171 validClass : 'has-success',
33172 iconTooltip : 'This field is required',
33173 indicatorpos : 'left',
33175 getAutoCreate : function(){
33178 if (!this.allowBlank) {
33184 cls : 'roo-bootstrap-field-label ' + this.cls,
33189 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33190 tooltip : this.iconTooltip
33199 if(this.indicatorpos == 'right'){
33202 cls : 'roo-bootstrap-field-label ' + this.cls,
33211 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33212 tooltip : this.iconTooltip
33221 initEvents: function()
33223 Roo.bootstrap.Element.superclass.initEvents.call(this);
33225 this.indicator = this.indicatorEl();
33227 if(this.indicator){
33228 this.indicator.removeClass('visible');
33229 this.indicator.addClass('invisible');
33232 Roo.bootstrap.FieldLabel.register(this);
33235 indicatorEl : function()
33237 var indicator = this.el.select('i.roo-required-indicator',true).first();
33248 * Mark this field as valid
33250 markValid : function()
33252 if(this.indicator){
33253 this.indicator.removeClass('visible');
33254 this.indicator.addClass('invisible');
33256 if (Roo.bootstrap.version == 3) {
33257 this.el.removeClass(this.invalidClass);
33258 this.el.addClass(this.validClass);
33260 this.el.removeClass('is-invalid');
33261 this.el.addClass('is-valid');
33265 this.fireEvent('valid', this);
33269 * Mark this field as invalid
33270 * @param {String} msg The validation message
33272 markInvalid : function(msg)
33274 if(this.indicator){
33275 this.indicator.removeClass('invisible');
33276 this.indicator.addClass('visible');
33278 if (Roo.bootstrap.version == 3) {
33279 this.el.removeClass(this.validClass);
33280 this.el.addClass(this.invalidClass);
33282 this.el.removeClass('is-valid');
33283 this.el.addClass('is-invalid');
33287 this.fireEvent('invalid', this, msg);
33293 Roo.apply(Roo.bootstrap.FieldLabel, {
33298 * register a FieldLabel Group
33299 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33301 register : function(label)
33303 if(this.groups.hasOwnProperty(label.target)){
33307 this.groups[label.target] = label;
33311 * fetch a FieldLabel Group based on the target
33312 * @param {string} target
33313 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33315 get: function(target) {
33316 if (typeof(this.groups[target]) == 'undefined') {
33320 return this.groups[target] ;
33329 * page DateSplitField.
33335 * @class Roo.bootstrap.DateSplitField
33336 * @extends Roo.bootstrap.Component
33337 * Bootstrap DateSplitField class
33338 * @cfg {string} fieldLabel - the label associated
33339 * @cfg {Number} labelWidth set the width of label (0-12)
33340 * @cfg {String} labelAlign (top|left)
33341 * @cfg {Boolean} dayAllowBlank (true|false) default false
33342 * @cfg {Boolean} monthAllowBlank (true|false) default false
33343 * @cfg {Boolean} yearAllowBlank (true|false) default false
33344 * @cfg {string} dayPlaceholder
33345 * @cfg {string} monthPlaceholder
33346 * @cfg {string} yearPlaceholder
33347 * @cfg {string} dayFormat default 'd'
33348 * @cfg {string} monthFormat default 'm'
33349 * @cfg {string} yearFormat default 'Y'
33350 * @cfg {Number} labellg set the width of label (1-12)
33351 * @cfg {Number} labelmd set the width of label (1-12)
33352 * @cfg {Number} labelsm set the width of label (1-12)
33353 * @cfg {Number} labelxs set the width of label (1-12)
33357 * Create a new DateSplitField
33358 * @param {Object} config The config object
33361 Roo.bootstrap.DateSplitField = function(config){
33362 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33368 * getting the data of years
33369 * @param {Roo.bootstrap.DateSplitField} this
33370 * @param {Object} years
33375 * getting the data of days
33376 * @param {Roo.bootstrap.DateSplitField} this
33377 * @param {Object} days
33382 * Fires after the field has been marked as invalid.
33383 * @param {Roo.form.Field} this
33384 * @param {String} msg The validation message
33389 * Fires after the field has been validated with no errors.
33390 * @param {Roo.form.Field} this
33396 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33399 labelAlign : 'top',
33401 dayAllowBlank : false,
33402 monthAllowBlank : false,
33403 yearAllowBlank : false,
33404 dayPlaceholder : '',
33405 monthPlaceholder : '',
33406 yearPlaceholder : '',
33410 isFormField : true,
33416 getAutoCreate : function()
33420 cls : 'row roo-date-split-field-group',
33425 cls : 'form-hidden-field roo-date-split-field-group-value',
33431 var labelCls = 'col-md-12';
33432 var contentCls = 'col-md-4';
33434 if(this.fieldLabel){
33438 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33442 html : this.fieldLabel
33447 if(this.labelAlign == 'left'){
33449 if(this.labelWidth > 12){
33450 label.style = "width: " + this.labelWidth + 'px';
33453 if(this.labelWidth < 13 && this.labelmd == 0){
33454 this.labelmd = this.labelWidth;
33457 if(this.labellg > 0){
33458 labelCls = ' col-lg-' + this.labellg;
33459 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33462 if(this.labelmd > 0){
33463 labelCls = ' col-md-' + this.labelmd;
33464 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33467 if(this.labelsm > 0){
33468 labelCls = ' col-sm-' + this.labelsm;
33469 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33472 if(this.labelxs > 0){
33473 labelCls = ' col-xs-' + this.labelxs;
33474 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33478 label.cls += ' ' + labelCls;
33480 cfg.cn.push(label);
33483 Roo.each(['day', 'month', 'year'], function(t){
33486 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33493 inputEl: function ()
33495 return this.el.select('.roo-date-split-field-group-value', true).first();
33498 onRender : function(ct, position)
33502 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33504 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33506 this.dayField = new Roo.bootstrap.ComboBox({
33507 allowBlank : this.dayAllowBlank,
33508 alwaysQuery : true,
33509 displayField : 'value',
33512 forceSelection : true,
33514 placeholder : this.dayPlaceholder,
33515 selectOnFocus : true,
33516 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33517 triggerAction : 'all',
33519 valueField : 'value',
33520 store : new Roo.data.SimpleStore({
33521 data : (function() {
33523 _this.fireEvent('days', _this, days);
33526 fields : [ 'value' ]
33529 select : function (_self, record, index)
33531 _this.setValue(_this.getValue());
33536 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33538 this.monthField = new Roo.bootstrap.MonthField({
33539 after : '<i class=\"fa fa-calendar\"></i>',
33540 allowBlank : this.monthAllowBlank,
33541 placeholder : this.monthPlaceholder,
33544 render : function (_self)
33546 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33547 e.preventDefault();
33551 select : function (_self, oldvalue, newvalue)
33553 _this.setValue(_this.getValue());
33558 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33560 this.yearField = new Roo.bootstrap.ComboBox({
33561 allowBlank : this.yearAllowBlank,
33562 alwaysQuery : true,
33563 displayField : 'value',
33566 forceSelection : true,
33568 placeholder : this.yearPlaceholder,
33569 selectOnFocus : true,
33570 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33571 triggerAction : 'all',
33573 valueField : 'value',
33574 store : new Roo.data.SimpleStore({
33575 data : (function() {
33577 _this.fireEvent('years', _this, years);
33580 fields : [ 'value' ]
33583 select : function (_self, record, index)
33585 _this.setValue(_this.getValue());
33590 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33593 setValue : function(v, format)
33595 this.inputEl.dom.value = v;
33597 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33599 var d = Date.parseDate(v, f);
33606 this.setDay(d.format(this.dayFormat));
33607 this.setMonth(d.format(this.monthFormat));
33608 this.setYear(d.format(this.yearFormat));
33615 setDay : function(v)
33617 this.dayField.setValue(v);
33618 this.inputEl.dom.value = this.getValue();
33623 setMonth : function(v)
33625 this.monthField.setValue(v, true);
33626 this.inputEl.dom.value = this.getValue();
33631 setYear : function(v)
33633 this.yearField.setValue(v);
33634 this.inputEl.dom.value = this.getValue();
33639 getDay : function()
33641 return this.dayField.getValue();
33644 getMonth : function()
33646 return this.monthField.getValue();
33649 getYear : function()
33651 return this.yearField.getValue();
33654 getValue : function()
33656 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33658 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33668 this.inputEl.dom.value = '';
33673 validate : function()
33675 var d = this.dayField.validate();
33676 var m = this.monthField.validate();
33677 var y = this.yearField.validate();
33682 (!this.dayAllowBlank && !d) ||
33683 (!this.monthAllowBlank && !m) ||
33684 (!this.yearAllowBlank && !y)
33689 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33698 this.markInvalid();
33703 markValid : function()
33706 var label = this.el.select('label', true).first();
33707 var icon = this.el.select('i.fa-star', true).first();
33713 this.fireEvent('valid', this);
33717 * Mark this field as invalid
33718 * @param {String} msg The validation message
33720 markInvalid : function(msg)
33723 var label = this.el.select('label', true).first();
33724 var icon = this.el.select('i.fa-star', true).first();
33726 if(label && !icon){
33727 this.el.select('.roo-date-split-field-label', true).createChild({
33729 cls : 'text-danger fa fa-lg fa-star',
33730 tooltip : 'This field is required',
33731 style : 'margin-right:5px;'
33735 this.fireEvent('invalid', this, msg);
33738 clearInvalid : function()
33740 var label = this.el.select('label', true).first();
33741 var icon = this.el.select('i.fa-star', true).first();
33747 this.fireEvent('valid', this);
33750 getName: function()
33760 * http://masonry.desandro.com
33762 * The idea is to render all the bricks based on vertical width...
33764 * The original code extends 'outlayer' - we might need to use that....
33770 * @class Roo.bootstrap.LayoutMasonry
33771 * @extends Roo.bootstrap.Component
33772 * Bootstrap Layout Masonry class
33775 * Create a new Element
33776 * @param {Object} config The config object
33779 Roo.bootstrap.LayoutMasonry = function(config){
33781 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33785 Roo.bootstrap.LayoutMasonry.register(this);
33791 * Fire after layout the items
33792 * @param {Roo.bootstrap.LayoutMasonry} this
33793 * @param {Roo.EventObject} e
33800 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33803 * @cfg {Boolean} isLayoutInstant = no animation?
33805 isLayoutInstant : false, // needed?
33808 * @cfg {Number} boxWidth width of the columns
33813 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33818 * @cfg {Number} padWidth padding below box..
33823 * @cfg {Number} gutter gutter width..
33828 * @cfg {Number} maxCols maximum number of columns
33834 * @cfg {Boolean} isAutoInitial defalut true
33836 isAutoInitial : true,
33841 * @cfg {Boolean} isHorizontal defalut false
33843 isHorizontal : false,
33845 currentSize : null,
33851 bricks: null, //CompositeElement
33855 _isLayoutInited : false,
33857 // isAlternative : false, // only use for vertical layout...
33860 * @cfg {Number} alternativePadWidth padding below box..
33862 alternativePadWidth : 50,
33864 selectedBrick : [],
33866 getAutoCreate : function(){
33868 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33872 cls: 'blog-masonary-wrapper ' + this.cls,
33874 cls : 'mas-boxes masonary'
33881 getChildContainer: function( )
33883 if (this.boxesEl) {
33884 return this.boxesEl;
33887 this.boxesEl = this.el.select('.mas-boxes').first();
33889 return this.boxesEl;
33893 initEvents : function()
33897 if(this.isAutoInitial){
33898 Roo.log('hook children rendered');
33899 this.on('childrenrendered', function() {
33900 Roo.log('children rendered');
33906 initial : function()
33908 this.selectedBrick = [];
33910 this.currentSize = this.el.getBox(true);
33912 Roo.EventManager.onWindowResize(this.resize, this);
33914 if(!this.isAutoInitial){
33922 //this.layout.defer(500,this);
33926 resize : function()
33928 var cs = this.el.getBox(true);
33931 this.currentSize.width == cs.width &&
33932 this.currentSize.x == cs.x &&
33933 this.currentSize.height == cs.height &&
33934 this.currentSize.y == cs.y
33936 Roo.log("no change in with or X or Y");
33940 this.currentSize = cs;
33946 layout : function()
33948 this._resetLayout();
33950 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33952 this.layoutItems( isInstant );
33954 this._isLayoutInited = true;
33956 this.fireEvent('layout', this);
33960 _resetLayout : function()
33962 if(this.isHorizontal){
33963 this.horizontalMeasureColumns();
33967 this.verticalMeasureColumns();
33971 verticalMeasureColumns : function()
33973 this.getContainerWidth();
33975 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33976 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33980 var boxWidth = this.boxWidth + this.padWidth;
33982 if(this.containerWidth < this.boxWidth){
33983 boxWidth = this.containerWidth
33986 var containerWidth = this.containerWidth;
33988 var cols = Math.floor(containerWidth / boxWidth);
33990 this.cols = Math.max( cols, 1 );
33992 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33994 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33996 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33998 this.colWidth = boxWidth + avail - this.padWidth;
34000 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34001 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34004 horizontalMeasureColumns : function()
34006 this.getContainerWidth();
34008 var boxWidth = this.boxWidth;
34010 if(this.containerWidth < boxWidth){
34011 boxWidth = this.containerWidth;
34014 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34016 this.el.setHeight(boxWidth);
34020 getContainerWidth : function()
34022 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34025 layoutItems : function( isInstant )
34027 Roo.log(this.bricks);
34029 var items = Roo.apply([], this.bricks);
34031 if(this.isHorizontal){
34032 this._horizontalLayoutItems( items , isInstant );
34036 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34037 // this._verticalAlternativeLayoutItems( items , isInstant );
34041 this._verticalLayoutItems( items , isInstant );
34045 _verticalLayoutItems : function ( items , isInstant)
34047 if ( !items || !items.length ) {
34052 ['xs', 'xs', 'xs', 'tall'],
34053 ['xs', 'xs', 'tall'],
34054 ['xs', 'xs', 'sm'],
34055 ['xs', 'xs', 'xs'],
34061 ['sm', 'xs', 'xs'],
34065 ['tall', 'xs', 'xs', 'xs'],
34066 ['tall', 'xs', 'xs'],
34078 Roo.each(items, function(item, k){
34080 switch (item.size) {
34081 // these layouts take up a full box,
34092 boxes.push([item]);
34115 var filterPattern = function(box, length)
34123 var pattern = box.slice(0, length);
34127 Roo.each(pattern, function(i){
34128 format.push(i.size);
34131 Roo.each(standard, function(s){
34133 if(String(s) != String(format)){
34142 if(!match && length == 1){
34147 filterPattern(box, length - 1);
34151 queue.push(pattern);
34153 box = box.slice(length, box.length);
34155 filterPattern(box, 4);
34161 Roo.each(boxes, function(box, k){
34167 if(box.length == 1){
34172 filterPattern(box, 4);
34176 this._processVerticalLayoutQueue( queue, isInstant );
34180 // _verticalAlternativeLayoutItems : function( items , isInstant )
34182 // if ( !items || !items.length ) {
34186 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34190 _horizontalLayoutItems : function ( items , isInstant)
34192 if ( !items || !items.length || items.length < 3) {
34198 var eItems = items.slice(0, 3);
34200 items = items.slice(3, items.length);
34203 ['xs', 'xs', 'xs', 'wide'],
34204 ['xs', 'xs', 'wide'],
34205 ['xs', 'xs', 'sm'],
34206 ['xs', 'xs', 'xs'],
34212 ['sm', 'xs', 'xs'],
34216 ['wide', 'xs', 'xs', 'xs'],
34217 ['wide', 'xs', 'xs'],
34230 Roo.each(items, function(item, k){
34232 switch (item.size) {
34243 boxes.push([item]);
34267 var filterPattern = function(box, length)
34275 var pattern = box.slice(0, length);
34279 Roo.each(pattern, function(i){
34280 format.push(i.size);
34283 Roo.each(standard, function(s){
34285 if(String(s) != String(format)){
34294 if(!match && length == 1){
34299 filterPattern(box, length - 1);
34303 queue.push(pattern);
34305 box = box.slice(length, box.length);
34307 filterPattern(box, 4);
34313 Roo.each(boxes, function(box, k){
34319 if(box.length == 1){
34324 filterPattern(box, 4);
34331 var pos = this.el.getBox(true);
34335 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34337 var hit_end = false;
34339 Roo.each(queue, function(box){
34343 Roo.each(box, function(b){
34345 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34355 Roo.each(box, function(b){
34357 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34360 mx = Math.max(mx, b.x);
34364 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34368 Roo.each(box, function(b){
34370 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34384 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34387 /** Sets position of item in DOM
34388 * @param {Element} item
34389 * @param {Number} x - horizontal position
34390 * @param {Number} y - vertical position
34391 * @param {Boolean} isInstant - disables transitions
34393 _processVerticalLayoutQueue : function( queue, isInstant )
34395 var pos = this.el.getBox(true);
34400 for (var i = 0; i < this.cols; i++){
34404 Roo.each(queue, function(box, k){
34406 var col = k % this.cols;
34408 Roo.each(box, function(b,kk){
34410 b.el.position('absolute');
34412 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34413 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34415 if(b.size == 'md-left' || b.size == 'md-right'){
34416 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34417 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34420 b.el.setWidth(width);
34421 b.el.setHeight(height);
34423 b.el.select('iframe',true).setSize(width,height);
34427 for (var i = 0; i < this.cols; i++){
34429 if(maxY[i] < maxY[col]){
34434 col = Math.min(col, i);
34438 x = pos.x + col * (this.colWidth + this.padWidth);
34442 var positions = [];
34444 switch (box.length){
34446 positions = this.getVerticalOneBoxColPositions(x, y, box);
34449 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34452 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34455 positions = this.getVerticalFourBoxColPositions(x, y, box);
34461 Roo.each(box, function(b,kk){
34463 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34465 var sz = b.el.getSize();
34467 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34475 for (var i = 0; i < this.cols; i++){
34476 mY = Math.max(mY, maxY[i]);
34479 this.el.setHeight(mY - pos.y);
34483 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34485 // var pos = this.el.getBox(true);
34488 // var maxX = pos.right;
34490 // var maxHeight = 0;
34492 // Roo.each(items, function(item, k){
34496 // item.el.position('absolute');
34498 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34500 // item.el.setWidth(width);
34502 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34504 // item.el.setHeight(height);
34507 // item.el.setXY([x, y], isInstant ? false : true);
34509 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34512 // y = y + height + this.alternativePadWidth;
34514 // maxHeight = maxHeight + height + this.alternativePadWidth;
34518 // this.el.setHeight(maxHeight);
34522 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34524 var pos = this.el.getBox(true);
34529 var maxX = pos.right;
34531 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34533 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34535 Roo.each(queue, function(box, k){
34537 Roo.each(box, function(b, kk){
34539 b.el.position('absolute');
34541 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34542 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34544 if(b.size == 'md-left' || b.size == 'md-right'){
34545 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34546 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34549 b.el.setWidth(width);
34550 b.el.setHeight(height);
34558 var positions = [];
34560 switch (box.length){
34562 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34565 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34568 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34571 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34577 Roo.each(box, function(b,kk){
34579 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34581 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34589 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34591 Roo.each(eItems, function(b,k){
34593 b.size = (k == 0) ? 'sm' : 'xs';
34594 b.x = (k == 0) ? 2 : 1;
34595 b.y = (k == 0) ? 2 : 1;
34597 b.el.position('absolute');
34599 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34601 b.el.setWidth(width);
34603 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34605 b.el.setHeight(height);
34609 var positions = [];
34612 x : maxX - this.unitWidth * 2 - this.gutter,
34617 x : maxX - this.unitWidth,
34618 y : minY + (this.unitWidth + this.gutter) * 2
34622 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34626 Roo.each(eItems, function(b,k){
34628 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34634 getVerticalOneBoxColPositions : function(x, y, box)
34638 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34640 if(box[0].size == 'md-left'){
34644 if(box[0].size == 'md-right'){
34649 x : x + (this.unitWidth + this.gutter) * rand,
34656 getVerticalTwoBoxColPositions : function(x, y, box)
34660 if(box[0].size == 'xs'){
34664 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34668 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34682 x : x + (this.unitWidth + this.gutter) * 2,
34683 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34690 getVerticalThreeBoxColPositions : function(x, y, box)
34694 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34702 x : x + (this.unitWidth + this.gutter) * 1,
34707 x : x + (this.unitWidth + this.gutter) * 2,
34715 if(box[0].size == 'xs' && box[1].size == 'xs'){
34724 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34728 x : x + (this.unitWidth + this.gutter) * 1,
34742 x : x + (this.unitWidth + this.gutter) * 2,
34747 x : x + (this.unitWidth + this.gutter) * 2,
34748 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34755 getVerticalFourBoxColPositions : function(x, y, box)
34759 if(box[0].size == 'xs'){
34768 y : y + (this.unitHeight + this.gutter) * 1
34773 y : y + (this.unitHeight + this.gutter) * 2
34777 x : x + (this.unitWidth + this.gutter) * 1,
34791 x : x + (this.unitWidth + this.gutter) * 2,
34796 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34797 y : y + (this.unitHeight + this.gutter) * 1
34801 x : x + (this.unitWidth + this.gutter) * 2,
34802 y : y + (this.unitWidth + this.gutter) * 2
34809 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34813 if(box[0].size == 'md-left'){
34815 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34822 if(box[0].size == 'md-right'){
34824 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34825 y : minY + (this.unitWidth + this.gutter) * 1
34831 var rand = Math.floor(Math.random() * (4 - box[0].y));
34834 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34835 y : minY + (this.unitWidth + this.gutter) * rand
34842 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34846 if(box[0].size == 'xs'){
34849 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34854 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34855 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34863 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34868 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34869 y : minY + (this.unitWidth + this.gutter) * 2
34876 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34880 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34883 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34888 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34889 y : minY + (this.unitWidth + this.gutter) * 1
34893 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34894 y : minY + (this.unitWidth + this.gutter) * 2
34901 if(box[0].size == 'xs' && box[1].size == 'xs'){
34904 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34909 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34914 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34915 y : minY + (this.unitWidth + this.gutter) * 1
34923 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34928 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34929 y : minY + (this.unitWidth + this.gutter) * 2
34933 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34934 y : minY + (this.unitWidth + this.gutter) * 2
34941 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34945 if(box[0].size == 'xs'){
34948 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34953 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34958 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),
34963 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34964 y : minY + (this.unitWidth + this.gutter) * 1
34972 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34977 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34978 y : minY + (this.unitWidth + this.gutter) * 2
34982 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34983 y : minY + (this.unitWidth + this.gutter) * 2
34987 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),
34988 y : minY + (this.unitWidth + this.gutter) * 2
34996 * remove a Masonry Brick
34997 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34999 removeBrick : function(brick_id)
35005 for (var i = 0; i<this.bricks.length; i++) {
35006 if (this.bricks[i].id == brick_id) {
35007 this.bricks.splice(i,1);
35008 this.el.dom.removeChild(Roo.get(brick_id).dom);
35015 * adds a Masonry Brick
35016 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35018 addBrick : function(cfg)
35020 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35021 //this.register(cn);
35022 cn.parentId = this.id;
35023 cn.render(this.el);
35028 * register a Masonry Brick
35029 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35032 register : function(brick)
35034 this.bricks.push(brick);
35035 brick.masonryId = this.id;
35039 * clear all the Masonry Brick
35041 clearAll : function()
35044 //this.getChildContainer().dom.innerHTML = "";
35045 this.el.dom.innerHTML = '';
35048 getSelected : function()
35050 if (!this.selectedBrick) {
35054 return this.selectedBrick;
35058 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35062 * register a Masonry Layout
35063 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35066 register : function(layout)
35068 this.groups[layout.id] = layout;
35071 * fetch a Masonry Layout based on the masonry layout ID
35072 * @param {string} the masonry layout to add
35073 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35076 get: function(layout_id) {
35077 if (typeof(this.groups[layout_id]) == 'undefined') {
35080 return this.groups[layout_id] ;
35092 * http://masonry.desandro.com
35094 * The idea is to render all the bricks based on vertical width...
35096 * The original code extends 'outlayer' - we might need to use that....
35102 * @class Roo.bootstrap.LayoutMasonryAuto
35103 * @extends Roo.bootstrap.Component
35104 * Bootstrap Layout Masonry class
35107 * Create a new Element
35108 * @param {Object} config The config object
35111 Roo.bootstrap.LayoutMasonryAuto = function(config){
35112 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35115 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35118 * @cfg {Boolean} isFitWidth - resize the width..
35120 isFitWidth : false, // options..
35122 * @cfg {Boolean} isOriginLeft = left align?
35124 isOriginLeft : true,
35126 * @cfg {Boolean} isOriginTop = top align?
35128 isOriginTop : false,
35130 * @cfg {Boolean} isLayoutInstant = no animation?
35132 isLayoutInstant : false, // needed?
35134 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35136 isResizingContainer : true,
35138 * @cfg {Number} columnWidth width of the columns
35144 * @cfg {Number} maxCols maximum number of columns
35149 * @cfg {Number} padHeight padding below box..
35155 * @cfg {Boolean} isAutoInitial defalut true
35158 isAutoInitial : true,
35164 initialColumnWidth : 0,
35165 currentSize : null,
35167 colYs : null, // array.
35174 bricks: null, //CompositeElement
35175 cols : 0, // array?
35176 // element : null, // wrapped now this.el
35177 _isLayoutInited : null,
35180 getAutoCreate : function(){
35184 cls: 'blog-masonary-wrapper ' + this.cls,
35186 cls : 'mas-boxes masonary'
35193 getChildContainer: function( )
35195 if (this.boxesEl) {
35196 return this.boxesEl;
35199 this.boxesEl = this.el.select('.mas-boxes').first();
35201 return this.boxesEl;
35205 initEvents : function()
35209 if(this.isAutoInitial){
35210 Roo.log('hook children rendered');
35211 this.on('childrenrendered', function() {
35212 Roo.log('children rendered');
35219 initial : function()
35221 this.reloadItems();
35223 this.currentSize = this.el.getBox(true);
35225 /// was window resize... - let's see if this works..
35226 Roo.EventManager.onWindowResize(this.resize, this);
35228 if(!this.isAutoInitial){
35233 this.layout.defer(500,this);
35236 reloadItems: function()
35238 this.bricks = this.el.select('.masonry-brick', true);
35240 this.bricks.each(function(b) {
35241 //Roo.log(b.getSize());
35242 if (!b.attr('originalwidth')) {
35243 b.attr('originalwidth', b.getSize().width);
35248 Roo.log(this.bricks.elements.length);
35251 resize : function()
35254 var cs = this.el.getBox(true);
35256 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35257 Roo.log("no change in with or X");
35260 this.currentSize = cs;
35264 layout : function()
35267 this._resetLayout();
35268 //this._manageStamps();
35270 // don't animate first layout
35271 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35272 this.layoutItems( isInstant );
35274 // flag for initalized
35275 this._isLayoutInited = true;
35278 layoutItems : function( isInstant )
35280 //var items = this._getItemsForLayout( this.items );
35281 // original code supports filtering layout items.. we just ignore it..
35283 this._layoutItems( this.bricks , isInstant );
35285 this._postLayout();
35287 _layoutItems : function ( items , isInstant)
35289 //this.fireEvent( 'layout', this, items );
35292 if ( !items || !items.elements.length ) {
35293 // no items, emit event with empty array
35298 items.each(function(item) {
35299 Roo.log("layout item");
35301 // get x/y object from method
35302 var position = this._getItemLayoutPosition( item );
35304 position.item = item;
35305 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35306 queue.push( position );
35309 this._processLayoutQueue( queue );
35311 /** Sets position of item in DOM
35312 * @param {Element} item
35313 * @param {Number} x - horizontal position
35314 * @param {Number} y - vertical position
35315 * @param {Boolean} isInstant - disables transitions
35317 _processLayoutQueue : function( queue )
35319 for ( var i=0, len = queue.length; i < len; i++ ) {
35320 var obj = queue[i];
35321 obj.item.position('absolute');
35322 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35328 * Any logic you want to do after each layout,
35329 * i.e. size the container
35331 _postLayout : function()
35333 this.resizeContainer();
35336 resizeContainer : function()
35338 if ( !this.isResizingContainer ) {
35341 var size = this._getContainerSize();
35343 this.el.setSize(size.width,size.height);
35344 this.boxesEl.setSize(size.width,size.height);
35350 _resetLayout : function()
35352 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35353 this.colWidth = this.el.getWidth();
35354 //this.gutter = this.el.getWidth();
35356 this.measureColumns();
35362 this.colYs.push( 0 );
35368 measureColumns : function()
35370 this.getContainerWidth();
35371 // if columnWidth is 0, default to outerWidth of first item
35372 if ( !this.columnWidth ) {
35373 var firstItem = this.bricks.first();
35374 Roo.log(firstItem);
35375 this.columnWidth = this.containerWidth;
35376 if (firstItem && firstItem.attr('originalwidth') ) {
35377 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35379 // columnWidth fall back to item of first element
35380 Roo.log("set column width?");
35381 this.initialColumnWidth = this.columnWidth ;
35383 // if first elem has no width, default to size of container
35388 if (this.initialColumnWidth) {
35389 this.columnWidth = this.initialColumnWidth;
35394 // column width is fixed at the top - however if container width get's smaller we should
35397 // this bit calcs how man columns..
35399 var columnWidth = this.columnWidth += this.gutter;
35401 // calculate columns
35402 var containerWidth = this.containerWidth + this.gutter;
35404 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35405 // fix rounding errors, typically with gutters
35406 var excess = columnWidth - containerWidth % columnWidth;
35409 // if overshoot is less than a pixel, round up, otherwise floor it
35410 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35411 cols = Math[ mathMethod ]( cols );
35412 this.cols = Math.max( cols, 1 );
35413 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35415 // padding positioning..
35416 var totalColWidth = this.cols * this.columnWidth;
35417 var padavail = this.containerWidth - totalColWidth;
35418 // so for 2 columns - we need 3 'pads'
35420 var padNeeded = (1+this.cols) * this.padWidth;
35422 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35424 this.columnWidth += padExtra
35425 //this.padWidth = Math.floor(padavail / ( this.cols));
35427 // adjust colum width so that padding is fixed??
35429 // we have 3 columns ... total = width * 3
35430 // we have X left over... that should be used by
35432 //if (this.expandC) {
35440 getContainerWidth : function()
35442 /* // container is parent if fit width
35443 var container = this.isFitWidth ? this.element.parentNode : this.element;
35444 // check that this.size and size are there
35445 // IE8 triggers resize on body size change, so they might not be
35447 var size = getSize( container ); //FIXME
35448 this.containerWidth = size && size.innerWidth; //FIXME
35451 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35455 _getItemLayoutPosition : function( item ) // what is item?
35457 // we resize the item to our columnWidth..
35459 item.setWidth(this.columnWidth);
35460 item.autoBoxAdjust = false;
35462 var sz = item.getSize();
35464 // how many columns does this brick span
35465 var remainder = this.containerWidth % this.columnWidth;
35467 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35468 // round if off by 1 pixel, otherwise use ceil
35469 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35470 colSpan = Math.min( colSpan, this.cols );
35472 // normally this should be '1' as we dont' currently allow multi width columns..
35474 var colGroup = this._getColGroup( colSpan );
35475 // get the minimum Y value from the columns
35476 var minimumY = Math.min.apply( Math, colGroup );
35477 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35479 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35481 // position the brick
35483 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35484 y: this.currentSize.y + minimumY + this.padHeight
35488 // apply setHeight to necessary columns
35489 var setHeight = minimumY + sz.height + this.padHeight;
35490 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35492 var setSpan = this.cols + 1 - colGroup.length;
35493 for ( var i = 0; i < setSpan; i++ ) {
35494 this.colYs[ shortColIndex + i ] = setHeight ;
35501 * @param {Number} colSpan - number of columns the element spans
35502 * @returns {Array} colGroup
35504 _getColGroup : function( colSpan )
35506 if ( colSpan < 2 ) {
35507 // if brick spans only one column, use all the column Ys
35512 // how many different places could this brick fit horizontally
35513 var groupCount = this.cols + 1 - colSpan;
35514 // for each group potential horizontal position
35515 for ( var i = 0; i < groupCount; i++ ) {
35516 // make an array of colY values for that one group
35517 var groupColYs = this.colYs.slice( i, i + colSpan );
35518 // and get the max value of the array
35519 colGroup[i] = Math.max.apply( Math, groupColYs );
35524 _manageStamp : function( stamp )
35526 var stampSize = stamp.getSize();
35527 var offset = stamp.getBox();
35528 // get the columns that this stamp affects
35529 var firstX = this.isOriginLeft ? offset.x : offset.right;
35530 var lastX = firstX + stampSize.width;
35531 var firstCol = Math.floor( firstX / this.columnWidth );
35532 firstCol = Math.max( 0, firstCol );
35534 var lastCol = Math.floor( lastX / this.columnWidth );
35535 // lastCol should not go over if multiple of columnWidth #425
35536 lastCol -= lastX % this.columnWidth ? 0 : 1;
35537 lastCol = Math.min( this.cols - 1, lastCol );
35539 // set colYs to bottom of the stamp
35540 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35543 for ( var i = firstCol; i <= lastCol; i++ ) {
35544 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35549 _getContainerSize : function()
35551 this.maxY = Math.max.apply( Math, this.colYs );
35556 if ( this.isFitWidth ) {
35557 size.width = this._getContainerFitWidth();
35563 _getContainerFitWidth : function()
35565 var unusedCols = 0;
35566 // count unused columns
35569 if ( this.colYs[i] !== 0 ) {
35574 // fit container to columns that have been used
35575 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35578 needsResizeLayout : function()
35580 var previousWidth = this.containerWidth;
35581 this.getContainerWidth();
35582 return previousWidth !== this.containerWidth;
35597 * @class Roo.bootstrap.MasonryBrick
35598 * @extends Roo.bootstrap.Component
35599 * Bootstrap MasonryBrick class
35602 * Create a new MasonryBrick
35603 * @param {Object} config The config object
35606 Roo.bootstrap.MasonryBrick = function(config){
35608 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35610 Roo.bootstrap.MasonryBrick.register(this);
35616 * When a MasonryBrick is clcik
35617 * @param {Roo.bootstrap.MasonryBrick} this
35618 * @param {Roo.EventObject} e
35624 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35627 * @cfg {String} title
35631 * @cfg {String} html
35635 * @cfg {String} bgimage
35639 * @cfg {String} videourl
35643 * @cfg {String} cls
35647 * @cfg {String} href
35651 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35656 * @cfg {String} placetitle (center|bottom)
35661 * @cfg {Boolean} isFitContainer defalut true
35663 isFitContainer : true,
35666 * @cfg {Boolean} preventDefault defalut false
35668 preventDefault : false,
35671 * @cfg {Boolean} inverse defalut false
35673 maskInverse : false,
35675 getAutoCreate : function()
35677 if(!this.isFitContainer){
35678 return this.getSplitAutoCreate();
35681 var cls = 'masonry-brick masonry-brick-full';
35683 if(this.href.length){
35684 cls += ' masonry-brick-link';
35687 if(this.bgimage.length){
35688 cls += ' masonry-brick-image';
35691 if(this.maskInverse){
35692 cls += ' mask-inverse';
35695 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35696 cls += ' enable-mask';
35700 cls += ' masonry-' + this.size + '-brick';
35703 if(this.placetitle.length){
35705 switch (this.placetitle) {
35707 cls += ' masonry-center-title';
35710 cls += ' masonry-bottom-title';
35717 if(!this.html.length && !this.bgimage.length){
35718 cls += ' masonry-center-title';
35721 if(!this.html.length && this.bgimage.length){
35722 cls += ' masonry-bottom-title';
35727 cls += ' ' + this.cls;
35731 tag: (this.href.length) ? 'a' : 'div',
35736 cls: 'masonry-brick-mask'
35740 cls: 'masonry-brick-paragraph',
35746 if(this.href.length){
35747 cfg.href = this.href;
35750 var cn = cfg.cn[1].cn;
35752 if(this.title.length){
35755 cls: 'masonry-brick-title',
35760 if(this.html.length){
35763 cls: 'masonry-brick-text',
35768 if (!this.title.length && !this.html.length) {
35769 cfg.cn[1].cls += ' hide';
35772 if(this.bgimage.length){
35775 cls: 'masonry-brick-image-view',
35780 if(this.videourl.length){
35781 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35782 // youtube support only?
35785 cls: 'masonry-brick-image-view',
35788 allowfullscreen : true
35796 getSplitAutoCreate : function()
35798 var cls = 'masonry-brick masonry-brick-split';
35800 if(this.href.length){
35801 cls += ' masonry-brick-link';
35804 if(this.bgimage.length){
35805 cls += ' masonry-brick-image';
35809 cls += ' masonry-' + this.size + '-brick';
35812 switch (this.placetitle) {
35814 cls += ' masonry-center-title';
35817 cls += ' masonry-bottom-title';
35820 if(!this.bgimage.length){
35821 cls += ' masonry-center-title';
35824 if(this.bgimage.length){
35825 cls += ' masonry-bottom-title';
35831 cls += ' ' + this.cls;
35835 tag: (this.href.length) ? 'a' : 'div',
35840 cls: 'masonry-brick-split-head',
35844 cls: 'masonry-brick-paragraph',
35851 cls: 'masonry-brick-split-body',
35857 if(this.href.length){
35858 cfg.href = this.href;
35861 if(this.title.length){
35862 cfg.cn[0].cn[0].cn.push({
35864 cls: 'masonry-brick-title',
35869 if(this.html.length){
35870 cfg.cn[1].cn.push({
35872 cls: 'masonry-brick-text',
35877 if(this.bgimage.length){
35878 cfg.cn[0].cn.push({
35880 cls: 'masonry-brick-image-view',
35885 if(this.videourl.length){
35886 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35887 // youtube support only?
35888 cfg.cn[0].cn.cn.push({
35890 cls: 'masonry-brick-image-view',
35893 allowfullscreen : true
35900 initEvents: function()
35902 switch (this.size) {
35935 this.el.on('touchstart', this.onTouchStart, this);
35936 this.el.on('touchmove', this.onTouchMove, this);
35937 this.el.on('touchend', this.onTouchEnd, this);
35938 this.el.on('contextmenu', this.onContextMenu, this);
35940 this.el.on('mouseenter' ,this.enter, this);
35941 this.el.on('mouseleave', this.leave, this);
35942 this.el.on('click', this.onClick, this);
35945 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35946 this.parent().bricks.push(this);
35951 onClick: function(e, el)
35953 var time = this.endTimer - this.startTimer;
35954 // Roo.log(e.preventDefault());
35957 e.preventDefault();
35962 if(!this.preventDefault){
35966 e.preventDefault();
35968 if (this.activeClass != '') {
35969 this.selectBrick();
35972 this.fireEvent('click', this, e);
35975 enter: function(e, el)
35977 e.preventDefault();
35979 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35983 if(this.bgimage.length && this.html.length){
35984 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35988 leave: function(e, el)
35990 e.preventDefault();
35992 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35996 if(this.bgimage.length && this.html.length){
35997 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36001 onTouchStart: function(e, el)
36003 // e.preventDefault();
36005 this.touchmoved = false;
36007 if(!this.isFitContainer){
36011 if(!this.bgimage.length || !this.html.length){
36015 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36017 this.timer = new Date().getTime();
36021 onTouchMove: function(e, el)
36023 this.touchmoved = true;
36026 onContextMenu : function(e,el)
36028 e.preventDefault();
36029 e.stopPropagation();
36033 onTouchEnd: function(e, el)
36035 // e.preventDefault();
36037 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36044 if(!this.bgimage.length || !this.html.length){
36046 if(this.href.length){
36047 window.location.href = this.href;
36053 if(!this.isFitContainer){
36057 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36059 window.location.href = this.href;
36062 //selection on single brick only
36063 selectBrick : function() {
36065 if (!this.parentId) {
36069 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36070 var index = m.selectedBrick.indexOf(this.id);
36073 m.selectedBrick.splice(index,1);
36074 this.el.removeClass(this.activeClass);
36078 for(var i = 0; i < m.selectedBrick.length; i++) {
36079 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36080 b.el.removeClass(b.activeClass);
36083 m.selectedBrick = [];
36085 m.selectedBrick.push(this.id);
36086 this.el.addClass(this.activeClass);
36090 isSelected : function(){
36091 return this.el.hasClass(this.activeClass);
36096 Roo.apply(Roo.bootstrap.MasonryBrick, {
36099 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36101 * register a Masonry Brick
36102 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36105 register : function(brick)
36107 //this.groups[brick.id] = brick;
36108 this.groups.add(brick.id, brick);
36111 * fetch a masonry brick based on the masonry brick ID
36112 * @param {string} the masonry brick to add
36113 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36116 get: function(brick_id)
36118 // if (typeof(this.groups[brick_id]) == 'undefined') {
36121 // return this.groups[brick_id] ;
36123 if(this.groups.key(brick_id)) {
36124 return this.groups.key(brick_id);
36142 * @class Roo.bootstrap.Brick
36143 * @extends Roo.bootstrap.Component
36144 * Bootstrap Brick class
36147 * Create a new Brick
36148 * @param {Object} config The config object
36151 Roo.bootstrap.Brick = function(config){
36152 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36158 * When a Brick is click
36159 * @param {Roo.bootstrap.Brick} this
36160 * @param {Roo.EventObject} e
36166 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36169 * @cfg {String} title
36173 * @cfg {String} html
36177 * @cfg {String} bgimage
36181 * @cfg {String} cls
36185 * @cfg {String} href
36189 * @cfg {String} video
36193 * @cfg {Boolean} square
36197 getAutoCreate : function()
36199 var cls = 'roo-brick';
36201 if(this.href.length){
36202 cls += ' roo-brick-link';
36205 if(this.bgimage.length){
36206 cls += ' roo-brick-image';
36209 if(!this.html.length && !this.bgimage.length){
36210 cls += ' roo-brick-center-title';
36213 if(!this.html.length && this.bgimage.length){
36214 cls += ' roo-brick-bottom-title';
36218 cls += ' ' + this.cls;
36222 tag: (this.href.length) ? 'a' : 'div',
36227 cls: 'roo-brick-paragraph',
36233 if(this.href.length){
36234 cfg.href = this.href;
36237 var cn = cfg.cn[0].cn;
36239 if(this.title.length){
36242 cls: 'roo-brick-title',
36247 if(this.html.length){
36250 cls: 'roo-brick-text',
36257 if(this.bgimage.length){
36260 cls: 'roo-brick-image-view',
36268 initEvents: function()
36270 if(this.title.length || this.html.length){
36271 this.el.on('mouseenter' ,this.enter, this);
36272 this.el.on('mouseleave', this.leave, this);
36275 Roo.EventManager.onWindowResize(this.resize, this);
36277 if(this.bgimage.length){
36278 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36279 this.imageEl.on('load', this.onImageLoad, this);
36286 onImageLoad : function()
36291 resize : function()
36293 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36295 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36297 if(this.bgimage.length){
36298 var image = this.el.select('.roo-brick-image-view', true).first();
36300 image.setWidth(paragraph.getWidth());
36303 image.setHeight(paragraph.getWidth());
36306 this.el.setHeight(image.getHeight());
36307 paragraph.setHeight(image.getHeight());
36313 enter: function(e, el)
36315 e.preventDefault();
36317 if(this.bgimage.length){
36318 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36319 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36323 leave: function(e, el)
36325 e.preventDefault();
36327 if(this.bgimage.length){
36328 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36329 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36344 * @class Roo.bootstrap.NumberField
36345 * @extends Roo.bootstrap.Input
36346 * Bootstrap NumberField class
36352 * Create a new NumberField
36353 * @param {Object} config The config object
36356 Roo.bootstrap.NumberField = function(config){
36357 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36360 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36363 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36365 allowDecimals : true,
36367 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36369 decimalSeparator : ".",
36371 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36373 decimalPrecision : 2,
36375 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36377 allowNegative : true,
36380 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36384 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36386 minValue : Number.NEGATIVE_INFINITY,
36388 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36390 maxValue : Number.MAX_VALUE,
36392 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36394 minText : "The minimum value for this field is {0}",
36396 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36398 maxText : "The maximum value for this field is {0}",
36400 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36401 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36403 nanText : "{0} is not a valid number",
36405 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36407 thousandsDelimiter : false,
36409 * @cfg {String} valueAlign alignment of value
36411 valueAlign : "left",
36413 getAutoCreate : function()
36415 var hiddenInput = {
36419 cls: 'hidden-number-input'
36423 hiddenInput.name = this.name;
36428 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36430 this.name = hiddenInput.name;
36432 if(cfg.cn.length > 0) {
36433 cfg.cn.push(hiddenInput);
36440 initEvents : function()
36442 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36444 var allowed = "0123456789";
36446 if(this.allowDecimals){
36447 allowed += this.decimalSeparator;
36450 if(this.allowNegative){
36454 if(this.thousandsDelimiter) {
36458 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36460 var keyPress = function(e){
36462 var k = e.getKey();
36464 var c = e.getCharCode();
36467 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36468 allowed.indexOf(String.fromCharCode(c)) === -1
36474 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36478 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36483 this.el.on("keypress", keyPress, this);
36486 validateValue : function(value)
36489 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36493 var num = this.parseValue(value);
36496 this.markInvalid(String.format(this.nanText, value));
36500 if(num < this.minValue){
36501 this.markInvalid(String.format(this.minText, this.minValue));
36505 if(num > this.maxValue){
36506 this.markInvalid(String.format(this.maxText, this.maxValue));
36513 getValue : function()
36515 var v = this.hiddenEl().getValue();
36517 return this.fixPrecision(this.parseValue(v));
36520 parseValue : function(value)
36522 if(this.thousandsDelimiter) {
36524 r = new RegExp(",", "g");
36525 value = value.replace(r, "");
36528 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36529 return isNaN(value) ? '' : value;
36532 fixPrecision : function(value)
36534 if(this.thousandsDelimiter) {
36536 r = new RegExp(",", "g");
36537 value = value.replace(r, "");
36540 var nan = isNaN(value);
36542 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36543 return nan ? '' : value;
36545 return parseFloat(value).toFixed(this.decimalPrecision);
36548 setValue : function(v)
36550 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36556 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36558 this.inputEl().dom.value = (v == '') ? '' :
36559 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36561 if(!this.allowZero && v === '0') {
36562 this.hiddenEl().dom.value = '';
36563 this.inputEl().dom.value = '';
36570 decimalPrecisionFcn : function(v)
36572 return Math.floor(v);
36575 beforeBlur : function()
36577 var v = this.parseValue(this.getRawValue());
36579 if(v || v === 0 || v === ''){
36584 hiddenEl : function()
36586 return this.el.select('input.hidden-number-input',true).first();
36598 * @class Roo.bootstrap.DocumentSlider
36599 * @extends Roo.bootstrap.Component
36600 * Bootstrap DocumentSlider class
36603 * Create a new DocumentViewer
36604 * @param {Object} config The config object
36607 Roo.bootstrap.DocumentSlider = function(config){
36608 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36615 * Fire after initEvent
36616 * @param {Roo.bootstrap.DocumentSlider} this
36621 * Fire after update
36622 * @param {Roo.bootstrap.DocumentSlider} this
36628 * @param {Roo.bootstrap.DocumentSlider} this
36634 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36640 getAutoCreate : function()
36644 cls : 'roo-document-slider',
36648 cls : 'roo-document-slider-header',
36652 cls : 'roo-document-slider-header-title'
36658 cls : 'roo-document-slider-body',
36662 cls : 'roo-document-slider-prev',
36666 cls : 'fa fa-chevron-left'
36672 cls : 'roo-document-slider-thumb',
36676 cls : 'roo-document-slider-image'
36682 cls : 'roo-document-slider-next',
36686 cls : 'fa fa-chevron-right'
36698 initEvents : function()
36700 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36701 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36703 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36704 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36706 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36707 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36709 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36710 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36712 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36713 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36715 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36716 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36718 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36719 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36721 this.thumbEl.on('click', this.onClick, this);
36723 this.prevIndicator.on('click', this.prev, this);
36725 this.nextIndicator.on('click', this.next, this);
36729 initial : function()
36731 if(this.files.length){
36732 this.indicator = 1;
36736 this.fireEvent('initial', this);
36739 update : function()
36741 this.imageEl.attr('src', this.files[this.indicator - 1]);
36743 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36745 this.prevIndicator.show();
36747 if(this.indicator == 1){
36748 this.prevIndicator.hide();
36751 this.nextIndicator.show();
36753 if(this.indicator == this.files.length){
36754 this.nextIndicator.hide();
36757 this.thumbEl.scrollTo('top');
36759 this.fireEvent('update', this);
36762 onClick : function(e)
36764 e.preventDefault();
36766 this.fireEvent('click', this);
36771 e.preventDefault();
36773 this.indicator = Math.max(1, this.indicator - 1);
36780 e.preventDefault();
36782 this.indicator = Math.min(this.files.length, this.indicator + 1);
36796 * @class Roo.bootstrap.RadioSet
36797 * @extends Roo.bootstrap.Input
36798 * Bootstrap RadioSet class
36799 * @cfg {String} indicatorpos (left|right) default left
36800 * @cfg {Boolean} inline (true|false) inline the element (default true)
36801 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36803 * Create a new RadioSet
36804 * @param {Object} config The config object
36807 Roo.bootstrap.RadioSet = function(config){
36809 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36813 Roo.bootstrap.RadioSet.register(this);
36818 * Fires when the element is checked or unchecked.
36819 * @param {Roo.bootstrap.RadioSet} this This radio
36820 * @param {Roo.bootstrap.Radio} item The checked item
36825 * Fires when the element is click.
36826 * @param {Roo.bootstrap.RadioSet} this This radio set
36827 * @param {Roo.bootstrap.Radio} item The checked item
36828 * @param {Roo.EventObject} e The event object
36835 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36843 indicatorpos : 'left',
36845 getAutoCreate : function()
36849 cls : 'roo-radio-set-label',
36853 html : this.fieldLabel
36857 if (Roo.bootstrap.version == 3) {
36860 if(this.indicatorpos == 'left'){
36863 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36864 tooltip : 'This field is required'
36869 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36870 tooltip : 'This field is required'
36876 cls : 'roo-radio-set-items'
36879 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36881 if (align === 'left' && this.fieldLabel.length) {
36884 cls : "roo-radio-set-right",
36890 if(this.labelWidth > 12){
36891 label.style = "width: " + this.labelWidth + 'px';
36894 if(this.labelWidth < 13 && this.labelmd == 0){
36895 this.labelmd = this.labelWidth;
36898 if(this.labellg > 0){
36899 label.cls += ' col-lg-' + this.labellg;
36900 items.cls += ' col-lg-' + (12 - this.labellg);
36903 if(this.labelmd > 0){
36904 label.cls += ' col-md-' + this.labelmd;
36905 items.cls += ' col-md-' + (12 - this.labelmd);
36908 if(this.labelsm > 0){
36909 label.cls += ' col-sm-' + this.labelsm;
36910 items.cls += ' col-sm-' + (12 - this.labelsm);
36913 if(this.labelxs > 0){
36914 label.cls += ' col-xs-' + this.labelxs;
36915 items.cls += ' col-xs-' + (12 - this.labelxs);
36921 cls : 'roo-radio-set',
36925 cls : 'roo-radio-set-input',
36928 value : this.value ? this.value : ''
36935 if(this.weight.length){
36936 cfg.cls += ' roo-radio-' + this.weight;
36940 cfg.cls += ' roo-radio-set-inline';
36944 ['xs','sm','md','lg'].map(function(size){
36945 if (settings[size]) {
36946 cfg.cls += ' col-' + size + '-' + settings[size];
36954 initEvents : function()
36956 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36957 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36959 if(!this.fieldLabel.length){
36960 this.labelEl.hide();
36963 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36964 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36966 this.indicator = this.indicatorEl();
36968 if(this.indicator){
36969 this.indicator.addClass('invisible');
36972 this.originalValue = this.getValue();
36976 inputEl: function ()
36978 return this.el.select('.roo-radio-set-input', true).first();
36981 getChildContainer : function()
36983 return this.itemsEl;
36986 register : function(item)
36988 this.radioes.push(item);
36992 validate : function()
36994 if(this.getVisibilityEl().hasClass('hidden')){
37000 Roo.each(this.radioes, function(i){
37009 if(this.allowBlank) {
37013 if(this.disabled || valid){
37018 this.markInvalid();
37023 markValid : function()
37025 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37026 this.indicatorEl().removeClass('visible');
37027 this.indicatorEl().addClass('invisible');
37031 if (Roo.bootstrap.version == 3) {
37032 this.el.removeClass([this.invalidClass, this.validClass]);
37033 this.el.addClass(this.validClass);
37035 this.el.removeClass(['is-invalid','is-valid']);
37036 this.el.addClass(['is-valid']);
37038 this.fireEvent('valid', this);
37041 markInvalid : function(msg)
37043 if(this.allowBlank || this.disabled){
37047 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37048 this.indicatorEl().removeClass('invisible');
37049 this.indicatorEl().addClass('visible');
37051 if (Roo.bootstrap.version == 3) {
37052 this.el.removeClass([this.invalidClass, this.validClass]);
37053 this.el.addClass(this.invalidClass);
37055 this.el.removeClass(['is-invalid','is-valid']);
37056 this.el.addClass(['is-invalid']);
37059 this.fireEvent('invalid', this, msg);
37063 setValue : function(v, suppressEvent)
37065 if(this.value === v){
37072 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37075 Roo.each(this.radioes, function(i){
37077 i.el.removeClass('checked');
37080 Roo.each(this.radioes, function(i){
37082 if(i.value === v || i.value.toString() === v.toString()){
37084 i.el.addClass('checked');
37086 if(suppressEvent !== true){
37087 this.fireEvent('check', this, i);
37098 clearInvalid : function(){
37100 if(!this.el || this.preventMark){
37104 this.el.removeClass([this.invalidClass]);
37106 this.fireEvent('valid', this);
37111 Roo.apply(Roo.bootstrap.RadioSet, {
37115 register : function(set)
37117 this.groups[set.name] = set;
37120 get: function(name)
37122 if (typeof(this.groups[name]) == 'undefined') {
37126 return this.groups[name] ;
37132 * Ext JS Library 1.1.1
37133 * Copyright(c) 2006-2007, Ext JS, LLC.
37135 * Originally Released Under LGPL - original licence link has changed is not relivant.
37138 * <script type="text/javascript">
37143 * @class Roo.bootstrap.SplitBar
37144 * @extends Roo.util.Observable
37145 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37149 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37150 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37151 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37152 split.minSize = 100;
37153 split.maxSize = 600;
37154 split.animate = true;
37155 split.on('moved', splitterMoved);
37158 * Create a new SplitBar
37159 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37160 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37161 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37162 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37163 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37164 position of the SplitBar).
37166 Roo.bootstrap.SplitBar = function(cfg){
37171 // dragElement : elm
37172 // resizingElement: el,
37174 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37175 // placement : Roo.bootstrap.SplitBar.LEFT ,
37176 // existingProxy ???
37179 this.el = Roo.get(cfg.dragElement, true);
37180 this.el.dom.unselectable = "on";
37182 this.resizingEl = Roo.get(cfg.resizingElement, true);
37186 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37187 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37190 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37193 * The minimum size of the resizing element. (Defaults to 0)
37199 * The maximum size of the resizing element. (Defaults to 2000)
37202 this.maxSize = 2000;
37205 * Whether to animate the transition to the new size
37208 this.animate = false;
37211 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37214 this.useShim = false;
37219 if(!cfg.existingProxy){
37221 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37223 this.proxy = Roo.get(cfg.existingProxy).dom;
37226 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37229 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37232 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37235 this.dragSpecs = {};
37238 * @private The adapter to use to positon and resize elements
37240 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37241 this.adapter.init(this);
37243 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37245 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37246 this.el.addClass("roo-splitbar-h");
37249 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37250 this.el.addClass("roo-splitbar-v");
37256 * Fires when the splitter is moved (alias for {@link #event-moved})
37257 * @param {Roo.bootstrap.SplitBar} this
37258 * @param {Number} newSize the new width or height
37263 * Fires when the splitter is moved
37264 * @param {Roo.bootstrap.SplitBar} this
37265 * @param {Number} newSize the new width or height
37269 * @event beforeresize
37270 * Fires before the splitter is dragged
37271 * @param {Roo.bootstrap.SplitBar} this
37273 "beforeresize" : true,
37275 "beforeapply" : true
37278 Roo.util.Observable.call(this);
37281 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37282 onStartProxyDrag : function(x, y){
37283 this.fireEvent("beforeresize", this);
37285 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37287 o.enableDisplayMode("block");
37288 // all splitbars share the same overlay
37289 Roo.bootstrap.SplitBar.prototype.overlay = o;
37291 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37292 this.overlay.show();
37293 Roo.get(this.proxy).setDisplayed("block");
37294 var size = this.adapter.getElementSize(this);
37295 this.activeMinSize = this.getMinimumSize();;
37296 this.activeMaxSize = this.getMaximumSize();;
37297 var c1 = size - this.activeMinSize;
37298 var c2 = Math.max(this.activeMaxSize - size, 0);
37299 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37300 this.dd.resetConstraints();
37301 this.dd.setXConstraint(
37302 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37303 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37305 this.dd.setYConstraint(0, 0);
37307 this.dd.resetConstraints();
37308 this.dd.setXConstraint(0, 0);
37309 this.dd.setYConstraint(
37310 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37311 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37314 this.dragSpecs.startSize = size;
37315 this.dragSpecs.startPoint = [x, y];
37316 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37320 * @private Called after the drag operation by the DDProxy
37322 onEndProxyDrag : function(e){
37323 Roo.get(this.proxy).setDisplayed(false);
37324 var endPoint = Roo.lib.Event.getXY(e);
37326 this.overlay.hide();
37329 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37330 newSize = this.dragSpecs.startSize +
37331 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37332 endPoint[0] - this.dragSpecs.startPoint[0] :
37333 this.dragSpecs.startPoint[0] - endPoint[0]
37336 newSize = this.dragSpecs.startSize +
37337 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37338 endPoint[1] - this.dragSpecs.startPoint[1] :
37339 this.dragSpecs.startPoint[1] - endPoint[1]
37342 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37343 if(newSize != this.dragSpecs.startSize){
37344 if(this.fireEvent('beforeapply', this, newSize) !== false){
37345 this.adapter.setElementSize(this, newSize);
37346 this.fireEvent("moved", this, newSize);
37347 this.fireEvent("resize", this, newSize);
37353 * Get the adapter this SplitBar uses
37354 * @return The adapter object
37356 getAdapter : function(){
37357 return this.adapter;
37361 * Set the adapter this SplitBar uses
37362 * @param {Object} adapter A SplitBar adapter object
37364 setAdapter : function(adapter){
37365 this.adapter = adapter;
37366 this.adapter.init(this);
37370 * Gets the minimum size for the resizing element
37371 * @return {Number} The minimum size
37373 getMinimumSize : function(){
37374 return this.minSize;
37378 * Sets the minimum size for the resizing element
37379 * @param {Number} minSize The minimum size
37381 setMinimumSize : function(minSize){
37382 this.minSize = minSize;
37386 * Gets the maximum size for the resizing element
37387 * @return {Number} The maximum size
37389 getMaximumSize : function(){
37390 return this.maxSize;
37394 * Sets the maximum size for the resizing element
37395 * @param {Number} maxSize The maximum size
37397 setMaximumSize : function(maxSize){
37398 this.maxSize = maxSize;
37402 * Sets the initialize size for the resizing element
37403 * @param {Number} size The initial size
37405 setCurrentSize : function(size){
37406 var oldAnimate = this.animate;
37407 this.animate = false;
37408 this.adapter.setElementSize(this, size);
37409 this.animate = oldAnimate;
37413 * Destroy this splitbar.
37414 * @param {Boolean} removeEl True to remove the element
37416 destroy : function(removeEl){
37418 this.shim.remove();
37421 this.proxy.parentNode.removeChild(this.proxy);
37429 * @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.
37431 Roo.bootstrap.SplitBar.createProxy = function(dir){
37432 var proxy = new Roo.Element(document.createElement("div"));
37433 proxy.unselectable();
37434 var cls = 'roo-splitbar-proxy';
37435 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37436 document.body.appendChild(proxy.dom);
37441 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37442 * Default Adapter. It assumes the splitter and resizing element are not positioned
37443 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37445 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37448 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37449 // do nothing for now
37450 init : function(s){
37454 * Called before drag operations to get the current size of the resizing element.
37455 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37457 getElementSize : function(s){
37458 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37459 return s.resizingEl.getWidth();
37461 return s.resizingEl.getHeight();
37466 * Called after drag operations to set the size of the resizing element.
37467 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37468 * @param {Number} newSize The new size to set
37469 * @param {Function} onComplete A function to be invoked when resizing is complete
37471 setElementSize : function(s, newSize, onComplete){
37472 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37474 s.resizingEl.setWidth(newSize);
37476 onComplete(s, newSize);
37479 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37484 s.resizingEl.setHeight(newSize);
37486 onComplete(s, newSize);
37489 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37496 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37497 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37498 * Adapter that moves the splitter element to align with the resized sizing element.
37499 * Used with an absolute positioned SplitBar.
37500 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37501 * document.body, make sure you assign an id to the body element.
37503 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37504 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37505 this.container = Roo.get(container);
37508 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37509 init : function(s){
37510 this.basic.init(s);
37513 getElementSize : function(s){
37514 return this.basic.getElementSize(s);
37517 setElementSize : function(s, newSize, onComplete){
37518 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37521 moveSplitter : function(s){
37522 var yes = Roo.bootstrap.SplitBar;
37523 switch(s.placement){
37525 s.el.setX(s.resizingEl.getRight());
37528 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37531 s.el.setY(s.resizingEl.getBottom());
37534 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37541 * Orientation constant - Create a vertical SplitBar
37545 Roo.bootstrap.SplitBar.VERTICAL = 1;
37548 * Orientation constant - Create a horizontal SplitBar
37552 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37555 * Placement constant - The resizing element is to the left of the splitter element
37559 Roo.bootstrap.SplitBar.LEFT = 1;
37562 * Placement constant - The resizing element is to the right of the splitter element
37566 Roo.bootstrap.SplitBar.RIGHT = 2;
37569 * Placement constant - The resizing element is positioned above the splitter element
37573 Roo.bootstrap.SplitBar.TOP = 3;
37576 * Placement constant - The resizing element is positioned under splitter element
37580 Roo.bootstrap.SplitBar.BOTTOM = 4;
37581 Roo.namespace("Roo.bootstrap.layout");/*
37583 * Ext JS Library 1.1.1
37584 * Copyright(c) 2006-2007, Ext JS, LLC.
37586 * Originally Released Under LGPL - original licence link has changed is not relivant.
37589 * <script type="text/javascript">
37593 * @class Roo.bootstrap.layout.Manager
37594 * @extends Roo.bootstrap.Component
37595 * Base class for layout managers.
37597 Roo.bootstrap.layout.Manager = function(config)
37599 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37605 /** false to disable window resize monitoring @type Boolean */
37606 this.monitorWindowResize = true;
37611 * Fires when a layout is performed.
37612 * @param {Roo.LayoutManager} this
37616 * @event regionresized
37617 * Fires when the user resizes a region.
37618 * @param {Roo.LayoutRegion} region The resized region
37619 * @param {Number} newSize The new size (width for east/west, height for north/south)
37621 "regionresized" : true,
37623 * @event regioncollapsed
37624 * Fires when a region is collapsed.
37625 * @param {Roo.LayoutRegion} region The collapsed region
37627 "regioncollapsed" : true,
37629 * @event regionexpanded
37630 * Fires when a region is expanded.
37631 * @param {Roo.LayoutRegion} region The expanded region
37633 "regionexpanded" : true
37635 this.updating = false;
37638 this.el = Roo.get(config.el);
37644 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37649 monitorWindowResize : true,
37655 onRender : function(ct, position)
37658 this.el = Roo.get(ct);
37661 //this.fireEvent('render',this);
37665 initEvents: function()
37669 // ie scrollbar fix
37670 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37671 document.body.scroll = "no";
37672 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37673 this.el.position('relative');
37675 this.id = this.el.id;
37676 this.el.addClass("roo-layout-container");
37677 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37678 if(this.el.dom != document.body ) {
37679 this.el.on('resize', this.layout,this);
37680 this.el.on('show', this.layout,this);
37686 * Returns true if this layout is currently being updated
37687 * @return {Boolean}
37689 isUpdating : function(){
37690 return this.updating;
37694 * Suspend the LayoutManager from doing auto-layouts while
37695 * making multiple add or remove calls
37697 beginUpdate : function(){
37698 this.updating = true;
37702 * Restore auto-layouts and optionally disable the manager from performing a layout
37703 * @param {Boolean} noLayout true to disable a layout update
37705 endUpdate : function(noLayout){
37706 this.updating = false;
37712 layout: function(){
37716 onRegionResized : function(region, newSize){
37717 this.fireEvent("regionresized", region, newSize);
37721 onRegionCollapsed : function(region){
37722 this.fireEvent("regioncollapsed", region);
37725 onRegionExpanded : function(region){
37726 this.fireEvent("regionexpanded", region);
37730 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37731 * performs box-model adjustments.
37732 * @return {Object} The size as an object {width: (the width), height: (the height)}
37734 getViewSize : function()
37737 if(this.el.dom != document.body){
37738 size = this.el.getSize();
37740 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37742 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37743 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37748 * Returns the Element this layout is bound to.
37749 * @return {Roo.Element}
37751 getEl : function(){
37756 * Returns the specified region.
37757 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37758 * @return {Roo.LayoutRegion}
37760 getRegion : function(target){
37761 return this.regions[target.toLowerCase()];
37764 onWindowResize : function(){
37765 if(this.monitorWindowResize){
37772 * Ext JS Library 1.1.1
37773 * Copyright(c) 2006-2007, Ext JS, LLC.
37775 * Originally Released Under LGPL - original licence link has changed is not relivant.
37778 * <script type="text/javascript">
37781 * @class Roo.bootstrap.layout.Border
37782 * @extends Roo.bootstrap.layout.Manager
37783 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37784 * please see: examples/bootstrap/nested.html<br><br>
37786 <b>The container the layout is rendered into can be either the body element or any other element.
37787 If it is not the body element, the container needs to either be an absolute positioned element,
37788 or you will need to add "position:relative" to the css of the container. You will also need to specify
37789 the container size if it is not the body element.</b>
37792 * Create a new Border
37793 * @param {Object} config Configuration options
37795 Roo.bootstrap.layout.Border = function(config){
37796 config = config || {};
37797 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37801 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37802 if(config[region]){
37803 config[region].region = region;
37804 this.addRegion(config[region]);
37810 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37812 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37814 parent : false, // this might point to a 'nest' or a ???
37817 * Creates and adds a new region if it doesn't already exist.
37818 * @param {String} target The target region key (north, south, east, west or center).
37819 * @param {Object} config The regions config object
37820 * @return {BorderLayoutRegion} The new region
37822 addRegion : function(config)
37824 if(!this.regions[config.region]){
37825 var r = this.factory(config);
37826 this.bindRegion(r);
37828 return this.regions[config.region];
37832 bindRegion : function(r){
37833 this.regions[r.config.region] = r;
37835 r.on("visibilitychange", this.layout, this);
37836 r.on("paneladded", this.layout, this);
37837 r.on("panelremoved", this.layout, this);
37838 r.on("invalidated", this.layout, this);
37839 r.on("resized", this.onRegionResized, this);
37840 r.on("collapsed", this.onRegionCollapsed, this);
37841 r.on("expanded", this.onRegionExpanded, this);
37845 * Performs a layout update.
37847 layout : function()
37849 if(this.updating) {
37853 // render all the rebions if they have not been done alreayd?
37854 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37855 if(this.regions[region] && !this.regions[region].bodyEl){
37856 this.regions[region].onRender(this.el)
37860 var size = this.getViewSize();
37861 var w = size.width;
37862 var h = size.height;
37867 //var x = 0, y = 0;
37869 var rs = this.regions;
37870 var north = rs["north"];
37871 var south = rs["south"];
37872 var west = rs["west"];
37873 var east = rs["east"];
37874 var center = rs["center"];
37875 //if(this.hideOnLayout){ // not supported anymore
37876 //c.el.setStyle("display", "none");
37878 if(north && north.isVisible()){
37879 var b = north.getBox();
37880 var m = north.getMargins();
37881 b.width = w - (m.left+m.right);
37884 centerY = b.height + b.y + m.bottom;
37885 centerH -= centerY;
37886 north.updateBox(this.safeBox(b));
37888 if(south && south.isVisible()){
37889 var b = south.getBox();
37890 var m = south.getMargins();
37891 b.width = w - (m.left+m.right);
37893 var totalHeight = (b.height + m.top + m.bottom);
37894 b.y = h - totalHeight + m.top;
37895 centerH -= totalHeight;
37896 south.updateBox(this.safeBox(b));
37898 if(west && west.isVisible()){
37899 var b = west.getBox();
37900 var m = west.getMargins();
37901 b.height = centerH - (m.top+m.bottom);
37903 b.y = centerY + m.top;
37904 var totalWidth = (b.width + m.left + m.right);
37905 centerX += totalWidth;
37906 centerW -= totalWidth;
37907 west.updateBox(this.safeBox(b));
37909 if(east && east.isVisible()){
37910 var b = east.getBox();
37911 var m = east.getMargins();
37912 b.height = centerH - (m.top+m.bottom);
37913 var totalWidth = (b.width + m.left + m.right);
37914 b.x = w - totalWidth + m.left;
37915 b.y = centerY + m.top;
37916 centerW -= totalWidth;
37917 east.updateBox(this.safeBox(b));
37920 var m = center.getMargins();
37922 x: centerX + m.left,
37923 y: centerY + m.top,
37924 width: centerW - (m.left+m.right),
37925 height: centerH - (m.top+m.bottom)
37927 //if(this.hideOnLayout){
37928 //center.el.setStyle("display", "block");
37930 center.updateBox(this.safeBox(centerBox));
37933 this.fireEvent("layout", this);
37937 safeBox : function(box){
37938 box.width = Math.max(0, box.width);
37939 box.height = Math.max(0, box.height);
37944 * Adds a ContentPanel (or subclass) to this layout.
37945 * @param {String} target The target region key (north, south, east, west or center).
37946 * @param {Roo.ContentPanel} panel The panel to add
37947 * @return {Roo.ContentPanel} The added panel
37949 add : function(target, panel){
37951 target = target.toLowerCase();
37952 return this.regions[target].add(panel);
37956 * Remove a ContentPanel (or subclass) to this layout.
37957 * @param {String} target The target region key (north, south, east, west or center).
37958 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37959 * @return {Roo.ContentPanel} The removed panel
37961 remove : function(target, panel){
37962 target = target.toLowerCase();
37963 return this.regions[target].remove(panel);
37967 * Searches all regions for a panel with the specified id
37968 * @param {String} panelId
37969 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37971 findPanel : function(panelId){
37972 var rs = this.regions;
37973 for(var target in rs){
37974 if(typeof rs[target] != "function"){
37975 var p = rs[target].getPanel(panelId);
37985 * Searches all regions for a panel with the specified id and activates (shows) it.
37986 * @param {String/ContentPanel} panelId The panels id or the panel itself
37987 * @return {Roo.ContentPanel} The shown panel or null
37989 showPanel : function(panelId) {
37990 var rs = this.regions;
37991 for(var target in rs){
37992 var r = rs[target];
37993 if(typeof r != "function"){
37994 if(r.hasPanel(panelId)){
37995 return r.showPanel(panelId);
38003 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38004 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38007 restoreState : function(provider){
38009 provider = Roo.state.Manager;
38011 var sm = new Roo.LayoutStateManager();
38012 sm.init(this, provider);
38018 * Adds a xtype elements to the layout.
38022 xtype : 'ContentPanel',
38029 xtype : 'NestedLayoutPanel',
38035 items : [ ... list of content panels or nested layout panels.. ]
38039 * @param {Object} cfg Xtype definition of item to add.
38041 addxtype : function(cfg)
38043 // basically accepts a pannel...
38044 // can accept a layout region..!?!?
38045 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38048 // theory? children can only be panels??
38050 //if (!cfg.xtype.match(/Panel$/)) {
38055 if (typeof(cfg.region) == 'undefined') {
38056 Roo.log("Failed to add Panel, region was not set");
38060 var region = cfg.region;
38066 xitems = cfg.items;
38071 if ( region == 'center') {
38072 Roo.log("Center: " + cfg.title);
38078 case 'Content': // ContentPanel (el, cfg)
38079 case 'Scroll': // ContentPanel (el, cfg)
38081 cfg.autoCreate = cfg.autoCreate || true;
38082 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38084 // var el = this.el.createChild();
38085 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38088 this.add(region, ret);
38092 case 'TreePanel': // our new panel!
38093 cfg.el = this.el.createChild();
38094 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38095 this.add(region, ret);
38100 // create a new Layout (which is a Border Layout...
38102 var clayout = cfg.layout;
38103 clayout.el = this.el.createChild();
38104 clayout.items = clayout.items || [];
38108 // replace this exitems with the clayout ones..
38109 xitems = clayout.items;
38111 // force background off if it's in center...
38112 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38113 cfg.background = false;
38115 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38118 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38119 //console.log('adding nested layout panel ' + cfg.toSource());
38120 this.add(region, ret);
38121 nb = {}; /// find first...
38126 // needs grid and region
38128 //var el = this.getRegion(region).el.createChild();
38130 *var el = this.el.createChild();
38131 // create the grid first...
38132 cfg.grid.container = el;
38133 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38136 if (region == 'center' && this.active ) {
38137 cfg.background = false;
38140 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38142 this.add(region, ret);
38144 if (cfg.background) {
38145 // render grid on panel activation (if panel background)
38146 ret.on('activate', function(gp) {
38147 if (!gp.grid.rendered) {
38148 // gp.grid.render(el);
38152 // cfg.grid.render(el);
38158 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38159 // it was the old xcomponent building that caused this before.
38160 // espeically if border is the top element in the tree.
38170 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38172 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38173 this.add(region, ret);
38177 throw "Can not add '" + cfg.xtype + "' to Border";
38183 this.beginUpdate();
38187 Roo.each(xitems, function(i) {
38188 region = nb && i.region ? i.region : false;
38190 var add = ret.addxtype(i);
38193 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38194 if (!i.background) {
38195 abn[region] = nb[region] ;
38202 // make the last non-background panel active..
38203 //if (nb) { Roo.log(abn); }
38206 for(var r in abn) {
38207 region = this.getRegion(r);
38209 // tried using nb[r], but it does not work..
38211 region.showPanel(abn[r]);
38222 factory : function(cfg)
38225 var validRegions = Roo.bootstrap.layout.Border.regions;
38227 var target = cfg.region;
38230 var r = Roo.bootstrap.layout;
38234 return new r.North(cfg);
38236 return new r.South(cfg);
38238 return new r.East(cfg);
38240 return new r.West(cfg);
38242 return new r.Center(cfg);
38244 throw 'Layout region "'+target+'" not supported.';
38251 * Ext JS Library 1.1.1
38252 * Copyright(c) 2006-2007, Ext JS, LLC.
38254 * Originally Released Under LGPL - original licence link has changed is not relivant.
38257 * <script type="text/javascript">
38261 * @class Roo.bootstrap.layout.Basic
38262 * @extends Roo.util.Observable
38263 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38264 * and does not have a titlebar, tabs or any other features. All it does is size and position
38265 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38266 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38267 * @cfg {string} region the region that it inhabits..
38268 * @cfg {bool} skipConfig skip config?
38272 Roo.bootstrap.layout.Basic = function(config){
38274 this.mgr = config.mgr;
38276 this.position = config.region;
38278 var skipConfig = config.skipConfig;
38282 * @scope Roo.BasicLayoutRegion
38286 * @event beforeremove
38287 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38288 * @param {Roo.LayoutRegion} this
38289 * @param {Roo.ContentPanel} panel The panel
38290 * @param {Object} e The cancel event object
38292 "beforeremove" : true,
38294 * @event invalidated
38295 * Fires when the layout for this region is changed.
38296 * @param {Roo.LayoutRegion} this
38298 "invalidated" : true,
38300 * @event visibilitychange
38301 * Fires when this region is shown or hidden
38302 * @param {Roo.LayoutRegion} this
38303 * @param {Boolean} visibility true or false
38305 "visibilitychange" : true,
38307 * @event paneladded
38308 * Fires when a panel is added.
38309 * @param {Roo.LayoutRegion} this
38310 * @param {Roo.ContentPanel} panel The panel
38312 "paneladded" : true,
38314 * @event panelremoved
38315 * Fires when a panel is removed.
38316 * @param {Roo.LayoutRegion} this
38317 * @param {Roo.ContentPanel} panel The panel
38319 "panelremoved" : true,
38321 * @event beforecollapse
38322 * Fires when this region before collapse.
38323 * @param {Roo.LayoutRegion} this
38325 "beforecollapse" : true,
38328 * Fires when this region is collapsed.
38329 * @param {Roo.LayoutRegion} this
38331 "collapsed" : true,
38334 * Fires when this region is expanded.
38335 * @param {Roo.LayoutRegion} this
38340 * Fires when this region is slid into view.
38341 * @param {Roo.LayoutRegion} this
38343 "slideshow" : true,
38346 * Fires when this region slides out of view.
38347 * @param {Roo.LayoutRegion} this
38349 "slidehide" : true,
38351 * @event panelactivated
38352 * Fires when a panel is activated.
38353 * @param {Roo.LayoutRegion} this
38354 * @param {Roo.ContentPanel} panel The activated panel
38356 "panelactivated" : true,
38359 * Fires when the user resizes this region.
38360 * @param {Roo.LayoutRegion} this
38361 * @param {Number} newSize The new size (width for east/west, height for north/south)
38365 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38366 this.panels = new Roo.util.MixedCollection();
38367 this.panels.getKey = this.getPanelId.createDelegate(this);
38369 this.activePanel = null;
38370 // ensure listeners are added...
38372 if (config.listeners || config.events) {
38373 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38374 listeners : config.listeners || {},
38375 events : config.events || {}
38379 if(skipConfig !== true){
38380 this.applyConfig(config);
38384 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38386 getPanelId : function(p){
38390 applyConfig : function(config){
38391 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38392 this.config = config;
38397 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38398 * the width, for horizontal (north, south) the height.
38399 * @param {Number} newSize The new width or height
38401 resizeTo : function(newSize){
38402 var el = this.el ? this.el :
38403 (this.activePanel ? this.activePanel.getEl() : null);
38405 switch(this.position){
38408 el.setWidth(newSize);
38409 this.fireEvent("resized", this, newSize);
38413 el.setHeight(newSize);
38414 this.fireEvent("resized", this, newSize);
38420 getBox : function(){
38421 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38424 getMargins : function(){
38425 return this.margins;
38428 updateBox : function(box){
38430 var el = this.activePanel.getEl();
38431 el.dom.style.left = box.x + "px";
38432 el.dom.style.top = box.y + "px";
38433 this.activePanel.setSize(box.width, box.height);
38437 * Returns the container element for this region.
38438 * @return {Roo.Element}
38440 getEl : function(){
38441 return this.activePanel;
38445 * Returns true if this region is currently visible.
38446 * @return {Boolean}
38448 isVisible : function(){
38449 return this.activePanel ? true : false;
38452 setActivePanel : function(panel){
38453 panel = this.getPanel(panel);
38454 if(this.activePanel && this.activePanel != panel){
38455 this.activePanel.setActiveState(false);
38456 this.activePanel.getEl().setLeftTop(-10000,-10000);
38458 this.activePanel = panel;
38459 panel.setActiveState(true);
38461 panel.setSize(this.box.width, this.box.height);
38463 this.fireEvent("panelactivated", this, panel);
38464 this.fireEvent("invalidated");
38468 * Show the specified panel.
38469 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38470 * @return {Roo.ContentPanel} The shown panel or null
38472 showPanel : function(panel){
38473 panel = this.getPanel(panel);
38475 this.setActivePanel(panel);
38481 * Get the active panel for this region.
38482 * @return {Roo.ContentPanel} The active panel or null
38484 getActivePanel : function(){
38485 return this.activePanel;
38489 * Add the passed ContentPanel(s)
38490 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38491 * @return {Roo.ContentPanel} The panel added (if only one was added)
38493 add : function(panel){
38494 if(arguments.length > 1){
38495 for(var i = 0, len = arguments.length; i < len; i++) {
38496 this.add(arguments[i]);
38500 if(this.hasPanel(panel)){
38501 this.showPanel(panel);
38504 var el = panel.getEl();
38505 if(el.dom.parentNode != this.mgr.el.dom){
38506 this.mgr.el.dom.appendChild(el.dom);
38508 if(panel.setRegion){
38509 panel.setRegion(this);
38511 this.panels.add(panel);
38512 el.setStyle("position", "absolute");
38513 if(!panel.background){
38514 this.setActivePanel(panel);
38515 if(this.config.initialSize && this.panels.getCount()==1){
38516 this.resizeTo(this.config.initialSize);
38519 this.fireEvent("paneladded", this, panel);
38524 * Returns true if the panel is in this region.
38525 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38526 * @return {Boolean}
38528 hasPanel : function(panel){
38529 if(typeof panel == "object"){ // must be panel obj
38530 panel = panel.getId();
38532 return this.getPanel(panel) ? true : false;
38536 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38537 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38538 * @param {Boolean} preservePanel Overrides the config preservePanel option
38539 * @return {Roo.ContentPanel} The panel that was removed
38541 remove : function(panel, preservePanel){
38542 panel = this.getPanel(panel);
38547 this.fireEvent("beforeremove", this, panel, e);
38548 if(e.cancel === true){
38551 var panelId = panel.getId();
38552 this.panels.removeKey(panelId);
38557 * Returns the panel specified or null if it's not in this region.
38558 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38559 * @return {Roo.ContentPanel}
38561 getPanel : function(id){
38562 if(typeof id == "object"){ // must be panel obj
38565 return this.panels.get(id);
38569 * Returns this regions position (north/south/east/west/center).
38572 getPosition: function(){
38573 return this.position;
38577 * Ext JS Library 1.1.1
38578 * Copyright(c) 2006-2007, Ext JS, LLC.
38580 * Originally Released Under LGPL - original licence link has changed is not relivant.
38583 * <script type="text/javascript">
38587 * @class Roo.bootstrap.layout.Region
38588 * @extends Roo.bootstrap.layout.Basic
38589 * This class represents a region in a layout manager.
38591 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38592 * @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})
38593 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38594 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38595 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38596 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38597 * @cfg {String} title The title for the region (overrides panel titles)
38598 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38599 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38600 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38601 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38602 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38603 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38604 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38605 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38606 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38607 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38609 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38610 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38611 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38612 * @cfg {Number} width For East/West panels
38613 * @cfg {Number} height For North/South panels
38614 * @cfg {Boolean} split To show the splitter
38615 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38617 * @cfg {string} cls Extra CSS classes to add to region
38619 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38620 * @cfg {string} region the region that it inhabits..
38623 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38624 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38626 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38627 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38628 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38630 Roo.bootstrap.layout.Region = function(config)
38632 this.applyConfig(config);
38634 var mgr = config.mgr;
38635 var pos = config.region;
38636 config.skipConfig = true;
38637 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38640 this.onRender(mgr.el);
38643 this.visible = true;
38644 this.collapsed = false;
38645 this.unrendered_panels = [];
38648 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38650 position: '', // set by wrapper (eg. north/south etc..)
38651 unrendered_panels : null, // unrendered panels.
38653 tabPosition : false,
38655 mgr: false, // points to 'Border'
38658 createBody : function(){
38659 /** This region's body element
38660 * @type Roo.Element */
38661 this.bodyEl = this.el.createChild({
38663 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38667 onRender: function(ctr, pos)
38669 var dh = Roo.DomHelper;
38670 /** This region's container element
38671 * @type Roo.Element */
38672 this.el = dh.append(ctr.dom, {
38674 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38676 /** This region's title element
38677 * @type Roo.Element */
38679 this.titleEl = dh.append(this.el.dom, {
38681 unselectable: "on",
38682 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38684 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38685 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38689 this.titleEl.enableDisplayMode();
38690 /** This region's title text element
38691 * @type HTMLElement */
38692 this.titleTextEl = this.titleEl.dom.firstChild;
38693 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38695 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38696 this.closeBtn.enableDisplayMode();
38697 this.closeBtn.on("click", this.closeClicked, this);
38698 this.closeBtn.hide();
38700 this.createBody(this.config);
38701 if(this.config.hideWhenEmpty){
38703 this.on("paneladded", this.validateVisibility, this);
38704 this.on("panelremoved", this.validateVisibility, this);
38706 if(this.autoScroll){
38707 this.bodyEl.setStyle("overflow", "auto");
38709 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38711 //if(c.titlebar !== false){
38712 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38713 this.titleEl.hide();
38715 this.titleEl.show();
38716 if(this.config.title){
38717 this.titleTextEl.innerHTML = this.config.title;
38721 if(this.config.collapsed){
38722 this.collapse(true);
38724 if(this.config.hidden){
38728 if (this.unrendered_panels && this.unrendered_panels.length) {
38729 for (var i =0;i< this.unrendered_panels.length; i++) {
38730 this.add(this.unrendered_panels[i]);
38732 this.unrendered_panels = null;
38738 applyConfig : function(c)
38741 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38742 var dh = Roo.DomHelper;
38743 if(c.titlebar !== false){
38744 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38745 this.collapseBtn.on("click", this.collapse, this);
38746 this.collapseBtn.enableDisplayMode();
38748 if(c.showPin === true || this.showPin){
38749 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38750 this.stickBtn.enableDisplayMode();
38751 this.stickBtn.on("click", this.expand, this);
38752 this.stickBtn.hide();
38757 /** This region's collapsed element
38758 * @type Roo.Element */
38761 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38762 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38765 if(c.floatable !== false){
38766 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38767 this.collapsedEl.on("click", this.collapseClick, this);
38770 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38771 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38772 id: "message", unselectable: "on", style:{"float":"left"}});
38773 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38775 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38776 this.expandBtn.on("click", this.expand, this);
38780 if(this.collapseBtn){
38781 this.collapseBtn.setVisible(c.collapsible == true);
38784 this.cmargins = c.cmargins || this.cmargins ||
38785 (this.position == "west" || this.position == "east" ?
38786 {top: 0, left: 2, right:2, bottom: 0} :
38787 {top: 2, left: 0, right:0, bottom: 2});
38789 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38792 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38794 this.autoScroll = c.autoScroll || false;
38799 this.duration = c.duration || .30;
38800 this.slideDuration = c.slideDuration || .45;
38805 * Returns true if this region is currently visible.
38806 * @return {Boolean}
38808 isVisible : function(){
38809 return this.visible;
38813 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38814 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38816 //setCollapsedTitle : function(title){
38817 // title = title || " ";
38818 // if(this.collapsedTitleTextEl){
38819 // this.collapsedTitleTextEl.innerHTML = title;
38823 getBox : function(){
38825 // if(!this.collapsed){
38826 b = this.el.getBox(false, true);
38828 // b = this.collapsedEl.getBox(false, true);
38833 getMargins : function(){
38834 return this.margins;
38835 //return this.collapsed ? this.cmargins : this.margins;
38838 highlight : function(){
38839 this.el.addClass("x-layout-panel-dragover");
38842 unhighlight : function(){
38843 this.el.removeClass("x-layout-panel-dragover");
38846 updateBox : function(box)
38848 if (!this.bodyEl) {
38849 return; // not rendered yet..
38853 if(!this.collapsed){
38854 this.el.dom.style.left = box.x + "px";
38855 this.el.dom.style.top = box.y + "px";
38856 this.updateBody(box.width, box.height);
38858 this.collapsedEl.dom.style.left = box.x + "px";
38859 this.collapsedEl.dom.style.top = box.y + "px";
38860 this.collapsedEl.setSize(box.width, box.height);
38863 this.tabs.autoSizeTabs();
38867 updateBody : function(w, h)
38870 this.el.setWidth(w);
38871 w -= this.el.getBorderWidth("rl");
38872 if(this.config.adjustments){
38873 w += this.config.adjustments[0];
38876 if(h !== null && h > 0){
38877 this.el.setHeight(h);
38878 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38879 h -= this.el.getBorderWidth("tb");
38880 if(this.config.adjustments){
38881 h += this.config.adjustments[1];
38883 this.bodyEl.setHeight(h);
38885 h = this.tabs.syncHeight(h);
38888 if(this.panelSize){
38889 w = w !== null ? w : this.panelSize.width;
38890 h = h !== null ? h : this.panelSize.height;
38892 if(this.activePanel){
38893 var el = this.activePanel.getEl();
38894 w = w !== null ? w : el.getWidth();
38895 h = h !== null ? h : el.getHeight();
38896 this.panelSize = {width: w, height: h};
38897 this.activePanel.setSize(w, h);
38899 if(Roo.isIE && this.tabs){
38900 this.tabs.el.repaint();
38905 * Returns the container element for this region.
38906 * @return {Roo.Element}
38908 getEl : function(){
38913 * Hides this region.
38916 //if(!this.collapsed){
38917 this.el.dom.style.left = "-2000px";
38920 // this.collapsedEl.dom.style.left = "-2000px";
38921 // this.collapsedEl.hide();
38923 this.visible = false;
38924 this.fireEvent("visibilitychange", this, false);
38928 * Shows this region if it was previously hidden.
38931 //if(!this.collapsed){
38934 // this.collapsedEl.show();
38936 this.visible = true;
38937 this.fireEvent("visibilitychange", this, true);
38940 closeClicked : function(){
38941 if(this.activePanel){
38942 this.remove(this.activePanel);
38946 collapseClick : function(e){
38948 e.stopPropagation();
38951 e.stopPropagation();
38957 * Collapses this region.
38958 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38961 collapse : function(skipAnim, skipCheck = false){
38962 if(this.collapsed) {
38966 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38968 this.collapsed = true;
38970 this.split.el.hide();
38972 if(this.config.animate && skipAnim !== true){
38973 this.fireEvent("invalidated", this);
38974 this.animateCollapse();
38976 this.el.setLocation(-20000,-20000);
38978 this.collapsedEl.show();
38979 this.fireEvent("collapsed", this);
38980 this.fireEvent("invalidated", this);
38986 animateCollapse : function(){
38991 * Expands this region if it was previously collapsed.
38992 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38993 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38996 expand : function(e, skipAnim){
38998 e.stopPropagation();
39000 if(!this.collapsed || this.el.hasActiveFx()) {
39004 this.afterSlideIn();
39007 this.collapsed = false;
39008 if(this.config.animate && skipAnim !== true){
39009 this.animateExpand();
39013 this.split.el.show();
39015 this.collapsedEl.setLocation(-2000,-2000);
39016 this.collapsedEl.hide();
39017 this.fireEvent("invalidated", this);
39018 this.fireEvent("expanded", this);
39022 animateExpand : function(){
39026 initTabs : function()
39028 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39030 var ts = new Roo.bootstrap.panel.Tabs({
39031 el: this.bodyEl.dom,
39033 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39034 disableTooltips: this.config.disableTabTips,
39035 toolbar : this.config.toolbar
39038 if(this.config.hideTabs){
39039 ts.stripWrap.setDisplayed(false);
39042 ts.resizeTabs = this.config.resizeTabs === true;
39043 ts.minTabWidth = this.config.minTabWidth || 40;
39044 ts.maxTabWidth = this.config.maxTabWidth || 250;
39045 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39046 ts.monitorResize = false;
39047 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39048 ts.bodyEl.addClass('roo-layout-tabs-body');
39049 this.panels.each(this.initPanelAsTab, this);
39052 initPanelAsTab : function(panel){
39053 var ti = this.tabs.addTab(
39057 this.config.closeOnTab && panel.isClosable(),
39060 if(panel.tabTip !== undefined){
39061 ti.setTooltip(panel.tabTip);
39063 ti.on("activate", function(){
39064 this.setActivePanel(panel);
39067 if(this.config.closeOnTab){
39068 ti.on("beforeclose", function(t, e){
39070 this.remove(panel);
39074 panel.tabItem = ti;
39079 updatePanelTitle : function(panel, title)
39081 if(this.activePanel == panel){
39082 this.updateTitle(title);
39085 var ti = this.tabs.getTab(panel.getEl().id);
39087 if(panel.tabTip !== undefined){
39088 ti.setTooltip(panel.tabTip);
39093 updateTitle : function(title){
39094 if(this.titleTextEl && !this.config.title){
39095 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39099 setActivePanel : function(panel)
39101 panel = this.getPanel(panel);
39102 if(this.activePanel && this.activePanel != panel){
39103 if(this.activePanel.setActiveState(false) === false){
39107 this.activePanel = panel;
39108 panel.setActiveState(true);
39109 if(this.panelSize){
39110 panel.setSize(this.panelSize.width, this.panelSize.height);
39113 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39115 this.updateTitle(panel.getTitle());
39117 this.fireEvent("invalidated", this);
39119 this.fireEvent("panelactivated", this, panel);
39123 * Shows the specified panel.
39124 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39125 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39127 showPanel : function(panel)
39129 panel = this.getPanel(panel);
39132 var tab = this.tabs.getTab(panel.getEl().id);
39133 if(tab.isHidden()){
39134 this.tabs.unhideTab(tab.id);
39138 this.setActivePanel(panel);
39145 * Get the active panel for this region.
39146 * @return {Roo.ContentPanel} The active panel or null
39148 getActivePanel : function(){
39149 return this.activePanel;
39152 validateVisibility : function(){
39153 if(this.panels.getCount() < 1){
39154 this.updateTitle(" ");
39155 this.closeBtn.hide();
39158 if(!this.isVisible()){
39165 * Adds the passed ContentPanel(s) to this region.
39166 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39167 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39169 add : function(panel)
39171 if(arguments.length > 1){
39172 for(var i = 0, len = arguments.length; i < len; i++) {
39173 this.add(arguments[i]);
39178 // if we have not been rendered yet, then we can not really do much of this..
39179 if (!this.bodyEl) {
39180 this.unrendered_panels.push(panel);
39187 if(this.hasPanel(panel)){
39188 this.showPanel(panel);
39191 panel.setRegion(this);
39192 this.panels.add(panel);
39193 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39194 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39195 // and hide them... ???
39196 this.bodyEl.dom.appendChild(panel.getEl().dom);
39197 if(panel.background !== true){
39198 this.setActivePanel(panel);
39200 this.fireEvent("paneladded", this, panel);
39207 this.initPanelAsTab(panel);
39211 if(panel.background !== true){
39212 this.tabs.activate(panel.getEl().id);
39214 this.fireEvent("paneladded", this, panel);
39219 * Hides the tab for the specified panel.
39220 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39222 hidePanel : function(panel){
39223 if(this.tabs && (panel = this.getPanel(panel))){
39224 this.tabs.hideTab(panel.getEl().id);
39229 * Unhides the tab for a previously hidden panel.
39230 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39232 unhidePanel : function(panel){
39233 if(this.tabs && (panel = this.getPanel(panel))){
39234 this.tabs.unhideTab(panel.getEl().id);
39238 clearPanels : function(){
39239 while(this.panels.getCount() > 0){
39240 this.remove(this.panels.first());
39245 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39246 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39247 * @param {Boolean} preservePanel Overrides the config preservePanel option
39248 * @return {Roo.ContentPanel} The panel that was removed
39250 remove : function(panel, preservePanel)
39252 panel = this.getPanel(panel);
39257 this.fireEvent("beforeremove", this, panel, e);
39258 if(e.cancel === true){
39261 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39262 var panelId = panel.getId();
39263 this.panels.removeKey(panelId);
39265 document.body.appendChild(panel.getEl().dom);
39268 this.tabs.removeTab(panel.getEl().id);
39269 }else if (!preservePanel){
39270 this.bodyEl.dom.removeChild(panel.getEl().dom);
39272 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39273 var p = this.panels.first();
39274 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39275 tempEl.appendChild(p.getEl().dom);
39276 this.bodyEl.update("");
39277 this.bodyEl.dom.appendChild(p.getEl().dom);
39279 this.updateTitle(p.getTitle());
39281 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39282 this.setActivePanel(p);
39284 panel.setRegion(null);
39285 if(this.activePanel == panel){
39286 this.activePanel = null;
39288 if(this.config.autoDestroy !== false && preservePanel !== true){
39289 try{panel.destroy();}catch(e){}
39291 this.fireEvent("panelremoved", this, panel);
39296 * Returns the TabPanel component used by this region
39297 * @return {Roo.TabPanel}
39299 getTabs : function(){
39303 createTool : function(parentEl, className){
39304 var btn = Roo.DomHelper.append(parentEl, {
39306 cls: "x-layout-tools-button",
39309 cls: "roo-layout-tools-button-inner " + className,
39313 btn.addClassOnOver("roo-layout-tools-button-over");
39318 * Ext JS Library 1.1.1
39319 * Copyright(c) 2006-2007, Ext JS, LLC.
39321 * Originally Released Under LGPL - original licence link has changed is not relivant.
39324 * <script type="text/javascript">
39330 * @class Roo.SplitLayoutRegion
39331 * @extends Roo.LayoutRegion
39332 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39334 Roo.bootstrap.layout.Split = function(config){
39335 this.cursor = config.cursor;
39336 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39339 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39341 splitTip : "Drag to resize.",
39342 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39343 useSplitTips : false,
39345 applyConfig : function(config){
39346 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39349 onRender : function(ctr,pos) {
39351 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39352 if(!this.config.split){
39357 var splitEl = Roo.DomHelper.append(ctr.dom, {
39359 id: this.el.id + "-split",
39360 cls: "roo-layout-split roo-layout-split-"+this.position,
39363 /** The SplitBar for this region
39364 * @type Roo.SplitBar */
39365 // does not exist yet...
39366 Roo.log([this.position, this.orientation]);
39368 this.split = new Roo.bootstrap.SplitBar({
39369 dragElement : splitEl,
39370 resizingElement: this.el,
39371 orientation : this.orientation
39374 this.split.on("moved", this.onSplitMove, this);
39375 this.split.useShim = this.config.useShim === true;
39376 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39377 if(this.useSplitTips){
39378 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39380 //if(config.collapsible){
39381 // this.split.el.on("dblclick", this.collapse, this);
39384 if(typeof this.config.minSize != "undefined"){
39385 this.split.minSize = this.config.minSize;
39387 if(typeof this.config.maxSize != "undefined"){
39388 this.split.maxSize = this.config.maxSize;
39390 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39391 this.hideSplitter();
39396 getHMaxSize : function(){
39397 var cmax = this.config.maxSize || 10000;
39398 var center = this.mgr.getRegion("center");
39399 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39402 getVMaxSize : function(){
39403 var cmax = this.config.maxSize || 10000;
39404 var center = this.mgr.getRegion("center");
39405 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39408 onSplitMove : function(split, newSize){
39409 this.fireEvent("resized", this, newSize);
39413 * Returns the {@link Roo.SplitBar} for this region.
39414 * @return {Roo.SplitBar}
39416 getSplitBar : function(){
39421 this.hideSplitter();
39422 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39425 hideSplitter : function(){
39427 this.split.el.setLocation(-2000,-2000);
39428 this.split.el.hide();
39434 this.split.el.show();
39436 Roo.bootstrap.layout.Split.superclass.show.call(this);
39439 beforeSlide: function(){
39440 if(Roo.isGecko){// firefox overflow auto bug workaround
39441 this.bodyEl.clip();
39443 this.tabs.bodyEl.clip();
39445 if(this.activePanel){
39446 this.activePanel.getEl().clip();
39448 if(this.activePanel.beforeSlide){
39449 this.activePanel.beforeSlide();
39455 afterSlide : function(){
39456 if(Roo.isGecko){// firefox overflow auto bug workaround
39457 this.bodyEl.unclip();
39459 this.tabs.bodyEl.unclip();
39461 if(this.activePanel){
39462 this.activePanel.getEl().unclip();
39463 if(this.activePanel.afterSlide){
39464 this.activePanel.afterSlide();
39470 initAutoHide : function(){
39471 if(this.autoHide !== false){
39472 if(!this.autoHideHd){
39473 var st = new Roo.util.DelayedTask(this.slideIn, this);
39474 this.autoHideHd = {
39475 "mouseout": function(e){
39476 if(!e.within(this.el, true)){
39480 "mouseover" : function(e){
39486 this.el.on(this.autoHideHd);
39490 clearAutoHide : function(){
39491 if(this.autoHide !== false){
39492 this.el.un("mouseout", this.autoHideHd.mouseout);
39493 this.el.un("mouseover", this.autoHideHd.mouseover);
39497 clearMonitor : function(){
39498 Roo.get(document).un("click", this.slideInIf, this);
39501 // these names are backwards but not changed for compat
39502 slideOut : function(){
39503 if(this.isSlid || this.el.hasActiveFx()){
39506 this.isSlid = true;
39507 if(this.collapseBtn){
39508 this.collapseBtn.hide();
39510 this.closeBtnState = this.closeBtn.getStyle('display');
39511 this.closeBtn.hide();
39513 this.stickBtn.show();
39516 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39517 this.beforeSlide();
39518 this.el.setStyle("z-index", 10001);
39519 this.el.slideIn(this.getSlideAnchor(), {
39520 callback: function(){
39522 this.initAutoHide();
39523 Roo.get(document).on("click", this.slideInIf, this);
39524 this.fireEvent("slideshow", this);
39531 afterSlideIn : function(){
39532 this.clearAutoHide();
39533 this.isSlid = false;
39534 this.clearMonitor();
39535 this.el.setStyle("z-index", "");
39536 if(this.collapseBtn){
39537 this.collapseBtn.show();
39539 this.closeBtn.setStyle('display', this.closeBtnState);
39541 this.stickBtn.hide();
39543 this.fireEvent("slidehide", this);
39546 slideIn : function(cb){
39547 if(!this.isSlid || this.el.hasActiveFx()){
39551 this.isSlid = false;
39552 this.beforeSlide();
39553 this.el.slideOut(this.getSlideAnchor(), {
39554 callback: function(){
39555 this.el.setLeftTop(-10000, -10000);
39557 this.afterSlideIn();
39565 slideInIf : function(e){
39566 if(!e.within(this.el)){
39571 animateCollapse : function(){
39572 this.beforeSlide();
39573 this.el.setStyle("z-index", 20000);
39574 var anchor = this.getSlideAnchor();
39575 this.el.slideOut(anchor, {
39576 callback : function(){
39577 this.el.setStyle("z-index", "");
39578 this.collapsedEl.slideIn(anchor, {duration:.3});
39580 this.el.setLocation(-10000,-10000);
39582 this.fireEvent("collapsed", this);
39589 animateExpand : function(){
39590 this.beforeSlide();
39591 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39592 this.el.setStyle("z-index", 20000);
39593 this.collapsedEl.hide({
39596 this.el.slideIn(this.getSlideAnchor(), {
39597 callback : function(){
39598 this.el.setStyle("z-index", "");
39601 this.split.el.show();
39603 this.fireEvent("invalidated", this);
39604 this.fireEvent("expanded", this);
39632 getAnchor : function(){
39633 return this.anchors[this.position];
39636 getCollapseAnchor : function(){
39637 return this.canchors[this.position];
39640 getSlideAnchor : function(){
39641 return this.sanchors[this.position];
39644 getAlignAdj : function(){
39645 var cm = this.cmargins;
39646 switch(this.position){
39662 getExpandAdj : function(){
39663 var c = this.collapsedEl, cm = this.cmargins;
39664 switch(this.position){
39666 return [-(cm.right+c.getWidth()+cm.left), 0];
39669 return [cm.right+c.getWidth()+cm.left, 0];
39672 return [0, -(cm.top+cm.bottom+c.getHeight())];
39675 return [0, cm.top+cm.bottom+c.getHeight()];
39681 * Ext JS Library 1.1.1
39682 * Copyright(c) 2006-2007, Ext JS, LLC.
39684 * Originally Released Under LGPL - original licence link has changed is not relivant.
39687 * <script type="text/javascript">
39690 * These classes are private internal classes
39692 Roo.bootstrap.layout.Center = function(config){
39693 config.region = "center";
39694 Roo.bootstrap.layout.Region.call(this, config);
39695 this.visible = true;
39696 this.minWidth = config.minWidth || 20;
39697 this.minHeight = config.minHeight || 20;
39700 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39702 // center panel can't be hidden
39706 // center panel can't be hidden
39709 getMinWidth: function(){
39710 return this.minWidth;
39713 getMinHeight: function(){
39714 return this.minHeight;
39728 Roo.bootstrap.layout.North = function(config)
39730 config.region = 'north';
39731 config.cursor = 'n-resize';
39733 Roo.bootstrap.layout.Split.call(this, config);
39737 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39738 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39739 this.split.el.addClass("roo-layout-split-v");
39741 //var size = config.initialSize || config.height;
39742 //if(this.el && typeof size != "undefined"){
39743 // this.el.setHeight(size);
39746 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39748 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39751 onRender : function(ctr, pos)
39753 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39754 var size = this.config.initialSize || this.config.height;
39755 if(this.el && typeof size != "undefined"){
39756 this.el.setHeight(size);
39761 getBox : function(){
39762 if(this.collapsed){
39763 return this.collapsedEl.getBox();
39765 var box = this.el.getBox();
39767 box.height += this.split.el.getHeight();
39772 updateBox : function(box){
39773 if(this.split && !this.collapsed){
39774 box.height -= this.split.el.getHeight();
39775 this.split.el.setLeft(box.x);
39776 this.split.el.setTop(box.y+box.height);
39777 this.split.el.setWidth(box.width);
39779 if(this.collapsed){
39780 this.updateBody(box.width, null);
39782 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39790 Roo.bootstrap.layout.South = function(config){
39791 config.region = 'south';
39792 config.cursor = 's-resize';
39793 Roo.bootstrap.layout.Split.call(this, config);
39795 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39796 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39797 this.split.el.addClass("roo-layout-split-v");
39802 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39803 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39805 onRender : function(ctr, pos)
39807 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39808 var size = this.config.initialSize || this.config.height;
39809 if(this.el && typeof size != "undefined"){
39810 this.el.setHeight(size);
39815 getBox : function(){
39816 if(this.collapsed){
39817 return this.collapsedEl.getBox();
39819 var box = this.el.getBox();
39821 var sh = this.split.el.getHeight();
39828 updateBox : function(box){
39829 if(this.split && !this.collapsed){
39830 var sh = this.split.el.getHeight();
39833 this.split.el.setLeft(box.x);
39834 this.split.el.setTop(box.y-sh);
39835 this.split.el.setWidth(box.width);
39837 if(this.collapsed){
39838 this.updateBody(box.width, null);
39840 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39844 Roo.bootstrap.layout.East = function(config){
39845 config.region = "east";
39846 config.cursor = "e-resize";
39847 Roo.bootstrap.layout.Split.call(this, config);
39849 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39850 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39851 this.split.el.addClass("roo-layout-split-h");
39855 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39856 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39858 onRender : function(ctr, pos)
39860 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39861 var size = this.config.initialSize || this.config.width;
39862 if(this.el && typeof size != "undefined"){
39863 this.el.setWidth(size);
39868 getBox : function(){
39869 if(this.collapsed){
39870 return this.collapsedEl.getBox();
39872 var box = this.el.getBox();
39874 var sw = this.split.el.getWidth();
39881 updateBox : function(box){
39882 if(this.split && !this.collapsed){
39883 var sw = this.split.el.getWidth();
39885 this.split.el.setLeft(box.x);
39886 this.split.el.setTop(box.y);
39887 this.split.el.setHeight(box.height);
39890 if(this.collapsed){
39891 this.updateBody(null, box.height);
39893 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39897 Roo.bootstrap.layout.West = function(config){
39898 config.region = "west";
39899 config.cursor = "w-resize";
39901 Roo.bootstrap.layout.Split.call(this, config);
39903 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39904 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39905 this.split.el.addClass("roo-layout-split-h");
39909 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39910 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39912 onRender: function(ctr, pos)
39914 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39915 var size = this.config.initialSize || this.config.width;
39916 if(typeof size != "undefined"){
39917 this.el.setWidth(size);
39921 getBox : function(){
39922 if(this.collapsed){
39923 return this.collapsedEl.getBox();
39925 var box = this.el.getBox();
39926 if (box.width == 0) {
39927 box.width = this.config.width; // kludge?
39930 box.width += this.split.el.getWidth();
39935 updateBox : function(box){
39936 if(this.split && !this.collapsed){
39937 var sw = this.split.el.getWidth();
39939 this.split.el.setLeft(box.x+box.width);
39940 this.split.el.setTop(box.y);
39941 this.split.el.setHeight(box.height);
39943 if(this.collapsed){
39944 this.updateBody(null, box.height);
39946 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39948 });Roo.namespace("Roo.bootstrap.panel");/*
39950 * Ext JS Library 1.1.1
39951 * Copyright(c) 2006-2007, Ext JS, LLC.
39953 * Originally Released Under LGPL - original licence link has changed is not relivant.
39956 * <script type="text/javascript">
39959 * @class Roo.ContentPanel
39960 * @extends Roo.util.Observable
39961 * A basic ContentPanel element.
39962 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39963 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39964 * @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
39965 * @cfg {Boolean} closable True if the panel can be closed/removed
39966 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39967 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39968 * @cfg {Toolbar} toolbar A toolbar for this panel
39969 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39970 * @cfg {String} title The title for this panel
39971 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39972 * @cfg {String} url Calls {@link #setUrl} with this value
39973 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39974 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39975 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39976 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39977 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39978 * @cfg {Boolean} badges render the badges
39979 * @cfg {String} cls extra classes to use
39980 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39983 * Create a new ContentPanel.
39984 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39985 * @param {String/Object} config A string to set only the title or a config object
39986 * @param {String} content (optional) Set the HTML content for this panel
39987 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39989 Roo.bootstrap.panel.Content = function( config){
39991 this.tpl = config.tpl || false;
39993 var el = config.el;
39994 var content = config.content;
39996 if(config.autoCreate){ // xtype is available if this is called from factory
39999 this.el = Roo.get(el);
40000 if(!this.el && config && config.autoCreate){
40001 if(typeof config.autoCreate == "object"){
40002 if(!config.autoCreate.id){
40003 config.autoCreate.id = config.id||el;
40005 this.el = Roo.DomHelper.append(document.body,
40006 config.autoCreate, true);
40010 cls: (config.cls || '') +
40011 (config.background ? ' bg-' + config.background : '') +
40012 " roo-layout-inactive-content",
40015 if (config.iframe) {
40019 style : 'border: 0px',
40020 src : 'about:blank'
40026 elcfg.html = config.html;
40030 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40031 if (config.iframe) {
40032 this.iframeEl = this.el.select('iframe',true).first();
40037 this.closable = false;
40038 this.loaded = false;
40039 this.active = false;
40042 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40044 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40046 this.wrapEl = this.el; //this.el.wrap();
40048 if (config.toolbar.items) {
40049 ti = config.toolbar.items ;
40050 delete config.toolbar.items ;
40054 this.toolbar.render(this.wrapEl, 'before');
40055 for(var i =0;i < ti.length;i++) {
40056 // Roo.log(['add child', items[i]]);
40057 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40059 this.toolbar.items = nitems;
40060 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40061 delete config.toolbar;
40065 // xtype created footer. - not sure if will work as we normally have to render first..
40066 if (this.footer && !this.footer.el && this.footer.xtype) {
40067 if (!this.wrapEl) {
40068 this.wrapEl = this.el.wrap();
40071 this.footer.container = this.wrapEl.createChild();
40073 this.footer = Roo.factory(this.footer, Roo);
40078 if(typeof config == "string"){
40079 this.title = config;
40081 Roo.apply(this, config);
40085 this.resizeEl = Roo.get(this.resizeEl, true);
40087 this.resizeEl = this.el;
40089 // handle view.xtype
40097 * Fires when this panel is activated.
40098 * @param {Roo.ContentPanel} this
40102 * @event deactivate
40103 * Fires when this panel is activated.
40104 * @param {Roo.ContentPanel} this
40106 "deactivate" : true,
40110 * Fires when this panel is resized if fitToFrame is true.
40111 * @param {Roo.ContentPanel} this
40112 * @param {Number} width The width after any component adjustments
40113 * @param {Number} height The height after any component adjustments
40119 * Fires when this tab is created
40120 * @param {Roo.ContentPanel} this
40131 if(this.autoScroll && !this.iframe){
40132 this.resizeEl.setStyle("overflow", "auto");
40134 // fix randome scrolling
40135 //this.el.on('scroll', function() {
40136 // Roo.log('fix random scolling');
40137 // this.scrollTo('top',0);
40140 content = content || this.content;
40142 this.setContent(content);
40144 if(config && config.url){
40145 this.setUrl(this.url, this.params, this.loadOnce);
40150 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40152 if (this.view && typeof(this.view.xtype) != 'undefined') {
40153 this.view.el = this.el.appendChild(document.createElement("div"));
40154 this.view = Roo.factory(this.view);
40155 this.view.render && this.view.render(false, '');
40159 this.fireEvent('render', this);
40162 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40172 setRegion : function(region){
40173 this.region = region;
40174 this.setActiveClass(region && !this.background);
40178 setActiveClass: function(state)
40181 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40182 this.el.setStyle('position','relative');
40184 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40185 this.el.setStyle('position', 'absolute');
40190 * Returns the toolbar for this Panel if one was configured.
40191 * @return {Roo.Toolbar}
40193 getToolbar : function(){
40194 return this.toolbar;
40197 setActiveState : function(active)
40199 this.active = active;
40200 this.setActiveClass(active);
40202 if(this.fireEvent("deactivate", this) === false){
40207 this.fireEvent("activate", this);
40211 * Updates this panel's element (not for iframe)
40212 * @param {String} content The new content
40213 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40215 setContent : function(content, loadScripts){
40220 this.el.update(content, loadScripts);
40223 ignoreResize : function(w, h){
40224 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40227 this.lastSize = {width: w, height: h};
40232 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40233 * @return {Roo.UpdateManager} The UpdateManager
40235 getUpdateManager : function(){
40239 return this.el.getUpdateManager();
40242 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40243 * Does not work with IFRAME contents
40244 * @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:
40247 url: "your-url.php",
40248 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40249 callback: yourFunction,
40250 scope: yourObject, //(optional scope)
40253 text: "Loading...",
40259 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40260 * 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.
40261 * @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}
40262 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40263 * @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.
40264 * @return {Roo.ContentPanel} this
40272 var um = this.el.getUpdateManager();
40273 um.update.apply(um, arguments);
40279 * 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.
40280 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40281 * @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)
40282 * @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)
40283 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40285 setUrl : function(url, params, loadOnce){
40287 this.iframeEl.dom.src = url;
40291 if(this.refreshDelegate){
40292 this.removeListener("activate", this.refreshDelegate);
40294 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40295 this.on("activate", this.refreshDelegate);
40296 return this.el.getUpdateManager();
40299 _handleRefresh : function(url, params, loadOnce){
40300 if(!loadOnce || !this.loaded){
40301 var updater = this.el.getUpdateManager();
40302 updater.update(url, params, this._setLoaded.createDelegate(this));
40306 _setLoaded : function(){
40307 this.loaded = true;
40311 * Returns this panel's id
40314 getId : function(){
40319 * Returns this panel's element - used by regiosn to add.
40320 * @return {Roo.Element}
40322 getEl : function(){
40323 return this.wrapEl || this.el;
40328 adjustForComponents : function(width, height)
40330 //Roo.log('adjustForComponents ');
40331 if(this.resizeEl != this.el){
40332 width -= this.el.getFrameWidth('lr');
40333 height -= this.el.getFrameWidth('tb');
40336 var te = this.toolbar.getEl();
40337 te.setWidth(width);
40338 height -= te.getHeight();
40341 var te = this.footer.getEl();
40342 te.setWidth(width);
40343 height -= te.getHeight();
40347 if(this.adjustments){
40348 width += this.adjustments[0];
40349 height += this.adjustments[1];
40351 return {"width": width, "height": height};
40354 setSize : function(width, height){
40355 if(this.fitToFrame && !this.ignoreResize(width, height)){
40356 if(this.fitContainer && this.resizeEl != this.el){
40357 this.el.setSize(width, height);
40359 var size = this.adjustForComponents(width, height);
40361 this.iframeEl.setSize(width,height);
40364 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40365 this.fireEvent('resize', this, size.width, size.height);
40372 * Returns this panel's title
40375 getTitle : function(){
40377 if (typeof(this.title) != 'object') {
40382 for (var k in this.title) {
40383 if (!this.title.hasOwnProperty(k)) {
40387 if (k.indexOf('-') >= 0) {
40388 var s = k.split('-');
40389 for (var i = 0; i<s.length; i++) {
40390 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40393 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40400 * Set this panel's title
40401 * @param {String} title
40403 setTitle : function(title){
40404 this.title = title;
40406 this.region.updatePanelTitle(this, title);
40411 * Returns true is this panel was configured to be closable
40412 * @return {Boolean}
40414 isClosable : function(){
40415 return this.closable;
40418 beforeSlide : function(){
40420 this.resizeEl.clip();
40423 afterSlide : function(){
40425 this.resizeEl.unclip();
40429 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40430 * Will fail silently if the {@link #setUrl} method has not been called.
40431 * This does not activate the panel, just updates its content.
40433 refresh : function(){
40434 if(this.refreshDelegate){
40435 this.loaded = false;
40436 this.refreshDelegate();
40441 * Destroys this panel
40443 destroy : function(){
40444 this.el.removeAllListeners();
40445 var tempEl = document.createElement("span");
40446 tempEl.appendChild(this.el.dom);
40447 tempEl.innerHTML = "";
40453 * form - if the content panel contains a form - this is a reference to it.
40454 * @type {Roo.form.Form}
40458 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40459 * This contains a reference to it.
40465 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40475 * @param {Object} cfg Xtype definition of item to add.
40479 getChildContainer: function () {
40480 return this.getEl();
40485 var ret = new Roo.factory(cfg);
40490 if (cfg.xtype.match(/^Form$/)) {
40493 //if (this.footer) {
40494 // el = this.footer.container.insertSibling(false, 'before');
40496 el = this.el.createChild();
40499 this.form = new Roo.form.Form(cfg);
40502 if ( this.form.allItems.length) {
40503 this.form.render(el.dom);
40507 // should only have one of theses..
40508 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40509 // views.. should not be just added - used named prop 'view''
40511 cfg.el = this.el.appendChild(document.createElement("div"));
40514 var ret = new Roo.factory(cfg);
40516 ret.render && ret.render(false, ''); // render blank..
40526 * @class Roo.bootstrap.panel.Grid
40527 * @extends Roo.bootstrap.panel.Content
40529 * Create a new GridPanel.
40530 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40531 * @param {Object} config A the config object
40537 Roo.bootstrap.panel.Grid = function(config)
40541 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40542 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40544 config.el = this.wrapper;
40545 //this.el = this.wrapper;
40547 if (config.container) {
40548 // ctor'ed from a Border/panel.grid
40551 this.wrapper.setStyle("overflow", "hidden");
40552 this.wrapper.addClass('roo-grid-container');
40557 if(config.toolbar){
40558 var tool_el = this.wrapper.createChild();
40559 this.toolbar = Roo.factory(config.toolbar);
40561 if (config.toolbar.items) {
40562 ti = config.toolbar.items ;
40563 delete config.toolbar.items ;
40567 this.toolbar.render(tool_el);
40568 for(var i =0;i < ti.length;i++) {
40569 // Roo.log(['add child', items[i]]);
40570 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40572 this.toolbar.items = nitems;
40574 delete config.toolbar;
40577 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40578 config.grid.scrollBody = true;;
40579 config.grid.monitorWindowResize = false; // turn off autosizing
40580 config.grid.autoHeight = false;
40581 config.grid.autoWidth = false;
40583 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40585 if (config.background) {
40586 // render grid on panel activation (if panel background)
40587 this.on('activate', function(gp) {
40588 if (!gp.grid.rendered) {
40589 gp.grid.render(this.wrapper);
40590 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40595 this.grid.render(this.wrapper);
40596 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40599 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40600 // ??? needed ??? config.el = this.wrapper;
40605 // xtype created footer. - not sure if will work as we normally have to render first..
40606 if (this.footer && !this.footer.el && this.footer.xtype) {
40608 var ctr = this.grid.getView().getFooterPanel(true);
40609 this.footer.dataSource = this.grid.dataSource;
40610 this.footer = Roo.factory(this.footer, Roo);
40611 this.footer.render(ctr);
40621 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40622 getId : function(){
40623 return this.grid.id;
40627 * Returns the grid for this panel
40628 * @return {Roo.bootstrap.Table}
40630 getGrid : function(){
40634 setSize : function(width, height){
40635 if(!this.ignoreResize(width, height)){
40636 var grid = this.grid;
40637 var size = this.adjustForComponents(width, height);
40638 // tfoot is not a footer?
40641 var gridel = grid.getGridEl();
40642 gridel.setSize(size.width, size.height);
40644 var tbd = grid.getGridEl().select('tbody', true).first();
40645 var thd = grid.getGridEl().select('thead',true).first();
40646 var tbf= grid.getGridEl().select('tfoot', true).first();
40649 size.height -= tbf.getHeight();
40652 size.height -= thd.getHeight();
40655 tbd.setSize(size.width, size.height );
40656 // this is for the account management tab -seems to work there.
40657 var thd = grid.getGridEl().select('thead',true).first();
40659 // tbd.setSize(size.width, size.height - thd.getHeight());
40668 beforeSlide : function(){
40669 this.grid.getView().scroller.clip();
40672 afterSlide : function(){
40673 this.grid.getView().scroller.unclip();
40676 destroy : function(){
40677 this.grid.destroy();
40679 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40684 * @class Roo.bootstrap.panel.Nest
40685 * @extends Roo.bootstrap.panel.Content
40687 * Create a new Panel, that can contain a layout.Border.
40690 * @param {Roo.BorderLayout} layout The layout for this panel
40691 * @param {String/Object} config A string to set only the title or a config object
40693 Roo.bootstrap.panel.Nest = function(config)
40695 // construct with only one argument..
40696 /* FIXME - implement nicer consturctors
40697 if (layout.layout) {
40699 layout = config.layout;
40700 delete config.layout;
40702 if (layout.xtype && !layout.getEl) {
40703 // then layout needs constructing..
40704 layout = Roo.factory(layout, Roo);
40708 config.el = config.layout.getEl();
40710 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40712 config.layout.monitorWindowResize = false; // turn off autosizing
40713 this.layout = config.layout;
40714 this.layout.getEl().addClass("roo-layout-nested-layout");
40715 this.layout.parent = this;
40722 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40724 setSize : function(width, height){
40725 if(!this.ignoreResize(width, height)){
40726 var size = this.adjustForComponents(width, height);
40727 var el = this.layout.getEl();
40728 if (size.height < 1) {
40729 el.setWidth(size.width);
40731 el.setSize(size.width, size.height);
40733 var touch = el.dom.offsetWidth;
40734 this.layout.layout();
40735 // ie requires a double layout on the first pass
40736 if(Roo.isIE && !this.initialized){
40737 this.initialized = true;
40738 this.layout.layout();
40743 // activate all subpanels if not currently active..
40745 setActiveState : function(active){
40746 this.active = active;
40747 this.setActiveClass(active);
40750 this.fireEvent("deactivate", this);
40754 this.fireEvent("activate", this);
40755 // not sure if this should happen before or after..
40756 if (!this.layout) {
40757 return; // should not happen..
40760 for (var r in this.layout.regions) {
40761 reg = this.layout.getRegion(r);
40762 if (reg.getActivePanel()) {
40763 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40764 reg.setActivePanel(reg.getActivePanel());
40767 if (!reg.panels.length) {
40770 reg.showPanel(reg.getPanel(0));
40779 * Returns the nested BorderLayout for this panel
40780 * @return {Roo.BorderLayout}
40782 getLayout : function(){
40783 return this.layout;
40787 * Adds a xtype elements to the layout of the nested panel
40791 xtype : 'ContentPanel',
40798 xtype : 'NestedLayoutPanel',
40804 items : [ ... list of content panels or nested layout panels.. ]
40808 * @param {Object} cfg Xtype definition of item to add.
40810 addxtype : function(cfg) {
40811 return this.layout.addxtype(cfg);
40816 * Ext JS Library 1.1.1
40817 * Copyright(c) 2006-2007, Ext JS, LLC.
40819 * Originally Released Under LGPL - original licence link has changed is not relivant.
40822 * <script type="text/javascript">
40825 * @class Roo.TabPanel
40826 * @extends Roo.util.Observable
40827 * A lightweight tab container.
40831 // basic tabs 1, built from existing content
40832 var tabs = new Roo.TabPanel("tabs1");
40833 tabs.addTab("script", "View Script");
40834 tabs.addTab("markup", "View Markup");
40835 tabs.activate("script");
40837 // more advanced tabs, built from javascript
40838 var jtabs = new Roo.TabPanel("jtabs");
40839 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40841 // set up the UpdateManager
40842 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40843 var updater = tab2.getUpdateManager();
40844 updater.setDefaultUrl("ajax1.htm");
40845 tab2.on('activate', updater.refresh, updater, true);
40847 // Use setUrl for Ajax loading
40848 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40849 tab3.setUrl("ajax2.htm", null, true);
40852 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40855 jtabs.activate("jtabs-1");
40858 * Create a new TabPanel.
40859 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40860 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40862 Roo.bootstrap.panel.Tabs = function(config){
40864 * The container element for this TabPanel.
40865 * @type Roo.Element
40867 this.el = Roo.get(config.el);
40870 if(typeof config == "boolean"){
40871 this.tabPosition = config ? "bottom" : "top";
40873 Roo.apply(this, config);
40877 if(this.tabPosition == "bottom"){
40878 // if tabs are at the bottom = create the body first.
40879 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40880 this.el.addClass("roo-tabs-bottom");
40882 // next create the tabs holders
40884 if (this.tabPosition == "west"){
40886 var reg = this.region; // fake it..
40888 if (!reg.mgr.parent) {
40891 reg = reg.mgr.parent.region;
40893 Roo.log("got nest?");
40895 if (reg.mgr.getRegion('west')) {
40896 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40897 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40898 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40899 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40900 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40908 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40909 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40910 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40911 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40916 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40919 // finally - if tabs are at the top, then create the body last..
40920 if(this.tabPosition != "bottom"){
40921 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40922 * @type Roo.Element
40924 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40925 this.el.addClass("roo-tabs-top");
40929 this.bodyEl.setStyle("position", "relative");
40931 this.active = null;
40932 this.activateDelegate = this.activate.createDelegate(this);
40937 * Fires when the active tab changes
40938 * @param {Roo.TabPanel} this
40939 * @param {Roo.TabPanelItem} activePanel The new active tab
40943 * @event beforetabchange
40944 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40945 * @param {Roo.TabPanel} this
40946 * @param {Object} e Set cancel to true on this object to cancel the tab change
40947 * @param {Roo.TabPanelItem} tab The tab being changed to
40949 "beforetabchange" : true
40952 Roo.EventManager.onWindowResize(this.onResize, this);
40953 this.cpad = this.el.getPadding("lr");
40954 this.hiddenCount = 0;
40957 // toolbar on the tabbar support...
40958 if (this.toolbar) {
40959 alert("no toolbar support yet");
40960 this.toolbar = false;
40962 var tcfg = this.toolbar;
40963 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40964 this.toolbar = new Roo.Toolbar(tcfg);
40965 if (Roo.isSafari) {
40966 var tbl = tcfg.container.child('table', true);
40967 tbl.setAttribute('width', '100%');
40975 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40978 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40980 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40982 tabPosition : "top",
40984 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40986 currentTabWidth : 0,
40988 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40992 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40996 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40998 preferredTabWidth : 175,
41000 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41002 resizeTabs : false,
41004 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41006 monitorResize : true,
41008 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41010 toolbar : false, // set by caller..
41012 region : false, /// set by caller
41014 disableTooltips : true, // not used yet...
41017 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41018 * @param {String} id The id of the div to use <b>or create</b>
41019 * @param {String} text The text for the tab
41020 * @param {String} content (optional) Content to put in the TabPanelItem body
41021 * @param {Boolean} closable (optional) True to create a close icon on the tab
41022 * @return {Roo.TabPanelItem} The created TabPanelItem
41024 addTab : function(id, text, content, closable, tpl)
41026 var item = new Roo.bootstrap.panel.TabItem({
41030 closable : closable,
41033 this.addTabItem(item);
41035 item.setContent(content);
41041 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41042 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41043 * @return {Roo.TabPanelItem}
41045 getTab : function(id){
41046 return this.items[id];
41050 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41051 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41053 hideTab : function(id){
41054 var t = this.items[id];
41057 this.hiddenCount++;
41058 this.autoSizeTabs();
41063 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41064 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41066 unhideTab : function(id){
41067 var t = this.items[id];
41069 t.setHidden(false);
41070 this.hiddenCount--;
41071 this.autoSizeTabs();
41076 * Adds an existing {@link Roo.TabPanelItem}.
41077 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41079 addTabItem : function(item)
41081 this.items[item.id] = item;
41082 this.items.push(item);
41083 this.autoSizeTabs();
41084 // if(this.resizeTabs){
41085 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41086 // this.autoSizeTabs();
41088 // item.autoSize();
41093 * Removes a {@link Roo.TabPanelItem}.
41094 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41096 removeTab : function(id){
41097 var items = this.items;
41098 var tab = items[id];
41099 if(!tab) { return; }
41100 var index = items.indexOf(tab);
41101 if(this.active == tab && items.length > 1){
41102 var newTab = this.getNextAvailable(index);
41107 this.stripEl.dom.removeChild(tab.pnode.dom);
41108 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41109 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41111 items.splice(index, 1);
41112 delete this.items[tab.id];
41113 tab.fireEvent("close", tab);
41114 tab.purgeListeners();
41115 this.autoSizeTabs();
41118 getNextAvailable : function(start){
41119 var items = this.items;
41121 // look for a next tab that will slide over to
41122 // replace the one being removed
41123 while(index < items.length){
41124 var item = items[++index];
41125 if(item && !item.isHidden()){
41129 // if one isn't found select the previous tab (on the left)
41132 var item = items[--index];
41133 if(item && !item.isHidden()){
41141 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41142 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41144 disableTab : function(id){
41145 var tab = this.items[id];
41146 if(tab && this.active != tab){
41152 * Enables a {@link Roo.TabPanelItem} that is disabled.
41153 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41155 enableTab : function(id){
41156 var tab = this.items[id];
41161 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41162 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41163 * @return {Roo.TabPanelItem} The TabPanelItem.
41165 activate : function(id)
41167 //Roo.log('activite:' + id);
41169 var tab = this.items[id];
41173 if(tab == this.active || tab.disabled){
41177 this.fireEvent("beforetabchange", this, e, tab);
41178 if(e.cancel !== true && !tab.disabled){
41180 this.active.hide();
41182 this.active = this.items[id];
41183 this.active.show();
41184 this.fireEvent("tabchange", this, this.active);
41190 * Gets the active {@link Roo.TabPanelItem}.
41191 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41193 getActiveTab : function(){
41194 return this.active;
41198 * Updates the tab body element to fit the height of the container element
41199 * for overflow scrolling
41200 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41202 syncHeight : function(targetHeight){
41203 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41204 var bm = this.bodyEl.getMargins();
41205 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41206 this.bodyEl.setHeight(newHeight);
41210 onResize : function(){
41211 if(this.monitorResize){
41212 this.autoSizeTabs();
41217 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41219 beginUpdate : function(){
41220 this.updating = true;
41224 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41226 endUpdate : function(){
41227 this.updating = false;
41228 this.autoSizeTabs();
41232 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41234 autoSizeTabs : function()
41236 var count = this.items.length;
41237 var vcount = count - this.hiddenCount;
41240 this.stripEl.hide();
41242 this.stripEl.show();
41245 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41250 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41251 var availWidth = Math.floor(w / vcount);
41252 var b = this.stripBody;
41253 if(b.getWidth() > w){
41254 var tabs = this.items;
41255 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41256 if(availWidth < this.minTabWidth){
41257 /*if(!this.sleft){ // incomplete scrolling code
41258 this.createScrollButtons();
41261 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41264 if(this.currentTabWidth < this.preferredTabWidth){
41265 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41271 * Returns the number of tabs in this TabPanel.
41274 getCount : function(){
41275 return this.items.length;
41279 * Resizes all the tabs to the passed width
41280 * @param {Number} The new width
41282 setTabWidth : function(width){
41283 this.currentTabWidth = width;
41284 for(var i = 0, len = this.items.length; i < len; i++) {
41285 if(!this.items[i].isHidden()) {
41286 this.items[i].setWidth(width);
41292 * Destroys this TabPanel
41293 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41295 destroy : function(removeEl){
41296 Roo.EventManager.removeResizeListener(this.onResize, this);
41297 for(var i = 0, len = this.items.length; i < len; i++){
41298 this.items[i].purgeListeners();
41300 if(removeEl === true){
41301 this.el.update("");
41306 createStrip : function(container)
41308 var strip = document.createElement("nav");
41309 strip.className = Roo.bootstrap.version == 4 ?
41310 "navbar-light bg-light" :
41311 "navbar navbar-default"; //"x-tabs-wrap";
41312 container.appendChild(strip);
41316 createStripList : function(strip)
41318 // div wrapper for retard IE
41319 // returns the "tr" element.
41320 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41321 //'<div class="x-tabs-strip-wrap">'+
41322 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41323 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41324 return strip.firstChild; //.firstChild.firstChild.firstChild;
41326 createBody : function(container)
41328 var body = document.createElement("div");
41329 Roo.id(body, "tab-body");
41330 //Roo.fly(body).addClass("x-tabs-body");
41331 Roo.fly(body).addClass("tab-content");
41332 container.appendChild(body);
41335 createItemBody :function(bodyEl, id){
41336 var body = Roo.getDom(id);
41338 body = document.createElement("div");
41341 //Roo.fly(body).addClass("x-tabs-item-body");
41342 Roo.fly(body).addClass("tab-pane");
41343 bodyEl.insertBefore(body, bodyEl.firstChild);
41347 createStripElements : function(stripEl, text, closable, tpl)
41349 var td = document.createElement("li"); // was td..
41350 td.className = 'nav-item';
41352 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41355 stripEl.appendChild(td);
41357 td.className = "x-tabs-closable";
41358 if(!this.closeTpl){
41359 this.closeTpl = new Roo.Template(
41360 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41361 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41362 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41365 var el = this.closeTpl.overwrite(td, {"text": text});
41366 var close = el.getElementsByTagName("div")[0];
41367 var inner = el.getElementsByTagName("em")[0];
41368 return {"el": el, "close": close, "inner": inner};
41371 // not sure what this is..
41372 // if(!this.tabTpl){
41373 //this.tabTpl = new Roo.Template(
41374 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41375 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41377 // this.tabTpl = new Roo.Template(
41378 // '<a href="#">' +
41379 // '<span unselectable="on"' +
41380 // (this.disableTooltips ? '' : ' title="{text}"') +
41381 // ' >{text}</span></a>'
41387 var template = tpl || this.tabTpl || false;
41390 template = new Roo.Template(
41391 Roo.bootstrap.version == 4 ?
41393 '<a class="nav-link" href="#" unselectable="on"' +
41394 (this.disableTooltips ? '' : ' title="{text}"') +
41397 '<a class="nav-link" href="#">' +
41398 '<span unselectable="on"' +
41399 (this.disableTooltips ? '' : ' title="{text}"') +
41400 ' >{text}</span></a>'
41405 switch (typeof(template)) {
41409 template = new Roo.Template(template);
41415 var el = template.overwrite(td, {"text": text});
41417 var inner = el.getElementsByTagName("span")[0];
41419 return {"el": el, "inner": inner};
41427 * @class Roo.TabPanelItem
41428 * @extends Roo.util.Observable
41429 * Represents an individual item (tab plus body) in a TabPanel.
41430 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41431 * @param {String} id The id of this TabPanelItem
41432 * @param {String} text The text for the tab of this TabPanelItem
41433 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41435 Roo.bootstrap.panel.TabItem = function(config){
41437 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41438 * @type Roo.TabPanel
41440 this.tabPanel = config.panel;
41442 * The id for this TabPanelItem
41445 this.id = config.id;
41447 this.disabled = false;
41449 this.text = config.text;
41451 this.loaded = false;
41452 this.closable = config.closable;
41455 * The body element for this TabPanelItem.
41456 * @type Roo.Element
41458 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41459 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41460 this.bodyEl.setStyle("display", "block");
41461 this.bodyEl.setStyle("zoom", "1");
41462 //this.hideAction();
41464 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41466 this.el = Roo.get(els.el);
41467 this.inner = Roo.get(els.inner, true);
41468 this.textEl = Roo.bootstrap.version == 4 ?
41469 this.el : Roo.get(this.el.dom.firstChild, true);
41471 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41472 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41475 // this.el.on("mousedown", this.onTabMouseDown, this);
41476 this.el.on("click", this.onTabClick, this);
41478 if(config.closable){
41479 var c = Roo.get(els.close, true);
41480 c.dom.title = this.closeText;
41481 c.addClassOnOver("close-over");
41482 c.on("click", this.closeClick, this);
41488 * Fires when this tab becomes the active tab.
41489 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41490 * @param {Roo.TabPanelItem} this
41494 * @event beforeclose
41495 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41496 * @param {Roo.TabPanelItem} this
41497 * @param {Object} e Set cancel to true on this object to cancel the close.
41499 "beforeclose": true,
41502 * Fires when this tab is closed.
41503 * @param {Roo.TabPanelItem} this
41507 * @event deactivate
41508 * Fires when this tab is no longer the active tab.
41509 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41510 * @param {Roo.TabPanelItem} this
41512 "deactivate" : true
41514 this.hidden = false;
41516 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41519 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41521 purgeListeners : function(){
41522 Roo.util.Observable.prototype.purgeListeners.call(this);
41523 this.el.removeAllListeners();
41526 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41529 this.status_node.addClass("active");
41532 this.tabPanel.stripWrap.repaint();
41534 this.fireEvent("activate", this.tabPanel, this);
41538 * Returns true if this tab is the active tab.
41539 * @return {Boolean}
41541 isActive : function(){
41542 return this.tabPanel.getActiveTab() == this;
41546 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41549 this.status_node.removeClass("active");
41551 this.fireEvent("deactivate", this.tabPanel, this);
41554 hideAction : function(){
41555 this.bodyEl.hide();
41556 this.bodyEl.setStyle("position", "absolute");
41557 this.bodyEl.setLeft("-20000px");
41558 this.bodyEl.setTop("-20000px");
41561 showAction : function(){
41562 this.bodyEl.setStyle("position", "relative");
41563 this.bodyEl.setTop("");
41564 this.bodyEl.setLeft("");
41565 this.bodyEl.show();
41569 * Set the tooltip for the tab.
41570 * @param {String} tooltip The tab's tooltip
41572 setTooltip : function(text){
41573 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41574 this.textEl.dom.qtip = text;
41575 this.textEl.dom.removeAttribute('title');
41577 this.textEl.dom.title = text;
41581 onTabClick : function(e){
41582 e.preventDefault();
41583 this.tabPanel.activate(this.id);
41586 onTabMouseDown : function(e){
41587 e.preventDefault();
41588 this.tabPanel.activate(this.id);
41591 getWidth : function(){
41592 return this.inner.getWidth();
41595 setWidth : function(width){
41596 var iwidth = width - this.linode.getPadding("lr");
41597 this.inner.setWidth(iwidth);
41598 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41599 this.linode.setWidth(width);
41603 * Show or hide the tab
41604 * @param {Boolean} hidden True to hide or false to show.
41606 setHidden : function(hidden){
41607 this.hidden = hidden;
41608 this.linode.setStyle("display", hidden ? "none" : "");
41612 * Returns true if this tab is "hidden"
41613 * @return {Boolean}
41615 isHidden : function(){
41616 return this.hidden;
41620 * Returns the text for this tab
41623 getText : function(){
41627 autoSize : function(){
41628 //this.el.beginMeasure();
41629 this.textEl.setWidth(1);
41631 * #2804 [new] Tabs in Roojs
41632 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41634 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41635 //this.el.endMeasure();
41639 * Sets the text for the tab (Note: this also sets the tooltip text)
41640 * @param {String} text The tab's text and tooltip
41642 setText : function(text){
41644 this.textEl.update(text);
41645 this.setTooltip(text);
41646 //if(!this.tabPanel.resizeTabs){
41647 // this.autoSize();
41651 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41653 activate : function(){
41654 this.tabPanel.activate(this.id);
41658 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41660 disable : function(){
41661 if(this.tabPanel.active != this){
41662 this.disabled = true;
41663 this.status_node.addClass("disabled");
41668 * Enables this TabPanelItem if it was previously disabled.
41670 enable : function(){
41671 this.disabled = false;
41672 this.status_node.removeClass("disabled");
41676 * Sets the content for this TabPanelItem.
41677 * @param {String} content The content
41678 * @param {Boolean} loadScripts true to look for and load scripts
41680 setContent : function(content, loadScripts){
41681 this.bodyEl.update(content, loadScripts);
41685 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41686 * @return {Roo.UpdateManager} The UpdateManager
41688 getUpdateManager : function(){
41689 return this.bodyEl.getUpdateManager();
41693 * Set a URL to be used to load the content for this TabPanelItem.
41694 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41695 * @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)
41696 * @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)
41697 * @return {Roo.UpdateManager} The UpdateManager
41699 setUrl : function(url, params, loadOnce){
41700 if(this.refreshDelegate){
41701 this.un('activate', this.refreshDelegate);
41703 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41704 this.on("activate", this.refreshDelegate);
41705 return this.bodyEl.getUpdateManager();
41709 _handleRefresh : function(url, params, loadOnce){
41710 if(!loadOnce || !this.loaded){
41711 var updater = this.bodyEl.getUpdateManager();
41712 updater.update(url, params, this._setLoaded.createDelegate(this));
41717 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41718 * Will fail silently if the setUrl method has not been called.
41719 * This does not activate the panel, just updates its content.
41721 refresh : function(){
41722 if(this.refreshDelegate){
41723 this.loaded = false;
41724 this.refreshDelegate();
41729 _setLoaded : function(){
41730 this.loaded = true;
41734 closeClick : function(e){
41737 this.fireEvent("beforeclose", this, o);
41738 if(o.cancel !== true){
41739 this.tabPanel.removeTab(this.id);
41743 * The text displayed in the tooltip for the close icon.
41746 closeText : "Close this tab"
41749 * This script refer to:
41750 * Title: International Telephone Input
41751 * Author: Jack O'Connor
41752 * Code version: v12.1.12
41753 * Availability: https://github.com/jackocnr/intl-tel-input.git
41756 Roo.bootstrap.PhoneInputData = function() {
41759 "Afghanistan (افغانستان)",
41764 "Albania (Shqipëri)",
41769 "Algeria (الجزائر)",
41794 "Antigua and Barbuda",
41804 "Armenia (Հայաստան)",
41820 "Austria (Österreich)",
41825 "Azerbaijan (Azərbaycan)",
41835 "Bahrain (البحرين)",
41840 "Bangladesh (বাংলাদেশ)",
41850 "Belarus (Беларусь)",
41855 "Belgium (België)",
41885 "Bosnia and Herzegovina (Босна и Херцеговина)",
41900 "British Indian Ocean Territory",
41905 "British Virgin Islands",
41915 "Bulgaria (България)",
41925 "Burundi (Uburundi)",
41930 "Cambodia (កម្ពុជា)",
41935 "Cameroon (Cameroun)",
41944 ["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"]
41947 "Cape Verde (Kabu Verdi)",
41952 "Caribbean Netherlands",
41963 "Central African Republic (République centrafricaine)",
41983 "Christmas Island",
41989 "Cocos (Keeling) Islands",
42000 "Comoros (جزر القمر)",
42005 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42010 "Congo (Republic) (Congo-Brazzaville)",
42030 "Croatia (Hrvatska)",
42051 "Czech Republic (Česká republika)",
42056 "Denmark (Danmark)",
42071 "Dominican Republic (República Dominicana)",
42075 ["809", "829", "849"]
42093 "Equatorial Guinea (Guinea Ecuatorial)",
42113 "Falkland Islands (Islas Malvinas)",
42118 "Faroe Islands (Føroyar)",
42139 "French Guiana (Guyane française)",
42144 "French Polynesia (Polynésie française)",
42159 "Georgia (საქართველო)",
42164 "Germany (Deutschland)",
42184 "Greenland (Kalaallit Nunaat)",
42221 "Guinea-Bissau (Guiné Bissau)",
42246 "Hungary (Magyarország)",
42251 "Iceland (Ísland)",
42271 "Iraq (العراق)",
42287 "Israel (ישראל)",
42314 "Jordan (الأردن)",
42319 "Kazakhstan (Казахстан)",
42340 "Kuwait (الكويت)",
42345 "Kyrgyzstan (Кыргызстан)",
42355 "Latvia (Latvija)",
42360 "Lebanon (لبنان)",
42375 "Libya (ليبيا)",
42385 "Lithuania (Lietuva)",
42400 "Macedonia (FYROM) (Македонија)",
42405 "Madagascar (Madagasikara)",
42435 "Marshall Islands",
42445 "Mauritania (موريتانيا)",
42450 "Mauritius (Moris)",
42471 "Moldova (Republica Moldova)",
42481 "Mongolia (Монгол)",
42486 "Montenegro (Crna Gora)",
42496 "Morocco (المغرب)",
42502 "Mozambique (Moçambique)",
42507 "Myanmar (Burma) (မြန်မာ)",
42512 "Namibia (Namibië)",
42527 "Netherlands (Nederland)",
42532 "New Caledonia (Nouvelle-Calédonie)",
42567 "North Korea (조선 민주주의 인민 공화국)",
42572 "Northern Mariana Islands",
42588 "Pakistan (پاکستان)",
42598 "Palestine (فلسطين)",
42608 "Papua New Guinea",
42650 "Réunion (La Réunion)",
42656 "Romania (România)",
42672 "Saint Barthélemy",
42683 "Saint Kitts and Nevis",
42693 "Saint Martin (Saint-Martin (partie française))",
42699 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42704 "Saint Vincent and the Grenadines",
42719 "São Tomé and Príncipe (São Tomé e Príncipe)",
42724 "Saudi Arabia (المملكة العربية السعودية)",
42729 "Senegal (Sénégal)",
42759 "Slovakia (Slovensko)",
42764 "Slovenia (Slovenija)",
42774 "Somalia (Soomaaliya)",
42784 "South Korea (대한민국)",
42789 "South Sudan (جنوب السودان)",
42799 "Sri Lanka (ශ්රී ලංකාව)",
42804 "Sudan (السودان)",
42814 "Svalbard and Jan Mayen",
42825 "Sweden (Sverige)",
42830 "Switzerland (Schweiz)",
42835 "Syria (سوريا)",
42880 "Trinidad and Tobago",
42885 "Tunisia (تونس)",
42890 "Turkey (Türkiye)",
42900 "Turks and Caicos Islands",
42910 "U.S. Virgin Islands",
42920 "Ukraine (Україна)",
42925 "United Arab Emirates (الإمارات العربية المتحدة)",
42947 "Uzbekistan (Oʻzbekiston)",
42957 "Vatican City (Città del Vaticano)",
42968 "Vietnam (Việt Nam)",
42973 "Wallis and Futuna (Wallis-et-Futuna)",
42978 "Western Sahara (الصحراء الغربية)",
42984 "Yemen (اليمن)",
43008 * This script refer to:
43009 * Title: International Telephone Input
43010 * Author: Jack O'Connor
43011 * Code version: v12.1.12
43012 * Availability: https://github.com/jackocnr/intl-tel-input.git
43016 * @class Roo.bootstrap.PhoneInput
43017 * @extends Roo.bootstrap.TriggerField
43018 * An input with International dial-code selection
43020 * @cfg {String} defaultDialCode default '+852'
43021 * @cfg {Array} preferedCountries default []
43024 * Create a new PhoneInput.
43025 * @param {Object} config Configuration options
43028 Roo.bootstrap.PhoneInput = function(config) {
43029 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43032 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43034 listWidth: undefined,
43036 selectedClass: 'active',
43038 invalidClass : "has-warning",
43040 validClass: 'has-success',
43042 allowed: '0123456789',
43047 * @cfg {String} defaultDialCode The default dial code when initializing the input
43049 defaultDialCode: '+852',
43052 * @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
43054 preferedCountries: false,
43056 getAutoCreate : function()
43058 var data = Roo.bootstrap.PhoneInputData();
43059 var align = this.labelAlign || this.parentLabelAlign();
43062 this.allCountries = [];
43063 this.dialCodeMapping = [];
43065 for (var i = 0; i < data.length; i++) {
43067 this.allCountries[i] = {
43071 priority: c[3] || 0,
43072 areaCodes: c[4] || null
43074 this.dialCodeMapping[c[2]] = {
43077 priority: c[3] || 0,
43078 areaCodes: c[4] || null
43090 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43091 maxlength: this.max_length,
43092 cls : 'form-control tel-input',
43093 autocomplete: 'new-password'
43096 var hiddenInput = {
43099 cls: 'hidden-tel-input'
43103 hiddenInput.name = this.name;
43106 if (this.disabled) {
43107 input.disabled = true;
43110 var flag_container = {
43127 cls: this.hasFeedback ? 'has-feedback' : '',
43133 cls: 'dial-code-holder',
43140 cls: 'roo-select2-container input-group',
43147 if (this.fieldLabel.length) {
43150 tooltip: 'This field is required'
43156 cls: 'control-label',
43162 html: this.fieldLabel
43165 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43171 if(this.indicatorpos == 'right') {
43172 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43179 if(align == 'left') {
43187 if(this.labelWidth > 12){
43188 label.style = "width: " + this.labelWidth + 'px';
43190 if(this.labelWidth < 13 && this.labelmd == 0){
43191 this.labelmd = this.labelWidth;
43193 if(this.labellg > 0){
43194 label.cls += ' col-lg-' + this.labellg;
43195 input.cls += ' col-lg-' + (12 - this.labellg);
43197 if(this.labelmd > 0){
43198 label.cls += ' col-md-' + this.labelmd;
43199 container.cls += ' col-md-' + (12 - this.labelmd);
43201 if(this.labelsm > 0){
43202 label.cls += ' col-sm-' + this.labelsm;
43203 container.cls += ' col-sm-' + (12 - this.labelsm);
43205 if(this.labelxs > 0){
43206 label.cls += ' col-xs-' + this.labelxs;
43207 container.cls += ' col-xs-' + (12 - this.labelxs);
43217 var settings = this;
43219 ['xs','sm','md','lg'].map(function(size){
43220 if (settings[size]) {
43221 cfg.cls += ' col-' + size + '-' + settings[size];
43225 this.store = new Roo.data.Store({
43226 proxy : new Roo.data.MemoryProxy({}),
43227 reader : new Roo.data.JsonReader({
43238 'name' : 'dialCode',
43242 'name' : 'priority',
43246 'name' : 'areaCodes',
43253 if(!this.preferedCountries) {
43254 this.preferedCountries = [
43261 var p = this.preferedCountries.reverse();
43264 for (var i = 0; i < p.length; i++) {
43265 for (var j = 0; j < this.allCountries.length; j++) {
43266 if(this.allCountries[j].iso2 == p[i]) {
43267 var t = this.allCountries[j];
43268 this.allCountries.splice(j,1);
43269 this.allCountries.unshift(t);
43275 this.store.proxy.data = {
43277 data: this.allCountries
43283 initEvents : function()
43286 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43288 this.indicator = this.indicatorEl();
43289 this.flag = this.flagEl();
43290 this.dialCodeHolder = this.dialCodeHolderEl();
43292 this.trigger = this.el.select('div.flag-box',true).first();
43293 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43298 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43299 _this.list.setWidth(lw);
43302 this.list.on('mouseover', this.onViewOver, this);
43303 this.list.on('mousemove', this.onViewMove, this);
43304 this.inputEl().on("keyup", this.onKeyUp, this);
43305 this.inputEl().on("keypress", this.onKeyPress, this);
43307 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43309 this.view = new Roo.View(this.list, this.tpl, {
43310 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43313 this.view.on('click', this.onViewClick, this);
43314 this.setValue(this.defaultDialCode);
43317 onTriggerClick : function(e)
43319 Roo.log('trigger click');
43324 if(this.isExpanded()){
43326 this.hasFocus = false;
43328 this.store.load({});
43329 this.hasFocus = true;
43334 isExpanded : function()
43336 return this.list.isVisible();
43339 collapse : function()
43341 if(!this.isExpanded()){
43345 Roo.get(document).un('mousedown', this.collapseIf, this);
43346 Roo.get(document).un('mousewheel', this.collapseIf, this);
43347 this.fireEvent('collapse', this);
43351 expand : function()
43355 if(this.isExpanded() || !this.hasFocus){
43359 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43360 this.list.setWidth(lw);
43363 this.restrictHeight();
43365 Roo.get(document).on('mousedown', this.collapseIf, this);
43366 Roo.get(document).on('mousewheel', this.collapseIf, this);
43368 this.fireEvent('expand', this);
43371 restrictHeight : function()
43373 this.list.alignTo(this.inputEl(), this.listAlign);
43374 this.list.alignTo(this.inputEl(), this.listAlign);
43377 onViewOver : function(e, t)
43379 if(this.inKeyMode){
43382 var item = this.view.findItemFromChild(t);
43385 var index = this.view.indexOf(item);
43386 this.select(index, false);
43391 onViewClick : function(view, doFocus, el, e)
43393 var index = this.view.getSelectedIndexes()[0];
43395 var r = this.store.getAt(index);
43398 this.onSelect(r, index);
43400 if(doFocus !== false && !this.blockFocus){
43401 this.inputEl().focus();
43405 onViewMove : function(e, t)
43407 this.inKeyMode = false;
43410 select : function(index, scrollIntoView)
43412 this.selectedIndex = index;
43413 this.view.select(index);
43414 if(scrollIntoView !== false){
43415 var el = this.view.getNode(index);
43417 this.list.scrollChildIntoView(el, false);
43422 createList : function()
43424 this.list = Roo.get(document.body).createChild({
43426 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43427 style: 'display:none'
43430 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43433 collapseIf : function(e)
43435 var in_combo = e.within(this.el);
43436 var in_list = e.within(this.list);
43437 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43439 if (in_combo || in_list || is_list) {
43445 onSelect : function(record, index)
43447 if(this.fireEvent('beforeselect', this, record, index) !== false){
43449 this.setFlagClass(record.data.iso2);
43450 this.setDialCode(record.data.dialCode);
43451 this.hasFocus = false;
43453 this.fireEvent('select', this, record, index);
43457 flagEl : function()
43459 var flag = this.el.select('div.flag',true).first();
43466 dialCodeHolderEl : function()
43468 var d = this.el.select('input.dial-code-holder',true).first();
43475 setDialCode : function(v)
43477 this.dialCodeHolder.dom.value = '+'+v;
43480 setFlagClass : function(n)
43482 this.flag.dom.className = 'flag '+n;
43485 getValue : function()
43487 var v = this.inputEl().getValue();
43488 if(this.dialCodeHolder) {
43489 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43494 setValue : function(v)
43496 var d = this.getDialCode(v);
43498 //invalid dial code
43499 if(v.length == 0 || !d || d.length == 0) {
43501 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43502 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43508 this.setFlagClass(this.dialCodeMapping[d].iso2);
43509 this.setDialCode(d);
43510 this.inputEl().dom.value = v.replace('+'+d,'');
43511 this.hiddenEl().dom.value = this.getValue();
43516 getDialCode : function(v)
43520 if (v.length == 0) {
43521 return this.dialCodeHolder.dom.value;
43525 if (v.charAt(0) != "+") {
43528 var numericChars = "";
43529 for (var i = 1; i < v.length; i++) {
43530 var c = v.charAt(i);
43533 if (this.dialCodeMapping[numericChars]) {
43534 dialCode = v.substr(1, i);
43536 if (numericChars.length == 4) {
43546 this.setValue(this.defaultDialCode);
43550 hiddenEl : function()
43552 return this.el.select('input.hidden-tel-input',true).first();
43555 // after setting val
43556 onKeyUp : function(e){
43557 this.setValue(this.getValue());
43560 onKeyPress : function(e){
43561 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43568 * @class Roo.bootstrap.MoneyField
43569 * @extends Roo.bootstrap.ComboBox
43570 * Bootstrap MoneyField class
43573 * Create a new MoneyField.
43574 * @param {Object} config Configuration options
43577 Roo.bootstrap.MoneyField = function(config) {
43579 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43583 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43586 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43588 allowDecimals : true,
43590 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43592 decimalSeparator : ".",
43594 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43596 decimalPrecision : 0,
43598 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43600 allowNegative : true,
43602 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43606 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43608 minValue : Number.NEGATIVE_INFINITY,
43610 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43612 maxValue : Number.MAX_VALUE,
43614 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43616 minText : "The minimum value for this field is {0}",
43618 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43620 maxText : "The maximum value for this field is {0}",
43622 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43623 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43625 nanText : "{0} is not a valid number",
43627 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43631 * @cfg {String} defaults currency of the MoneyField
43632 * value should be in lkey
43634 defaultCurrency : false,
43636 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43638 thousandsDelimiter : false,
43640 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43651 getAutoCreate : function()
43653 var align = this.labelAlign || this.parentLabelAlign();
43665 cls : 'form-control roo-money-amount-input',
43666 autocomplete: 'new-password'
43669 var hiddenInput = {
43673 cls: 'hidden-number-input'
43676 if(this.max_length) {
43677 input.maxlength = this.max_length;
43681 hiddenInput.name = this.name;
43684 if (this.disabled) {
43685 input.disabled = true;
43688 var clg = 12 - this.inputlg;
43689 var cmd = 12 - this.inputmd;
43690 var csm = 12 - this.inputsm;
43691 var cxs = 12 - this.inputxs;
43695 cls : 'row roo-money-field',
43699 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43703 cls: 'roo-select2-container input-group',
43707 cls : 'form-control roo-money-currency-input',
43708 autocomplete: 'new-password',
43710 name : this.currencyName
43714 cls : 'input-group-addon',
43728 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43732 cls: this.hasFeedback ? 'has-feedback' : '',
43743 if (this.fieldLabel.length) {
43746 tooltip: 'This field is required'
43752 cls: 'control-label',
43758 html: this.fieldLabel
43761 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43767 if(this.indicatorpos == 'right') {
43768 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43775 if(align == 'left') {
43783 if(this.labelWidth > 12){
43784 label.style = "width: " + this.labelWidth + 'px';
43786 if(this.labelWidth < 13 && this.labelmd == 0){
43787 this.labelmd = this.labelWidth;
43789 if(this.labellg > 0){
43790 label.cls += ' col-lg-' + this.labellg;
43791 input.cls += ' col-lg-' + (12 - this.labellg);
43793 if(this.labelmd > 0){
43794 label.cls += ' col-md-' + this.labelmd;
43795 container.cls += ' col-md-' + (12 - this.labelmd);
43797 if(this.labelsm > 0){
43798 label.cls += ' col-sm-' + this.labelsm;
43799 container.cls += ' col-sm-' + (12 - this.labelsm);
43801 if(this.labelxs > 0){
43802 label.cls += ' col-xs-' + this.labelxs;
43803 container.cls += ' col-xs-' + (12 - this.labelxs);
43814 var settings = this;
43816 ['xs','sm','md','lg'].map(function(size){
43817 if (settings[size]) {
43818 cfg.cls += ' col-' + size + '-' + settings[size];
43825 initEvents : function()
43827 this.indicator = this.indicatorEl();
43829 this.initCurrencyEvent();
43831 this.initNumberEvent();
43834 initCurrencyEvent : function()
43837 throw "can not find store for combo";
43840 this.store = Roo.factory(this.store, Roo.data);
43841 this.store.parent = this;
43845 this.triggerEl = this.el.select('.input-group-addon', true).first();
43847 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43852 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43853 _this.list.setWidth(lw);
43856 this.list.on('mouseover', this.onViewOver, this);
43857 this.list.on('mousemove', this.onViewMove, this);
43858 this.list.on('scroll', this.onViewScroll, this);
43861 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43864 this.view = new Roo.View(this.list, this.tpl, {
43865 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43868 this.view.on('click', this.onViewClick, this);
43870 this.store.on('beforeload', this.onBeforeLoad, this);
43871 this.store.on('load', this.onLoad, this);
43872 this.store.on('loadexception', this.onLoadException, this);
43874 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43875 "up" : function(e){
43876 this.inKeyMode = true;
43880 "down" : function(e){
43881 if(!this.isExpanded()){
43882 this.onTriggerClick();
43884 this.inKeyMode = true;
43889 "enter" : function(e){
43892 if(this.fireEvent("specialkey", this, e)){
43893 this.onViewClick(false);
43899 "esc" : function(e){
43903 "tab" : function(e){
43906 if(this.fireEvent("specialkey", this, e)){
43907 this.onViewClick(false);
43915 doRelay : function(foo, bar, hname){
43916 if(hname == 'down' || this.scope.isExpanded()){
43917 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43925 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43929 initNumberEvent : function(e)
43931 this.inputEl().on("keydown" , this.fireKey, this);
43932 this.inputEl().on("focus", this.onFocus, this);
43933 this.inputEl().on("blur", this.onBlur, this);
43935 this.inputEl().relayEvent('keyup', this);
43937 if(this.indicator){
43938 this.indicator.addClass('invisible');
43941 this.originalValue = this.getValue();
43943 if(this.validationEvent == 'keyup'){
43944 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43945 this.inputEl().on('keyup', this.filterValidation, this);
43947 else if(this.validationEvent !== false){
43948 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43951 if(this.selectOnFocus){
43952 this.on("focus", this.preFocus, this);
43955 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43956 this.inputEl().on("keypress", this.filterKeys, this);
43958 this.inputEl().relayEvent('keypress', this);
43961 var allowed = "0123456789";
43963 if(this.allowDecimals){
43964 allowed += this.decimalSeparator;
43967 if(this.allowNegative){
43971 if(this.thousandsDelimiter) {
43975 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43977 var keyPress = function(e){
43979 var k = e.getKey();
43981 var c = e.getCharCode();
43984 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43985 allowed.indexOf(String.fromCharCode(c)) === -1
43991 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43995 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44000 this.inputEl().on("keypress", keyPress, this);
44004 onTriggerClick : function(e)
44011 this.loadNext = false;
44013 if(this.isExpanded()){
44018 this.hasFocus = true;
44020 if(this.triggerAction == 'all') {
44021 this.doQuery(this.allQuery, true);
44025 this.doQuery(this.getRawValue());
44028 getCurrency : function()
44030 var v = this.currencyEl().getValue();
44035 restrictHeight : function()
44037 this.list.alignTo(this.currencyEl(), this.listAlign);
44038 this.list.alignTo(this.currencyEl(), this.listAlign);
44041 onViewClick : function(view, doFocus, el, e)
44043 var index = this.view.getSelectedIndexes()[0];
44045 var r = this.store.getAt(index);
44048 this.onSelect(r, index);
44052 onSelect : function(record, index){
44054 if(this.fireEvent('beforeselect', this, record, index) !== false){
44056 this.setFromCurrencyData(index > -1 ? record.data : false);
44060 this.fireEvent('select', this, record, index);
44064 setFromCurrencyData : function(o)
44068 this.lastCurrency = o;
44070 if (this.currencyField) {
44071 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44073 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44076 this.lastSelectionText = currency;
44078 //setting default currency
44079 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44080 this.setCurrency(this.defaultCurrency);
44084 this.setCurrency(currency);
44087 setFromData : function(o)
44091 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44093 this.setFromCurrencyData(c);
44098 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44100 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44103 this.setValue(value);
44107 setCurrency : function(v)
44109 this.currencyValue = v;
44112 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44117 setValue : function(v)
44119 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44125 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44127 this.inputEl().dom.value = (v == '') ? '' :
44128 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44130 if(!this.allowZero && v === '0') {
44131 this.hiddenEl().dom.value = '';
44132 this.inputEl().dom.value = '';
44139 getRawValue : function()
44141 var v = this.inputEl().getValue();
44146 getValue : function()
44148 return this.fixPrecision(this.parseValue(this.getRawValue()));
44151 parseValue : function(value)
44153 if(this.thousandsDelimiter) {
44155 r = new RegExp(",", "g");
44156 value = value.replace(r, "");
44159 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44160 return isNaN(value) ? '' : value;
44164 fixPrecision : function(value)
44166 if(this.thousandsDelimiter) {
44168 r = new RegExp(",", "g");
44169 value = value.replace(r, "");
44172 var nan = isNaN(value);
44174 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44175 return nan ? '' : value;
44177 return parseFloat(value).toFixed(this.decimalPrecision);
44180 decimalPrecisionFcn : function(v)
44182 return Math.floor(v);
44185 validateValue : function(value)
44187 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44191 var num = this.parseValue(value);
44194 this.markInvalid(String.format(this.nanText, value));
44198 if(num < this.minValue){
44199 this.markInvalid(String.format(this.minText, this.minValue));
44203 if(num > this.maxValue){
44204 this.markInvalid(String.format(this.maxText, this.maxValue));
44211 validate : function()
44213 if(this.disabled || this.allowBlank){
44218 var currency = this.getCurrency();
44220 if(this.validateValue(this.getRawValue()) && currency.length){
44225 this.markInvalid();
44229 getName: function()
44234 beforeBlur : function()
44240 var v = this.parseValue(this.getRawValue());
44247 onBlur : function()
44251 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44252 //this.el.removeClass(this.focusClass);
44255 this.hasFocus = false;
44257 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44261 var v = this.getValue();
44263 if(String(v) !== String(this.startValue)){
44264 this.fireEvent('change', this, v, this.startValue);
44267 this.fireEvent("blur", this);
44270 inputEl : function()
44272 return this.el.select('.roo-money-amount-input', true).first();
44275 currencyEl : function()
44277 return this.el.select('.roo-money-currency-input', true).first();
44280 hiddenEl : function()
44282 return this.el.select('input.hidden-number-input',true).first();
44286 * @class Roo.bootstrap.BezierSignature
44287 * @extends Roo.bootstrap.Component
44288 * Bootstrap BezierSignature class
44289 * This script refer to:
44290 * Title: Signature Pad
44292 * Availability: https://github.com/szimek/signature_pad
44295 * Create a new BezierSignature
44296 * @param {Object} config The config object
44299 Roo.bootstrap.BezierSignature = function(config){
44300 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44306 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44313 mouse_btn_down: true,
44316 * @cfg {int} canvas height
44318 canvas_height: '200px',
44321 * @cfg {float|function} Radius of a single dot.
44326 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44331 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44336 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44341 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44346 * @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.
44348 bg_color: 'rgba(0, 0, 0, 0)',
44351 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44353 dot_color: 'black',
44356 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44358 velocity_filter_weight: 0.7,
44361 * @cfg {function} Callback when stroke begin.
44366 * @cfg {function} Callback when stroke end.
44370 getAutoCreate : function()
44372 var cls = 'roo-signature column';
44375 cls += ' ' + this.cls;
44385 for(var i = 0; i < col_sizes.length; i++) {
44386 if(this[col_sizes[i]]) {
44387 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44397 cls: 'roo-signature-body',
44401 cls: 'roo-signature-body-canvas',
44402 height: this.canvas_height,
44403 width: this.canvas_width
44410 style: 'display: none'
44418 initEvents: function()
44420 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44422 var canvas = this.canvasEl();
44424 // mouse && touch event swapping...
44425 canvas.dom.style.touchAction = 'none';
44426 canvas.dom.style.msTouchAction = 'none';
44428 this.mouse_btn_down = false;
44429 canvas.on('mousedown', this._handleMouseDown, this);
44430 canvas.on('mousemove', this._handleMouseMove, this);
44431 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44433 if (window.PointerEvent) {
44434 canvas.on('pointerdown', this._handleMouseDown, this);
44435 canvas.on('pointermove', this._handleMouseMove, this);
44436 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44439 if ('ontouchstart' in window) {
44440 canvas.on('touchstart', this._handleTouchStart, this);
44441 canvas.on('touchmove', this._handleTouchMove, this);
44442 canvas.on('touchend', this._handleTouchEnd, this);
44445 Roo.EventManager.onWindowResize(this.resize, this, true);
44447 // file input event
44448 this.fileEl().on('change', this.uploadImage, this);
44455 resize: function(){
44457 var canvas = this.canvasEl().dom;
44458 var ctx = this.canvasElCtx();
44459 var img_data = false;
44461 if(canvas.width > 0) {
44462 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44464 // setting canvas width will clean img data
44467 var style = window.getComputedStyle ?
44468 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44470 var padding_left = parseInt(style.paddingLeft) || 0;
44471 var padding_right = parseInt(style.paddingRight) || 0;
44473 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44476 ctx.putImageData(img_data, 0, 0);
44480 _handleMouseDown: function(e)
44482 if (e.browserEvent.which === 1) {
44483 this.mouse_btn_down = true;
44484 this.strokeBegin(e);
44488 _handleMouseMove: function (e)
44490 if (this.mouse_btn_down) {
44491 this.strokeMoveUpdate(e);
44495 _handleMouseUp: function (e)
44497 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44498 this.mouse_btn_down = false;
44503 _handleTouchStart: function (e) {
44505 e.preventDefault();
44506 if (e.browserEvent.targetTouches.length === 1) {
44507 // var touch = e.browserEvent.changedTouches[0];
44508 // this.strokeBegin(touch);
44510 this.strokeBegin(e); // assume e catching the correct xy...
44514 _handleTouchMove: function (e) {
44515 e.preventDefault();
44516 // var touch = event.targetTouches[0];
44517 // _this._strokeMoveUpdate(touch);
44518 this.strokeMoveUpdate(e);
44521 _handleTouchEnd: function (e) {
44522 var wasCanvasTouched = e.target === this.canvasEl().dom;
44523 if (wasCanvasTouched) {
44524 e.preventDefault();
44525 // var touch = event.changedTouches[0];
44526 // _this._strokeEnd(touch);
44531 reset: function () {
44532 this._lastPoints = [];
44533 this._lastVelocity = 0;
44534 this._lastWidth = (this.min_width + this.max_width) / 2;
44535 this.canvasElCtx().fillStyle = this.dot_color;
44538 strokeMoveUpdate: function(e)
44540 this.strokeUpdate(e);
44542 if (this.throttle) {
44543 this.throttleStroke(this.strokeUpdate, this.throttle);
44546 this.strokeUpdate(e);
44550 strokeBegin: function(e)
44552 var newPointGroup = {
44553 color: this.dot_color,
44557 if (typeof this.onBegin === 'function') {
44561 this.curve_data.push(newPointGroup);
44563 this.strokeUpdate(e);
44566 strokeUpdate: function(e)
44568 var rect = this.canvasEl().dom.getBoundingClientRect();
44569 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44570 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44571 var lastPoints = lastPointGroup.points;
44572 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44573 var isLastPointTooClose = lastPoint
44574 ? point.distanceTo(lastPoint) <= this.min_distance
44576 var color = lastPointGroup.color;
44577 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44578 var curve = this.addPoint(point);
44580 this.drawDot({color: color, point: point});
44583 this.drawCurve({color: color, curve: curve});
44593 strokeEnd: function(e)
44595 this.strokeUpdate(e);
44596 if (typeof this.onEnd === 'function') {
44601 addPoint: function (point) {
44602 var _lastPoints = this._lastPoints;
44603 _lastPoints.push(point);
44604 if (_lastPoints.length > 2) {
44605 if (_lastPoints.length === 3) {
44606 _lastPoints.unshift(_lastPoints[0]);
44608 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44609 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44610 _lastPoints.shift();
44616 calculateCurveWidths: function (startPoint, endPoint) {
44617 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44618 (1 - this.velocity_filter_weight) * this._lastVelocity;
44620 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44623 start: this._lastWidth
44626 this._lastVelocity = velocity;
44627 this._lastWidth = newWidth;
44631 drawDot: function (_a) {
44632 var color = _a.color, point = _a.point;
44633 var ctx = this.canvasElCtx();
44634 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44636 this.drawCurveSegment(point.x, point.y, width);
44638 ctx.fillStyle = color;
44642 drawCurve: function (_a) {
44643 var color = _a.color, curve = _a.curve;
44644 var ctx = this.canvasElCtx();
44645 var widthDelta = curve.endWidth - curve.startWidth;
44646 var drawSteps = Math.floor(curve.length()) * 2;
44648 ctx.fillStyle = color;
44649 for (var i = 0; i < drawSteps; i += 1) {
44650 var t = i / drawSteps;
44656 var x = uuu * curve.startPoint.x;
44657 x += 3 * uu * t * curve.control1.x;
44658 x += 3 * u * tt * curve.control2.x;
44659 x += ttt * curve.endPoint.x;
44660 var y = uuu * curve.startPoint.y;
44661 y += 3 * uu * t * curve.control1.y;
44662 y += 3 * u * tt * curve.control2.y;
44663 y += ttt * curve.endPoint.y;
44664 var width = curve.startWidth + ttt * widthDelta;
44665 this.drawCurveSegment(x, y, width);
44671 drawCurveSegment: function (x, y, width) {
44672 var ctx = this.canvasElCtx();
44674 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44675 this.is_empty = false;
44680 var ctx = this.canvasElCtx();
44681 var canvas = this.canvasEl().dom;
44682 ctx.fillStyle = this.bg_color;
44683 ctx.clearRect(0, 0, canvas.width, canvas.height);
44684 ctx.fillRect(0, 0, canvas.width, canvas.height);
44685 this.curve_data = [];
44687 this.is_empty = true;
44692 return this.el.select('input',true).first();
44695 canvasEl: function()
44697 return this.el.select('canvas',true).first();
44700 canvasElCtx: function()
44702 return this.el.select('canvas',true).first().dom.getContext('2d');
44705 getImage: function(type)
44707 if(this.is_empty) {
44712 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44715 drawFromImage: function(img_src)
44717 var img = new Image();
44719 img.onload = function(){
44720 this.canvasElCtx().drawImage(img, 0, 0);
44725 this.is_empty = false;
44728 selectImage: function()
44730 this.fileEl().dom.click();
44733 uploadImage: function(e)
44735 var reader = new FileReader();
44737 reader.onload = function(e){
44738 var img = new Image();
44739 img.onload = function(){
44741 this.canvasElCtx().drawImage(img, 0, 0);
44743 img.src = e.target.result;
44746 reader.readAsDataURL(e.target.files[0]);
44749 // Bezier Point Constructor
44750 Point: (function () {
44751 function Point(x, y, time) {
44754 this.time = time || Date.now();
44756 Point.prototype.distanceTo = function (start) {
44757 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44759 Point.prototype.equals = function (other) {
44760 return this.x === other.x && this.y === other.y && this.time === other.time;
44762 Point.prototype.velocityFrom = function (start) {
44763 return this.time !== start.time
44764 ? this.distanceTo(start) / (this.time - start.time)
44771 // Bezier Constructor
44772 Bezier: (function () {
44773 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44774 this.startPoint = startPoint;
44775 this.control2 = control2;
44776 this.control1 = control1;
44777 this.endPoint = endPoint;
44778 this.startWidth = startWidth;
44779 this.endWidth = endWidth;
44781 Bezier.fromPoints = function (points, widths, scope) {
44782 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44783 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44784 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44786 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44787 var dx1 = s1.x - s2.x;
44788 var dy1 = s1.y - s2.y;
44789 var dx2 = s2.x - s3.x;
44790 var dy2 = s2.y - s3.y;
44791 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44792 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44793 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44794 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44795 var dxm = m1.x - m2.x;
44796 var dym = m1.y - m2.y;
44797 var k = l2 / (l1 + l2);
44798 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44799 var tx = s2.x - cm.x;
44800 var ty = s2.y - cm.y;
44802 c1: new scope.Point(m1.x + tx, m1.y + ty),
44803 c2: new scope.Point(m2.x + tx, m2.y + ty)
44806 Bezier.prototype.length = function () {
44811 for (var i = 0; i <= steps; i += 1) {
44813 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44814 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44816 var xdiff = cx - px;
44817 var ydiff = cy - py;
44818 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44825 Bezier.prototype.point = function (t, start, c1, c2, end) {
44826 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44827 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44828 + (3.0 * c2 * (1.0 - t) * t * t)
44829 + (end * t * t * t);
44834 throttleStroke: function(fn, wait) {
44835 if (wait === void 0) { wait = 250; }
44837 var timeout = null;
44841 var later = function () {
44842 previous = Date.now();
44844 result = fn.apply(storedContext, storedArgs);
44846 storedContext = null;
44850 return function wrapper() {
44852 for (var _i = 0; _i < arguments.length; _i++) {
44853 args[_i] = arguments[_i];
44855 var now = Date.now();
44856 var remaining = wait - (now - previous);
44857 storedContext = this;
44859 if (remaining <= 0 || remaining > wait) {
44861 clearTimeout(timeout);
44865 result = fn.apply(storedContext, storedArgs);
44867 storedContext = null;
44871 else if (!timeout) {
44872 timeout = window.setTimeout(later, remaining);