2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
365 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367 return Roo.get(document.body);
371 * Fetch the element to display the tooltip on.
372 * @return {Roo.Element} defaults to this.el
374 tooltipEl : function()
379 addxtype : function(tree,cntr)
383 cn = Roo.factory(tree);
384 //Roo.log(['addxtype', cn]);
386 cn.parentType = this.xtype; //??
387 cn.parentId = this.id;
389 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390 if (typeof(cn.container_method) == 'string') {
391 cntr = cn.container_method;
395 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
397 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
399 var build_from_html = Roo.XComponent.build_from_html;
401 var is_body = (tree.xtype == 'Body') ;
403 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405 var self_cntr_el = Roo.get(this[cntr](false));
407 // do not try and build conditional elements
408 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414 return this.addxtypeChild(tree,cntr, is_body);
417 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
420 return this.addxtypeChild(Roo.apply({}, tree),cntr);
423 Roo.log('skipping render');
429 if (!build_from_html) {
433 // this i think handles overlaying multiple children of the same type
434 // with the sam eelement.. - which might be buggy..
436 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
442 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453 addxtypeChild : function (tree, cntr, is_body)
455 Roo.debug && Roo.log('addxtypeChild:' + cntr);
457 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
460 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461 (typeof(tree['flexy:foreach']) != 'undefined');
465 skip_children = false;
466 // render the element if it's not BODY.
469 // if parent was disabled, then do not try and create the children..
470 if(!this[cntr](true)){
475 cn = Roo.factory(tree);
477 cn.parentType = this.xtype; //??
478 cn.parentId = this.id;
480 var build_from_html = Roo.XComponent.build_from_html;
483 // does the container contain child eleemnts with 'xtype' attributes.
484 // that match this xtype..
485 // note - when we render we create these as well..
486 // so we should check to see if body has xtype set.
487 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489 var self_cntr_el = Roo.get(this[cntr](false));
490 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492 //Roo.log(Roo.XComponent.build_from_html);
493 //Roo.log("got echild:");
496 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497 // and are not displayed -this causes this to use up the wrong element when matching.
498 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
501 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
508 //echild.dom.removeAttribute('xtype');
510 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511 Roo.debug && Roo.log(self_cntr_el);
512 Roo.debug && Roo.log(echild);
513 Roo.debug && Roo.log(cn);
519 // if object has flexy:if - then it may or may not be rendered.
520 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
521 // skip a flexy if element.
522 Roo.debug && Roo.log('skipping render');
523 Roo.debug && Roo.log(tree);
525 Roo.debug && Roo.log('skipping all children');
526 skip_children = true;
531 // actually if flexy:foreach is found, we really want to create
532 // multiple copies here...
534 //Roo.log(this[cntr]());
535 // some elements do not have render methods.. like the layouts...
537 if(this[cntr](true) === false){
542 cn.render && cn.render(this[cntr](true));
545 // then add the element..
552 if (typeof (tree.menu) != 'undefined') {
553 tree.menu.parentType = cn.xtype;
554 tree.menu.triggerEl = cn.el;
555 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559 if (!tree.items || !tree.items.length) {
561 //Roo.log(["no children", this]);
566 var items = tree.items;
569 //Roo.log(items.length);
571 if (!skip_children) {
572 for(var i =0;i < items.length;i++) {
573 // Roo.log(['add child', items[i]]);
574 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
580 //Roo.log("fire childrenrendered");
582 cn.fireEvent('childrenrendered', this);
588 * Set the element that will be used to show or hide
590 setVisibilityEl : function(el)
592 this.visibilityEl = el;
596 * Get the element that will be used to show or hide
598 getVisibilityEl : function()
600 if (typeof(this.visibilityEl) == 'object') {
601 return this.visibilityEl;
604 if (typeof(this.visibilityEl) == 'string') {
605 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612 * Show a component - removes 'hidden' class
616 if(!this.getVisibilityEl()){
620 this.getVisibilityEl().removeClass(['hidden','d-none']);
622 this.fireEvent('show', this);
627 * Hide a component - adds 'hidden' class
631 if(!this.getVisibilityEl()){
635 this.getVisibilityEl().addClass(['hidden','d-none']);
637 this.fireEvent('hide', this);
650 * @class Roo.bootstrap.Element
651 * @extends Roo.bootstrap.Component
652 * Bootstrap Element class
653 * @cfg {String} html contents of the element
654 * @cfg {String} tag tag of the element
655 * @cfg {String} cls class of the element
656 * @cfg {Boolean} preventDefault (true|false) default false
657 * @cfg {Boolean} clickable (true|false) default false
658 * @cfg {String} role default blank - set to button to force cursor pointer
662 * Create a new Element
663 * @param {Object} config The config object
666 Roo.bootstrap.Element = function(config){
667 Roo.bootstrap.Element.superclass.constructor.call(this, config);
673 * When a element is chick
674 * @param {Roo.bootstrap.Element} this
675 * @param {Roo.EventObject} e
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
688 preventDefault: false,
693 getAutoCreate : function(){
697 // cls: this.cls, double assign in parent class Component.js :: onRender
700 if (this.role !== false) {
701 cfg.role = this.role;
707 initEvents: function()
709 Roo.bootstrap.Element.superclass.initEvents.call(this);
712 this.el.on('click', this.onClick, this);
718 onClick : function(e)
720 if(this.preventDefault){
724 this.fireEvent('click', this, e); // why was this double click before?
732 getValue : function()
734 return this.el.dom.innerHTML;
737 setValue : function(value)
739 this.el.dom.innerHTML = value;
754 * @class Roo.bootstrap.DropTarget
755 * @extends Roo.bootstrap.Element
756 * Bootstrap DropTarget class
758 * @cfg {string} name dropable name
761 * Create a new Dropable Area
762 * @param {Object} config The config object
765 Roo.bootstrap.DropTarget = function(config){
766 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
772 * When a element is chick
773 * @param {Roo.bootstrap.Element} this
774 * @param {Roo.EventObject} e
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
783 getAutoCreate : function(){
788 initEvents: function()
790 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
794 drop : this.dragDrop.createDelegate(this),
795 enter : this.dragEnter.createDelegate(this),
796 out : this.dragOut.createDelegate(this),
797 over : this.dragOver.createDelegate(this)
801 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
804 dragDrop : function(source,e,data)
806 // user has to decide how to impliment this.
809 //this.fireEvent('drop', this, source, e ,data);
813 dragEnter : function(n, dd, e, data)
815 // probably want to resize the element to match the dropped element..
817 this.originalSize = this.el.getSize();
818 this.el.setSize( n.el.getSize());
819 this.dropZone.DDM.refreshCache(this.name);
820 Roo.log([n, dd, e, data]);
823 dragOut : function(value)
825 // resize back to normal
827 this.el.setSize(this.originalSize);
828 this.dropZone.resetConstraints();
831 dragOver : function()
848 * @class Roo.bootstrap.Body
849 * @extends Roo.bootstrap.Component
850 * Bootstrap Body class
854 * @param {Object} config The config object
857 Roo.bootstrap.Body = function(config){
859 config = config || {};
861 Roo.bootstrap.Body.superclass.constructor.call(this, config);
862 this.el = Roo.get(config.el ? config.el : document.body );
863 if (this.cls && this.cls.length) {
864 Roo.get(document.body).addClass(this.cls);
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
870 is_body : true,// just to make sure it's constructed?
875 onRender : function(ct, position)
877 /* Roo.log("Roo.bootstrap.Body - onRender");
878 if (this.cls && this.cls.length) {
879 Roo.get(document.body).addClass(this.cls);
898 * @class Roo.bootstrap.ButtonGroup
899 * @extends Roo.bootstrap.Component
900 * Bootstrap ButtonGroup class
901 * @cfg {String} size lg | sm | xs (default empty normal)
902 * @cfg {String} align vertical | justified (default none)
903 * @cfg {String} direction up | down (default down)
904 * @cfg {Boolean} toolbar false | true
905 * @cfg {Boolean} btn true | false
910 * @param {Object} config The config object
913 Roo.bootstrap.ButtonGroup = function(config){
914 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
925 getAutoCreate : function(){
931 cfg.html = this.html || cfg.html;
942 if (['vertical','justified'].indexOf(this.align)!==-1) {
943 cfg.cls = 'btn-group-' + this.align;
945 if (this.align == 'justified') {
946 console.log(this.items);
950 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951 cfg.cls += ' btn-group-' + this.size;
954 if (this.direction == 'up') {
955 cfg.cls += ' dropup' ;
961 * Add a button to the group (similar to NavItem API.)
963 addItem : function(cfg)
965 var cn = new Roo.bootstrap.Button(cfg);
967 cn.parentId = this.id;
968 cn.onRender(this.el, null);
982 * @class Roo.bootstrap.Button
983 * @extends Roo.bootstrap.Component
984 * Bootstrap Button class
985 * @cfg {String} html The button content
986 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989 * @cfg {String} size (lg|sm|xs)
990 * @cfg {String} tag (a|input|submit)
991 * @cfg {String} href empty or href
992 * @cfg {Boolean} disabled default false;
993 * @cfg {Boolean} isClose default false;
994 * @cfg {String} glyphicon depricated - use fa
995 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996 * @cfg {String} badge text for badge
997 * @cfg {String} theme (default|glow)
998 * @cfg {Boolean} inverse dark themed version
999 * @cfg {Boolean} toggle is it a slidy toggle button
1000 * @cfg {Boolean} pressed default null - if the button ahs active state
1001 * @cfg {String} ontext text for on slidy toggle state
1002 * @cfg {String} offtext text for off slidy toggle state
1003 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1004 * @cfg {Boolean} removeClass remove the standard class..
1005 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1006 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1009 * Create a new button
1010 * @param {Object} config The config object
1014 Roo.bootstrap.Button = function(config){
1015 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1021 * When a button is pressed
1022 * @param {Roo.bootstrap.Button} btn
1023 * @param {Roo.EventObject} e
1028 * When a button is double clicked
1029 * @param {Roo.bootstrap.Button} btn
1030 * @param {Roo.EventObject} e
1035 * After the button has been toggles
1036 * @param {Roo.bootstrap.Button} btn
1037 * @param {Roo.EventObject} e
1038 * @param {boolean} pressed (also available as button.pressed)
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1065 preventDefault: true,
1074 getAutoCreate : function(){
1082 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084 this.tag = 'button';
1088 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090 if (this.toggle == true) {
1093 cls: 'slider-frame roo-button',
1097 'data-on-text':'ON',
1098 'data-off-text':'OFF',
1099 cls: 'slider-button',
1104 // why are we validating the weights?
1105 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106 cfg.cls += ' ' + this.weight;
1113 cfg.cls += ' close';
1115 cfg["aria-hidden"] = true;
1117 cfg.html = "×";
1123 if (this.theme==='default') {
1124 cfg.cls = 'btn roo-button';
1126 //if (this.parentType != 'Navbar') {
1127 this.weight = this.weight.length ? this.weight : 'default';
1129 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133 cfg.cls += ' btn-' + outline + weight;
1134 if (this.weight == 'default') {
1136 cfg.cls += ' btn-' + this.weight;
1139 } else if (this.theme==='glow') {
1142 cfg.cls = 'btn-glow roo-button';
1144 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146 cfg.cls += ' ' + this.weight;
1152 this.cls += ' inverse';
1156 if (this.active || this.pressed === true) {
1157 cfg.cls += ' active';
1160 if (this.disabled) {
1161 cfg.disabled = 'disabled';
1165 Roo.log('changing to ul' );
1167 this.glyphicon = 'caret';
1168 if (Roo.bootstrap.version == 4) {
1169 this.fa = 'caret-down';
1174 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176 //gsRoo.log(this.parentType);
1177 if (this.parentType === 'Navbar' && !this.parent().bar) {
1178 Roo.log('changing to li?');
1187 href : this.href || '#'
1190 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1191 cfg.cls += ' dropdown';
1198 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1200 if (this.glyphicon) {
1201 cfg.html = ' ' + cfg.html;
1206 cls: 'glyphicon glyphicon-' + this.glyphicon
1211 cfg.html = ' ' + cfg.html;
1216 cls: 'fa fas fa-' + this.fa
1226 // cfg.cls='btn roo-button';
1230 var value = cfg.html;
1235 cls: 'glyphicon glyphicon-' + this.glyphicon,
1242 cls: 'fa fas fa-' + this.fa,
1247 var bw = this.badge_weight.length ? this.badge_weight :
1248 (this.weight.length ? this.weight : 'secondary');
1249 bw = bw == 'default' ? 'secondary' : bw;
1255 cls: 'badge badge-' + bw,
1264 cfg.cls += ' dropdown';
1265 cfg.html = typeof(cfg.html) != 'undefined' ?
1266 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1269 if (cfg.tag !== 'a' && this.href !== '') {
1270 throw "Tag must be a to set href.";
1271 } else if (this.href.length > 0) {
1272 cfg.href = this.href;
1275 if(this.removeClass){
1280 cfg.target = this.target;
1285 initEvents: function() {
1286 // Roo.log('init events?');
1287 // Roo.log(this.el.dom);
1290 if (typeof (this.menu) != 'undefined') {
1291 this.menu.parentType = this.xtype;
1292 this.menu.triggerEl = this.el;
1293 this.addxtype(Roo.apply({}, this.menu));
1297 if (this.el.hasClass('roo-button')) {
1298 this.el.on('click', this.onClick, this);
1299 this.el.on('dblclick', this.onDblClick, this);
1301 this.el.select('.roo-button').on('click', this.onClick, this);
1302 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1306 if(this.removeClass){
1307 this.el.on('click', this.onClick, this);
1310 if (this.group === true) {
1311 if (this.pressed === false || this.pressed === true) {
1314 this.pressed = false;
1315 this.setActive(this.pressed);
1320 this.el.enableDisplayMode();
1323 onClick : function(e)
1325 if (this.disabled) {
1329 Roo.log('button on click ');
1330 if(this.preventDefault){
1339 this.setActive(true);
1340 var pi = this.parent().items;
1341 for (var i = 0;i < pi.length;i++) {
1342 if (this == pi[i]) {
1345 if (pi[i].el.hasClass('roo-button')) {
1346 pi[i].setActive(false);
1349 this.fireEvent('click', this, e);
1353 if (this.pressed === true || this.pressed === false) {
1354 this.toggleActive(e);
1358 this.fireEvent('click', this, e);
1360 onDblClick: function(e)
1362 if (this.disabled) {
1365 if(this.preventDefault){
1368 this.fireEvent('dblclick', this, e);
1371 * Enables this button
1375 this.disabled = false;
1376 this.el.removeClass('disabled');
1377 this.el.dom.removeAttribute("disabled");
1381 * Disable this button
1383 disable : function()
1385 this.disabled = true;
1386 this.el.addClass('disabled');
1387 this.el.attr("disabled", "disabled")
1390 * sets the active state on/off,
1391 * @param {Boolean} state (optional) Force a particular state
1393 setActive : function(v) {
1395 this.el[v ? 'addClass' : 'removeClass']('active');
1399 * toggles the current active state
1401 toggleActive : function(e)
1403 this.setActive(!this.pressed); // this modifies pressed...
1404 this.fireEvent('toggle', this, e, this.pressed);
1407 * get the current active state
1408 * @return {boolean} true if it's active
1410 isActive : function()
1412 return this.el.hasClass('active');
1415 * set the text of the first selected button
1417 setText : function(str)
1419 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1422 * get the text of the first selected button
1424 getText : function()
1426 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1429 setWeight : function(str)
1431 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434 var outline = this.outline ? 'outline-' : '';
1435 if (str == 'default') {
1436 this.el.addClass('btn-default btn-outline-secondary');
1439 this.el.addClass('btn-' + outline + str);
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446 Roo.bootstrap.Button.weights = [
1466 * @class Roo.bootstrap.Column
1467 * @extends Roo.bootstrap.Component
1468 * Bootstrap Column class
1469 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1479 * @cfg {Boolean} hidden (true|false) hide the element
1480 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481 * @cfg {String} fa (ban|check|...) font awesome icon
1482 * @cfg {Number} fasize (1|2|....) font awsome size
1484 * @cfg {String} icon (info-sign|check|...) glyphicon name
1486 * @cfg {String} html content of column.
1489 * Create a new Column
1490 * @param {Object} config The config object
1493 Roo.bootstrap.Column = function(config){
1494 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1515 getAutoCreate : function(){
1516 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1524 var sizes = ['xs','sm','md','lg'];
1525 sizes.map(function(size ,ix){
1526 //Roo.log( size + ':' + settings[size]);
1528 if (settings[size+'off'] !== false) {
1529 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1532 if (settings[size] === false) {
1536 if (!settings[size]) { // 0 = hidden
1537 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539 for (var i = ix; i > -1; i--) {
1540 cfg.cls += ' d-' + sizes[i] + '-none';
1546 cfg.cls += ' col-' + size + '-' + settings[size] + (
1547 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1553 cfg.cls += ' hidden';
1556 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557 cfg.cls +=' alert alert-' + this.alert;
1561 if (this.html.length) {
1562 cfg.html = this.html;
1566 if (this.fasize > 1) {
1567 fasize = ' fa-' + this.fasize + 'x';
1569 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1574 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1593 * @class Roo.bootstrap.Container
1594 * @extends Roo.bootstrap.Component
1595 * Bootstrap Container class
1596 * @cfg {Boolean} jumbotron is it a jumbotron element
1597 * @cfg {String} html content of element
1598 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1600 * @cfg {String} header content of header (for panel)
1601 * @cfg {String} footer content of footer (for panel)
1602 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603 * @cfg {String} tag (header|aside|section) type of HTML tag.
1604 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605 * @cfg {String} fa font awesome icon
1606 * @cfg {String} icon (info-sign|check|...) glyphicon name
1607 * @cfg {Boolean} hidden (true|false) hide the element
1608 * @cfg {Boolean} expandable (true|false) default false
1609 * @cfg {Boolean} expanded (true|false) default true
1610 * @cfg {String} rheader contet on the right of header
1611 * @cfg {Boolean} clickable (true|false) default false
1615 * Create a new Container
1616 * @param {Object} config The config object
1619 Roo.bootstrap.Container = function(config){
1620 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1626 * After the panel has been expand
1628 * @param {Roo.bootstrap.Container} this
1633 * After the panel has been collapsed
1635 * @param {Roo.bootstrap.Container} this
1640 * When a element is chick
1641 * @param {Roo.bootstrap.Container} this
1642 * @param {Roo.EventObject} e
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1666 getChildContainer : function() {
1672 if (this.panel.length) {
1673 return this.el.select('.panel-body',true).first();
1680 getAutoCreate : function(){
1683 tag : this.tag || 'div',
1687 if (this.jumbotron) {
1688 cfg.cls = 'jumbotron';
1693 // - this is applied by the parent..
1695 // cfg.cls = this.cls + '';
1698 if (this.sticky.length) {
1700 var bd = Roo.get(document.body);
1701 if (!bd.hasClass('bootstrap-sticky')) {
1702 bd.addClass('bootstrap-sticky');
1703 Roo.select('html',true).setStyle('height', '100%');
1706 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1710 if (this.well.length) {
1711 switch (this.well) {
1714 cfg.cls +=' well well-' +this.well;
1723 cfg.cls += ' hidden';
1727 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728 cfg.cls +=' alert alert-' + this.alert;
1733 if (this.panel.length) {
1734 cfg.cls += ' panel panel-' + this.panel;
1736 if (this.header.length) {
1740 if(this.expandable){
1742 cfg.cls = cfg.cls + ' expandable';
1746 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1754 cls : 'panel-title',
1755 html : (this.expandable ? ' ' : '') + this.header
1759 cls: 'panel-header-right',
1765 cls : 'panel-heading',
1766 style : this.expandable ? 'cursor: pointer' : '',
1774 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1779 if (this.footer.length) {
1781 cls : 'panel-footer',
1790 body.html = this.html || cfg.html;
1791 // prefix with the icons..
1793 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1796 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1801 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802 cfg.cls = 'container';
1808 initEvents: function()
1810 if(this.expandable){
1811 var headerEl = this.headerEl();
1814 headerEl.on('click', this.onToggleClick, this);
1819 this.el.on('click', this.onClick, this);
1824 onToggleClick : function()
1826 var headerEl = this.headerEl();
1842 if(this.fireEvent('expand', this)) {
1844 this.expanded = true;
1846 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848 this.el.select('.panel-body',true).first().removeClass('hide');
1850 var toggleEl = this.toggleEl();
1856 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1861 collapse : function()
1863 if(this.fireEvent('collapse', this)) {
1865 this.expanded = false;
1867 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868 this.el.select('.panel-body',true).first().addClass('hide');
1870 var toggleEl = this.toggleEl();
1876 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1880 toggleEl : function()
1882 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1886 return this.el.select('.panel-heading .fa',true).first();
1889 headerEl : function()
1891 if(!this.el || !this.panel.length || !this.header.length){
1895 return this.el.select('.panel-heading',true).first()
1900 if(!this.el || !this.panel.length){
1904 return this.el.select('.panel-body',true).first()
1907 titleEl : function()
1909 if(!this.el || !this.panel.length || !this.header.length){
1913 return this.el.select('.panel-title',true).first();
1916 setTitle : function(v)
1918 var titleEl = this.titleEl();
1924 titleEl.dom.innerHTML = v;
1927 getTitle : function()
1930 var titleEl = this.titleEl();
1936 return titleEl.dom.innerHTML;
1939 setRightTitle : function(v)
1941 var t = this.el.select('.panel-header-right',true).first();
1947 t.dom.innerHTML = v;
1950 onClick : function(e)
1954 this.fireEvent('click', this, e);
1961 * This is BS4's Card element.. - similar to our containers probably..
1965 * @class Roo.bootstrap.Card
1966 * @extends Roo.bootstrap.Component
1967 * Bootstrap Card class
1970 * possible... may not be implemented..
1971 * @cfg {String} header_image src url of image.
1972 * @cfg {String|Object} header
1973 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1976 * @cfg {String} title
1977 * @cfg {String} subtitle
1978 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979 * @cfg {String} footer
1981 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983 * @cfg {String} margin (0|1|2|3|4|5|auto)
1984 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991 * @cfg {String} padding (0|1|2|3|4|5)
1992 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994 * @cfg {String} padding_left (0|1|2|3|4|5)
1995 * @cfg {String} padding_right (0|1|2|3|4|5)
1996 * @cfg {String} padding_x (0|1|2|3|4|5)
1997 * @cfg {String} padding_y (0|1|2|3|4|5)
1999 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005 * @config {Boolean} dragable if this card can be dragged.
2006 * @config {String} drag_group group for drag
2007 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2008 * @config {String} drop_group group for drag
2010 * @config {Boolean} collapsable can the body be collapsed.
2011 * @config {Boolean} collapsed is the body collapsed when rendered...
2012 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013 * @config {Boolean} rotated is the body rotated when rendered...
2016 * Create a new Container
2017 * @param {Object} config The config object
2020 Roo.bootstrap.Card = function(config){
2021 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2027 * When a element a card is dropped
2028 * @param {Roo.bootstrap.Card} this
2031 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032 * @param {String} position 'above' or 'below'
2033 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2039 * When a element a card is rotate
2040 * @param {Roo.bootstrap.Card} this
2041 * @param {Roo.Element} n the node being dropped?
2042 * @param {Boolean} rotate status
2047 * When a card element is dragged over ready to drop (return false to block dropable)
2048 * @param {Roo.bootstrap.Card} this
2049 * @param {Object} data from dragdrop
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2062 margin: '', /// may be better in component?
2092 collapsable : false,
2101 childContainer : false,
2102 dropEl : false, /// the dom placeholde element that indicates drop location.
2103 containerEl: false, // body container
2104 bodyEl: false, // card-body
2105 headerContainerEl : false, //
2107 header_imageEl : false,
2110 layoutCls : function()
2114 Roo.log(this.margin_bottom.length);
2115 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2121 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2126 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2132 // more generic support?
2140 // Roo.log("Call onRender: " + this.xtype);
2141 /* We are looking at something like this.
2143 <img src="..." class="card-img-top" alt="...">
2144 <div class="card-body">
2145 <h5 class="card-title">Card title</h5>
2146 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148 >> this bit is really the body...
2149 <div> << we will ad dthis in hopefully it will not break shit.
2151 ** card text does not actually have any styling...
2153 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2156 <a href="#" class="card-link">Card link</a>
2159 <div class="card-footer">
2160 <small class="text-muted">Last updated 3 mins ago</small>
2164 getAutoCreate : function(){
2172 if (this.weight.length && this.weight != 'light') {
2173 cfg.cls += ' text-white';
2175 cfg.cls += ' text-dark'; // need as it's nested..
2177 if (this.weight.length) {
2178 cfg.cls += ' bg-' + this.weight;
2181 cfg.cls += ' ' + this.layoutCls();
2184 var hdr_ctr = false;
2185 if (this.header.length) {
2187 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2196 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2202 if (this.collapsable) {
2205 cls : 'd-block user-select-none',
2209 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2214 hdr.cn.push(hdr_ctr);
2219 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2224 if (this.header_image.length) {
2227 cls : 'card-img-top',
2228 src: this.header_image // escape?
2233 cls : 'card-img-top d-none'
2239 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2243 if (this.collapsable || this.rotateable) {
2246 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2253 if (this.title.length) {
2257 src: this.title // escape?
2261 if (this.subtitle.length) {
2265 src: this.subtitle // escape?
2271 cls : 'roo-card-body-ctr'
2274 if (this.html.length) {
2280 // fixme ? handle objects?
2282 if (this.footer.length) {
2285 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2290 cfg.cn.push({cls : 'card-footer d-none'});
2299 getCardHeader : function()
2301 var ret = this.el.select('.card-header',true).first();
2302 if (ret.hasClass('d-none')) {
2303 ret.removeClass('d-none');
2308 getCardFooter : function()
2310 var ret = this.el.select('.card-footer',true).first();
2311 if (ret.hasClass('d-none')) {
2312 ret.removeClass('d-none');
2317 getCardImageTop : function()
2319 var ret = this.header_imageEl;
2320 if (ret.hasClass('d-none')) {
2321 ret.removeClass('d-none');
2327 getChildContainer : function()
2333 return this.el.select('.roo-card-body-ctr',true).first();
2336 initEvents: function()
2338 this.bodyEl = this.el.select('.card-body',true).first();
2339 this.containerEl = this.getChildContainer();
2341 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342 containerScroll: true,
2343 ddGroup: this.drag_group || 'default_card_drag_group'
2345 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347 if (this.dropable) {
2348 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349 containerScroll: true,
2350 ddGroup: this.drop_group || 'default_card_drag_group'
2352 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2359 if (this.collapsable) {
2360 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362 if (this.rotateable) {
2363 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367 this.footerEl = this.el.select('.card-footer',true).first();
2368 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370 this.headerEl = this.el.select('.card-header',true).first();
2373 this.el.addClass('roo-card-rotated');
2374 this.fireEvent('rotate', this, true);
2376 this.header_imageEl = this.el.select('.card-img-top',true).first();
2377 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2380 getDragData : function(e)
2382 var target = this.getEl();
2384 //this.handleSelection(e);
2389 nodes: this.getEl(),
2394 dragData.ddel = target.dom ; // the div element
2395 Roo.log(target.getWidth( ));
2396 dragData.ddel.style.width = target.getWidth() + 'px';
2403 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2404 * whole Element becomes the target, and this causes the drop gesture to append.
2406 * Returns an object:
2409 position : 'below' or 'above'
2410 card : relateive to card OBJECT (or true for no cards listed)
2411 items_n : relative to nth item in list
2412 card_n : relative to nth card in list
2417 getTargetFromEvent : function(e, dragged_card_el)
2419 var target = e.getTarget();
2420 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421 target = target.parentNode;
2432 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433 // see if target is one of the 'cards'...
2436 //Roo.log(this.items.length);
2439 var last_card_n = 0;
2441 for (var i = 0;i< this.items.length;i++) {
2443 if (!this.items[i].el.hasClass('card')) {
2446 pos = this.getDropPoint(e, this.items[i].el.dom);
2448 cards_len = ret.cards.length;
2449 //Roo.log(this.items[i].el.dom.id);
2450 ret.cards.push(this.items[i]);
2452 if (ret.card_n < 0 && pos == 'above') {
2453 ret.position = cards_len > 0 ? 'below' : pos;
2454 ret.items_n = i > 0 ? i - 1 : 0;
2455 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2456 ret.card = ret.cards[ret.card_n];
2459 if (!ret.cards.length) {
2461 ret.position = 'below';
2465 // could not find a card.. stick it at the end..
2466 if (ret.card_n < 0) {
2467 ret.card_n = last_card_n;
2468 ret.card = ret.cards[last_card_n];
2469 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470 ret.position = 'below';
2473 if (this.items[ret.items_n].el == dragged_card_el) {
2477 if (ret.position == 'below') {
2478 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480 if (card_after && card_after.el == dragged_card_el) {
2487 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489 if (card_before && card_before.el == dragged_card_el) {
2496 onNodeEnter : function(n, dd, e, data){
2499 onNodeOver : function(n, dd, e, data)
2502 var target_info = this.getTargetFromEvent(e,data.source.el);
2503 if (target_info === false) {
2504 this.dropPlaceHolder('hide');
2507 Roo.log(['getTargetFromEvent', target_info ]);
2510 if (this.fireEvent('cardover', this, [ data ]) === false) {
2514 this.dropPlaceHolder('show', target_info,data);
2518 onNodeOut : function(n, dd, e, data){
2519 this.dropPlaceHolder('hide');
2522 onNodeDrop : function(n, dd, e, data)
2525 // call drop - return false if
2527 // this could actually fail - if the Network drops..
2528 // we will ignore this at present..- client should probably reload
2529 // the whole set of cards if stuff like that fails.
2532 var info = this.getTargetFromEvent(e,data.source.el);
2533 if (info === false) {
2536 this.dropPlaceHolder('hide');
2540 this.acceptCard(data.source, info.position, info.card, info.items_n);
2544 firstChildCard : function()
2546 for (var i = 0;i< this.items.length;i++) {
2548 if (!this.items[i].el.hasClass('card')) {
2551 return this.items[i];
2553 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2558 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2560 acceptCard : function(move_card, position, next_to_card )
2562 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2566 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568 move_card.parent().removeCard(move_card);
2571 var dom = move_card.el.dom;
2572 dom.style.width = ''; // clear with - which is set by drag.
2574 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575 var cardel = next_to_card.el.dom;
2577 if (position == 'above' ) {
2578 cardel.parentNode.insertBefore(dom, cardel);
2579 } else if (cardel.nextSibling) {
2580 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582 cardel.parentNode.append(dom);
2585 // card container???
2586 this.containerEl.dom.append(dom);
2589 //FIXME HANDLE card = true
2591 // add this to the correct place in items.
2593 // remove Card from items.
2596 if (this.items.length) {
2598 //Roo.log([info.items_n, info.position, this.items.length]);
2599 for (var i =0; i < this.items.length; i++) {
2600 if (i == to_items_n && position == 'above') {
2601 nitems.push(move_card);
2603 nitems.push(this.items[i]);
2604 if (i == to_items_n && position == 'below') {
2605 nitems.push(move_card);
2608 this.items = nitems;
2609 Roo.log(this.items);
2611 this.items.push(move_card);
2614 move_card.parentId = this.id;
2620 removeCard : function(c)
2622 this.items = this.items.filter(function(e) { return e != c });
2625 dom.parentNode.removeChild(dom);
2626 dom.style.width = ''; // clear with - which is set by drag.
2631 /** Decide whether to drop above or below a View node. */
2632 getDropPoint : function(e, n, dd)
2637 if (n == this.containerEl.dom) {
2640 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641 var c = t + (b - t) / 2;
2642 var y = Roo.lib.Event.getPageY(e);
2649 onToggleCollapse : function(e)
2651 if (this.collapsed) {
2652 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653 this.collapsableEl.addClass('show');
2654 this.collapsed = false;
2657 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658 this.collapsableEl.removeClass('show');
2659 this.collapsed = true;
2664 onToggleRotate : function(e)
2666 this.collapsableEl.removeClass('show');
2667 this.footerEl.removeClass('d-none');
2668 this.el.removeClass('roo-card-rotated');
2669 this.el.removeClass('d-none');
2672 this.collapsableEl.addClass('show');
2673 this.rotated = false;
2674 this.fireEvent('rotate', this, this.rotated);
2677 this.el.addClass('roo-card-rotated');
2678 this.footerEl.addClass('d-none');
2679 this.el.select('.roo-collapsable').removeClass('show');
2681 this.rotated = true;
2682 this.fireEvent('rotate', this, this.rotated);
2686 dropPlaceHolder: function (action, info, data)
2688 if (this.dropEl === false) {
2689 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2693 this.dropEl.removeClass(['d-none', 'd-block']);
2694 if (action == 'hide') {
2696 this.dropEl.addClass('d-none');
2699 // FIXME - info.card == true!!!
2700 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702 if (info.card !== true) {
2703 var cardel = info.card.el.dom;
2705 if (info.position == 'above') {
2706 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707 } else if (cardel.nextSibling) {
2708 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710 cardel.parentNode.append(this.dropEl.dom);
2713 // card container???
2714 this.containerEl.dom.append(this.dropEl.dom);
2717 this.dropEl.addClass('d-block roo-card-dropzone');
2719 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2726 setHeaderText: function(html)
2729 if (this.headerContainerEl) {
2730 this.headerContainerEl.dom.innerHTML = html;
2733 onHeaderImageLoad : function(ev, he)
2735 if (!this.header_image_fit_square) {
2739 var hw = he.naturalHeight / he.naturalWidth;
2742 //var w = he.dom.naturalWidth;
2745 he.style.position = 'relative';
2747 var nw = (ww * (1/hw));
2748 Roo.get(he).setSize( ww * (1/hw), ww);
2749 he.style.left = ((ww - nw)/ 2) + 'px';
2750 he.style.position = 'relative';
2761 * Card header - holder for the card header elements.
2766 * @class Roo.bootstrap.CardHeader
2767 * @extends Roo.bootstrap.Element
2768 * Bootstrap CardHeader class
2770 * Create a new Card Header - that you can embed children into
2771 * @param {Object} config The config object
2774 Roo.bootstrap.CardHeader = function(config){
2775 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2781 container_method : 'getCardHeader'
2794 * Card footer - holder for the card footer elements.
2799 * @class Roo.bootstrap.CardFooter
2800 * @extends Roo.bootstrap.Element
2801 * Bootstrap CardFooter class
2803 * Create a new Card Footer - that you can embed children into
2804 * @param {Object} config The config object
2807 Roo.bootstrap.CardFooter = function(config){
2808 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2814 container_method : 'getCardFooter'
2827 * Card header - holder for the card header elements.
2832 * @class Roo.bootstrap.CardImageTop
2833 * @extends Roo.bootstrap.Element
2834 * Bootstrap CardImageTop class
2836 * Create a new Card Image Top container
2837 * @param {Object} config The config object
2840 Roo.bootstrap.CardImageTop = function(config){
2841 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2847 container_method : 'getCardImageTop'
2862 * @class Roo.bootstrap.ButtonUploader
2863 * @extends Roo.bootstrap.Button
2864 * Bootstrap Button Uploader class - it's a button which when you add files to it
2867 * @cfg {Number} errorTimeout default 3000
2868 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2869 * @cfg {Array} html The button text.
2870 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2873 * Create a new CardUploader
2874 * @param {Object} config The config object
2877 Roo.bootstrap.ButtonUploader = function(config){
2881 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2887 * @event beforeselect
2888 * When button is pressed, before show upload files dialog is shown
2889 * @param {Roo.bootstrap.UploaderButton} this
2892 'beforeselect' : true,
2894 * @event fired when files have been selected,
2895 * When a the download link is clicked
2896 * @param {Roo.bootstrap.UploaderButton} this
2897 * @param {Array} Array of files that have been uploaded
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2907 errorTimeout : 3000,
2911 fileCollection : false,
2916 getAutoCreate : function()
2921 cls : 'd-none roo-card-upload-selector'
2924 if (this.multiple) {
2925 im.multiple = 'multiple';
2931 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2941 initEvents : function()
2944 Roo.bootstrap.Button.prototype.initEvents.call(this);
2950 this.urlAPI = (window.createObjectURL && window) ||
2951 (window.URL && URL.revokeObjectURL && URL) ||
2952 (window.webkitURL && webkitURL);
2957 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959 this.selectorEl.on('change', this.onFileSelected, this);
2966 onClick : function(e)
2970 if ( this.fireEvent('beforeselect', this) === false) {
2974 this.selectorEl.dom.click();
2978 onFileSelected : function(e)
2982 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2985 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986 this.selectorEl.dom.value = '';// hopefully reset..
2988 this.fireEvent('uploaded', this, files );
2996 * addCard - add an Attachment to the uploader
2997 * @param data - the data about the image to upload
3001 title : "Title of file",
3002 is_uploaded : false,
3003 src : "http://.....",
3004 srcfile : { the File upload object },
3005 mimetype : file.type,
3008 .. any other data...
3033 * @class Roo.bootstrap.Img
3034 * @extends Roo.bootstrap.Component
3035 * Bootstrap Img class
3036 * @cfg {Boolean} imgResponsive false | true
3037 * @cfg {String} border rounded | circle | thumbnail
3038 * @cfg {String} src image source
3039 * @cfg {String} alt image alternative text
3040 * @cfg {String} href a tag href
3041 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042 * @cfg {String} xsUrl xs image source
3043 * @cfg {String} smUrl sm image source
3044 * @cfg {String} mdUrl md image source
3045 * @cfg {String} lgUrl lg image source
3048 * Create a new Input
3049 * @param {Object} config The config object
3052 Roo.bootstrap.Img = function(config){
3053 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3059 * The img click event for the img.
3060 * @param {Roo.EventObject} e
3066 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3068 imgResponsive: true,
3078 getAutoCreate : function()
3080 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3081 return this.createSingleImg();
3086 cls: 'roo-image-responsive-group',
3091 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3093 if(!_this[size + 'Url']){
3099 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3100 html: _this.html || cfg.html,
3101 src: _this[size + 'Url']
3104 img.cls += ' roo-image-responsive-' + size;
3106 var s = ['xs', 'sm', 'md', 'lg'];
3108 s.splice(s.indexOf(size), 1);
3110 Roo.each(s, function(ss){
3111 img.cls += ' hidden-' + ss;
3114 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3115 cfg.cls += ' img-' + _this.border;
3119 cfg.alt = _this.alt;
3132 a.target = _this.target;
3136 cfg.cn.push((_this.href) ? a : img);
3143 createSingleImg : function()
3147 cls: (this.imgResponsive) ? 'img-responsive' : '',
3149 src : 'about:blank' // just incase src get's set to undefined?!?
3152 cfg.html = this.html || cfg.html;
3154 cfg.src = this.src || cfg.src;
3156 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3157 cfg.cls += ' img-' + this.border;
3174 a.target = this.target;
3179 return (this.href) ? a : cfg;
3182 initEvents: function()
3185 this.el.on('click', this.onClick, this);
3190 onClick : function(e)
3192 Roo.log('img onclick');
3193 this.fireEvent('click', this, e);
3196 * Sets the url of the image - used to update it
3197 * @param {String} url the url of the image
3200 setSrc : function(url)
3204 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205 this.el.dom.src = url;
3209 this.el.select('img', true).first().dom.src = url;
3225 * @class Roo.bootstrap.Link
3226 * @extends Roo.bootstrap.Component
3227 * Bootstrap Link Class
3228 * @cfg {String} alt image alternative text
3229 * @cfg {String} href a tag href
3230 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3231 * @cfg {String} html the content of the link.
3232 * @cfg {String} anchor name for the anchor link
3233 * @cfg {String} fa - favicon
3235 * @cfg {Boolean} preventDefault (true | false) default false
3239 * Create a new Input
3240 * @param {Object} config The config object
3243 Roo.bootstrap.Link = function(config){
3244 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3250 * The img click event for the img.
3251 * @param {Roo.EventObject} e
3257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3261 preventDefault: false,
3267 getAutoCreate : function()
3269 var html = this.html || '';
3271 if (this.fa !== false) {
3272 html = '<i class="fa fa-' + this.fa + '"></i>';
3277 // anchor's do not require html/href...
3278 if (this.anchor === false) {
3280 cfg.href = this.href || '#';
3282 cfg.name = this.anchor;
3283 if (this.html !== false || this.fa !== false) {
3286 if (this.href !== false) {
3287 cfg.href = this.href;
3291 if(this.alt !== false){
3296 if(this.target !== false) {
3297 cfg.target = this.target;
3303 initEvents: function() {
3305 if(!this.href || this.preventDefault){
3306 this.el.on('click', this.onClick, this);
3310 onClick : function(e)
3312 if(this.preventDefault){
3315 //Roo.log('img onclick');
3316 this.fireEvent('click', this, e);
3329 * @class Roo.bootstrap.Header
3330 * @extends Roo.bootstrap.Component
3331 * Bootstrap Header class
3332 * @cfg {String} html content of header
3333 * @cfg {Number} level (1|2|3|4|5|6) default 1
3336 * Create a new Header
3337 * @param {Object} config The config object
3341 Roo.bootstrap.Header = function(config){
3342 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3345 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3353 getAutoCreate : function(){
3358 tag: 'h' + (1 *this.level),
3359 html: this.html || ''
3371 * Ext JS Library 1.1.1
3372 * Copyright(c) 2006-2007, Ext JS, LLC.
3374 * Originally Released Under LGPL - original licence link has changed is not relivant.
3377 * <script type="text/javascript">
3381 * @class Roo.bootstrap.MenuMgr
3382 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3385 Roo.bootstrap.MenuMgr = function(){
3386 var menus, active, groups = {}, attached = false, lastShow = new Date();
3388 // private - called when first menu is created
3391 active = new Roo.util.MixedCollection();
3392 Roo.get(document).addKeyListener(27, function(){
3393 if(active.length > 0){
3401 if(active && active.length > 0){
3402 var c = active.clone();
3412 if(active.length < 1){
3413 Roo.get(document).un("mouseup", onMouseDown);
3421 var last = active.last();
3422 lastShow = new Date();
3425 Roo.get(document).on("mouseup", onMouseDown);
3430 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3431 m.parentMenu.activeChild = m;
3432 }else if(last && last.isVisible()){
3433 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3438 function onBeforeHide(m){
3440 m.activeChild.hide();
3442 if(m.autoHideTimer){
3443 clearTimeout(m.autoHideTimer);
3444 delete m.autoHideTimer;
3449 function onBeforeShow(m){
3450 var pm = m.parentMenu;
3451 if(!pm && !m.allowOtherMenus){
3453 }else if(pm && pm.activeChild && active != m){
3454 pm.activeChild.hide();
3458 // private this should really trigger on mouseup..
3459 function onMouseDown(e){
3460 Roo.log("on Mouse Up");
3462 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3463 Roo.log("MenuManager hideAll");
3472 function onBeforeCheck(mi, state){
3474 var g = groups[mi.group];
3475 for(var i = 0, l = g.length; i < l; i++){
3477 g[i].setChecked(false);
3486 * Hides all menus that are currently visible
3488 hideAll : function(){
3493 register : function(menu){
3497 menus[menu.id] = menu;
3498 menu.on("beforehide", onBeforeHide);
3499 menu.on("hide", onHide);
3500 menu.on("beforeshow", onBeforeShow);
3501 menu.on("show", onShow);
3503 if(g && menu.events["checkchange"]){
3507 groups[g].push(menu);
3508 menu.on("checkchange", onCheck);
3513 * Returns a {@link Roo.menu.Menu} object
3514 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3515 * be used to generate and return a new Menu instance.
3517 get : function(menu){
3518 if(typeof menu == "string"){ // menu id
3520 }else if(menu.events){ // menu instance
3523 /*else if(typeof menu.length == 'number'){ // array of menu items?
3524 return new Roo.bootstrap.Menu({items:menu});
3525 }else{ // otherwise, must be a config
3526 return new Roo.bootstrap.Menu(menu);
3533 unregister : function(menu){
3534 delete menus[menu.id];
3535 menu.un("beforehide", onBeforeHide);
3536 menu.un("hide", onHide);
3537 menu.un("beforeshow", onBeforeShow);
3538 menu.un("show", onShow);
3540 if(g && menu.events["checkchange"]){
3541 groups[g].remove(menu);
3542 menu.un("checkchange", onCheck);
3547 registerCheckable : function(menuItem){
3548 var g = menuItem.group;
3553 groups[g].push(menuItem);
3554 menuItem.on("beforecheckchange", onBeforeCheck);
3559 unregisterCheckable : function(menuItem){
3560 var g = menuItem.group;
3562 groups[g].remove(menuItem);
3563 menuItem.un("beforecheckchange", onBeforeCheck);
3575 * @class Roo.bootstrap.Menu
3576 * @extends Roo.bootstrap.Component
3577 * Bootstrap Menu class - container for MenuItems
3578 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3579 * @cfg {bool} hidden if the menu should be hidden when rendered.
3580 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3581 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3582 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3583 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3587 * @param {Object} config The config object
3591 Roo.bootstrap.Menu = function(config){
3593 if (config.type == 'treeview') {
3594 // normally menu's are drawn attached to the document to handle layering etc..
3595 // however treeview (used by the docs menu is drawn into the parent element)
3596 this.container_method = 'getChildContainer';
3599 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3600 if (this.registerMenu && this.type != 'treeview') {
3601 Roo.bootstrap.MenuMgr.register(this);
3608 * Fires before this menu is displayed (return false to block)
3609 * @param {Roo.menu.Menu} this
3614 * Fires before this menu is hidden (return false to block)
3615 * @param {Roo.menu.Menu} this
3620 * Fires after this menu is displayed
3621 * @param {Roo.menu.Menu} this
3626 * Fires after this menu is hidden
3627 * @param {Roo.menu.Menu} this
3632 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3633 * @param {Roo.menu.Menu} this
3634 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3635 * @param {Roo.EventObject} e
3640 * Fires when the mouse is hovering over this menu
3641 * @param {Roo.menu.Menu} this
3642 * @param {Roo.EventObject} e
3643 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3648 * Fires when the mouse exits this menu
3649 * @param {Roo.menu.Menu} this
3650 * @param {Roo.EventObject} e
3651 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3656 * Fires when a menu item contained in this menu is clicked
3657 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3658 * @param {Roo.EventObject} e
3662 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3665 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3669 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3672 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3674 registerMenu : true,
3676 menuItems :false, // stores the menu items..
3686 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3688 hideTrigger : false,
3693 getChildContainer : function() {
3697 getAutoCreate : function(){
3699 //if (['right'].indexOf(this.align)!==-1) {
3700 // cfg.cn[1].cls += ' pull-right'
3705 cls : 'dropdown-menu shadow' ,
3706 style : 'z-index:1000'
3710 if (this.type === 'submenu') {
3711 cfg.cls = 'submenu active';
3713 if (this.type === 'treeview') {
3714 cfg.cls = 'treeview-menu';
3719 initEvents : function() {
3721 // Roo.log("ADD event");
3722 // Roo.log(this.triggerEl.dom);
3723 if (this.triggerEl) {
3725 this.triggerEl.on('click', this.onTriggerClick, this);
3727 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3729 if (!this.hideTrigger) {
3730 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3731 // dropdown toggle on the 'a' in BS4?
3732 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3734 this.triggerEl.addClass('dropdown-toggle');
3740 this.el.on('touchstart' , this.onTouch, this);
3742 this.el.on('click' , this.onClick, this);
3744 this.el.on("mouseover", this.onMouseOver, this);
3745 this.el.on("mouseout", this.onMouseOut, this);
3749 findTargetItem : function(e)
3751 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3755 //Roo.log(t); Roo.log(t.id);
3757 //Roo.log(this.menuitems);
3758 return this.menuitems.get(t.id);
3760 //return this.items.get(t.menuItemId);
3766 onTouch : function(e)
3768 Roo.log("menu.onTouch");
3769 //e.stopEvent(); this make the user popdown broken
3773 onClick : function(e)
3775 Roo.log("menu.onClick");
3777 var t = this.findTargetItem(e);
3778 if(!t || t.isContainer){
3783 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3784 if(t == this.activeItem && t.shouldDeactivate(e)){
3785 this.activeItem.deactivate();
3786 delete this.activeItem;
3790 this.setActiveItem(t, true);
3798 Roo.log('pass click event');
3802 this.fireEvent("click", this, t, e);
3806 if(!t.href.length || t.href == '#'){
3807 (function() { _this.hide(); }).defer(100);
3812 onMouseOver : function(e){
3813 var t = this.findTargetItem(e);
3816 // if(t.canActivate && !t.disabled){
3817 // this.setActiveItem(t, true);
3821 this.fireEvent("mouseover", this, e, t);
3823 isVisible : function(){
3824 return !this.hidden;
3826 onMouseOut : function(e){
3827 var t = this.findTargetItem(e);
3830 // if(t == this.activeItem && t.shouldDeactivate(e)){
3831 // this.activeItem.deactivate();
3832 // delete this.activeItem;
3835 this.fireEvent("mouseout", this, e, t);
3840 * Displays this menu relative to another element
3841 * @param {String/HTMLElement/Roo.Element} element The element to align to
3842 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3843 * the element (defaults to this.defaultAlign)
3844 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3846 show : function(el, pos, parentMenu)
3848 if (false === this.fireEvent("beforeshow", this)) {
3849 Roo.log("show canceled");
3852 this.parentMenu = parentMenu;
3856 this.el.addClass('show'); // show otherwise we do not know how big we are..
3858 var xy = this.el.getAlignToXY(el, pos);
3860 // bl-tl << left align below
3861 // tl-bl << left align
3863 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3864 // if it goes to far to the right.. -> align left.
3865 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3868 // was left align - go right?
3869 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3872 // goes down the bottom
3873 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3875 var a = this.align.replace('?', '').split('-');
3876 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3880 this.showAt( xy , parentMenu, false);
3883 * Displays this menu at a specific xy position
3884 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3885 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3887 showAt : function(xy, parentMenu, /* private: */_e){
3888 this.parentMenu = parentMenu;
3893 this.fireEvent("beforeshow", this);
3894 //xy = this.el.adjustForConstraints(xy);
3898 this.hideMenuItems();
3899 this.hidden = false;
3900 if (this.triggerEl) {
3901 this.triggerEl.addClass('open');
3904 this.el.addClass('show');
3908 // reassign x when hitting right
3910 // reassign y when hitting bottom
3912 // but the list may align on trigger left or trigger top... should it be a properity?
3914 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3919 this.fireEvent("show", this);
3925 this.doFocus.defer(50, this);
3929 doFocus : function(){
3931 this.focusEl.focus();
3936 * Hides this menu and optionally all parent menus
3937 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3939 hide : function(deep)
3941 if (false === this.fireEvent("beforehide", this)) {
3942 Roo.log("hide canceled");
3945 this.hideMenuItems();
3946 if(this.el && this.isVisible()){
3948 if(this.activeItem){
3949 this.activeItem.deactivate();
3950 this.activeItem = null;
3952 if (this.triggerEl) {
3953 this.triggerEl.removeClass('open');
3956 this.el.removeClass('show');
3958 this.fireEvent("hide", this);
3960 if(deep === true && this.parentMenu){
3961 this.parentMenu.hide(true);
3965 onTriggerClick : function(e)
3967 Roo.log('trigger click');
3969 var target = e.getTarget();
3971 Roo.log(target.nodeName.toLowerCase());
3973 if(target.nodeName.toLowerCase() === 'i'){
3979 onTriggerPress : function(e)
3981 Roo.log('trigger press');
3982 //Roo.log(e.getTarget());
3983 // Roo.log(this.triggerEl.dom);
3985 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3986 var pel = Roo.get(e.getTarget());
3987 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3988 Roo.log('is treeview or dropdown?');
3992 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3996 if (this.isVisible()) {
4002 this.show(this.triggerEl, this.align, false);
4005 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4012 hideMenuItems : function()
4014 Roo.log("hide Menu Items");
4019 this.el.select('.open',true).each(function(aa) {
4021 aa.removeClass('open');
4025 addxtypeChild : function (tree, cntr) {
4026 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4028 this.menuitems.add(comp);
4040 this.getEl().dom.innerHTML = '';
4041 this.menuitems.clear();
4055 * @class Roo.bootstrap.MenuItem
4056 * @extends Roo.bootstrap.Component
4057 * Bootstrap MenuItem class
4058 * @cfg {String} html the menu label
4059 * @cfg {String} href the link
4060 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4061 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4062 * @cfg {Boolean} active used on sidebars to highlight active itesm
4063 * @cfg {String} fa favicon to show on left of menu item.
4064 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4068 * Create a new MenuItem
4069 * @param {Object} config The config object
4073 Roo.bootstrap.MenuItem = function(config){
4074 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4079 * The raw click event for the entire grid.
4080 * @param {Roo.bootstrap.MenuItem} this
4081 * @param {Roo.EventObject} e
4087 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4091 preventDefault: false,
4092 isContainer : false,
4096 getAutoCreate : function(){
4098 if(this.isContainer){
4101 cls: 'dropdown-menu-item '
4111 cls : 'dropdown-item',
4116 if (this.fa !== false) {
4119 cls : 'fa fa-' + this.fa
4128 cls: 'dropdown-menu-item',
4131 if (this.parent().type == 'treeview') {
4132 cfg.cls = 'treeview-menu';
4135 cfg.cls += ' active';
4140 anc.href = this.href || cfg.cn[0].href ;
4141 ctag.html = this.html || cfg.cn[0].html ;
4145 initEvents: function()
4147 if (this.parent().type == 'treeview') {
4148 this.el.select('a').on('click', this.onClick, this);
4152 this.menu.parentType = this.xtype;
4153 this.menu.triggerEl = this.el;
4154 this.menu = this.addxtype(Roo.apply({}, this.menu));
4158 onClick : function(e)
4160 Roo.log('item on click ');
4162 if(this.preventDefault){
4165 //this.parent().hideMenuItems();
4167 this.fireEvent('click', this, e);
4186 * @class Roo.bootstrap.MenuSeparator
4187 * @extends Roo.bootstrap.Component
4188 * Bootstrap MenuSeparator class
4191 * Create a new MenuItem
4192 * @param {Object} config The config object
4196 Roo.bootstrap.MenuSeparator = function(config){
4197 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4200 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4202 getAutoCreate : function(){
4221 * @class Roo.bootstrap.Modal
4222 * @extends Roo.bootstrap.Component
4223 * Bootstrap Modal class
4224 * @cfg {String} title Title of dialog
4225 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4226 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4227 * @cfg {Boolean} specificTitle default false
4228 * @cfg {Array} buttons Array of buttons or standard button set..
4229 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4230 * @cfg {Boolean} animate default true
4231 * @cfg {Boolean} allow_close default true
4232 * @cfg {Boolean} fitwindow default false
4233 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4234 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4235 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4236 * @cfg {String} size (sm|lg|xl) default empty
4237 * @cfg {Number} max_width set the max width of modal
4238 * @cfg {Boolean} editableTitle can the title be edited
4243 * Create a new Modal Dialog
4244 * @param {Object} config The config object
4247 Roo.bootstrap.Modal = function(config){
4248 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4253 * The raw btnclick event for the button
4254 * @param {Roo.EventObject} e
4259 * Fire when dialog resize
4260 * @param {Roo.bootstrap.Modal} this
4261 * @param {Roo.EventObject} e
4265 * @event titlechanged
4266 * Fire when the editable title has been changed
4267 * @param {Roo.bootstrap.Modal} this
4268 * @param {Roo.EventObject} value
4270 "titlechanged" : true
4273 this.buttons = this.buttons || [];
4276 this.tmpl = Roo.factory(this.tmpl);
4281 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4283 title : 'test dialog',
4293 specificTitle: false,
4295 buttonPosition: 'right',
4317 editableTitle : false,
4319 onRender : function(ct, position)
4321 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4324 var cfg = Roo.apply({}, this.getAutoCreate());
4327 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4329 //if (!cfg.name.length) {
4333 cfg.cls += ' ' + this.cls;
4336 cfg.style = this.style;
4338 this.el = Roo.get(document.body).createChild(cfg, position);
4340 //var type = this.el.dom.type;
4343 if(this.tabIndex !== undefined){
4344 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4347 this.dialogEl = this.el.select('.modal-dialog',true).first();
4348 this.bodyEl = this.el.select('.modal-body',true).first();
4349 this.closeEl = this.el.select('.modal-header .close', true).first();
4350 this.headerEl = this.el.select('.modal-header',true).first();
4351 this.titleEl = this.el.select('.modal-title',true).first();
4352 this.footerEl = this.el.select('.modal-footer',true).first();
4354 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4356 //this.el.addClass("x-dlg-modal");
4358 if (this.buttons.length) {
4359 Roo.each(this.buttons, function(bb) {
4360 var b = Roo.apply({}, bb);
4361 b.xns = b.xns || Roo.bootstrap;
4362 b.xtype = b.xtype || 'Button';
4363 if (typeof(b.listeners) == 'undefined') {
4364 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4367 var btn = Roo.factory(b);
4369 btn.render(this.getButtonContainer());
4373 // render the children.
4376 if(typeof(this.items) != 'undefined'){
4377 var items = this.items;
4380 for(var i =0;i < items.length;i++) {
4381 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4385 this.items = nitems;
4387 // where are these used - they used to be body/close/footer
4391 //this.el.addClass([this.fieldClass, this.cls]);
4395 getAutoCreate : function()
4397 // we will default to modal-body-overflow - might need to remove or make optional later.
4399 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4400 html : this.html || ''
4405 cls : 'modal-title',
4409 if(this.specificTitle){ // WTF is this?
4414 if (this.allow_close && Roo.bootstrap.version == 3) {
4424 if (this.editableTitle) {
4426 cls: 'form-control roo-editable-title d-none',
4432 if (this.allow_close && Roo.bootstrap.version == 4) {
4442 if(this.size.length){
4443 size = 'modal-' + this.size;
4446 var footer = Roo.bootstrap.version == 3 ?
4448 cls : 'modal-footer',
4452 cls: 'btn-' + this.buttonPosition
4457 { // BS4 uses mr-auto on left buttons....
4458 cls : 'modal-footer'
4469 cls: "modal-dialog " + size,
4472 cls : "modal-content",
4475 cls : 'modal-header',
4490 modal.cls += ' fade';
4496 getChildContainer : function() {
4501 getButtonContainer : function() {
4503 return Roo.bootstrap.version == 4 ?
4504 this.el.select('.modal-footer',true).first()
4505 : this.el.select('.modal-footer div',true).first();
4508 initEvents : function()
4510 if (this.allow_close) {
4511 this.closeEl.on('click', this.hide, this);
4513 Roo.EventManager.onWindowResize(this.resize, this, true);
4514 if (this.editableTitle) {
4515 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4516 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4517 this.headerEditEl.on('keyup', function(e) {
4518 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4519 this.toggleHeaderInput(false)
4522 this.headerEditEl.on('blur', function(e) {
4523 this.toggleHeaderInput(false)
4532 this.maskEl.setSize(
4533 Roo.lib.Dom.getViewWidth(true),
4534 Roo.lib.Dom.getViewHeight(true)
4537 if (this.fitwindow) {
4539 this.dialogEl.setStyle( { 'max-width' : '100%' });
4541 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4542 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4547 if(this.max_width !== 0) {
4549 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4552 this.setSize(w, this.height);
4556 if(this.max_height) {
4557 this.setSize(w,Math.min(
4559 Roo.lib.Dom.getViewportHeight(true) - 60
4565 if(!this.fit_content) {
4566 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4570 this.setSize(w, Math.min(
4572 this.headerEl.getHeight() +
4573 this.footerEl.getHeight() +
4574 this.getChildHeight(this.bodyEl.dom.childNodes),
4575 Roo.lib.Dom.getViewportHeight(true) - 60)
4581 setSize : function(w,h)
4592 if (!this.rendered) {
4595 this.toggleHeaderInput(false);
4596 //this.el.setStyle('display', 'block');
4597 this.el.removeClass('hideing');
4598 this.el.dom.style.display='block';
4600 Roo.get(document.body).addClass('modal-open');
4602 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4605 this.el.addClass('show');
4606 this.el.addClass('in');
4609 this.el.addClass('show');
4610 this.el.addClass('in');
4613 // not sure how we can show data in here..
4615 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4618 Roo.get(document.body).addClass("x-body-masked");
4620 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4621 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4622 this.maskEl.dom.style.display = 'block';
4623 this.maskEl.addClass('show');
4628 this.fireEvent('show', this);
4630 // set zindex here - otherwise it appears to be ignored...
4631 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4634 this.items.forEach( function(e) {
4635 e.layout ? e.layout() : false;
4643 if(this.fireEvent("beforehide", this) !== false){
4645 this.maskEl.removeClass('show');
4647 this.maskEl.dom.style.display = '';
4648 Roo.get(document.body).removeClass("x-body-masked");
4649 this.el.removeClass('in');
4650 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4652 if(this.animate){ // why
4653 this.el.addClass('hideing');
4654 this.el.removeClass('show');
4656 if (!this.el.hasClass('hideing')) {
4657 return; // it's been shown again...
4660 this.el.dom.style.display='';
4662 Roo.get(document.body).removeClass('modal-open');
4663 this.el.removeClass('hideing');
4667 this.el.removeClass('show');
4668 this.el.dom.style.display='';
4669 Roo.get(document.body).removeClass('modal-open');
4672 this.fireEvent('hide', this);
4675 isVisible : function()
4678 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4682 addButton : function(str, cb)
4686 var b = Roo.apply({}, { html : str } );
4687 b.xns = b.xns || Roo.bootstrap;
4688 b.xtype = b.xtype || 'Button';
4689 if (typeof(b.listeners) == 'undefined') {
4690 b.listeners = { click : cb.createDelegate(this) };
4693 var btn = Roo.factory(b);
4695 btn.render(this.getButtonContainer());
4701 setDefaultButton : function(btn)
4703 //this.el.select('.modal-footer').()
4706 resizeTo: function(w,h)
4708 this.dialogEl.setWidth(w);
4710 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4712 this.bodyEl.setHeight(h - diff);
4714 this.fireEvent('resize', this);
4717 setContentSize : function(w, h)
4721 onButtonClick: function(btn,e)
4724 this.fireEvent('btnclick', btn.name, e);
4727 * Set the title of the Dialog
4728 * @param {String} str new Title
4730 setTitle: function(str) {
4731 this.titleEl.dom.innerHTML = str;
4735 * Set the body of the Dialog
4736 * @param {String} str new Title
4738 setBody: function(str) {
4739 this.bodyEl.dom.innerHTML = str;
4742 * Set the body of the Dialog using the template
4743 * @param {Obj} data - apply this data to the template and replace the body contents.
4745 applyBody: function(obj)
4748 Roo.log("Error - using apply Body without a template");
4751 this.tmpl.overwrite(this.bodyEl, obj);
4754 getChildHeight : function(child_nodes)
4758 child_nodes.length == 0
4763 var child_height = 0;
4765 for(var i = 0; i < child_nodes.length; i++) {
4768 * for modal with tabs...
4769 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4771 var layout_childs = child_nodes[i].childNodes;
4773 for(var j = 0; j < layout_childs.length; j++) {
4775 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4777 var layout_body_childs = layout_childs[j].childNodes;
4779 for(var k = 0; k < layout_body_childs.length; k++) {
4781 if(layout_body_childs[k].classList.contains('navbar')) {
4782 child_height += layout_body_childs[k].offsetHeight;
4786 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4788 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4790 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4792 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4793 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4808 child_height += child_nodes[i].offsetHeight;
4809 // Roo.log(child_nodes[i].offsetHeight);
4812 return child_height;
4814 toggleHeaderInput : function(is_edit)
4816 if (!this.editableTitle) {
4817 return; // not editable.
4819 if (is_edit && this.is_header_editing) {
4820 return; // already editing..
4824 this.headerEditEl.dom.value = this.title;
4825 this.headerEditEl.removeClass('d-none');
4826 this.headerEditEl.dom.focus();
4827 this.titleEl.addClass('d-none');
4829 this.is_header_editing = true;
4832 // flip back to not editing.
4833 this.title = this.headerEditEl.dom.value;
4834 this.headerEditEl.addClass('d-none');
4835 this.titleEl.removeClass('d-none');
4836 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4837 this.is_header_editing = false;
4838 this.fireEvent('titlechanged', this, this.title);
4847 Roo.apply(Roo.bootstrap.Modal, {
4849 * Button config that displays a single OK button
4858 * Button config that displays Yes and No buttons
4874 * Button config that displays OK and Cancel buttons
4889 * Button config that displays Yes, No and Cancel buttons
4914 * messagebox - can be used as a replace
4918 * @class Roo.MessageBox
4919 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4923 Roo.Msg.alert('Status', 'Changes saved successfully.');
4925 // Prompt for user data:
4926 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4928 // process text value...
4932 // Show a dialog using config options:
4934 title:'Save Changes?',
4935 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4936 buttons: Roo.Msg.YESNOCANCEL,
4943 Roo.bootstrap.MessageBox = function(){
4944 var dlg, opt, mask, waitTimer;
4945 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4946 var buttons, activeTextEl, bwidth;
4950 var handleButton = function(button){
4952 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4956 var handleHide = function(){
4958 dlg.el.removeClass(opt.cls);
4961 // Roo.TaskMgr.stop(waitTimer);
4962 // waitTimer = null;
4967 var updateButtons = function(b){
4970 buttons["ok"].hide();
4971 buttons["cancel"].hide();
4972 buttons["yes"].hide();
4973 buttons["no"].hide();
4974 dlg.footerEl.hide();
4978 dlg.footerEl.show();
4979 for(var k in buttons){
4980 if(typeof buttons[k] != "function"){
4983 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4984 width += buttons[k].el.getWidth()+15;
4994 var handleEsc = function(d, k, e){
4995 if(opt && opt.closable !== false){
5005 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5006 * @return {Roo.BasicDialog} The BasicDialog element
5008 getDialog : function(){
5010 dlg = new Roo.bootstrap.Modal( {
5013 //constraintoviewport:false,
5015 //collapsible : false,
5020 //buttonAlign:"center",
5021 closeClick : function(){
5022 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5025 handleButton("cancel");
5030 dlg.on("hide", handleHide);
5032 //dlg.addKeyListener(27, handleEsc);
5034 this.buttons = buttons;
5035 var bt = this.buttonText;
5036 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5037 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5038 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5039 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5041 bodyEl = dlg.bodyEl.createChild({
5043 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5044 '<textarea class="roo-mb-textarea"></textarea>' +
5045 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5047 msgEl = bodyEl.dom.firstChild;
5048 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5049 textboxEl.enableDisplayMode();
5050 textboxEl.addKeyListener([10,13], function(){
5051 if(dlg.isVisible() && opt && opt.buttons){
5054 }else if(opt.buttons.yes){
5055 handleButton("yes");
5059 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5060 textareaEl.enableDisplayMode();
5061 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5062 progressEl.enableDisplayMode();
5064 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5065 var pf = progressEl.dom.firstChild;
5067 pp = Roo.get(pf.firstChild);
5068 pp.setHeight(pf.offsetHeight);
5076 * Updates the message box body text
5077 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5078 * the XHTML-compliant non-breaking space character '&#160;')
5079 * @return {Roo.MessageBox} This message box
5081 updateText : function(text)
5083 if(!dlg.isVisible() && !opt.width){
5084 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5085 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5087 msgEl.innerHTML = text || ' ';
5089 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5090 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5092 Math.min(opt.width || cw , this.maxWidth),
5093 Math.max(opt.minWidth || this.minWidth, bwidth)
5096 activeTextEl.setWidth(w);
5098 if(dlg.isVisible()){
5099 dlg.fixedcenter = false;
5101 // to big, make it scroll. = But as usual stupid IE does not support
5104 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5105 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5106 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5108 bodyEl.dom.style.height = '';
5109 bodyEl.dom.style.overflowY = '';
5112 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5114 bodyEl.dom.style.overflowX = '';
5117 dlg.setContentSize(w, bodyEl.getHeight());
5118 if(dlg.isVisible()){
5119 dlg.fixedcenter = true;
5125 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5126 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5127 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5128 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5129 * @return {Roo.MessageBox} This message box
5131 updateProgress : function(value, text){
5133 this.updateText(text);
5136 if (pp) { // weird bug on my firefox - for some reason this is not defined
5137 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5138 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5144 * Returns true if the message box is currently displayed
5145 * @return {Boolean} True if the message box is visible, else false
5147 isVisible : function(){
5148 return dlg && dlg.isVisible();
5152 * Hides the message box if it is displayed
5155 if(this.isVisible()){
5161 * Displays a new message box, or reinitializes an existing message box, based on the config options
5162 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5163 * The following config object properties are supported:
5165 Property Type Description
5166 ---------- --------------- ------------------------------------------------------------------------------------
5167 animEl String/Element An id or Element from which the message box should animate as it opens and
5168 closes (defaults to undefined)
5169 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5170 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5171 closable Boolean False to hide the top-right close button (defaults to true). Note that
5172 progress and wait dialogs will ignore this property and always hide the
5173 close button as they can only be closed programmatically.
5174 cls String A custom CSS class to apply to the message box element
5175 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5176 displayed (defaults to 75)
5177 fn Function A callback function to execute after closing the dialog. The arguments to the
5178 function will be btn (the name of the button that was clicked, if applicable,
5179 e.g. "ok"), and text (the value of the active text field, if applicable).
5180 Progress and wait dialogs will ignore this option since they do not respond to
5181 user actions and can only be closed programmatically, so any required function
5182 should be called by the same code after it closes the dialog.
5183 icon String A CSS class that provides a background image to be used as an icon for
5184 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5185 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5186 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5187 modal Boolean False to allow user interaction with the page while the message box is
5188 displayed (defaults to true)
5189 msg String A string that will replace the existing message box body text (defaults
5190 to the XHTML-compliant non-breaking space character ' ')
5191 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5192 progress Boolean True to display a progress bar (defaults to false)
5193 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5194 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5195 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5196 title String The title text
5197 value String The string value to set into the active textbox element if displayed
5198 wait Boolean True to display a progress bar (defaults to false)
5199 width Number The width of the dialog in pixels
5206 msg: 'Please enter your address:',
5208 buttons: Roo.MessageBox.OKCANCEL,
5211 animEl: 'addAddressBtn'
5214 * @param {Object} config Configuration options
5215 * @return {Roo.MessageBox} This message box
5217 show : function(options)
5220 // this causes nightmares if you show one dialog after another
5221 // especially on callbacks..
5223 if(this.isVisible()){
5226 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5227 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5228 Roo.log("New Dialog Message:" + options.msg )
5229 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5230 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5233 var d = this.getDialog();
5235 d.setTitle(opt.title || " ");
5236 d.closeEl.setDisplayed(opt.closable !== false);
5237 activeTextEl = textboxEl;
5238 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5243 textareaEl.setHeight(typeof opt.multiline == "number" ?
5244 opt.multiline : this.defaultTextHeight);
5245 activeTextEl = textareaEl;
5254 progressEl.setDisplayed(opt.progress === true);
5256 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5258 this.updateProgress(0);
5259 activeTextEl.dom.value = opt.value || "";
5261 dlg.setDefaultButton(activeTextEl);
5263 var bs = opt.buttons;
5267 }else if(bs && bs.yes){
5268 db = buttons["yes"];
5270 dlg.setDefaultButton(db);
5272 bwidth = updateButtons(opt.buttons);
5273 this.updateText(opt.msg);
5275 d.el.addClass(opt.cls);
5277 d.proxyDrag = opt.proxyDrag === true;
5278 d.modal = opt.modal !== false;
5279 d.mask = opt.modal !== false ? mask : false;
5281 // force it to the end of the z-index stack so it gets a cursor in FF
5282 document.body.appendChild(dlg.el.dom);
5283 d.animateTarget = null;
5284 d.show(options.animEl);
5290 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5291 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5292 * and closing the message box when the process is complete.
5293 * @param {String} title The title bar text
5294 * @param {String} msg The message box body text
5295 * @return {Roo.MessageBox} This message box
5297 progress : function(title, msg){
5304 minWidth: this.minProgressWidth,
5311 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5312 * If a callback function is passed it will be called after the user clicks the button, and the
5313 * id of the button that was clicked will be passed as the only parameter to the callback
5314 * (could also be the top-right close button).
5315 * @param {String} title The title bar text
5316 * @param {String} msg The message box body text
5317 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5318 * @param {Object} scope (optional) The scope of the callback function
5319 * @return {Roo.MessageBox} This message box
5321 alert : function(title, msg, fn, scope)
5336 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5337 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5338 * You are responsible for closing the message box when the process is complete.
5339 * @param {String} msg The message box body text
5340 * @param {String} title (optional) The title bar text
5341 * @return {Roo.MessageBox} This message box
5343 wait : function(msg, title){
5354 waitTimer = Roo.TaskMgr.start({
5356 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5364 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5365 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5366 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5367 * @param {String} title The title bar text
5368 * @param {String} msg The message box body text
5369 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5370 * @param {Object} scope (optional) The scope of the callback function
5371 * @return {Roo.MessageBox} This message box
5373 confirm : function(title, msg, fn, scope){
5377 buttons: this.YESNO,
5386 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5387 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5388 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5389 * (could also be the top-right close button) and the text that was entered will be passed as the two
5390 * parameters to the callback.
5391 * @param {String} title The title bar text
5392 * @param {String} msg The message box body text
5393 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5394 * @param {Object} scope (optional) The scope of the callback function
5395 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5396 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5397 * @return {Roo.MessageBox} This message box
5399 prompt : function(title, msg, fn, scope, multiline){
5403 buttons: this.OKCANCEL,
5408 multiline: multiline,
5415 * Button config that displays a single OK button
5420 * Button config that displays Yes and No buttons
5423 YESNO : {yes:true, no:true},
5425 * Button config that displays OK and Cancel buttons
5428 OKCANCEL : {ok:true, cancel:true},
5430 * Button config that displays Yes, No and Cancel buttons
5433 YESNOCANCEL : {yes:true, no:true, cancel:true},
5436 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5439 defaultTextHeight : 75,
5441 * The maximum width in pixels of the message box (defaults to 600)
5446 * The minimum width in pixels of the message box (defaults to 100)
5451 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5452 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5455 minProgressWidth : 250,
5457 * An object containing the default button text strings that can be overriden for localized language support.
5458 * Supported properties are: ok, cancel, yes and no.
5459 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5472 * Shorthand for {@link Roo.MessageBox}
5474 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5475 Roo.Msg = Roo.Msg || Roo.MessageBox;
5484 * @class Roo.bootstrap.Navbar
5485 * @extends Roo.bootstrap.Component
5486 * Bootstrap Navbar class
5489 * Create a new Navbar
5490 * @param {Object} config The config object
5494 Roo.bootstrap.Navbar = function(config){
5495 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5499 * @event beforetoggle
5500 * Fire before toggle the menu
5501 * @param {Roo.EventObject} e
5503 "beforetoggle" : true
5507 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5516 getAutoCreate : function(){
5519 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5523 initEvents :function ()
5525 //Roo.log(this.el.select('.navbar-toggle',true));
5526 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5533 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5535 var size = this.el.getSize();
5536 this.maskEl.setSize(size.width, size.height);
5537 this.maskEl.enableDisplayMode("block");
5546 getChildContainer : function()
5548 if (this.el && this.el.select('.collapse').getCount()) {
5549 return this.el.select('.collapse',true).first();
5564 onToggle : function()
5567 if(this.fireEvent('beforetoggle', this) === false){
5570 var ce = this.el.select('.navbar-collapse',true).first();
5572 if (!ce.hasClass('show')) {
5582 * Expand the navbar pulldown
5584 expand : function ()
5587 var ce = this.el.select('.navbar-collapse',true).first();
5588 if (ce.hasClass('collapsing')) {
5591 ce.dom.style.height = '';
5593 ce.addClass('in'); // old...
5594 ce.removeClass('collapse');
5595 ce.addClass('show');
5596 var h = ce.getHeight();
5598 ce.removeClass('show');
5599 // at this point we should be able to see it..
5600 ce.addClass('collapsing');
5602 ce.setHeight(0); // resize it ...
5603 ce.on('transitionend', function() {
5604 //Roo.log('done transition');
5605 ce.removeClass('collapsing');
5606 ce.addClass('show');
5607 ce.removeClass('collapse');
5609 ce.dom.style.height = '';
5610 }, this, { single: true} );
5612 ce.dom.scrollTop = 0;
5615 * Collapse the navbar pulldown
5617 collapse : function()
5619 var ce = this.el.select('.navbar-collapse',true).first();
5621 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5622 // it's collapsed or collapsing..
5625 ce.removeClass('in'); // old...
5626 ce.setHeight(ce.getHeight());
5627 ce.removeClass('show');
5628 ce.addClass('collapsing');
5630 ce.on('transitionend', function() {
5631 ce.dom.style.height = '';
5632 ce.removeClass('collapsing');
5633 ce.addClass('collapse');
5634 }, this, { single: true} );
5654 * @class Roo.bootstrap.NavSimplebar
5655 * @extends Roo.bootstrap.Navbar
5656 * Bootstrap Sidebar class
5658 * @cfg {Boolean} inverse is inverted color
5660 * @cfg {String} type (nav | pills | tabs)
5661 * @cfg {Boolean} arrangement stacked | justified
5662 * @cfg {String} align (left | right) alignment
5664 * @cfg {Boolean} main (true|false) main nav bar? default false
5665 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5667 * @cfg {String} tag (header|footer|nav|div) default is nav
5669 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5673 * Create a new Sidebar
5674 * @param {Object} config The config object
5678 Roo.bootstrap.NavSimplebar = function(config){
5679 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5682 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5698 getAutoCreate : function(){
5702 tag : this.tag || 'div',
5703 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5705 if (['light','white'].indexOf(this.weight) > -1) {
5706 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5708 cfg.cls += ' bg-' + this.weight;
5711 cfg.cls += ' navbar-inverse';
5715 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5717 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5726 cls: 'nav nav-' + this.xtype,
5732 this.type = this.type || 'nav';
5733 if (['tabs','pills'].indexOf(this.type) != -1) {
5734 cfg.cn[0].cls += ' nav-' + this.type
5738 if (this.type!=='nav') {
5739 Roo.log('nav type must be nav/tabs/pills')
5741 cfg.cn[0].cls += ' navbar-nav'
5747 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5748 cfg.cn[0].cls += ' nav-' + this.arrangement;
5752 if (this.align === 'right') {
5753 cfg.cn[0].cls += ' navbar-right';
5778 * navbar-expand-md fixed-top
5782 * @class Roo.bootstrap.NavHeaderbar
5783 * @extends Roo.bootstrap.NavSimplebar
5784 * Bootstrap Sidebar class
5786 * @cfg {String} brand what is brand
5787 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5788 * @cfg {String} brand_href href of the brand
5789 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5790 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5791 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5792 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5795 * Create a new Sidebar
5796 * @param {Object} config The config object
5800 Roo.bootstrap.NavHeaderbar = function(config){
5801 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5805 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5812 desktopCenter : false,
5815 getAutoCreate : function(){
5818 tag: this.nav || 'nav',
5819 cls: 'navbar navbar-expand-md',
5825 if (this.desktopCenter) {
5826 cn.push({cls : 'container', cn : []});
5834 cls: 'navbar-toggle navbar-toggler',
5835 'data-toggle': 'collapse',
5840 html: 'Toggle navigation'
5844 cls: 'icon-bar navbar-toggler-icon'
5857 cn.push( Roo.bootstrap.version == 4 ? btn : {
5859 cls: 'navbar-header',
5868 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5872 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5874 if (['light','white'].indexOf(this.weight) > -1) {
5875 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5877 cfg.cls += ' bg-' + this.weight;
5880 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5881 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5883 // tag can override this..
5885 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5888 if (this.brand !== '') {
5889 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5890 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5892 href: this.brand_href ? this.brand_href : '#',
5893 cls: 'navbar-brand',
5901 cfg.cls += ' main-nav';
5909 getHeaderChildContainer : function()
5911 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5912 return this.el.select('.navbar-header',true).first();
5915 return this.getChildContainer();
5918 getChildContainer : function()
5921 return this.el.select('.roo-navbar-collapse',true).first();
5926 initEvents : function()
5928 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5930 if (this.autohide) {
5935 Roo.get(document).on('scroll',function(e) {
5936 var ns = Roo.get(document).getScroll().top;
5937 var os = prevScroll;
5941 ft.removeClass('slideDown');
5942 ft.addClass('slideUp');
5945 ft.removeClass('slideUp');
5946 ft.addClass('slideDown');
5967 * @class Roo.bootstrap.NavSidebar
5968 * @extends Roo.bootstrap.Navbar
5969 * Bootstrap Sidebar class
5972 * Create a new Sidebar
5973 * @param {Object} config The config object
5977 Roo.bootstrap.NavSidebar = function(config){
5978 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5981 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5983 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5985 getAutoCreate : function(){
5990 cls: 'sidebar sidebar-nav'
6012 * @class Roo.bootstrap.NavGroup
6013 * @extends Roo.bootstrap.Component
6014 * Bootstrap NavGroup class
6015 * @cfg {String} align (left|right)
6016 * @cfg {Boolean} inverse
6017 * @cfg {String} type (nav|pills|tab) default nav
6018 * @cfg {String} navId - reference Id for navbar.
6019 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6022 * Create a new nav group
6023 * @param {Object} config The config object
6026 Roo.bootstrap.NavGroup = function(config){
6027 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6030 Roo.bootstrap.NavGroup.register(this);
6034 * Fires when the active item changes
6035 * @param {Roo.bootstrap.NavGroup} this
6036 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6037 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6044 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6056 getAutoCreate : function()
6058 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6064 if (Roo.bootstrap.version == 4) {
6065 if (['tabs','pills'].indexOf(this.type) != -1) {
6066 cfg.cls += ' nav-' + this.type;
6068 // trying to remove so header bar can right align top?
6069 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6070 // do not use on header bar...
6071 cfg.cls += ' navbar-nav';
6076 if (['tabs','pills'].indexOf(this.type) != -1) {
6077 cfg.cls += ' nav-' + this.type
6079 if (this.type !== 'nav') {
6080 Roo.log('nav type must be nav/tabs/pills')
6082 cfg.cls += ' navbar-nav'
6086 if (this.parent() && this.parent().sidebar) {
6089 cls: 'dashboard-menu sidebar-menu'
6095 if (this.form === true) {
6098 cls: 'navbar-form form-inline'
6100 //nav navbar-right ml-md-auto
6101 if (this.align === 'right') {
6102 cfg.cls += ' navbar-right ml-md-auto';
6104 cfg.cls += ' navbar-left';
6108 if (this.align === 'right') {
6109 cfg.cls += ' navbar-right ml-md-auto';
6111 cfg.cls += ' mr-auto';
6115 cfg.cls += ' navbar-inverse';
6123 * sets the active Navigation item
6124 * @param {Roo.bootstrap.NavItem} the new current navitem
6126 setActiveItem : function(item)
6129 Roo.each(this.navItems, function(v){
6134 v.setActive(false, true);
6141 item.setActive(true, true);
6142 this.fireEvent('changed', this, item, prev);
6147 * gets the active Navigation item
6148 * @return {Roo.bootstrap.NavItem} the current navitem
6150 getActive : function()
6154 Roo.each(this.navItems, function(v){
6165 indexOfNav : function()
6169 Roo.each(this.navItems, function(v,i){
6180 * adds a Navigation item
6181 * @param {Roo.bootstrap.NavItem} the navitem to add
6183 addItem : function(cfg)
6185 if (this.form && Roo.bootstrap.version == 4) {
6188 var cn = new Roo.bootstrap.NavItem(cfg);
6190 cn.parentId = this.id;
6191 cn.onRender(this.el, null);
6195 * register a Navigation item
6196 * @param {Roo.bootstrap.NavItem} the navitem to add
6198 register : function(item)
6200 this.navItems.push( item);
6201 item.navId = this.navId;
6206 * clear all the Navigation item
6209 clearAll : function()
6212 this.el.dom.innerHTML = '';
6215 getNavItem: function(tabId)
6218 Roo.each(this.navItems, function(e) {
6219 if (e.tabId == tabId) {
6229 setActiveNext : function()
6231 var i = this.indexOfNav(this.getActive());
6232 if (i > this.navItems.length) {
6235 this.setActiveItem(this.navItems[i+1]);
6237 setActivePrev : function()
6239 var i = this.indexOfNav(this.getActive());
6243 this.setActiveItem(this.navItems[i-1]);
6245 clearWasActive : function(except) {
6246 Roo.each(this.navItems, function(e) {
6247 if (e.tabId != except.tabId && e.was_active) {
6248 e.was_active = false;
6255 getWasActive : function ()
6258 Roo.each(this.navItems, function(e) {
6273 Roo.apply(Roo.bootstrap.NavGroup, {
6277 * register a Navigation Group
6278 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6280 register : function(navgrp)
6282 this.groups[navgrp.navId] = navgrp;
6286 * fetch a Navigation Group based on the navigation ID
6287 * @param {string} the navgroup to add
6288 * @returns {Roo.bootstrap.NavGroup} the navgroup
6290 get: function(navId) {
6291 if (typeof(this.groups[navId]) == 'undefined') {
6293 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6295 return this.groups[navId] ;
6310 * @class Roo.bootstrap.NavItem
6311 * @extends Roo.bootstrap.Component
6312 * Bootstrap Navbar.NavItem class
6313 * @cfg {String} href link to
6314 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6315 * @cfg {Boolean} button_outline show and outlined button
6316 * @cfg {String} html content of button
6317 * @cfg {String} badge text inside badge
6318 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6319 * @cfg {String} glyphicon DEPRICATED - use fa
6320 * @cfg {String} icon DEPRICATED - use fa
6321 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6322 * @cfg {Boolean} active Is item active
6323 * @cfg {Boolean} disabled Is item disabled
6324 * @cfg {String} linkcls Link Class
6325 * @cfg {Boolean} preventDefault (true | false) default false
6326 * @cfg {String} tabId the tab that this item activates.
6327 * @cfg {String} tagtype (a|span) render as a href or span?
6328 * @cfg {Boolean} animateRef (true|false) link to element default false
6331 * Create a new Navbar Item
6332 * @param {Object} config The config object
6334 Roo.bootstrap.NavItem = function(config){
6335 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6340 * The raw click event for the entire grid.
6341 * @param {Roo.EventObject} e
6346 * Fires when the active item active state changes
6347 * @param {Roo.bootstrap.NavItem} this
6348 * @param {boolean} state the new state
6354 * Fires when scroll to element
6355 * @param {Roo.bootstrap.NavItem} this
6356 * @param {Object} options
6357 * @param {Roo.EventObject} e
6365 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6374 preventDefault : false,
6382 button_outline : false,
6386 getAutoCreate : function(){
6393 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6396 cfg.cls += ' active' ;
6398 if (this.disabled) {
6399 cfg.cls += ' disabled';
6403 if (this.button_weight.length) {
6404 cfg.tag = this.href ? 'a' : 'button';
6405 cfg.html = this.html || '';
6406 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6408 cfg.href = this.href;
6411 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6413 cfg.cls += " nav-html";
6416 // menu .. should add dropdown-menu class - so no need for carat..
6418 if (this.badge !== '') {
6420 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6425 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6429 href : this.href || "#",
6430 html: this.html || '',
6434 if (this.tagtype == 'a') {
6435 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6439 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6440 } else if (this.fa) {
6441 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6442 } else if(this.glyphicon) {
6443 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6445 cfg.cn[0].cls += " nav-html";
6449 cfg.cn[0].html += " <span class='caret'></span>";
6453 if (this.badge !== '') {
6454 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6462 onRender : function(ct, position)
6464 // Roo.log("Call onRender: " + this.xtype);
6465 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6469 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6470 this.navLink = this.el.select('.nav-link',true).first();
6471 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6476 initEvents: function()
6478 if (typeof (this.menu) != 'undefined') {
6479 this.menu.parentType = this.xtype;
6480 this.menu.triggerEl = this.el;
6481 this.menu = this.addxtype(Roo.apply({}, this.menu));
6484 this.el.on('click', this.onClick, this);
6486 //if(this.tagtype == 'span'){
6487 // this.el.select('span',true).on('click', this.onClick, this);
6490 // at this point parent should be available..
6491 this.parent().register(this);
6494 onClick : function(e)
6496 if (e.getTarget('.dropdown-menu-item')) {
6497 // did you click on a menu itemm.... - then don't trigger onclick..
6502 this.preventDefault ||
6505 Roo.log("NavItem - prevent Default?");
6509 if (this.disabled) {
6513 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6514 if (tg && tg.transition) {
6515 Roo.log("waiting for the transitionend");
6521 //Roo.log("fire event clicked");
6522 if(this.fireEvent('click', this, e) === false){
6526 if(this.tagtype == 'span'){
6530 //Roo.log(this.href);
6531 var ael = this.el.select('a',true).first();
6534 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6535 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6536 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6537 return; // ignore... - it's a 'hash' to another page.
6539 Roo.log("NavItem - prevent Default?");
6541 this.scrollToElement(e);
6545 var p = this.parent();
6547 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6548 if (typeof(p.setActiveItem) !== 'undefined') {
6549 p.setActiveItem(this);
6553 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6554 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6555 // remove the collapsed menu expand...
6556 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6560 isActive: function () {
6563 setActive : function(state, fire, is_was_active)
6565 if (this.active && !state && this.navId) {
6566 this.was_active = true;
6567 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6569 nv.clearWasActive(this);
6573 this.active = state;
6576 this.el.removeClass('active');
6577 this.navLink ? this.navLink.removeClass('active') : false;
6578 } else if (!this.el.hasClass('active')) {
6580 this.el.addClass('active');
6581 if (Roo.bootstrap.version == 4 && this.navLink ) {
6582 this.navLink.addClass('active');
6587 this.fireEvent('changed', this, state);
6590 // show a panel if it's registered and related..
6592 if (!this.navId || !this.tabId || !state || is_was_active) {
6596 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6600 var pan = tg.getPanelByName(this.tabId);
6604 // if we can not flip to new panel - go back to old nav highlight..
6605 if (false == tg.showPanel(pan)) {
6606 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6608 var onav = nv.getWasActive();
6610 onav.setActive(true, false, true);
6619 // this should not be here...
6620 setDisabled : function(state)
6622 this.disabled = state;
6624 this.el.removeClass('disabled');
6625 } else if (!this.el.hasClass('disabled')) {
6626 this.el.addClass('disabled');
6632 * Fetch the element to display the tooltip on.
6633 * @return {Roo.Element} defaults to this.el
6635 tooltipEl : function()
6637 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6640 scrollToElement : function(e)
6642 var c = document.body;
6645 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6647 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6648 c = document.documentElement;
6651 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6657 var o = target.calcOffsetsTo(c);
6664 this.fireEvent('scrollto', this, options, e);
6666 Roo.get(c).scrollTo('top', options.value, true);
6671 * Set the HTML (text content) of the item
6672 * @param {string} html content for the nav item
6674 setHtml : function(html)
6677 this.htmlEl.dom.innerHTML = html;
6689 * <span> icon </span>
6690 * <span> text </span>
6691 * <span>badge </span>
6695 * @class Roo.bootstrap.NavSidebarItem
6696 * @extends Roo.bootstrap.NavItem
6697 * Bootstrap Navbar.NavSidebarItem class
6698 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6699 * {Boolean} open is the menu open
6700 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6701 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6702 * {String} buttonSize (sm|md|lg)the extra classes for the button
6703 * {Boolean} showArrow show arrow next to the text (default true)
6705 * Create a new Navbar Button
6706 * @param {Object} config The config object
6708 Roo.bootstrap.NavSidebarItem = function(config){
6709 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6714 * The raw click event for the entire grid.
6715 * @param {Roo.EventObject} e
6720 * Fires when the active item active state changes
6721 * @param {Roo.bootstrap.NavSidebarItem} this
6722 * @param {boolean} state the new state
6730 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6732 badgeWeight : 'default',
6738 buttonWeight : 'default',
6744 getAutoCreate : function(){
6749 href : this.href || '#',
6755 if(this.buttonView){
6758 href : this.href || '#',
6759 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6772 cfg.cls += ' active';
6775 if (this.disabled) {
6776 cfg.cls += ' disabled';
6779 cfg.cls += ' open x-open';
6782 if (this.glyphicon || this.icon) {
6783 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6784 a.cn.push({ tag : 'i', cls : c }) ;
6787 if(!this.buttonView){
6790 html : this.html || ''
6797 if (this.badge !== '') {
6798 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6804 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6807 a.cls += ' dropdown-toggle treeview' ;
6813 initEvents : function()
6815 if (typeof (this.menu) != 'undefined') {
6816 this.menu.parentType = this.xtype;
6817 this.menu.triggerEl = this.el;
6818 this.menu = this.addxtype(Roo.apply({}, this.menu));
6821 this.el.on('click', this.onClick, this);
6823 if(this.badge !== ''){
6824 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6829 onClick : function(e)
6836 if(this.preventDefault){
6840 this.fireEvent('click', this, e);
6843 disable : function()
6845 this.setDisabled(true);
6850 this.setDisabled(false);
6853 setDisabled : function(state)
6855 if(this.disabled == state){
6859 this.disabled = state;
6862 this.el.addClass('disabled');
6866 this.el.removeClass('disabled');
6871 setActive : function(state)
6873 if(this.active == state){
6877 this.active = state;
6880 this.el.addClass('active');
6884 this.el.removeClass('active');
6889 isActive: function ()
6894 setBadge : function(str)
6900 this.badgeEl.dom.innerHTML = str;
6915 Roo.namespace('Roo.bootstrap.breadcrumb');
6919 * @class Roo.bootstrap.breadcrumb.Nav
6920 * @extends Roo.bootstrap.Component
6921 * Bootstrap Breadcrumb Nav Class
6923 * @children Roo.bootstrap.breadcrumb.Item
6926 * Create a new breadcrumb.Nav
6927 * @param {Object} config The config object
6931 Roo.bootstrap.breadcrumb.Nav = function(config){
6932 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6937 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6939 getAutoCreate : function()
6956 initEvents: function()
6958 this.olEl = this.el.select('ol',true).first();
6960 getChildContainer : function()
6976 * @class Roo.bootstrap.breadcrumb.Nav
6977 * @extends Roo.bootstrap.Component
6978 * Bootstrap Breadcrumb Nav Class
6980 * @children Roo.bootstrap.breadcrumb.Component
6981 * @cfg {String} html the content of the link.
6982 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6983 * @cfg {Boolean} active is it active
6987 * Create a new breadcrumb.Nav
6988 * @param {Object} config The config object
6991 Roo.bootstrap.breadcrumb.Item = function(config){
6992 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6997 * The img click event for the img.
6998 * @param {Roo.EventObject} e
7005 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7010 getAutoCreate : function()
7015 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7017 if (this.href !== false) {
7024 cfg.html = this.html;
7030 initEvents: function()
7033 this.el.select('a', true).first().on('click',this.onClick, this)
7037 onClick : function(e)
7040 this.fireEvent('click',this, e);
7053 * @class Roo.bootstrap.Row
7054 * @extends Roo.bootstrap.Component
7055 * Bootstrap Row class (contains columns...)
7059 * @param {Object} config The config object
7062 Roo.bootstrap.Row = function(config){
7063 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7066 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7068 getAutoCreate : function(){
7087 * @class Roo.bootstrap.Pagination
7088 * @extends Roo.bootstrap.Component
7089 * Bootstrap Pagination class
7090 * @cfg {String} size xs | sm | md | lg
7091 * @cfg {Boolean} inverse false | true
7094 * Create a new Pagination
7095 * @param {Object} config The config object
7098 Roo.bootstrap.Pagination = function(config){
7099 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7102 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7108 getAutoCreate : function(){
7114 cfg.cls += ' inverse';
7120 cfg.cls += " " + this.cls;
7138 * @class Roo.bootstrap.PaginationItem
7139 * @extends Roo.bootstrap.Component
7140 * Bootstrap PaginationItem class
7141 * @cfg {String} html text
7142 * @cfg {String} href the link
7143 * @cfg {Boolean} preventDefault (true | false) default true
7144 * @cfg {Boolean} active (true | false) default false
7145 * @cfg {Boolean} disabled default false
7149 * Create a new PaginationItem
7150 * @param {Object} config The config object
7154 Roo.bootstrap.PaginationItem = function(config){
7155 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7160 * The raw click event for the entire grid.
7161 * @param {Roo.EventObject} e
7167 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7171 preventDefault: true,
7176 getAutoCreate : function(){
7182 href : this.href ? this.href : '#',
7183 html : this.html ? this.html : ''
7193 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7197 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7203 initEvents: function() {
7205 this.el.on('click', this.onClick, this);
7208 onClick : function(e)
7210 Roo.log('PaginationItem on click ');
7211 if(this.preventDefault){
7219 this.fireEvent('click', this, e);
7235 * @class Roo.bootstrap.Slider
7236 * @extends Roo.bootstrap.Component
7237 * Bootstrap Slider class
7240 * Create a new Slider
7241 * @param {Object} config The config object
7244 Roo.bootstrap.Slider = function(config){
7245 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7248 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7250 getAutoCreate : function(){
7254 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7258 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7270 * Ext JS Library 1.1.1
7271 * Copyright(c) 2006-2007, Ext JS, LLC.
7273 * Originally Released Under LGPL - original licence link has changed is not relivant.
7276 * <script type="text/javascript">
7281 * @class Roo.grid.ColumnModel
7282 * @extends Roo.util.Observable
7283 * This is the default implementation of a ColumnModel used by the Grid. It defines
7284 * the columns in the grid.
7287 var colModel = new Roo.grid.ColumnModel([
7288 {header: "Ticker", width: 60, sortable: true, locked: true},
7289 {header: "Company Name", width: 150, sortable: true},
7290 {header: "Market Cap.", width: 100, sortable: true},
7291 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7292 {header: "Employees", width: 100, sortable: true, resizable: false}
7297 * The config options listed for this class are options which may appear in each
7298 * individual column definition.
7299 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7301 * @param {Object} config An Array of column config objects. See this class's
7302 * config objects for details.
7304 Roo.grid.ColumnModel = function(config){
7306 * The config passed into the constructor
7308 this.config = config;
7311 // if no id, create one
7312 // if the column does not have a dataIndex mapping,
7313 // map it to the order it is in the config
7314 for(var i = 0, len = config.length; i < len; i++){
7316 if(typeof c.dataIndex == "undefined"){
7319 if(typeof c.renderer == "string"){
7320 c.renderer = Roo.util.Format[c.renderer];
7322 if(typeof c.id == "undefined"){
7325 if(c.editor && c.editor.xtype){
7326 c.editor = Roo.factory(c.editor, Roo.grid);
7328 if(c.editor && c.editor.isFormField){
7329 c.editor = new Roo.grid.GridEditor(c.editor);
7331 this.lookup[c.id] = c;
7335 * The width of columns which have no width specified (defaults to 100)
7338 this.defaultWidth = 100;
7341 * Default sortable of columns which have no sortable specified (defaults to false)
7344 this.defaultSortable = false;
7348 * @event widthchange
7349 * Fires when the width of a column changes.
7350 * @param {ColumnModel} this
7351 * @param {Number} columnIndex The column index
7352 * @param {Number} newWidth The new width
7354 "widthchange": true,
7356 * @event headerchange
7357 * Fires when the text of a header changes.
7358 * @param {ColumnModel} this
7359 * @param {Number} columnIndex The column index
7360 * @param {Number} newText The new header text
7362 "headerchange": true,
7364 * @event hiddenchange
7365 * Fires when a column is hidden or "unhidden".
7366 * @param {ColumnModel} this
7367 * @param {Number} columnIndex The column index
7368 * @param {Boolean} hidden true if hidden, false otherwise
7370 "hiddenchange": true,
7372 * @event columnmoved
7373 * Fires when a column is moved.
7374 * @param {ColumnModel} this
7375 * @param {Number} oldIndex
7376 * @param {Number} newIndex
7378 "columnmoved" : true,
7380 * @event columlockchange
7381 * Fires when a column's locked state is changed
7382 * @param {ColumnModel} this
7383 * @param {Number} colIndex
7384 * @param {Boolean} locked true if locked
7386 "columnlockchange" : true
7388 Roo.grid.ColumnModel.superclass.constructor.call(this);
7390 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7392 * @cfg {String} header The header text to display in the Grid view.
7395 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7396 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7397 * specified, the column's index is used as an index into the Record's data Array.
7400 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7401 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7404 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7405 * Defaults to the value of the {@link #defaultSortable} property.
7406 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7409 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7412 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7415 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7418 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7421 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7422 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7423 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7424 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7427 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7430 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7433 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7436 * @cfg {String} cursor (Optional)
7439 * @cfg {String} tooltip (Optional)
7442 * @cfg {Number} xs (Optional)
7445 * @cfg {Number} sm (Optional)
7448 * @cfg {Number} md (Optional)
7451 * @cfg {Number} lg (Optional)
7454 * Returns the id of the column at the specified index.
7455 * @param {Number} index The column index
7456 * @return {String} the id
7458 getColumnId : function(index){
7459 return this.config[index].id;
7463 * Returns the column for a specified id.
7464 * @param {String} id The column id
7465 * @return {Object} the column
7467 getColumnById : function(id){
7468 return this.lookup[id];
7473 * Returns the column for a specified dataIndex.
7474 * @param {String} dataIndex The column dataIndex
7475 * @return {Object|Boolean} the column or false if not found
7477 getColumnByDataIndex: function(dataIndex){
7478 var index = this.findColumnIndex(dataIndex);
7479 return index > -1 ? this.config[index] : false;
7483 * Returns the index for a specified column id.
7484 * @param {String} id The column id
7485 * @return {Number} the index, or -1 if not found
7487 getIndexById : function(id){
7488 for(var i = 0, len = this.config.length; i < len; i++){
7489 if(this.config[i].id == id){
7497 * Returns the index for a specified column dataIndex.
7498 * @param {String} dataIndex The column dataIndex
7499 * @return {Number} the index, or -1 if not found
7502 findColumnIndex : function(dataIndex){
7503 for(var i = 0, len = this.config.length; i < len; i++){
7504 if(this.config[i].dataIndex == dataIndex){
7512 moveColumn : function(oldIndex, newIndex){
7513 var c = this.config[oldIndex];
7514 this.config.splice(oldIndex, 1);
7515 this.config.splice(newIndex, 0, c);
7516 this.dataMap = null;
7517 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7520 isLocked : function(colIndex){
7521 return this.config[colIndex].locked === true;
7524 setLocked : function(colIndex, value, suppressEvent){
7525 if(this.isLocked(colIndex) == value){
7528 this.config[colIndex].locked = value;
7530 this.fireEvent("columnlockchange", this, colIndex, value);
7534 getTotalLockedWidth : function(){
7536 for(var i = 0; i < this.config.length; i++){
7537 if(this.isLocked(i) && !this.isHidden(i)){
7538 this.totalWidth += this.getColumnWidth(i);
7544 getLockedCount : function(){
7545 for(var i = 0, len = this.config.length; i < len; i++){
7546 if(!this.isLocked(i)){
7551 return this.config.length;
7555 * Returns the number of columns.
7558 getColumnCount : function(visibleOnly){
7559 if(visibleOnly === true){
7561 for(var i = 0, len = this.config.length; i < len; i++){
7562 if(!this.isHidden(i)){
7568 return this.config.length;
7572 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7573 * @param {Function} fn
7574 * @param {Object} scope (optional)
7575 * @return {Array} result
7577 getColumnsBy : function(fn, scope){
7579 for(var i = 0, len = this.config.length; i < len; i++){
7580 var c = this.config[i];
7581 if(fn.call(scope||this, c, i) === true){
7589 * Returns true if the specified column is sortable.
7590 * @param {Number} col The column index
7593 isSortable : function(col){
7594 if(typeof this.config[col].sortable == "undefined"){
7595 return this.defaultSortable;
7597 return this.config[col].sortable;
7601 * Returns the rendering (formatting) function defined for the column.
7602 * @param {Number} col The column index.
7603 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7605 getRenderer : function(col){
7606 if(!this.config[col].renderer){
7607 return Roo.grid.ColumnModel.defaultRenderer;
7609 return this.config[col].renderer;
7613 * Sets the rendering (formatting) function for a column.
7614 * @param {Number} col The column index
7615 * @param {Function} fn The function to use to process the cell's raw data
7616 * to return HTML markup for the grid view. The render function is called with
7617 * the following parameters:<ul>
7618 * <li>Data value.</li>
7619 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7620 * <li>css A CSS style string to apply to the table cell.</li>
7621 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7622 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7623 * <li>Row index</li>
7624 * <li>Column index</li>
7625 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7627 setRenderer : function(col, fn){
7628 this.config[col].renderer = fn;
7632 * Returns the width for the specified column.
7633 * @param {Number} col The column index
7636 getColumnWidth : function(col){
7637 return this.config[col].width * 1 || this.defaultWidth;
7641 * Sets the width for a column.
7642 * @param {Number} col The column index
7643 * @param {Number} width The new width
7645 setColumnWidth : function(col, width, suppressEvent){
7646 this.config[col].width = width;
7647 this.totalWidth = null;
7649 this.fireEvent("widthchange", this, col, width);
7654 * Returns the total width of all columns.
7655 * @param {Boolean} includeHidden True to include hidden column widths
7658 getTotalWidth : function(includeHidden){
7659 if(!this.totalWidth){
7660 this.totalWidth = 0;
7661 for(var i = 0, len = this.config.length; i < len; i++){
7662 if(includeHidden || !this.isHidden(i)){
7663 this.totalWidth += this.getColumnWidth(i);
7667 return this.totalWidth;
7671 * Returns the header for the specified column.
7672 * @param {Number} col The column index
7675 getColumnHeader : function(col){
7676 return this.config[col].header;
7680 * Sets the header for a column.
7681 * @param {Number} col The column index
7682 * @param {String} header The new header
7684 setColumnHeader : function(col, header){
7685 this.config[col].header = header;
7686 this.fireEvent("headerchange", this, col, header);
7690 * Returns the tooltip for the specified column.
7691 * @param {Number} col The column index
7694 getColumnTooltip : function(col){
7695 return this.config[col].tooltip;
7698 * Sets the tooltip for a column.
7699 * @param {Number} col The column index
7700 * @param {String} tooltip The new tooltip
7702 setColumnTooltip : function(col, tooltip){
7703 this.config[col].tooltip = tooltip;
7707 * Returns the dataIndex for the specified column.
7708 * @param {Number} col The column index
7711 getDataIndex : function(col){
7712 return this.config[col].dataIndex;
7716 * Sets the dataIndex for a column.
7717 * @param {Number} col The column index
7718 * @param {Number} dataIndex The new dataIndex
7720 setDataIndex : function(col, dataIndex){
7721 this.config[col].dataIndex = dataIndex;
7727 * Returns true if the cell is editable.
7728 * @param {Number} colIndex The column index
7729 * @param {Number} rowIndex The row index - this is nto actually used..?
7732 isCellEditable : function(colIndex, rowIndex){
7733 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7737 * Returns the editor defined for the cell/column.
7738 * return false or null to disable editing.
7739 * @param {Number} colIndex The column index
7740 * @param {Number} rowIndex The row index
7743 getCellEditor : function(colIndex, rowIndex){
7744 return this.config[colIndex].editor;
7748 * Sets if a column is editable.
7749 * @param {Number} col The column index
7750 * @param {Boolean} editable True if the column is editable
7752 setEditable : function(col, editable){
7753 this.config[col].editable = editable;
7758 * Returns true if the column is hidden.
7759 * @param {Number} colIndex The column index
7762 isHidden : function(colIndex){
7763 return this.config[colIndex].hidden;
7768 * Returns true if the column width cannot be changed
7770 isFixed : function(colIndex){
7771 return this.config[colIndex].fixed;
7775 * Returns true if the column can be resized
7778 isResizable : function(colIndex){
7779 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7782 * Sets if a column is hidden.
7783 * @param {Number} colIndex The column index
7784 * @param {Boolean} hidden True if the column is hidden
7786 setHidden : function(colIndex, hidden){
7787 this.config[colIndex].hidden = hidden;
7788 this.totalWidth = null;
7789 this.fireEvent("hiddenchange", this, colIndex, hidden);
7793 * Sets the editor for a column.
7794 * @param {Number} col The column index
7795 * @param {Object} editor The editor object
7797 setEditor : function(col, editor){
7798 this.config[col].editor = editor;
7802 Roo.grid.ColumnModel.defaultRenderer = function(value)
7804 if(typeof value == "object") {
7807 if(typeof value == "string" && value.length < 1){
7811 return String.format("{0}", value);
7814 // Alias for backwards compatibility
7815 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7818 * Ext JS Library 1.1.1
7819 * Copyright(c) 2006-2007, Ext JS, LLC.
7821 * Originally Released Under LGPL - original licence link has changed is not relivant.
7824 * <script type="text/javascript">
7828 * @class Roo.LoadMask
7829 * A simple utility class for generically masking elements while loading data. If the element being masked has
7830 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7831 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7832 * element's UpdateManager load indicator and will be destroyed after the initial load.
7834 * Create a new LoadMask
7835 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7836 * @param {Object} config The config object
7838 Roo.LoadMask = function(el, config){
7839 this.el = Roo.get(el);
7840 Roo.apply(this, config);
7842 this.store.on('beforeload', this.onBeforeLoad, this);
7843 this.store.on('load', this.onLoad, this);
7844 this.store.on('loadexception', this.onLoadException, this);
7845 this.removeMask = false;
7847 var um = this.el.getUpdateManager();
7848 um.showLoadIndicator = false; // disable the default indicator
7849 um.on('beforeupdate', this.onBeforeLoad, this);
7850 um.on('update', this.onLoad, this);
7851 um.on('failure', this.onLoad, this);
7852 this.removeMask = true;
7856 Roo.LoadMask.prototype = {
7858 * @cfg {Boolean} removeMask
7859 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7860 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7864 * The text to display in a centered loading message box (defaults to 'Loading...')
7868 * @cfg {String} msgCls
7869 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7871 msgCls : 'x-mask-loading',
7874 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7880 * Disables the mask to prevent it from being displayed
7882 disable : function(){
7883 this.disabled = true;
7887 * Enables the mask so that it can be displayed
7889 enable : function(){
7890 this.disabled = false;
7893 onLoadException : function()
7897 if (typeof(arguments[3]) != 'undefined') {
7898 Roo.MessageBox.alert("Error loading",arguments[3]);
7902 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7903 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7910 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7915 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7919 onBeforeLoad : function(){
7921 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7926 destroy : function(){
7928 this.store.un('beforeload', this.onBeforeLoad, this);
7929 this.store.un('load', this.onLoad, this);
7930 this.store.un('loadexception', this.onLoadException, this);
7932 var um = this.el.getUpdateManager();
7933 um.un('beforeupdate', this.onBeforeLoad, this);
7934 um.un('update', this.onLoad, this);
7935 um.un('failure', this.onLoad, this);
7946 * @class Roo.bootstrap.Table
7947 * @extends Roo.bootstrap.Component
7948 * Bootstrap Table class
7949 * @cfg {String} cls table class
7950 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7951 * @cfg {String} bgcolor Specifies the background color for a table
7952 * @cfg {Number} border Specifies whether the table cells should have borders or not
7953 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7954 * @cfg {Number} cellspacing Specifies the space between cells
7955 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7956 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7957 * @cfg {String} sortable Specifies that the table should be sortable
7958 * @cfg {String} summary Specifies a summary of the content of a table
7959 * @cfg {Number} width Specifies the width of a table
7960 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7962 * @cfg {boolean} striped Should the rows be alternative striped
7963 * @cfg {boolean} bordered Add borders to the table
7964 * @cfg {boolean} hover Add hover highlighting
7965 * @cfg {boolean} condensed Format condensed
7966 * @cfg {boolean} responsive Format condensed
7967 * @cfg {Boolean} loadMask (true|false) default false
7968 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7969 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7970 * @cfg {Boolean} rowSelection (true|false) default false
7971 * @cfg {Boolean} cellSelection (true|false) default false
7972 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7973 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7974 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7975 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7979 * Create a new Table
7980 * @param {Object} config The config object
7983 Roo.bootstrap.Table = function(config){
7984 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7989 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7990 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7991 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7992 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7994 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7996 this.sm.grid = this;
7997 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7998 this.sm = this.selModel;
7999 this.sm.xmodule = this.xmodule || false;
8002 if (this.cm && typeof(this.cm.config) == 'undefined') {
8003 this.colModel = new Roo.grid.ColumnModel(this.cm);
8004 this.cm = this.colModel;
8005 this.cm.xmodule = this.xmodule || false;
8008 this.store= Roo.factory(this.store, Roo.data);
8009 this.ds = this.store;
8010 this.ds.xmodule = this.xmodule || false;
8013 if (this.footer && this.store) {
8014 this.footer.dataSource = this.ds;
8015 this.footer = Roo.factory(this.footer);
8022 * Fires when a cell is clicked
8023 * @param {Roo.bootstrap.Table} this
8024 * @param {Roo.Element} el
8025 * @param {Number} rowIndex
8026 * @param {Number} columnIndex
8027 * @param {Roo.EventObject} e
8031 * @event celldblclick
8032 * Fires when a cell is double clicked
8033 * @param {Roo.bootstrap.Table} this
8034 * @param {Roo.Element} el
8035 * @param {Number} rowIndex
8036 * @param {Number} columnIndex
8037 * @param {Roo.EventObject} e
8039 "celldblclick" : true,
8042 * Fires when a row is clicked
8043 * @param {Roo.bootstrap.Table} this
8044 * @param {Roo.Element} el
8045 * @param {Number} rowIndex
8046 * @param {Roo.EventObject} e
8050 * @event rowdblclick
8051 * Fires when a row is double clicked
8052 * @param {Roo.bootstrap.Table} this
8053 * @param {Roo.Element} el
8054 * @param {Number} rowIndex
8055 * @param {Roo.EventObject} e
8057 "rowdblclick" : true,
8060 * Fires when a mouseover occur
8061 * @param {Roo.bootstrap.Table} this
8062 * @param {Roo.Element} el
8063 * @param {Number} rowIndex
8064 * @param {Number} columnIndex
8065 * @param {Roo.EventObject} e
8070 * Fires when a mouseout occur
8071 * @param {Roo.bootstrap.Table} this
8072 * @param {Roo.Element} el
8073 * @param {Number} rowIndex
8074 * @param {Number} columnIndex
8075 * @param {Roo.EventObject} e
8080 * Fires when a row is rendered, so you can change add a style to it.
8081 * @param {Roo.bootstrap.Table} this
8082 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8086 * @event rowsrendered
8087 * Fires when all the rows have been rendered
8088 * @param {Roo.bootstrap.Table} this
8090 'rowsrendered' : true,
8092 * @event contextmenu
8093 * The raw contextmenu event for the entire grid.
8094 * @param {Roo.EventObject} e
8096 "contextmenu" : true,
8098 * @event rowcontextmenu
8099 * Fires when a row is right clicked
8100 * @param {Roo.bootstrap.Table} this
8101 * @param {Number} rowIndex
8102 * @param {Roo.EventObject} e
8104 "rowcontextmenu" : true,
8106 * @event cellcontextmenu
8107 * Fires when a cell is right clicked
8108 * @param {Roo.bootstrap.Table} this
8109 * @param {Number} rowIndex
8110 * @param {Number} cellIndex
8111 * @param {Roo.EventObject} e
8113 "cellcontextmenu" : true,
8115 * @event headercontextmenu
8116 * Fires when a header is right clicked
8117 * @param {Roo.bootstrap.Table} this
8118 * @param {Number} columnIndex
8119 * @param {Roo.EventObject} e
8121 "headercontextmenu" : true
8125 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8151 rowSelection : false,
8152 cellSelection : false,
8155 // Roo.Element - the tbody
8157 // Roo.Element - thead element
8160 container: false, // used by gridpanel...
8166 auto_hide_footer : false,
8168 getAutoCreate : function()
8170 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8177 if (this.scrollBody) {
8178 cfg.cls += ' table-body-fixed';
8181 cfg.cls += ' table-striped';
8185 cfg.cls += ' table-hover';
8187 if (this.bordered) {
8188 cfg.cls += ' table-bordered';
8190 if (this.condensed) {
8191 cfg.cls += ' table-condensed';
8193 if (this.responsive) {
8194 cfg.cls += ' table-responsive';
8198 cfg.cls+= ' ' +this.cls;
8201 // this lot should be simplifed...
8214 ].forEach(function(k) {
8222 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8225 if(this.store || this.cm){
8226 if(this.headerShow){
8227 cfg.cn.push(this.renderHeader());
8230 cfg.cn.push(this.renderBody());
8232 if(this.footerShow){
8233 cfg.cn.push(this.renderFooter());
8235 // where does this come from?
8236 //cfg.cls+= ' TableGrid';
8239 return { cn : [ cfg ] };
8242 initEvents : function()
8244 if(!this.store || !this.cm){
8247 if (this.selModel) {
8248 this.selModel.initEvents();
8252 //Roo.log('initEvents with ds!!!!');
8254 this.mainBody = this.el.select('tbody', true).first();
8255 this.mainHead = this.el.select('thead', true).first();
8256 this.mainFoot = this.el.select('tfoot', true).first();
8262 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8263 e.on('click', _this.sort, _this);
8266 this.mainBody.on("click", this.onClick, this);
8267 this.mainBody.on("dblclick", this.onDblClick, this);
8269 // why is this done????? = it breaks dialogs??
8270 //this.parent().el.setStyle('position', 'relative');
8274 this.footer.parentId = this.id;
8275 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8278 this.el.select('tfoot tr td').first().addClass('hide');
8283 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8286 this.store.on('load', this.onLoad, this);
8287 this.store.on('beforeload', this.onBeforeLoad, this);
8288 this.store.on('update', this.onUpdate, this);
8289 this.store.on('add', this.onAdd, this);
8290 this.store.on("clear", this.clear, this);
8292 this.el.on("contextmenu", this.onContextMenu, this);
8294 this.mainBody.on('scroll', this.onBodyScroll, this);
8296 this.cm.on("headerchange", this.onHeaderChange, this);
8298 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8302 onContextMenu : function(e, t)
8304 this.processEvent("contextmenu", e);
8307 processEvent : function(name, e)
8309 if (name != 'touchstart' ) {
8310 this.fireEvent(name, e);
8313 var t = e.getTarget();
8315 var cell = Roo.get(t);
8321 if(cell.findParent('tfoot', false, true)){
8325 if(cell.findParent('thead', false, true)){
8327 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8328 cell = Roo.get(t).findParent('th', false, true);
8330 Roo.log("failed to find th in thead?");
8331 Roo.log(e.getTarget());
8336 var cellIndex = cell.dom.cellIndex;
8338 var ename = name == 'touchstart' ? 'click' : name;
8339 this.fireEvent("header" + ename, this, cellIndex, e);
8344 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8345 cell = Roo.get(t).findParent('td', false, true);
8347 Roo.log("failed to find th in tbody?");
8348 Roo.log(e.getTarget());
8353 var row = cell.findParent('tr', false, true);
8354 var cellIndex = cell.dom.cellIndex;
8355 var rowIndex = row.dom.rowIndex - 1;
8359 this.fireEvent("row" + name, this, rowIndex, e);
8363 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8369 onMouseover : function(e, el)
8371 var cell = Roo.get(el);
8377 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8378 cell = cell.findParent('td', false, true);
8381 var row = cell.findParent('tr', false, true);
8382 var cellIndex = cell.dom.cellIndex;
8383 var rowIndex = row.dom.rowIndex - 1; // start from 0
8385 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8389 onMouseout : function(e, el)
8391 var cell = Roo.get(el);
8397 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8398 cell = cell.findParent('td', false, true);
8401 var row = cell.findParent('tr', false, true);
8402 var cellIndex = cell.dom.cellIndex;
8403 var rowIndex = row.dom.rowIndex - 1; // start from 0
8405 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8409 onClick : function(e, el)
8411 var cell = Roo.get(el);
8413 if(!cell || (!this.cellSelection && !this.rowSelection)){
8417 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8418 cell = cell.findParent('td', false, true);
8421 if(!cell || typeof(cell) == 'undefined'){
8425 var row = cell.findParent('tr', false, true);
8427 if(!row || typeof(row) == 'undefined'){
8431 var cellIndex = cell.dom.cellIndex;
8432 var rowIndex = this.getRowIndex(row);
8434 // why??? - should these not be based on SelectionModel?
8435 if(this.cellSelection){
8436 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8439 if(this.rowSelection){
8440 this.fireEvent('rowclick', this, row, rowIndex, e);
8446 onDblClick : function(e,el)
8448 var cell = Roo.get(el);
8450 if(!cell || (!this.cellSelection && !this.rowSelection)){
8454 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8455 cell = cell.findParent('td', false, true);
8458 if(!cell || typeof(cell) == 'undefined'){
8462 var row = cell.findParent('tr', false, true);
8464 if(!row || typeof(row) == 'undefined'){
8468 var cellIndex = cell.dom.cellIndex;
8469 var rowIndex = this.getRowIndex(row);
8471 if(this.cellSelection){
8472 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8475 if(this.rowSelection){
8476 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8480 sort : function(e,el)
8482 var col = Roo.get(el);
8484 if(!col.hasClass('sortable')){
8488 var sort = col.attr('sort');
8491 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8495 this.store.sortInfo = {field : sort, direction : dir};
8498 Roo.log("calling footer first");
8499 this.footer.onClick('first');
8502 this.store.load({ params : { start : 0 } });
8506 renderHeader : function()
8514 this.totalWidth = 0;
8516 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8518 var config = cm.config[i];
8522 cls : 'x-hcol-' + i,
8524 html: cm.getColumnHeader(i)
8529 if(typeof(config.sortable) != 'undefined' && config.sortable){
8531 c.html = '<i class="glyphicon"></i>' + c.html;
8534 // could use BS4 hidden-..-down
8536 if(typeof(config.lgHeader) != 'undefined'){
8537 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8540 if(typeof(config.mdHeader) != 'undefined'){
8541 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8544 if(typeof(config.smHeader) != 'undefined'){
8545 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8548 if(typeof(config.xsHeader) != 'undefined'){
8549 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8556 if(typeof(config.tooltip) != 'undefined'){
8557 c.tooltip = config.tooltip;
8560 if(typeof(config.colspan) != 'undefined'){
8561 c.colspan = config.colspan;
8564 if(typeof(config.hidden) != 'undefined' && config.hidden){
8565 c.style += ' display:none;';
8568 if(typeof(config.dataIndex) != 'undefined'){
8569 c.sort = config.dataIndex;
8574 if(typeof(config.align) != 'undefined' && config.align.length){
8575 c.style += ' text-align:' + config.align + ';';
8578 if(typeof(config.width) != 'undefined'){
8579 c.style += ' width:' + config.width + 'px;';
8580 this.totalWidth += config.width;
8582 this.totalWidth += 100; // assume minimum of 100 per column?
8585 if(typeof(config.cls) != 'undefined'){
8586 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8589 ['xs','sm','md','lg'].map(function(size){
8591 if(typeof(config[size]) == 'undefined'){
8595 if (!config[size]) { // 0 = hidden
8596 // BS 4 '0' is treated as hide that column and below.
8597 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8601 c.cls += ' col-' + size + '-' + config[size] + (
8602 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8614 renderBody : function()
8624 colspan : this.cm.getColumnCount()
8634 renderFooter : function()
8644 colspan : this.cm.getColumnCount()
8658 // Roo.log('ds onload');
8663 var ds = this.store;
8665 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8666 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8667 if (_this.store.sortInfo) {
8669 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8670 e.select('i', true).addClass(['glyphicon-arrow-up']);
8673 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8674 e.select('i', true).addClass(['glyphicon-arrow-down']);
8679 var tbody = this.mainBody;
8681 if(ds.getCount() > 0){
8682 ds.data.each(function(d,rowIndex){
8683 var row = this.renderRow(cm, ds, rowIndex);
8685 tbody.createChild(row);
8689 if(row.cellObjects.length){
8690 Roo.each(row.cellObjects, function(r){
8691 _this.renderCellObject(r);
8698 var tfoot = this.el.select('tfoot', true).first();
8700 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8702 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8704 var total = this.ds.getTotalCount();
8706 if(this.footer.pageSize < total){
8707 this.mainFoot.show();
8711 Roo.each(this.el.select('tbody td', true).elements, function(e){
8712 e.on('mouseover', _this.onMouseover, _this);
8715 Roo.each(this.el.select('tbody td', true).elements, function(e){
8716 e.on('mouseout', _this.onMouseout, _this);
8718 this.fireEvent('rowsrendered', this);
8724 onUpdate : function(ds,record)
8726 this.refreshRow(record);
8730 onRemove : function(ds, record, index, isUpdate){
8731 if(isUpdate !== true){
8732 this.fireEvent("beforerowremoved", this, index, record);
8734 var bt = this.mainBody.dom;
8736 var rows = this.el.select('tbody > tr', true).elements;
8738 if(typeof(rows[index]) != 'undefined'){
8739 bt.removeChild(rows[index].dom);
8742 // if(bt.rows[index]){
8743 // bt.removeChild(bt.rows[index]);
8746 if(isUpdate !== true){
8747 //this.stripeRows(index);
8748 //this.syncRowHeights(index, index);
8750 this.fireEvent("rowremoved", this, index, record);
8754 onAdd : function(ds, records, rowIndex)
8756 //Roo.log('on Add called');
8757 // - note this does not handle multiple adding very well..
8758 var bt = this.mainBody.dom;
8759 for (var i =0 ; i < records.length;i++) {
8760 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8761 //Roo.log(records[i]);
8762 //Roo.log(this.store.getAt(rowIndex+i));
8763 this.insertRow(this.store, rowIndex + i, false);
8770 refreshRow : function(record){
8771 var ds = this.store, index;
8772 if(typeof record == 'number'){
8774 record = ds.getAt(index);
8776 index = ds.indexOf(record);
8778 return; // should not happen - but seems to
8781 this.insertRow(ds, index, true);
8783 this.onRemove(ds, record, index+1, true);
8785 //this.syncRowHeights(index, index);
8787 this.fireEvent("rowupdated", this, index, record);
8790 insertRow : function(dm, rowIndex, isUpdate){
8793 this.fireEvent("beforerowsinserted", this, rowIndex);
8795 //var s = this.getScrollState();
8796 var row = this.renderRow(this.cm, this.store, rowIndex);
8797 // insert before rowIndex..
8798 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8802 if(row.cellObjects.length){
8803 Roo.each(row.cellObjects, function(r){
8804 _this.renderCellObject(r);
8809 this.fireEvent("rowsinserted", this, rowIndex);
8810 //this.syncRowHeights(firstRow, lastRow);
8811 //this.stripeRows(firstRow);
8818 getRowDom : function(rowIndex)
8820 var rows = this.el.select('tbody > tr', true).elements;
8822 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8825 // returns the object tree for a tr..
8828 renderRow : function(cm, ds, rowIndex)
8830 var d = ds.getAt(rowIndex);
8834 cls : 'x-row-' + rowIndex,
8838 var cellObjects = [];
8840 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8841 var config = cm.config[i];
8843 var renderer = cm.getRenderer(i);
8847 if(typeof(renderer) !== 'undefined'){
8848 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8850 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8851 // and are rendered into the cells after the row is rendered - using the id for the element.
8853 if(typeof(value) === 'object'){
8863 rowIndex : rowIndex,
8868 this.fireEvent('rowclass', this, rowcfg);
8872 cls : rowcfg.rowClass + ' x-col-' + i,
8874 html: (typeof(value) === 'object') ? '' : value
8881 if(typeof(config.colspan) != 'undefined'){
8882 td.colspan = config.colspan;
8885 if(typeof(config.hidden) != 'undefined' && config.hidden){
8886 td.style += ' display:none;';
8889 if(typeof(config.align) != 'undefined' && config.align.length){
8890 td.style += ' text-align:' + config.align + ';';
8892 if(typeof(config.valign) != 'undefined' && config.valign.length){
8893 td.style += ' vertical-align:' + config.valign + ';';
8896 if(typeof(config.width) != 'undefined'){
8897 td.style += ' width:' + config.width + 'px;';
8900 if(typeof(config.cursor) != 'undefined'){
8901 td.style += ' cursor:' + config.cursor + ';';
8904 if(typeof(config.cls) != 'undefined'){
8905 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8908 ['xs','sm','md','lg'].map(function(size){
8910 if(typeof(config[size]) == 'undefined'){
8916 if (!config[size]) { // 0 = hidden
8917 // BS 4 '0' is treated as hide that column and below.
8918 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8922 td.cls += ' col-' + size + '-' + config[size] + (
8923 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8933 row.cellObjects = cellObjects;
8941 onBeforeLoad : function()
8950 this.el.select('tbody', true).first().dom.innerHTML = '';
8953 * Show or hide a row.
8954 * @param {Number} rowIndex to show or hide
8955 * @param {Boolean} state hide
8957 setRowVisibility : function(rowIndex, state)
8959 var bt = this.mainBody.dom;
8961 var rows = this.el.select('tbody > tr', true).elements;
8963 if(typeof(rows[rowIndex]) == 'undefined'){
8966 rows[rowIndex].dom.style.display = state ? '' : 'none';
8970 getSelectionModel : function(){
8972 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8974 return this.selModel;
8977 * Render the Roo.bootstrap object from renderder
8979 renderCellObject : function(r)
8983 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8985 var t = r.cfg.render(r.container);
8988 Roo.each(r.cfg.cn, function(c){
8990 container: t.getChildContainer(),
8993 _this.renderCellObject(child);
8998 getRowIndex : function(row)
9002 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9013 * Returns the grid's underlying element = used by panel.Grid
9014 * @return {Element} The element
9016 getGridEl : function(){
9020 * Forces a resize - used by panel.Grid
9021 * @return {Element} The element
9023 autoSize : function()
9025 //var ctr = Roo.get(this.container.dom.parentElement);
9026 var ctr = Roo.get(this.el.dom);
9028 var thd = this.getGridEl().select('thead',true).first();
9029 var tbd = this.getGridEl().select('tbody', true).first();
9030 var tfd = this.getGridEl().select('tfoot', true).first();
9032 var cw = ctr.getWidth();
9033 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9037 tbd.setWidth(ctr.getWidth());
9038 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9039 // this needs fixing for various usage - currently only hydra job advers I think..
9041 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9043 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9046 cw = Math.max(cw, this.totalWidth);
9047 this.getGridEl().select('tbody tr',true).setWidth(cw);
9049 // resize 'expandable coloumn?
9051 return; // we doe not have a view in this design..
9054 onBodyScroll: function()
9056 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9058 this.mainHead.setStyle({
9059 'position' : 'relative',
9060 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9066 var scrollHeight = this.mainBody.dom.scrollHeight;
9068 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9070 var height = this.mainBody.getHeight();
9072 if(scrollHeight - height == scrollTop) {
9074 var total = this.ds.getTotalCount();
9076 if(this.footer.cursor + this.footer.pageSize < total){
9078 this.footer.ds.load({
9080 start : this.footer.cursor + this.footer.pageSize,
9081 limit : this.footer.pageSize
9091 onHeaderChange : function()
9093 var header = this.renderHeader();
9094 var table = this.el.select('table', true).first();
9096 this.mainHead.remove();
9097 this.mainHead = table.createChild(header, this.mainBody, false);
9100 onHiddenChange : function(colModel, colIndex, hidden)
9102 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9103 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9105 this.CSS.updateRule(thSelector, "display", "");
9106 this.CSS.updateRule(tdSelector, "display", "");
9109 this.CSS.updateRule(thSelector, "display", "none");
9110 this.CSS.updateRule(tdSelector, "display", "none");
9113 this.onHeaderChange();
9117 setColumnWidth: function(col_index, width)
9119 // width = "md-2 xs-2..."
9120 if(!this.colModel.config[col_index]) {
9124 var w = width.split(" ");
9126 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9128 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9131 for(var j = 0; j < w.length; j++) {
9137 var size_cls = w[j].split("-");
9139 if(!Number.isInteger(size_cls[1] * 1)) {
9143 if(!this.colModel.config[col_index][size_cls[0]]) {
9147 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9151 h_row[0].classList.replace(
9152 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9153 "col-"+size_cls[0]+"-"+size_cls[1]
9156 for(var i = 0; i < rows.length; i++) {
9158 var size_cls = w[j].split("-");
9160 if(!Number.isInteger(size_cls[1] * 1)) {
9164 if(!this.colModel.config[col_index][size_cls[0]]) {
9168 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9172 rows[i].classList.replace(
9173 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9174 "col-"+size_cls[0]+"-"+size_cls[1]
9178 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9193 * @class Roo.bootstrap.TableCell
9194 * @extends Roo.bootstrap.Component
9195 * Bootstrap TableCell class
9196 * @cfg {String} html cell contain text
9197 * @cfg {String} cls cell class
9198 * @cfg {String} tag cell tag (td|th) default td
9199 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9200 * @cfg {String} align Aligns the content in a cell
9201 * @cfg {String} axis Categorizes cells
9202 * @cfg {String} bgcolor Specifies the background color of a cell
9203 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9204 * @cfg {Number} colspan Specifies the number of columns a cell should span
9205 * @cfg {String} headers Specifies one or more header cells a cell is related to
9206 * @cfg {Number} height Sets the height of a cell
9207 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9208 * @cfg {Number} rowspan Sets the number of rows a cell should span
9209 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9210 * @cfg {String} valign Vertical aligns the content in a cell
9211 * @cfg {Number} width Specifies the width of a cell
9214 * Create a new TableCell
9215 * @param {Object} config The config object
9218 Roo.bootstrap.TableCell = function(config){
9219 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9222 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9242 getAutoCreate : function(){
9243 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9263 cfg.align=this.align
9269 cfg.bgcolor=this.bgcolor
9272 cfg.charoff=this.charoff
9275 cfg.colspan=this.colspan
9278 cfg.headers=this.headers
9281 cfg.height=this.height
9284 cfg.nowrap=this.nowrap
9287 cfg.rowspan=this.rowspan
9290 cfg.scope=this.scope
9293 cfg.valign=this.valign
9296 cfg.width=this.width
9315 * @class Roo.bootstrap.TableRow
9316 * @extends Roo.bootstrap.Component
9317 * Bootstrap TableRow class
9318 * @cfg {String} cls row class
9319 * @cfg {String} align Aligns the content in a table row
9320 * @cfg {String} bgcolor Specifies a background color for a table row
9321 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9322 * @cfg {String} valign Vertical aligns the content in a table row
9325 * Create a new TableRow
9326 * @param {Object} config The config object
9329 Roo.bootstrap.TableRow = function(config){
9330 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9333 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9341 getAutoCreate : function(){
9342 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9352 cfg.align = this.align;
9355 cfg.bgcolor = this.bgcolor;
9358 cfg.charoff = this.charoff;
9361 cfg.valign = this.valign;
9379 * @class Roo.bootstrap.TableBody
9380 * @extends Roo.bootstrap.Component
9381 * Bootstrap TableBody class
9382 * @cfg {String} cls element class
9383 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9384 * @cfg {String} align Aligns the content inside the element
9385 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9386 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9389 * Create a new TableBody
9390 * @param {Object} config The config object
9393 Roo.bootstrap.TableBody = function(config){
9394 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9397 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9405 getAutoCreate : function(){
9406 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9420 cfg.align = this.align;
9423 cfg.charoff = this.charoff;
9426 cfg.valign = this.valign;
9433 // initEvents : function()
9440 // this.store = Roo.factory(this.store, Roo.data);
9441 // this.store.on('load', this.onLoad, this);
9443 // this.store.load();
9447 // onLoad: function ()
9449 // this.fireEvent('load', this);
9459 * Ext JS Library 1.1.1
9460 * Copyright(c) 2006-2007, Ext JS, LLC.
9462 * Originally Released Under LGPL - original licence link has changed is not relivant.
9465 * <script type="text/javascript">
9468 // as we use this in bootstrap.
9469 Roo.namespace('Roo.form');
9471 * @class Roo.form.Action
9472 * Internal Class used to handle form actions
9474 * @param {Roo.form.BasicForm} el The form element or its id
9475 * @param {Object} config Configuration options
9480 // define the action interface
9481 Roo.form.Action = function(form, options){
9483 this.options = options || {};
9486 * Client Validation Failed
9489 Roo.form.Action.CLIENT_INVALID = 'client';
9491 * Server Validation Failed
9494 Roo.form.Action.SERVER_INVALID = 'server';
9496 * Connect to Server Failed
9499 Roo.form.Action.CONNECT_FAILURE = 'connect';
9501 * Reading Data from Server Failed
9504 Roo.form.Action.LOAD_FAILURE = 'load';
9506 Roo.form.Action.prototype = {
9508 failureType : undefined,
9509 response : undefined,
9513 run : function(options){
9518 success : function(response){
9523 handleResponse : function(response){
9527 // default connection failure
9528 failure : function(response){
9530 this.response = response;
9531 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9532 this.form.afterAction(this, false);
9535 processResponse : function(response){
9536 this.response = response;
9537 if(!response.responseText){
9540 this.result = this.handleResponse(response);
9544 // utility functions used internally
9545 getUrl : function(appendParams){
9546 var url = this.options.url || this.form.url || this.form.el.dom.action;
9548 var p = this.getParams();
9550 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9556 getMethod : function(){
9557 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9560 getParams : function(){
9561 var bp = this.form.baseParams;
9562 var p = this.options.params;
9564 if(typeof p == "object"){
9565 p = Roo.urlEncode(Roo.applyIf(p, bp));
9566 }else if(typeof p == 'string' && bp){
9567 p += '&' + Roo.urlEncode(bp);
9570 p = Roo.urlEncode(bp);
9575 createCallback : function(){
9577 success: this.success,
9578 failure: this.failure,
9580 timeout: (this.form.timeout*1000),
9581 upload: this.form.fileUpload ? this.success : undefined
9586 Roo.form.Action.Submit = function(form, options){
9587 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9590 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9593 haveProgress : false,
9594 uploadComplete : false,
9596 // uploadProgress indicator.
9597 uploadProgress : function()
9599 if (!this.form.progressUrl) {
9603 if (!this.haveProgress) {
9604 Roo.MessageBox.progress("Uploading", "Uploading");
9606 if (this.uploadComplete) {
9607 Roo.MessageBox.hide();
9611 this.haveProgress = true;
9613 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9615 var c = new Roo.data.Connection();
9617 url : this.form.progressUrl,
9622 success : function(req){
9623 //console.log(data);
9627 rdata = Roo.decode(req.responseText)
9629 Roo.log("Invalid data from server..");
9633 if (!rdata || !rdata.success) {
9635 Roo.MessageBox.alert(Roo.encode(rdata));
9638 var data = rdata.data;
9640 if (this.uploadComplete) {
9641 Roo.MessageBox.hide();
9646 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9647 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9650 this.uploadProgress.defer(2000,this);
9653 failure: function(data) {
9654 Roo.log('progress url failed ');
9665 // run get Values on the form, so it syncs any secondary forms.
9666 this.form.getValues();
9668 var o = this.options;
9669 var method = this.getMethod();
9670 var isPost = method == 'POST';
9671 if(o.clientValidation === false || this.form.isValid()){
9673 if (this.form.progressUrl) {
9674 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9675 (new Date() * 1) + '' + Math.random());
9680 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9681 form:this.form.el.dom,
9682 url:this.getUrl(!isPost),
9684 params:isPost ? this.getParams() : null,
9685 isUpload: this.form.fileUpload,
9686 formData : this.form.formData
9689 this.uploadProgress();
9691 }else if (o.clientValidation !== false){ // client validation failed
9692 this.failureType = Roo.form.Action.CLIENT_INVALID;
9693 this.form.afterAction(this, false);
9697 success : function(response)
9699 this.uploadComplete= true;
9700 if (this.haveProgress) {
9701 Roo.MessageBox.hide();
9705 var result = this.processResponse(response);
9706 if(result === true || result.success){
9707 this.form.afterAction(this, true);
9711 this.form.markInvalid(result.errors);
9712 this.failureType = Roo.form.Action.SERVER_INVALID;
9714 this.form.afterAction(this, false);
9716 failure : function(response)
9718 this.uploadComplete= true;
9719 if (this.haveProgress) {
9720 Roo.MessageBox.hide();
9723 this.response = response;
9724 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9725 this.form.afterAction(this, false);
9728 handleResponse : function(response){
9729 if(this.form.errorReader){
9730 var rs = this.form.errorReader.read(response);
9733 for(var i = 0, len = rs.records.length; i < len; i++) {
9734 var r = rs.records[i];
9738 if(errors.length < 1){
9742 success : rs.success,
9748 ret = Roo.decode(response.responseText);
9752 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9762 Roo.form.Action.Load = function(form, options){
9763 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9764 this.reader = this.form.reader;
9767 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9772 Roo.Ajax.request(Roo.apply(
9773 this.createCallback(), {
9774 method:this.getMethod(),
9775 url:this.getUrl(false),
9776 params:this.getParams()
9780 success : function(response){
9782 var result = this.processResponse(response);
9783 if(result === true || !result.success || !result.data){
9784 this.failureType = Roo.form.Action.LOAD_FAILURE;
9785 this.form.afterAction(this, false);
9788 this.form.clearInvalid();
9789 this.form.setValues(result.data);
9790 this.form.afterAction(this, true);
9793 handleResponse : function(response){
9794 if(this.form.reader){
9795 var rs = this.form.reader.read(response);
9796 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9798 success : rs.success,
9802 return Roo.decode(response.responseText);
9806 Roo.form.Action.ACTION_TYPES = {
9807 'load' : Roo.form.Action.Load,
9808 'submit' : Roo.form.Action.Submit
9817 * @class Roo.bootstrap.Form
9818 * @extends Roo.bootstrap.Component
9819 * Bootstrap Form class
9820 * @cfg {String} method GET | POST (default POST)
9821 * @cfg {String} labelAlign top | left (default top)
9822 * @cfg {String} align left | right - for navbars
9823 * @cfg {Boolean} loadMask load mask when submit (default true)
9828 * @param {Object} config The config object
9832 Roo.bootstrap.Form = function(config){
9834 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9836 Roo.bootstrap.Form.popover.apply();
9840 * @event clientvalidation
9841 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9842 * @param {Form} this
9843 * @param {Boolean} valid true if the form has passed client-side validation
9845 clientvalidation: true,
9847 * @event beforeaction
9848 * Fires before any action is performed. Return false to cancel the action.
9849 * @param {Form} this
9850 * @param {Action} action The action to be performed
9854 * @event actionfailed
9855 * Fires when an action fails.
9856 * @param {Form} this
9857 * @param {Action} action The action that failed
9859 actionfailed : true,
9861 * @event actioncomplete
9862 * Fires when an action is completed.
9863 * @param {Form} this
9864 * @param {Action} action The action that completed
9866 actioncomplete : true
9870 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9873 * @cfg {String} method
9874 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9879 * The URL to use for form actions if one isn't supplied in the action options.
9882 * @cfg {Boolean} fileUpload
9883 * Set to true if this form is a file upload.
9887 * @cfg {Object} baseParams
9888 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9892 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9896 * @cfg {Sting} align (left|right) for navbar forms
9901 activeAction : null,
9904 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9905 * element by passing it or its id or mask the form itself by passing in true.
9908 waitMsgTarget : false,
9913 * @cfg {Boolean} errorMask (true|false) default false
9918 * @cfg {Number} maskOffset Default 100
9923 * @cfg {Boolean} maskBody
9927 getAutoCreate : function(){
9931 method : this.method || 'POST',
9932 id : this.id || Roo.id(),
9935 if (this.parent().xtype.match(/^Nav/)) {
9936 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9940 if (this.labelAlign == 'left' ) {
9941 cfg.cls += ' form-horizontal';
9947 initEvents : function()
9949 this.el.on('submit', this.onSubmit, this);
9950 // this was added as random key presses on the form where triggering form submit.
9951 this.el.on('keypress', function(e) {
9952 if (e.getCharCode() != 13) {
9955 // we might need to allow it for textareas.. and some other items.
9956 // check e.getTarget().
9958 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9962 Roo.log("keypress blocked");
9970 onSubmit : function(e){
9975 * Returns true if client-side validation on the form is successful.
9978 isValid : function(){
9979 var items = this.getItems();
9983 items.each(function(f){
9989 Roo.log('invalid field: ' + f.name);
9993 if(!target && f.el.isVisible(true)){
9999 if(this.errorMask && !valid){
10000 Roo.bootstrap.Form.popover.mask(this, target);
10007 * Returns true if any fields in this form have changed since their original load.
10010 isDirty : function(){
10012 var items = this.getItems();
10013 items.each(function(f){
10023 * Performs a predefined action (submit or load) or custom actions you define on this form.
10024 * @param {String} actionName The name of the action type
10025 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10026 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10027 * accept other config options):
10029 Property Type Description
10030 ---------------- --------------- ----------------------------------------------------------------------------------
10031 url String The url for the action (defaults to the form's url)
10032 method String The form method to use (defaults to the form's method, or POST if not defined)
10033 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10034 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10035 validate the form on the client (defaults to false)
10037 * @return {BasicForm} this
10039 doAction : function(action, options){
10040 if(typeof action == 'string'){
10041 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10043 if(this.fireEvent('beforeaction', this, action) !== false){
10044 this.beforeAction(action);
10045 action.run.defer(100, action);
10051 beforeAction : function(action){
10052 var o = action.options;
10057 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10059 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10062 // not really supported yet.. ??
10064 //if(this.waitMsgTarget === true){
10065 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10066 //}else if(this.waitMsgTarget){
10067 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10068 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10070 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10076 afterAction : function(action, success){
10077 this.activeAction = null;
10078 var o = action.options;
10083 Roo.get(document.body).unmask();
10089 //if(this.waitMsgTarget === true){
10090 // this.el.unmask();
10091 //}else if(this.waitMsgTarget){
10092 // this.waitMsgTarget.unmask();
10094 // Roo.MessageBox.updateProgress(1);
10095 // Roo.MessageBox.hide();
10102 Roo.callback(o.success, o.scope, [this, action]);
10103 this.fireEvent('actioncomplete', this, action);
10107 // failure condition..
10108 // we have a scenario where updates need confirming.
10109 // eg. if a locking scenario exists..
10110 // we look for { errors : { needs_confirm : true }} in the response.
10112 (typeof(action.result) != 'undefined') &&
10113 (typeof(action.result.errors) != 'undefined') &&
10114 (typeof(action.result.errors.needs_confirm) != 'undefined')
10117 Roo.log("not supported yet");
10120 Roo.MessageBox.confirm(
10121 "Change requires confirmation",
10122 action.result.errorMsg,
10127 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10137 Roo.callback(o.failure, o.scope, [this, action]);
10138 // show an error message if no failed handler is set..
10139 if (!this.hasListener('actionfailed')) {
10140 Roo.log("need to add dialog support");
10142 Roo.MessageBox.alert("Error",
10143 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10144 action.result.errorMsg :
10145 "Saving Failed, please check your entries or try again"
10150 this.fireEvent('actionfailed', this, action);
10155 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10156 * @param {String} id The value to search for
10159 findField : function(id){
10160 var items = this.getItems();
10161 var field = items.get(id);
10163 items.each(function(f){
10164 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10171 return field || null;
10174 * Mark fields in this form invalid in bulk.
10175 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10176 * @return {BasicForm} this
10178 markInvalid : function(errors){
10179 if(errors instanceof Array){
10180 for(var i = 0, len = errors.length; i < len; i++){
10181 var fieldError = errors[i];
10182 var f = this.findField(fieldError.id);
10184 f.markInvalid(fieldError.msg);
10190 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10191 field.markInvalid(errors[id]);
10195 //Roo.each(this.childForms || [], function (f) {
10196 // f.markInvalid(errors);
10203 * Set values for fields in this form in bulk.
10204 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10205 * @return {BasicForm} this
10207 setValues : function(values){
10208 if(values instanceof Array){ // array of objects
10209 for(var i = 0, len = values.length; i < len; i++){
10211 var f = this.findField(v.id);
10213 f.setValue(v.value);
10214 if(this.trackResetOnLoad){
10215 f.originalValue = f.getValue();
10219 }else{ // object hash
10222 if(typeof values[id] != 'function' && (field = this.findField(id))){
10224 if (field.setFromData &&
10225 field.valueField &&
10226 field.displayField &&
10227 // combos' with local stores can
10228 // be queried via setValue()
10229 // to set their value..
10230 (field.store && !field.store.isLocal)
10234 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10235 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10236 field.setFromData(sd);
10238 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10240 field.setFromData(values);
10243 field.setValue(values[id]);
10247 if(this.trackResetOnLoad){
10248 field.originalValue = field.getValue();
10254 //Roo.each(this.childForms || [], function (f) {
10255 // f.setValues(values);
10262 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10263 * they are returned as an array.
10264 * @param {Boolean} asString
10267 getValues : function(asString){
10268 //if (this.childForms) {
10269 // copy values from the child forms
10270 // Roo.each(this.childForms, function (f) {
10271 // this.setValues(f.getValues());
10277 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10278 if(asString === true){
10281 return Roo.urlDecode(fs);
10285 * Returns the fields in this form as an object with key/value pairs.
10286 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10289 getFieldValues : function(with_hidden)
10291 var items = this.getItems();
10293 items.each(function(f){
10295 if (!f.getName()) {
10299 var v = f.getValue();
10301 if (f.inputType =='radio') {
10302 if (typeof(ret[f.getName()]) == 'undefined') {
10303 ret[f.getName()] = ''; // empty..
10306 if (!f.el.dom.checked) {
10310 v = f.el.dom.value;
10314 if(f.xtype == 'MoneyField'){
10315 ret[f.currencyName] = f.getCurrency();
10318 // not sure if this supported any more..
10319 if ((typeof(v) == 'object') && f.getRawValue) {
10320 v = f.getRawValue() ; // dates..
10322 // combo boxes where name != hiddenName...
10323 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10324 ret[f.name] = f.getRawValue();
10326 ret[f.getName()] = v;
10333 * Clears all invalid messages in this form.
10334 * @return {BasicForm} this
10336 clearInvalid : function(){
10337 var items = this.getItems();
10339 items.each(function(f){
10347 * Resets this form.
10348 * @return {BasicForm} this
10350 reset : function(){
10351 var items = this.getItems();
10352 items.each(function(f){
10356 Roo.each(this.childForms || [], function (f) {
10364 getItems : function()
10366 var r=new Roo.util.MixedCollection(false, function(o){
10367 return o.id || (o.id = Roo.id());
10369 var iter = function(el) {
10376 Roo.each(el.items,function(e) {
10385 hideFields : function(items)
10387 Roo.each(items, function(i){
10389 var f = this.findField(i);
10400 showFields : function(items)
10402 Roo.each(items, function(i){
10404 var f = this.findField(i);
10417 Roo.apply(Roo.bootstrap.Form, {
10433 intervalID : false,
10439 if(this.isApplied){
10444 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10445 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10446 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10447 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10450 this.maskEl.top.enableDisplayMode("block");
10451 this.maskEl.left.enableDisplayMode("block");
10452 this.maskEl.bottom.enableDisplayMode("block");
10453 this.maskEl.right.enableDisplayMode("block");
10455 this.toolTip = new Roo.bootstrap.Tooltip({
10456 cls : 'roo-form-error-popover',
10458 'left' : ['r-l', [-2,0], 'right'],
10459 'right' : ['l-r', [2,0], 'left'],
10460 'bottom' : ['tl-bl', [0,2], 'top'],
10461 'top' : [ 'bl-tl', [0,-2], 'bottom']
10465 this.toolTip.render(Roo.get(document.body));
10467 this.toolTip.el.enableDisplayMode("block");
10469 Roo.get(document.body).on('click', function(){
10473 Roo.get(document.body).on('touchstart', function(){
10477 this.isApplied = true
10480 mask : function(form, target)
10484 this.target = target;
10486 if(!this.form.errorMask || !target.el){
10490 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10492 Roo.log(scrollable);
10494 var ot = this.target.el.calcOffsetsTo(scrollable);
10496 var scrollTo = ot[1] - this.form.maskOffset;
10498 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10500 scrollable.scrollTo('top', scrollTo);
10502 var box = this.target.el.getBox();
10504 var zIndex = Roo.bootstrap.Modal.zIndex++;
10507 this.maskEl.top.setStyle('position', 'absolute');
10508 this.maskEl.top.setStyle('z-index', zIndex);
10509 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10510 this.maskEl.top.setLeft(0);
10511 this.maskEl.top.setTop(0);
10512 this.maskEl.top.show();
10514 this.maskEl.left.setStyle('position', 'absolute');
10515 this.maskEl.left.setStyle('z-index', zIndex);
10516 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10517 this.maskEl.left.setLeft(0);
10518 this.maskEl.left.setTop(box.y - this.padding);
10519 this.maskEl.left.show();
10521 this.maskEl.bottom.setStyle('position', 'absolute');
10522 this.maskEl.bottom.setStyle('z-index', zIndex);
10523 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10524 this.maskEl.bottom.setLeft(0);
10525 this.maskEl.bottom.setTop(box.bottom + this.padding);
10526 this.maskEl.bottom.show();
10528 this.maskEl.right.setStyle('position', 'absolute');
10529 this.maskEl.right.setStyle('z-index', zIndex);
10530 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10531 this.maskEl.right.setLeft(box.right + this.padding);
10532 this.maskEl.right.setTop(box.y - this.padding);
10533 this.maskEl.right.show();
10535 this.toolTip.bindEl = this.target.el;
10537 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10539 var tip = this.target.blankText;
10541 if(this.target.getValue() !== '' ) {
10543 if (this.target.invalidText.length) {
10544 tip = this.target.invalidText;
10545 } else if (this.target.regexText.length){
10546 tip = this.target.regexText;
10550 this.toolTip.show(tip);
10552 this.intervalID = window.setInterval(function() {
10553 Roo.bootstrap.Form.popover.unmask();
10556 window.onwheel = function(){ return false;};
10558 (function(){ this.isMasked = true; }).defer(500, this);
10562 unmask : function()
10564 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10568 this.maskEl.top.setStyle('position', 'absolute');
10569 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10570 this.maskEl.top.hide();
10572 this.maskEl.left.setStyle('position', 'absolute');
10573 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10574 this.maskEl.left.hide();
10576 this.maskEl.bottom.setStyle('position', 'absolute');
10577 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10578 this.maskEl.bottom.hide();
10580 this.maskEl.right.setStyle('position', 'absolute');
10581 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10582 this.maskEl.right.hide();
10584 this.toolTip.hide();
10586 this.toolTip.el.hide();
10588 window.onwheel = function(){ return true;};
10590 if(this.intervalID){
10591 window.clearInterval(this.intervalID);
10592 this.intervalID = false;
10595 this.isMasked = false;
10605 * Ext JS Library 1.1.1
10606 * Copyright(c) 2006-2007, Ext JS, LLC.
10608 * Originally Released Under LGPL - original licence link has changed is not relivant.
10611 * <script type="text/javascript">
10614 * @class Roo.form.VTypes
10615 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10618 Roo.form.VTypes = function(){
10619 // closure these in so they are only created once.
10620 var alpha = /^[a-zA-Z_]+$/;
10621 var alphanum = /^[a-zA-Z0-9_]+$/;
10622 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10623 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10625 // All these messages and functions are configurable
10628 * The function used to validate email addresses
10629 * @param {String} value The email address
10631 'email' : function(v){
10632 return email.test(v);
10635 * The error text to display when the email validation function returns false
10638 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10640 * The keystroke filter mask to be applied on email input
10643 'emailMask' : /[a-z0-9_\.\-@]/i,
10646 * The function used to validate URLs
10647 * @param {String} value The URL
10649 'url' : function(v){
10650 return url.test(v);
10653 * The error text to display when the url validation function returns false
10656 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10659 * The function used to validate alpha values
10660 * @param {String} value The value
10662 'alpha' : function(v){
10663 return alpha.test(v);
10666 * The error text to display when the alpha validation function returns false
10669 'alphaText' : 'This field should only contain letters and _',
10671 * The keystroke filter mask to be applied on alpha input
10674 'alphaMask' : /[a-z_]/i,
10677 * The function used to validate alphanumeric values
10678 * @param {String} value The value
10680 'alphanum' : function(v){
10681 return alphanum.test(v);
10684 * The error text to display when the alphanumeric validation function returns false
10687 'alphanumText' : 'This field should only contain letters, numbers and _',
10689 * The keystroke filter mask to be applied on alphanumeric input
10692 'alphanumMask' : /[a-z0-9_]/i
10702 * @class Roo.bootstrap.Input
10703 * @extends Roo.bootstrap.Component
10704 * Bootstrap Input class
10705 * @cfg {Boolean} disabled is it disabled
10706 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10707 * @cfg {String} name name of the input
10708 * @cfg {string} fieldLabel - the label associated
10709 * @cfg {string} placeholder - placeholder to put in text.
10710 * @cfg {string} before - input group add on before
10711 * @cfg {string} after - input group add on after
10712 * @cfg {string} size - (lg|sm) or leave empty..
10713 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10714 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10715 * @cfg {Number} md colspan out of 12 for computer-sized screens
10716 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10717 * @cfg {string} value default value of the input
10718 * @cfg {Number} labelWidth set the width of label
10719 * @cfg {Number} labellg set the width of label (1-12)
10720 * @cfg {Number} labelmd set the width of label (1-12)
10721 * @cfg {Number} labelsm set the width of label (1-12)
10722 * @cfg {Number} labelxs set the width of label (1-12)
10723 * @cfg {String} labelAlign (top|left)
10724 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10725 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10726 * @cfg {String} indicatorpos (left|right) default left
10727 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10728 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10729 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10731 * @cfg {String} align (left|center|right) Default left
10732 * @cfg {Boolean} forceFeedback (true|false) Default false
10735 * Create a new Input
10736 * @param {Object} config The config object
10739 Roo.bootstrap.Input = function(config){
10741 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10746 * Fires when this field receives input focus.
10747 * @param {Roo.form.Field} this
10752 * Fires when this field loses input focus.
10753 * @param {Roo.form.Field} this
10757 * @event specialkey
10758 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10759 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10760 * @param {Roo.form.Field} this
10761 * @param {Roo.EventObject} e The event object
10766 * Fires just before the field blurs if the field value has changed.
10767 * @param {Roo.form.Field} this
10768 * @param {Mixed} newValue The new value
10769 * @param {Mixed} oldValue The original value
10774 * Fires after the field has been marked as invalid.
10775 * @param {Roo.form.Field} this
10776 * @param {String} msg The validation message
10781 * Fires after the field has been validated with no errors.
10782 * @param {Roo.form.Field} this
10787 * Fires after the key up
10788 * @param {Roo.form.Field} this
10789 * @param {Roo.EventObject} e The event Object
10794 * Fires after the user pastes into input
10795 * @param {Roo.form.Field} this
10796 * @param {Roo.EventObject} e The event Object
10802 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10804 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10805 automatic validation (defaults to "keyup").
10807 validationEvent : "keyup",
10809 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10811 validateOnBlur : true,
10813 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10815 validationDelay : 250,
10817 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10819 focusClass : "x-form-focus", // not needed???
10823 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10825 invalidClass : "has-warning",
10828 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10830 validClass : "has-success",
10833 * @cfg {Boolean} hasFeedback (true|false) default true
10835 hasFeedback : true,
10838 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10840 invalidFeedbackClass : "glyphicon-warning-sign",
10843 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10845 validFeedbackClass : "glyphicon-ok",
10848 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10850 selectOnFocus : false,
10853 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10857 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10862 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10864 disableKeyFilter : false,
10867 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10871 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10875 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10877 blankText : "Please complete this mandatory field",
10880 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10884 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10886 maxLength : Number.MAX_VALUE,
10888 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10890 minLengthText : "The minimum length for this field is {0}",
10892 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10894 maxLengthText : "The maximum length for this field is {0}",
10898 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10899 * If available, this function will be called only after the basic validators all return true, and will be passed the
10900 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10904 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10905 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10906 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10910 * @cfg {String} regexText -- Depricated - use Invalid Text
10915 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10921 autocomplete: false,
10925 inputType : 'text',
10928 placeholder: false,
10933 preventMark: false,
10934 isFormField : true,
10937 labelAlign : false,
10940 formatedValue : false,
10941 forceFeedback : false,
10943 indicatorpos : 'left',
10953 parentLabelAlign : function()
10956 while (parent.parent()) {
10957 parent = parent.parent();
10958 if (typeof(parent.labelAlign) !='undefined') {
10959 return parent.labelAlign;
10966 getAutoCreate : function()
10968 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10974 if(this.inputType != 'hidden'){
10975 cfg.cls = 'form-group' //input-group
10981 type : this.inputType,
10982 value : this.value,
10983 cls : 'form-control',
10984 placeholder : this.placeholder || '',
10985 autocomplete : this.autocomplete || 'new-password'
10987 if (this.inputType == 'file') {
10988 input.style = 'overflow:hidden'; // why not in CSS?
10991 if(this.capture.length){
10992 input.capture = this.capture;
10995 if(this.accept.length){
10996 input.accept = this.accept + "/*";
11000 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11003 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11004 input.maxLength = this.maxLength;
11007 if (this.disabled) {
11008 input.disabled=true;
11011 if (this.readOnly) {
11012 input.readonly=true;
11016 input.name = this.name;
11020 input.cls += ' input-' + this.size;
11024 ['xs','sm','md','lg'].map(function(size){
11025 if (settings[size]) {
11026 cfg.cls += ' col-' + size + '-' + settings[size];
11030 var inputblock = input;
11034 cls: 'glyphicon form-control-feedback'
11037 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11040 cls : 'has-feedback',
11048 if (this.before || this.after) {
11051 cls : 'input-group',
11055 if (this.before && typeof(this.before) == 'string') {
11057 inputblock.cn.push({
11059 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11063 if (this.before && typeof(this.before) == 'object') {
11064 this.before = Roo.factory(this.before);
11066 inputblock.cn.push({
11068 cls : 'roo-input-before input-group-prepend input-group-' +
11069 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11073 inputblock.cn.push(input);
11075 if (this.after && typeof(this.after) == 'string') {
11076 inputblock.cn.push({
11078 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11082 if (this.after && typeof(this.after) == 'object') {
11083 this.after = Roo.factory(this.after);
11085 inputblock.cn.push({
11087 cls : 'roo-input-after input-group-append input-group-' +
11088 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11092 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11093 inputblock.cls += ' has-feedback';
11094 inputblock.cn.push(feedback);
11099 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11100 tooltip : 'This field is required'
11102 if (this.allowBlank ) {
11103 indicator.style = this.allowBlank ? ' display:none' : '';
11105 if (align ==='left' && this.fieldLabel.length) {
11107 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11114 cls : 'control-label col-form-label',
11115 html : this.fieldLabel
11126 var labelCfg = cfg.cn[1];
11127 var contentCfg = cfg.cn[2];
11129 if(this.indicatorpos == 'right'){
11134 cls : 'control-label col-form-label',
11138 html : this.fieldLabel
11152 labelCfg = cfg.cn[0];
11153 contentCfg = cfg.cn[1];
11157 if(this.labelWidth > 12){
11158 labelCfg.style = "width: " + this.labelWidth + 'px';
11161 if(this.labelWidth < 13 && this.labelmd == 0){
11162 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11165 if(this.labellg > 0){
11166 labelCfg.cls += ' col-lg-' + this.labellg;
11167 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11170 if(this.labelmd > 0){
11171 labelCfg.cls += ' col-md-' + this.labelmd;
11172 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11175 if(this.labelsm > 0){
11176 labelCfg.cls += ' col-sm-' + this.labelsm;
11177 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11180 if(this.labelxs > 0){
11181 labelCfg.cls += ' col-xs-' + this.labelxs;
11182 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11186 } else if ( this.fieldLabel.length) {
11193 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11194 tooltip : 'This field is required',
11195 style : this.allowBlank ? ' display:none' : ''
11199 //cls : 'input-group-addon',
11200 html : this.fieldLabel
11208 if(this.indicatorpos == 'right'){
11213 //cls : 'input-group-addon',
11214 html : this.fieldLabel
11219 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11220 tooltip : 'This field is required',
11221 style : this.allowBlank ? ' display:none' : ''
11241 if (this.parentType === 'Navbar' && this.parent().bar) {
11242 cfg.cls += ' navbar-form';
11245 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11246 // on BS4 we do this only if not form
11247 cfg.cls += ' navbar-form';
11255 * return the real input element.
11257 inputEl: function ()
11259 return this.el.select('input.form-control',true).first();
11262 tooltipEl : function()
11264 return this.inputEl();
11267 indicatorEl : function()
11269 if (Roo.bootstrap.version == 4) {
11270 return false; // not enabled in v4 yet.
11273 var indicator = this.el.select('i.roo-required-indicator',true).first();
11283 setDisabled : function(v)
11285 var i = this.inputEl().dom;
11287 i.removeAttribute('disabled');
11291 i.setAttribute('disabled','true');
11293 initEvents : function()
11296 this.inputEl().on("keydown" , this.fireKey, this);
11297 this.inputEl().on("focus", this.onFocus, this);
11298 this.inputEl().on("blur", this.onBlur, this);
11300 this.inputEl().relayEvent('keyup', this);
11301 this.inputEl().relayEvent('paste', this);
11303 this.indicator = this.indicatorEl();
11305 if(this.indicator){
11306 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11309 // reference to original value for reset
11310 this.originalValue = this.getValue();
11311 //Roo.form.TextField.superclass.initEvents.call(this);
11312 if(this.validationEvent == 'keyup'){
11313 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11314 this.inputEl().on('keyup', this.filterValidation, this);
11316 else if(this.validationEvent !== false){
11317 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11320 if(this.selectOnFocus){
11321 this.on("focus", this.preFocus, this);
11324 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11325 this.inputEl().on("keypress", this.filterKeys, this);
11327 this.inputEl().relayEvent('keypress', this);
11330 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11331 this.el.on("click", this.autoSize, this);
11334 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11335 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11338 if (typeof(this.before) == 'object') {
11339 this.before.render(this.el.select('.roo-input-before',true).first());
11341 if (typeof(this.after) == 'object') {
11342 this.after.render(this.el.select('.roo-input-after',true).first());
11345 this.inputEl().on('change', this.onChange, this);
11348 filterValidation : function(e){
11349 if(!e.isNavKeyPress()){
11350 this.validationTask.delay(this.validationDelay);
11354 * Validates the field value
11355 * @return {Boolean} True if the value is valid, else false
11357 validate : function(){
11358 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11359 if(this.disabled || this.validateValue(this.getRawValue())){
11364 this.markInvalid();
11370 * Validates a value according to the field's validation rules and marks the field as invalid
11371 * if the validation fails
11372 * @param {Mixed} value The value to validate
11373 * @return {Boolean} True if the value is valid, else false
11375 validateValue : function(value)
11377 if(this.getVisibilityEl().hasClass('hidden')){
11381 if(value.length < 1) { // if it's blank
11382 if(this.allowBlank){
11388 if(value.length < this.minLength){
11391 if(value.length > this.maxLength){
11395 var vt = Roo.form.VTypes;
11396 if(!vt[this.vtype](value, this)){
11400 if(typeof this.validator == "function"){
11401 var msg = this.validator(value);
11405 if (typeof(msg) == 'string') {
11406 this.invalidText = msg;
11410 if(this.regex && !this.regex.test(value)){
11418 fireKey : function(e){
11419 //Roo.log('field ' + e.getKey());
11420 if(e.isNavKeyPress()){
11421 this.fireEvent("specialkey", this, e);
11424 focus : function (selectText){
11426 this.inputEl().focus();
11427 if(selectText === true){
11428 this.inputEl().dom.select();
11434 onFocus : function(){
11435 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11436 // this.el.addClass(this.focusClass);
11438 if(!this.hasFocus){
11439 this.hasFocus = true;
11440 this.startValue = this.getValue();
11441 this.fireEvent("focus", this);
11445 beforeBlur : Roo.emptyFn,
11449 onBlur : function(){
11451 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11452 //this.el.removeClass(this.focusClass);
11454 this.hasFocus = false;
11455 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11458 var v = this.getValue();
11459 if(String(v) !== String(this.startValue)){
11460 this.fireEvent('change', this, v, this.startValue);
11462 this.fireEvent("blur", this);
11465 onChange : function(e)
11467 var v = this.getValue();
11468 if(String(v) !== String(this.startValue)){
11469 this.fireEvent('change', this, v, this.startValue);
11475 * Resets the current field value to the originally loaded value and clears any validation messages
11477 reset : function(){
11478 this.setValue(this.originalValue);
11482 * Returns the name of the field
11483 * @return {Mixed} name The name field
11485 getName: function(){
11489 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11490 * @return {Mixed} value The field value
11492 getValue : function(){
11494 var v = this.inputEl().getValue();
11499 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11500 * @return {Mixed} value The field value
11502 getRawValue : function(){
11503 var v = this.inputEl().getValue();
11509 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11510 * @param {Mixed} value The value to set
11512 setRawValue : function(v){
11513 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11516 selectText : function(start, end){
11517 var v = this.getRawValue();
11519 start = start === undefined ? 0 : start;
11520 end = end === undefined ? v.length : end;
11521 var d = this.inputEl().dom;
11522 if(d.setSelectionRange){
11523 d.setSelectionRange(start, end);
11524 }else if(d.createTextRange){
11525 var range = d.createTextRange();
11526 range.moveStart("character", start);
11527 range.moveEnd("character", v.length-end);
11534 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11535 * @param {Mixed} value The value to set
11537 setValue : function(v){
11540 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11546 processValue : function(value){
11547 if(this.stripCharsRe){
11548 var newValue = value.replace(this.stripCharsRe, '');
11549 if(newValue !== value){
11550 this.setRawValue(newValue);
11557 preFocus : function(){
11559 if(this.selectOnFocus){
11560 this.inputEl().dom.select();
11563 filterKeys : function(e){
11564 var k = e.getKey();
11565 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11568 var c = e.getCharCode(), cc = String.fromCharCode(c);
11569 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11572 if(!this.maskRe.test(cc)){
11577 * Clear any invalid styles/messages for this field
11579 clearInvalid : function(){
11581 if(!this.el || this.preventMark){ // not rendered
11586 this.el.removeClass([this.invalidClass, 'is-invalid']);
11588 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11590 var feedback = this.el.select('.form-control-feedback', true).first();
11593 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11598 if(this.indicator){
11599 this.indicator.removeClass('visible');
11600 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11603 this.fireEvent('valid', this);
11607 * Mark this field as valid
11609 markValid : function()
11611 if(!this.el || this.preventMark){ // not rendered...
11615 this.el.removeClass([this.invalidClass, this.validClass]);
11616 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11618 var feedback = this.el.select('.form-control-feedback', true).first();
11621 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11624 if(this.indicator){
11625 this.indicator.removeClass('visible');
11626 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11634 if(this.allowBlank && !this.getRawValue().length){
11637 if (Roo.bootstrap.version == 3) {
11638 this.el.addClass(this.validClass);
11640 this.inputEl().addClass('is-valid');
11643 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11645 var feedback = this.el.select('.form-control-feedback', true).first();
11648 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11649 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11654 this.fireEvent('valid', this);
11658 * Mark this field as invalid
11659 * @param {String} msg The validation message
11661 markInvalid : function(msg)
11663 if(!this.el || this.preventMark){ // not rendered
11667 this.el.removeClass([this.invalidClass, this.validClass]);
11668 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11670 var feedback = this.el.select('.form-control-feedback', true).first();
11673 this.el.select('.form-control-feedback', true).first().removeClass(
11674 [this.invalidFeedbackClass, this.validFeedbackClass]);
11681 if(this.allowBlank && !this.getRawValue().length){
11685 if(this.indicator){
11686 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11687 this.indicator.addClass('visible');
11689 if (Roo.bootstrap.version == 3) {
11690 this.el.addClass(this.invalidClass);
11692 this.inputEl().addClass('is-invalid');
11697 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11699 var feedback = this.el.select('.form-control-feedback', true).first();
11702 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11704 if(this.getValue().length || this.forceFeedback){
11705 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11712 this.fireEvent('invalid', this, msg);
11715 SafariOnKeyDown : function(event)
11717 // this is a workaround for a password hang bug on chrome/ webkit.
11718 if (this.inputEl().dom.type != 'password') {
11722 var isSelectAll = false;
11724 if(this.inputEl().dom.selectionEnd > 0){
11725 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11727 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11728 event.preventDefault();
11733 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11735 event.preventDefault();
11736 // this is very hacky as keydown always get's upper case.
11738 var cc = String.fromCharCode(event.getCharCode());
11739 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11743 adjustWidth : function(tag, w){
11744 tag = tag.toLowerCase();
11745 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11746 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11747 if(tag == 'input'){
11750 if(tag == 'textarea'){
11753 }else if(Roo.isOpera){
11754 if(tag == 'input'){
11757 if(tag == 'textarea'){
11765 setFieldLabel : function(v)
11767 if(!this.rendered){
11771 if(this.indicatorEl()){
11772 var ar = this.el.select('label > span',true);
11774 if (ar.elements.length) {
11775 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11776 this.fieldLabel = v;
11780 var br = this.el.select('label',true);
11782 if(br.elements.length) {
11783 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11784 this.fieldLabel = v;
11788 Roo.log('Cannot Found any of label > span || label in input');
11792 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11793 this.fieldLabel = v;
11808 * @class Roo.bootstrap.TextArea
11809 * @extends Roo.bootstrap.Input
11810 * Bootstrap TextArea class
11811 * @cfg {Number} cols Specifies the visible width of a text area
11812 * @cfg {Number} rows Specifies the visible number of lines in a text area
11813 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11814 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11815 * @cfg {string} html text
11818 * Create a new TextArea
11819 * @param {Object} config The config object
11822 Roo.bootstrap.TextArea = function(config){
11823 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11827 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11837 getAutoCreate : function(){
11839 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11845 if(this.inputType != 'hidden'){
11846 cfg.cls = 'form-group' //input-group
11854 value : this.value || '',
11855 html: this.html || '',
11856 cls : 'form-control',
11857 placeholder : this.placeholder || ''
11861 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11862 input.maxLength = this.maxLength;
11866 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11870 input.cols = this.cols;
11873 if (this.readOnly) {
11874 input.readonly = true;
11878 input.name = this.name;
11882 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11886 ['xs','sm','md','lg'].map(function(size){
11887 if (settings[size]) {
11888 cfg.cls += ' col-' + size + '-' + settings[size];
11892 var inputblock = input;
11894 if(this.hasFeedback && !this.allowBlank){
11898 cls: 'glyphicon form-control-feedback'
11902 cls : 'has-feedback',
11911 if (this.before || this.after) {
11914 cls : 'input-group',
11918 inputblock.cn.push({
11920 cls : 'input-group-addon',
11925 inputblock.cn.push(input);
11927 if(this.hasFeedback && !this.allowBlank){
11928 inputblock.cls += ' has-feedback';
11929 inputblock.cn.push(feedback);
11933 inputblock.cn.push({
11935 cls : 'input-group-addon',
11942 if (align ==='left' && this.fieldLabel.length) {
11947 cls : 'control-label',
11948 html : this.fieldLabel
11959 if(this.labelWidth > 12){
11960 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11963 if(this.labelWidth < 13 && this.labelmd == 0){
11964 this.labelmd = this.labelWidth;
11967 if(this.labellg > 0){
11968 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11969 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11972 if(this.labelmd > 0){
11973 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11974 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11977 if(this.labelsm > 0){
11978 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11979 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11982 if(this.labelxs > 0){
11983 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11984 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11987 } else if ( this.fieldLabel.length) {
11992 //cls : 'input-group-addon',
11993 html : this.fieldLabel
12011 if (this.disabled) {
12012 input.disabled=true;
12019 * return the real textarea element.
12021 inputEl: function ()
12023 return this.el.select('textarea.form-control',true).first();
12027 * Clear any invalid styles/messages for this field
12029 clearInvalid : function()
12032 if(!this.el || this.preventMark){ // not rendered
12036 var label = this.el.select('label', true).first();
12037 var icon = this.el.select('i.fa-star', true).first();
12042 this.el.removeClass( this.validClass);
12043 this.inputEl().removeClass('is-invalid');
12045 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12047 var feedback = this.el.select('.form-control-feedback', true).first();
12050 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12055 this.fireEvent('valid', this);
12059 * Mark this field as valid
12061 markValid : function()
12063 if(!this.el || this.preventMark){ // not rendered
12067 this.el.removeClass([this.invalidClass, this.validClass]);
12068 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12070 var feedback = this.el.select('.form-control-feedback', true).first();
12073 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12076 if(this.disabled || this.allowBlank){
12080 var label = this.el.select('label', true).first();
12081 var icon = this.el.select('i.fa-star', true).first();
12086 if (Roo.bootstrap.version == 3) {
12087 this.el.addClass(this.validClass);
12089 this.inputEl().addClass('is-valid');
12093 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12095 var feedback = this.el.select('.form-control-feedback', true).first();
12098 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12099 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12104 this.fireEvent('valid', this);
12108 * Mark this field as invalid
12109 * @param {String} msg The validation message
12111 markInvalid : function(msg)
12113 if(!this.el || this.preventMark){ // not rendered
12117 this.el.removeClass([this.invalidClass, this.validClass]);
12118 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12120 var feedback = this.el.select('.form-control-feedback', true).first();
12123 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12126 if(this.disabled || this.allowBlank){
12130 var label = this.el.select('label', true).first();
12131 var icon = this.el.select('i.fa-star', true).first();
12133 if(!this.getValue().length && label && !icon){
12134 this.el.createChild({
12136 cls : 'text-danger fa fa-lg fa-star',
12137 tooltip : 'This field is required',
12138 style : 'margin-right:5px;'
12142 if (Roo.bootstrap.version == 3) {
12143 this.el.addClass(this.invalidClass);
12145 this.inputEl().addClass('is-invalid');
12148 // fixme ... this may be depricated need to test..
12149 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12151 var feedback = this.el.select('.form-control-feedback', true).first();
12154 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12156 if(this.getValue().length || this.forceFeedback){
12157 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12164 this.fireEvent('invalid', this, msg);
12172 * trigger field - base class for combo..
12177 * @class Roo.bootstrap.TriggerField
12178 * @extends Roo.bootstrap.Input
12179 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12180 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12181 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12182 * for which you can provide a custom implementation. For example:
12184 var trigger = new Roo.bootstrap.TriggerField();
12185 trigger.onTriggerClick = myTriggerFn;
12186 trigger.applyTo('my-field');
12189 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12190 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12191 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12192 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12193 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12196 * Create a new TriggerField.
12197 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12198 * to the base TextField)
12200 Roo.bootstrap.TriggerField = function(config){
12201 this.mimicing = false;
12202 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12205 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12207 * @cfg {String} triggerClass A CSS class to apply to the trigger
12210 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12215 * @cfg {Boolean} removable (true|false) special filter default false
12219 /** @cfg {Boolean} grow @hide */
12220 /** @cfg {Number} growMin @hide */
12221 /** @cfg {Number} growMax @hide */
12227 autoSize: Roo.emptyFn,
12231 deferHeight : true,
12234 actionMode : 'wrap',
12239 getAutoCreate : function(){
12241 var align = this.labelAlign || this.parentLabelAlign();
12246 cls: 'form-group' //input-group
12253 type : this.inputType,
12254 cls : 'form-control',
12255 autocomplete: 'new-password',
12256 placeholder : this.placeholder || ''
12260 input.name = this.name;
12263 input.cls += ' input-' + this.size;
12266 if (this.disabled) {
12267 input.disabled=true;
12270 var inputblock = input;
12272 if(this.hasFeedback && !this.allowBlank){
12276 cls: 'glyphicon form-control-feedback'
12279 if(this.removable && !this.editable ){
12281 cls : 'has-feedback',
12287 cls : 'roo-combo-removable-btn close'
12294 cls : 'has-feedback',
12303 if(this.removable && !this.editable ){
12305 cls : 'roo-removable',
12311 cls : 'roo-combo-removable-btn close'
12318 if (this.before || this.after) {
12321 cls : 'input-group',
12325 inputblock.cn.push({
12327 cls : 'input-group-addon input-group-prepend input-group-text',
12332 inputblock.cn.push(input);
12334 if(this.hasFeedback && !this.allowBlank){
12335 inputblock.cls += ' has-feedback';
12336 inputblock.cn.push(feedback);
12340 inputblock.cn.push({
12342 cls : 'input-group-addon input-group-append input-group-text',
12351 var ibwrap = inputblock;
12356 cls: 'roo-select2-choices',
12360 cls: 'roo-select2-search-field',
12372 cls: 'roo-select2-container input-group',
12377 cls: 'form-hidden-field'
12383 if(!this.multiple && this.showToggleBtn){
12389 if (this.caret != false) {
12392 cls: 'fa fa-' + this.caret
12399 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12401 Roo.bootstrap.version == 3 ? caret : '',
12404 cls: 'combobox-clear',
12418 combobox.cls += ' roo-select2-container-multi';
12422 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12423 tooltip : 'This field is required'
12425 if (Roo.bootstrap.version == 4) {
12428 style : 'display:none'
12433 if (align ==='left' && this.fieldLabel.length) {
12435 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12442 cls : 'control-label',
12443 html : this.fieldLabel
12455 var labelCfg = cfg.cn[1];
12456 var contentCfg = cfg.cn[2];
12458 if(this.indicatorpos == 'right'){
12463 cls : 'control-label',
12467 html : this.fieldLabel
12481 labelCfg = cfg.cn[0];
12482 contentCfg = cfg.cn[1];
12485 if(this.labelWidth > 12){
12486 labelCfg.style = "width: " + this.labelWidth + 'px';
12489 if(this.labelWidth < 13 && this.labelmd == 0){
12490 this.labelmd = this.labelWidth;
12493 if(this.labellg > 0){
12494 labelCfg.cls += ' col-lg-' + this.labellg;
12495 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12498 if(this.labelmd > 0){
12499 labelCfg.cls += ' col-md-' + this.labelmd;
12500 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12503 if(this.labelsm > 0){
12504 labelCfg.cls += ' col-sm-' + this.labelsm;
12505 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12508 if(this.labelxs > 0){
12509 labelCfg.cls += ' col-xs-' + this.labelxs;
12510 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12513 } else if ( this.fieldLabel.length) {
12514 // Roo.log(" label");
12519 //cls : 'input-group-addon',
12520 html : this.fieldLabel
12528 if(this.indicatorpos == 'right'){
12536 html : this.fieldLabel
12550 // Roo.log(" no label && no align");
12557 ['xs','sm','md','lg'].map(function(size){
12558 if (settings[size]) {
12559 cfg.cls += ' col-' + size + '-' + settings[size];
12570 onResize : function(w, h){
12571 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12572 // if(typeof w == 'number'){
12573 // var x = w - this.trigger.getWidth();
12574 // this.inputEl().setWidth(this.adjustWidth('input', x));
12575 // this.trigger.setStyle('left', x+'px');
12580 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12583 getResizeEl : function(){
12584 return this.inputEl();
12588 getPositionEl : function(){
12589 return this.inputEl();
12593 alignErrorIcon : function(){
12594 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12598 initEvents : function(){
12602 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12603 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12604 if(!this.multiple && this.showToggleBtn){
12605 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12606 if(this.hideTrigger){
12607 this.trigger.setDisplayed(false);
12609 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12613 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12616 if(this.removable && !this.editable && !this.tickable){
12617 var close = this.closeTriggerEl();
12620 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12621 close.on('click', this.removeBtnClick, this, close);
12625 //this.trigger.addClassOnOver('x-form-trigger-over');
12626 //this.trigger.addClassOnClick('x-form-trigger-click');
12629 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12633 closeTriggerEl : function()
12635 var close = this.el.select('.roo-combo-removable-btn', true).first();
12636 return close ? close : false;
12639 removeBtnClick : function(e, h, el)
12641 e.preventDefault();
12643 if(this.fireEvent("remove", this) !== false){
12645 this.fireEvent("afterremove", this)
12649 createList : function()
12651 this.list = Roo.get(document.body).createChild({
12652 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12653 cls: 'typeahead typeahead-long dropdown-menu shadow',
12654 style: 'display:none'
12657 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12662 initTrigger : function(){
12667 onDestroy : function(){
12669 this.trigger.removeAllListeners();
12670 // this.trigger.remove();
12673 // this.wrap.remove();
12675 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12679 onFocus : function(){
12680 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12682 if(!this.mimicing){
12683 this.wrap.addClass('x-trigger-wrap-focus');
12684 this.mimicing = true;
12685 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12686 if(this.monitorTab){
12687 this.el.on("keydown", this.checkTab, this);
12694 checkTab : function(e){
12695 if(e.getKey() == e.TAB){
12696 this.triggerBlur();
12701 onBlur : function(){
12706 mimicBlur : function(e, t){
12708 if(!this.wrap.contains(t) && this.validateBlur()){
12709 this.triggerBlur();
12715 triggerBlur : function(){
12716 this.mimicing = false;
12717 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12718 if(this.monitorTab){
12719 this.el.un("keydown", this.checkTab, this);
12721 //this.wrap.removeClass('x-trigger-wrap-focus');
12722 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12726 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12727 validateBlur : function(e, t){
12732 onDisable : function(){
12733 this.inputEl().dom.disabled = true;
12734 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12736 // this.wrap.addClass('x-item-disabled');
12741 onEnable : function(){
12742 this.inputEl().dom.disabled = false;
12743 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12745 // this.el.removeClass('x-item-disabled');
12750 onShow : function(){
12751 var ae = this.getActionEl();
12754 ae.dom.style.display = '';
12755 ae.dom.style.visibility = 'visible';
12761 onHide : function(){
12762 var ae = this.getActionEl();
12763 ae.dom.style.display = 'none';
12767 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12768 * by an implementing function.
12770 * @param {EventObject} e
12772 onTriggerClick : Roo.emptyFn
12780 * @class Roo.bootstrap.CardUploader
12781 * @extends Roo.bootstrap.Button
12782 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12783 * @cfg {Number} errorTimeout default 3000
12784 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12785 * @cfg {Array} html The button text.
12789 * Create a new CardUploader
12790 * @param {Object} config The config object
12793 Roo.bootstrap.CardUploader = function(config){
12797 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12800 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12808 * When a image is clicked on - and needs to display a slideshow or similar..
12809 * @param {Roo.bootstrap.Card} this
12810 * @param {Object} The image information data
12816 * When a the download link is clicked
12817 * @param {Roo.bootstrap.Card} this
12818 * @param {Object} The image information data contains
12825 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12828 errorTimeout : 3000,
12832 fileCollection : false,
12835 getAutoCreate : function()
12839 cls :'form-group' ,
12844 //cls : 'input-group-addon',
12845 html : this.fieldLabel
12853 value : this.value,
12854 cls : 'd-none form-control'
12859 multiple : 'multiple',
12861 cls : 'd-none roo-card-upload-selector'
12865 cls : 'roo-card-uploader-button-container w-100 mb-2'
12868 cls : 'card-columns roo-card-uploader-container'
12878 getChildContainer : function() /// what children are added to.
12880 return this.containerEl;
12883 getButtonContainer : function() /// what children are added to.
12885 return this.el.select(".roo-card-uploader-button-container").first();
12888 initEvents : function()
12891 Roo.bootstrap.Input.prototype.initEvents.call(this);
12895 xns: Roo.bootstrap,
12898 container_method : 'getButtonContainer' ,
12899 html : this.html, // fix changable?
12902 'click' : function(btn, e) {
12911 this.urlAPI = (window.createObjectURL && window) ||
12912 (window.URL && URL.revokeObjectURL && URL) ||
12913 (window.webkitURL && webkitURL);
12918 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12920 this.selectorEl.on('change', this.onFileSelected, this);
12923 this.images.forEach(function(img) {
12926 this.images = false;
12928 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12934 onClick : function(e)
12936 e.preventDefault();
12938 this.selectorEl.dom.click();
12942 onFileSelected : function(e)
12944 e.preventDefault();
12946 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12950 Roo.each(this.selectorEl.dom.files, function(file){
12951 this.addFile(file);
12960 addFile : function(file)
12963 if(typeof(file) === 'string'){
12964 throw "Add file by name?"; // should not happen
12968 if(!file || !this.urlAPI){
12978 var url = _this.urlAPI.createObjectURL( file);
12981 id : Roo.bootstrap.CardUploader.ID--,
12982 is_uploaded : false,
12986 mimetype : file.type,
12994 * addCard - add an Attachment to the uploader
12995 * @param data - the data about the image to upload
12999 title : "Title of file",
13000 is_uploaded : false,
13001 src : "http://.....",
13002 srcfile : { the File upload object },
13003 mimetype : file.type,
13006 .. any other data...
13012 addCard : function (data)
13014 // hidden input element?
13015 // if the file is not an image...
13016 //then we need to use something other that and header_image
13021 xns : Roo.bootstrap,
13022 xtype : 'CardFooter',
13025 xns : Roo.bootstrap,
13031 xns : Roo.bootstrap,
13033 html : String.format("<small>{0}</small>", data.title),
13034 cls : 'col-10 text-left',
13039 click : function() {
13041 t.fireEvent( "download", t, data );
13047 xns : Roo.bootstrap,
13049 style: 'max-height: 28px; ',
13055 click : function() {
13056 t.removeCard(data.id)
13068 var cn = this.addxtype(
13071 xns : Roo.bootstrap,
13074 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13075 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13076 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13081 initEvents : function() {
13082 Roo.bootstrap.Card.prototype.initEvents.call(this);
13084 this.imgEl = this.el.select('.card-img-top').first();
13086 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13087 this.imgEl.set({ 'pointer' : 'cursor' });
13090 this.getCardFooter().addClass('p-1');
13097 // dont' really need ot update items.
13098 // this.items.push(cn);
13099 this.fileCollection.add(cn);
13101 if (!data.srcfile) {
13102 this.updateInput();
13107 var reader = new FileReader();
13108 reader.addEventListener("load", function() {
13109 data.srcdata = reader.result;
13112 reader.readAsDataURL(data.srcfile);
13117 removeCard : function(id)
13120 var card = this.fileCollection.get(id);
13121 card.data.is_deleted = 1;
13122 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13123 //this.fileCollection.remove(card);
13124 //this.items = this.items.filter(function(e) { return e != card });
13125 // dont' really need ot update items.
13126 card.el.dom.parentNode.removeChild(card.el.dom);
13127 this.updateInput();
13133 this.fileCollection.each(function(card) {
13134 if (card.el.dom && card.el.dom.parentNode) {
13135 card.el.dom.parentNode.removeChild(card.el.dom);
13138 this.fileCollection.clear();
13139 this.updateInput();
13142 updateInput : function()
13145 this.fileCollection.each(function(e) {
13149 this.inputEl().dom.value = JSON.stringify(data);
13159 Roo.bootstrap.CardUploader.ID = -1;/*
13161 * Ext JS Library 1.1.1
13162 * Copyright(c) 2006-2007, Ext JS, LLC.
13164 * Originally Released Under LGPL - original licence link has changed is not relivant.
13167 * <script type="text/javascript">
13172 * @class Roo.data.SortTypes
13174 * Defines the default sorting (casting?) comparison functions used when sorting data.
13176 Roo.data.SortTypes = {
13178 * Default sort that does nothing
13179 * @param {Mixed} s The value being converted
13180 * @return {Mixed} The comparison value
13182 none : function(s){
13187 * The regular expression used to strip tags
13191 stripTagsRE : /<\/?[^>]+>/gi,
13194 * Strips all HTML tags to sort on text only
13195 * @param {Mixed} s The value being converted
13196 * @return {String} The comparison value
13198 asText : function(s){
13199 return String(s).replace(this.stripTagsRE, "");
13203 * Strips all HTML tags to sort on text only - Case insensitive
13204 * @param {Mixed} s The value being converted
13205 * @return {String} The comparison value
13207 asUCText : function(s){
13208 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13212 * Case insensitive string
13213 * @param {Mixed} s The value being converted
13214 * @return {String} The comparison value
13216 asUCString : function(s) {
13217 return String(s).toUpperCase();
13222 * @param {Mixed} s The value being converted
13223 * @return {Number} The comparison value
13225 asDate : function(s) {
13229 if(s instanceof Date){
13230 return s.getTime();
13232 return Date.parse(String(s));
13237 * @param {Mixed} s The value being converted
13238 * @return {Float} The comparison value
13240 asFloat : function(s) {
13241 var val = parseFloat(String(s).replace(/,/g, ""));
13250 * @param {Mixed} s The value being converted
13251 * @return {Number} The comparison value
13253 asInt : function(s) {
13254 var val = parseInt(String(s).replace(/,/g, ""));
13262 * Ext JS Library 1.1.1
13263 * Copyright(c) 2006-2007, Ext JS, LLC.
13265 * Originally Released Under LGPL - original licence link has changed is not relivant.
13268 * <script type="text/javascript">
13272 * @class Roo.data.Record
13273 * Instances of this class encapsulate both record <em>definition</em> information, and record
13274 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13275 * to access Records cached in an {@link Roo.data.Store} object.<br>
13277 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13278 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13281 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13283 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13284 * {@link #create}. The parameters are the same.
13285 * @param {Array} data An associative Array of data values keyed by the field name.
13286 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13287 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13288 * not specified an integer id is generated.
13290 Roo.data.Record = function(data, id){
13291 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13296 * Generate a constructor for a specific record layout.
13297 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13298 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13299 * Each field definition object may contain the following properties: <ul>
13300 * <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,
13301 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13302 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13303 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13304 * is being used, then this is a string containing the javascript expression to reference the data relative to
13305 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13306 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13307 * this may be omitted.</p></li>
13308 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13309 * <ul><li>auto (Default, implies no conversion)</li>
13314 * <li>date</li></ul></p></li>
13315 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13316 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13317 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13318 * by the Reader into an object that will be stored in the Record. It is passed the
13319 * following parameters:<ul>
13320 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13322 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13324 * <br>usage:<br><pre><code>
13325 var TopicRecord = Roo.data.Record.create(
13326 {name: 'title', mapping: 'topic_title'},
13327 {name: 'author', mapping: 'username'},
13328 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13329 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13330 {name: 'lastPoster', mapping: 'user2'},
13331 {name: 'excerpt', mapping: 'post_text'}
13334 var myNewRecord = new TopicRecord({
13335 title: 'Do my job please',
13338 lastPost: new Date(),
13339 lastPoster: 'Animal',
13340 excerpt: 'No way dude!'
13342 myStore.add(myNewRecord);
13347 Roo.data.Record.create = function(o){
13348 var f = function(){
13349 f.superclass.constructor.apply(this, arguments);
13351 Roo.extend(f, Roo.data.Record);
13352 var p = f.prototype;
13353 p.fields = new Roo.util.MixedCollection(false, function(field){
13356 for(var i = 0, len = o.length; i < len; i++){
13357 p.fields.add(new Roo.data.Field(o[i]));
13359 f.getField = function(name){
13360 return p.fields.get(name);
13365 Roo.data.Record.AUTO_ID = 1000;
13366 Roo.data.Record.EDIT = 'edit';
13367 Roo.data.Record.REJECT = 'reject';
13368 Roo.data.Record.COMMIT = 'commit';
13370 Roo.data.Record.prototype = {
13372 * Readonly flag - true if this record has been modified.
13381 join : function(store){
13382 this.store = store;
13386 * Set the named field to the specified value.
13387 * @param {String} name The name of the field to set.
13388 * @param {Object} value The value to set the field to.
13390 set : function(name, value){
13391 if(this.data[name] == value){
13395 if(!this.modified){
13396 this.modified = {};
13398 if(typeof this.modified[name] == 'undefined'){
13399 this.modified[name] = this.data[name];
13401 this.data[name] = value;
13402 if(!this.editing && this.store){
13403 this.store.afterEdit(this);
13408 * Get the value of the named field.
13409 * @param {String} name The name of the field to get the value of.
13410 * @return {Object} The value of the field.
13412 get : function(name){
13413 return this.data[name];
13417 beginEdit : function(){
13418 this.editing = true;
13419 this.modified = {};
13423 cancelEdit : function(){
13424 this.editing = false;
13425 delete this.modified;
13429 endEdit : function(){
13430 this.editing = false;
13431 if(this.dirty && this.store){
13432 this.store.afterEdit(this);
13437 * Usually called by the {@link Roo.data.Store} which owns the Record.
13438 * Rejects all changes made to the Record since either creation, or the last commit operation.
13439 * Modified fields are reverted to their original values.
13441 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13442 * of reject operations.
13444 reject : function(){
13445 var m = this.modified;
13447 if(typeof m[n] != "function"){
13448 this.data[n] = m[n];
13451 this.dirty = false;
13452 delete this.modified;
13453 this.editing = false;
13455 this.store.afterReject(this);
13460 * Usually called by the {@link Roo.data.Store} which owns the Record.
13461 * Commits all changes made to the Record since either creation, or the last commit operation.
13463 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13464 * of commit operations.
13466 commit : function(){
13467 this.dirty = false;
13468 delete this.modified;
13469 this.editing = false;
13471 this.store.afterCommit(this);
13476 hasError : function(){
13477 return this.error != null;
13481 clearError : function(){
13486 * Creates a copy of this record.
13487 * @param {String} id (optional) A new record id if you don't want to use this record's id
13490 copy : function(newId) {
13491 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13495 * Ext JS Library 1.1.1
13496 * Copyright(c) 2006-2007, Ext JS, LLC.
13498 * Originally Released Under LGPL - original licence link has changed is not relivant.
13501 * <script type="text/javascript">
13507 * @class Roo.data.Store
13508 * @extends Roo.util.Observable
13509 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13510 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13512 * 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
13513 * has no knowledge of the format of the data returned by the Proxy.<br>
13515 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13516 * instances from the data object. These records are cached and made available through accessor functions.
13518 * Creates a new Store.
13519 * @param {Object} config A config object containing the objects needed for the Store to access data,
13520 * and read the data into Records.
13522 Roo.data.Store = function(config){
13523 this.data = new Roo.util.MixedCollection(false);
13524 this.data.getKey = function(o){
13527 this.baseParams = {};
13529 this.paramNames = {
13534 "multisort" : "_multisort"
13537 if(config && config.data){
13538 this.inlineData = config.data;
13539 delete config.data;
13542 Roo.apply(this, config);
13544 if(this.reader){ // reader passed
13545 this.reader = Roo.factory(this.reader, Roo.data);
13546 this.reader.xmodule = this.xmodule || false;
13547 if(!this.recordType){
13548 this.recordType = this.reader.recordType;
13550 if(this.reader.onMetaChange){
13551 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13555 if(this.recordType){
13556 this.fields = this.recordType.prototype.fields;
13558 this.modified = [];
13562 * @event datachanged
13563 * Fires when the data cache has changed, and a widget which is using this Store
13564 * as a Record cache should refresh its view.
13565 * @param {Store} this
13567 datachanged : true,
13569 * @event metachange
13570 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13571 * @param {Store} this
13572 * @param {Object} meta The JSON metadata
13577 * Fires when Records have been added to the Store
13578 * @param {Store} this
13579 * @param {Roo.data.Record[]} records The array of Records added
13580 * @param {Number} index The index at which the record(s) were added
13585 * Fires when a Record has been removed from the Store
13586 * @param {Store} this
13587 * @param {Roo.data.Record} record The Record that was removed
13588 * @param {Number} index The index at which the record was removed
13593 * Fires when a Record has been updated
13594 * @param {Store} this
13595 * @param {Roo.data.Record} record The Record that was updated
13596 * @param {String} operation The update operation being performed. Value may be one of:
13598 Roo.data.Record.EDIT
13599 Roo.data.Record.REJECT
13600 Roo.data.Record.COMMIT
13606 * Fires when the data cache has been cleared.
13607 * @param {Store} this
13611 * @event beforeload
13612 * Fires before a request is made for a new data object. If the beforeload handler returns false
13613 * the load action will be canceled.
13614 * @param {Store} this
13615 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13619 * @event beforeloadadd
13620 * Fires after a new set of Records has been loaded.
13621 * @param {Store} this
13622 * @param {Roo.data.Record[]} records The Records that were loaded
13623 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13625 beforeloadadd : true,
13628 * Fires after a new set of Records has been loaded, before they are added to the store.
13629 * @param {Store} this
13630 * @param {Roo.data.Record[]} records The Records that were loaded
13631 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13632 * @params {Object} return from reader
13636 * @event loadexception
13637 * Fires if an exception occurs in the Proxy during loading.
13638 * Called with the signature of the Proxy's "loadexception" event.
13639 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13642 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13643 * @param {Object} load options
13644 * @param {Object} jsonData from your request (normally this contains the Exception)
13646 loadexception : true
13650 this.proxy = Roo.factory(this.proxy, Roo.data);
13651 this.proxy.xmodule = this.xmodule || false;
13652 this.relayEvents(this.proxy, ["loadexception"]);
13654 this.sortToggle = {};
13655 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13657 Roo.data.Store.superclass.constructor.call(this);
13659 if(this.inlineData){
13660 this.loadData(this.inlineData);
13661 delete this.inlineData;
13665 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13667 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13668 * without a remote query - used by combo/forms at present.
13672 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13675 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13678 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13679 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13682 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13683 * on any HTTP request
13686 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13689 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13693 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13694 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13696 remoteSort : false,
13699 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13700 * loaded or when a record is removed. (defaults to false).
13702 pruneModifiedRecords : false,
13705 lastOptions : null,
13708 * Add Records to the Store and fires the add event.
13709 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13711 add : function(records){
13712 records = [].concat(records);
13713 for(var i = 0, len = records.length; i < len; i++){
13714 records[i].join(this);
13716 var index = this.data.length;
13717 this.data.addAll(records);
13718 this.fireEvent("add", this, records, index);
13722 * Remove a Record from the Store and fires the remove event.
13723 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13725 remove : function(record){
13726 var index = this.data.indexOf(record);
13727 this.data.removeAt(index);
13729 if(this.pruneModifiedRecords){
13730 this.modified.remove(record);
13732 this.fireEvent("remove", this, record, index);
13736 * Remove all Records from the Store and fires the clear event.
13738 removeAll : function(){
13740 if(this.pruneModifiedRecords){
13741 this.modified = [];
13743 this.fireEvent("clear", this);
13747 * Inserts Records to the Store at the given index and fires the add event.
13748 * @param {Number} index The start index at which to insert the passed Records.
13749 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13751 insert : function(index, records){
13752 records = [].concat(records);
13753 for(var i = 0, len = records.length; i < len; i++){
13754 this.data.insert(index, records[i]);
13755 records[i].join(this);
13757 this.fireEvent("add", this, records, index);
13761 * Get the index within the cache of the passed Record.
13762 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13763 * @return {Number} The index of the passed Record. Returns -1 if not found.
13765 indexOf : function(record){
13766 return this.data.indexOf(record);
13770 * Get the index within the cache of the Record with the passed id.
13771 * @param {String} id The id of the Record to find.
13772 * @return {Number} The index of the Record. Returns -1 if not found.
13774 indexOfId : function(id){
13775 return this.data.indexOfKey(id);
13779 * Get the Record with the specified id.
13780 * @param {String} id The id of the Record to find.
13781 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13783 getById : function(id){
13784 return this.data.key(id);
13788 * Get the Record at the specified index.
13789 * @param {Number} index The index of the Record to find.
13790 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13792 getAt : function(index){
13793 return this.data.itemAt(index);
13797 * Returns a range of Records between specified indices.
13798 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13799 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13800 * @return {Roo.data.Record[]} An array of Records
13802 getRange : function(start, end){
13803 return this.data.getRange(start, end);
13807 storeOptions : function(o){
13808 o = Roo.apply({}, o);
13811 this.lastOptions = o;
13815 * Loads the Record cache from the configured Proxy using the configured Reader.
13817 * If using remote paging, then the first load call must specify the <em>start</em>
13818 * and <em>limit</em> properties in the options.params property to establish the initial
13819 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13821 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13822 * and this call will return before the new data has been loaded. Perform any post-processing
13823 * in a callback function, or in a "load" event handler.</strong>
13825 * @param {Object} options An object containing properties which control loading options:<ul>
13826 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13827 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13828 * passed the following arguments:<ul>
13829 * <li>r : Roo.data.Record[]</li>
13830 * <li>options: Options object from the load call</li>
13831 * <li>success: Boolean success indicator</li></ul></li>
13832 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13833 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13836 load : function(options){
13837 options = options || {};
13838 if(this.fireEvent("beforeload", this, options) !== false){
13839 this.storeOptions(options);
13840 var p = Roo.apply(options.params || {}, this.baseParams);
13841 // if meta was not loaded from remote source.. try requesting it.
13842 if (!this.reader.metaFromRemote) {
13843 p._requestMeta = 1;
13845 if(this.sortInfo && this.remoteSort){
13846 var pn = this.paramNames;
13847 p[pn["sort"]] = this.sortInfo.field;
13848 p[pn["dir"]] = this.sortInfo.direction;
13850 if (this.multiSort) {
13851 var pn = this.paramNames;
13852 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13855 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13860 * Reloads the Record cache from the configured Proxy using the configured Reader and
13861 * the options from the last load operation performed.
13862 * @param {Object} options (optional) An object containing properties which may override the options
13863 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13864 * the most recently used options are reused).
13866 reload : function(options){
13867 this.load(Roo.applyIf(options||{}, this.lastOptions));
13871 // Called as a callback by the Reader during a load operation.
13872 loadRecords : function(o, options, success){
13873 if(!o || success === false){
13874 if(success !== false){
13875 this.fireEvent("load", this, [], options, o);
13877 if(options.callback){
13878 options.callback.call(options.scope || this, [], options, false);
13882 // if data returned failure - throw an exception.
13883 if (o.success === false) {
13884 // show a message if no listener is registered.
13885 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13886 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13888 // loadmask wil be hooked into this..
13889 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13892 var r = o.records, t = o.totalRecords || r.length;
13894 this.fireEvent("beforeloadadd", this, r, options, o);
13896 if(!options || options.add !== true){
13897 if(this.pruneModifiedRecords){
13898 this.modified = [];
13900 for(var i = 0, len = r.length; i < len; i++){
13904 this.data = this.snapshot;
13905 delete this.snapshot;
13908 this.data.addAll(r);
13909 this.totalLength = t;
13911 this.fireEvent("datachanged", this);
13913 this.totalLength = Math.max(t, this.data.length+r.length);
13917 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13919 var e = new Roo.data.Record({});
13921 e.set(this.parent.displayField, this.parent.emptyTitle);
13922 e.set(this.parent.valueField, '');
13927 this.fireEvent("load", this, r, options, o);
13928 if(options.callback){
13929 options.callback.call(options.scope || this, r, options, true);
13935 * Loads data from a passed data block. A Reader which understands the format of the data
13936 * must have been configured in the constructor.
13937 * @param {Object} data The data block from which to read the Records. The format of the data expected
13938 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13939 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13941 loadData : function(o, append){
13942 var r = this.reader.readRecords(o);
13943 this.loadRecords(r, {add: append}, true);
13947 * using 'cn' the nested child reader read the child array into it's child stores.
13948 * @param {Object} rec The record with a 'children array
13950 loadDataFromChildren : function(rec)
13952 this.loadData(this.reader.toLoadData(rec));
13957 * Gets the number of cached records.
13959 * <em>If using paging, this may not be the total size of the dataset. If the data object
13960 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13961 * the data set size</em>
13963 getCount : function(){
13964 return this.data.length || 0;
13968 * Gets the total number of records in the dataset as returned by the server.
13970 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13971 * the dataset size</em>
13973 getTotalCount : function(){
13974 return this.totalLength || 0;
13978 * Returns the sort state of the Store as an object with two properties:
13980 field {String} The name of the field by which the Records are sorted
13981 direction {String} The sort order, "ASC" or "DESC"
13984 getSortState : function(){
13985 return this.sortInfo;
13989 applySort : function(){
13990 if(this.sortInfo && !this.remoteSort){
13991 var s = this.sortInfo, f = s.field;
13992 var st = this.fields.get(f).sortType;
13993 var fn = function(r1, r2){
13994 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13995 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13997 this.data.sort(s.direction, fn);
13998 if(this.snapshot && this.snapshot != this.data){
13999 this.snapshot.sort(s.direction, fn);
14005 * Sets the default sort column and order to be used by the next load operation.
14006 * @param {String} fieldName The name of the field to sort by.
14007 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14009 setDefaultSort : function(field, dir){
14010 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14014 * Sort the Records.
14015 * If remote sorting is used, the sort is performed on the server, and the cache is
14016 * reloaded. If local sorting is used, the cache is sorted internally.
14017 * @param {String} fieldName The name of the field to sort by.
14018 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14020 sort : function(fieldName, dir){
14021 var f = this.fields.get(fieldName);
14023 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14025 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14026 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14031 this.sortToggle[f.name] = dir;
14032 this.sortInfo = {field: f.name, direction: dir};
14033 if(!this.remoteSort){
14035 this.fireEvent("datachanged", this);
14037 this.load(this.lastOptions);
14042 * Calls the specified function for each of the Records in the cache.
14043 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14044 * Returning <em>false</em> aborts and exits the iteration.
14045 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14047 each : function(fn, scope){
14048 this.data.each(fn, scope);
14052 * Gets all records modified since the last commit. Modified records are persisted across load operations
14053 * (e.g., during paging).
14054 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14056 getModifiedRecords : function(){
14057 return this.modified;
14061 createFilterFn : function(property, value, anyMatch){
14062 if(!value.exec){ // not a regex
14063 value = String(value);
14064 if(value.length == 0){
14067 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14069 return function(r){
14070 return value.test(r.data[property]);
14075 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14076 * @param {String} property A field on your records
14077 * @param {Number} start The record index to start at (defaults to 0)
14078 * @param {Number} end The last record index to include (defaults to length - 1)
14079 * @return {Number} The sum
14081 sum : function(property, start, end){
14082 var rs = this.data.items, v = 0;
14083 start = start || 0;
14084 end = (end || end === 0) ? end : rs.length-1;
14086 for(var i = start; i <= end; i++){
14087 v += (rs[i].data[property] || 0);
14093 * Filter the records by a specified property.
14094 * @param {String} field A field on your records
14095 * @param {String/RegExp} value Either a string that the field
14096 * should start with or a RegExp to test against the field
14097 * @param {Boolean} anyMatch True to match any part not just the beginning
14099 filter : function(property, value, anyMatch){
14100 var fn = this.createFilterFn(property, value, anyMatch);
14101 return fn ? this.filterBy(fn) : this.clearFilter();
14105 * Filter by a function. The specified function will be called with each
14106 * record in this data source. If the function returns true the record is included,
14107 * otherwise it is filtered.
14108 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14109 * @param {Object} scope (optional) The scope of the function (defaults to this)
14111 filterBy : function(fn, scope){
14112 this.snapshot = this.snapshot || this.data;
14113 this.data = this.queryBy(fn, scope||this);
14114 this.fireEvent("datachanged", this);
14118 * Query the records by a specified property.
14119 * @param {String} field A field on your records
14120 * @param {String/RegExp} value Either a string that the field
14121 * should start with or a RegExp to test against the field
14122 * @param {Boolean} anyMatch True to match any part not just the beginning
14123 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14125 query : function(property, value, anyMatch){
14126 var fn = this.createFilterFn(property, value, anyMatch);
14127 return fn ? this.queryBy(fn) : this.data.clone();
14131 * Query by a function. The specified function will be called with each
14132 * record in this data source. If the function returns true the record is included
14134 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14135 * @param {Object} scope (optional) The scope of the function (defaults to this)
14136 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14138 queryBy : function(fn, scope){
14139 var data = this.snapshot || this.data;
14140 return data.filterBy(fn, scope||this);
14144 * Collects unique values for a particular dataIndex from this store.
14145 * @param {String} dataIndex The property to collect
14146 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14147 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14148 * @return {Array} An array of the unique values
14150 collect : function(dataIndex, allowNull, bypassFilter){
14151 var d = (bypassFilter === true && this.snapshot) ?
14152 this.snapshot.items : this.data.items;
14153 var v, sv, r = [], l = {};
14154 for(var i = 0, len = d.length; i < len; i++){
14155 v = d[i].data[dataIndex];
14157 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14166 * Revert to a view of the Record cache with no filtering applied.
14167 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14169 clearFilter : function(suppressEvent){
14170 if(this.snapshot && this.snapshot != this.data){
14171 this.data = this.snapshot;
14172 delete this.snapshot;
14173 if(suppressEvent !== true){
14174 this.fireEvent("datachanged", this);
14180 afterEdit : function(record){
14181 if(this.modified.indexOf(record) == -1){
14182 this.modified.push(record);
14184 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14188 afterReject : function(record){
14189 this.modified.remove(record);
14190 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14194 afterCommit : function(record){
14195 this.modified.remove(record);
14196 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14200 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14201 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14203 commitChanges : function(){
14204 var m = this.modified.slice(0);
14205 this.modified = [];
14206 for(var i = 0, len = m.length; i < len; i++){
14212 * Cancel outstanding changes on all changed records.
14214 rejectChanges : function(){
14215 var m = this.modified.slice(0);
14216 this.modified = [];
14217 for(var i = 0, len = m.length; i < len; i++){
14222 onMetaChange : function(meta, rtype, o){
14223 this.recordType = rtype;
14224 this.fields = rtype.prototype.fields;
14225 delete this.snapshot;
14226 this.sortInfo = meta.sortInfo || this.sortInfo;
14227 this.modified = [];
14228 this.fireEvent('metachange', this, this.reader.meta);
14231 moveIndex : function(data, type)
14233 var index = this.indexOf(data);
14235 var newIndex = index + type;
14239 this.insert(newIndex, data);
14244 * Ext JS Library 1.1.1
14245 * Copyright(c) 2006-2007, Ext JS, LLC.
14247 * Originally Released Under LGPL - original licence link has changed is not relivant.
14250 * <script type="text/javascript">
14254 * @class Roo.data.SimpleStore
14255 * @extends Roo.data.Store
14256 * Small helper class to make creating Stores from Array data easier.
14257 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14258 * @cfg {Array} fields An array of field definition objects, or field name strings.
14259 * @cfg {Object} an existing reader (eg. copied from another store)
14260 * @cfg {Array} data The multi-dimensional array of data
14262 * @param {Object} config
14264 Roo.data.SimpleStore = function(config)
14266 Roo.data.SimpleStore.superclass.constructor.call(this, {
14268 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14271 Roo.data.Record.create(config.fields)
14273 proxy : new Roo.data.MemoryProxy(config.data)
14277 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14279 * Ext JS Library 1.1.1
14280 * Copyright(c) 2006-2007, Ext JS, LLC.
14282 * Originally Released Under LGPL - original licence link has changed is not relivant.
14285 * <script type="text/javascript">
14290 * @extends Roo.data.Store
14291 * @class Roo.data.JsonStore
14292 * Small helper class to make creating Stores for JSON data easier. <br/>
14294 var store = new Roo.data.JsonStore({
14295 url: 'get-images.php',
14297 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14300 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14301 * JsonReader and HttpProxy (unless inline data is provided).</b>
14302 * @cfg {Array} fields An array of field definition objects, or field name strings.
14304 * @param {Object} config
14306 Roo.data.JsonStore = function(c){
14307 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14308 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14309 reader: new Roo.data.JsonReader(c, c.fields)
14312 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14314 * Ext JS Library 1.1.1
14315 * Copyright(c) 2006-2007, Ext JS, LLC.
14317 * Originally Released Under LGPL - original licence link has changed is not relivant.
14320 * <script type="text/javascript">
14324 Roo.data.Field = function(config){
14325 if(typeof config == "string"){
14326 config = {name: config};
14328 Roo.apply(this, config);
14331 this.type = "auto";
14334 var st = Roo.data.SortTypes;
14335 // named sortTypes are supported, here we look them up
14336 if(typeof this.sortType == "string"){
14337 this.sortType = st[this.sortType];
14340 // set default sortType for strings and dates
14341 if(!this.sortType){
14344 this.sortType = st.asUCString;
14347 this.sortType = st.asDate;
14350 this.sortType = st.none;
14355 var stripRe = /[\$,%]/g;
14357 // prebuilt conversion function for this field, instead of
14358 // switching every time we're reading a value
14360 var cv, dateFormat = this.dateFormat;
14365 cv = function(v){ return v; };
14368 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14372 return v !== undefined && v !== null && v !== '' ?
14373 parseInt(String(v).replace(stripRe, ""), 10) : '';
14378 return v !== undefined && v !== null && v !== '' ?
14379 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14384 cv = function(v){ return v === true || v === "true" || v == 1; };
14391 if(v instanceof Date){
14395 if(dateFormat == "timestamp"){
14396 return new Date(v*1000);
14398 return Date.parseDate(v, dateFormat);
14400 var parsed = Date.parse(v);
14401 return parsed ? new Date(parsed) : null;
14410 Roo.data.Field.prototype = {
14418 * Ext JS Library 1.1.1
14419 * Copyright(c) 2006-2007, Ext JS, LLC.
14421 * Originally Released Under LGPL - original licence link has changed is not relivant.
14424 * <script type="text/javascript">
14427 // Base class for reading structured data from a data source. This class is intended to be
14428 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14431 * @class Roo.data.DataReader
14432 * Base class for reading structured data from a data source. This class is intended to be
14433 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14436 Roo.data.DataReader = function(meta, recordType){
14440 this.recordType = recordType instanceof Array ?
14441 Roo.data.Record.create(recordType) : recordType;
14444 Roo.data.DataReader.prototype = {
14447 readerType : 'Data',
14449 * Create an empty record
14450 * @param {Object} data (optional) - overlay some values
14451 * @return {Roo.data.Record} record created.
14453 newRow : function(d) {
14455 this.recordType.prototype.fields.each(function(c) {
14457 case 'int' : da[c.name] = 0; break;
14458 case 'date' : da[c.name] = new Date(); break;
14459 case 'float' : da[c.name] = 0.0; break;
14460 case 'boolean' : da[c.name] = false; break;
14461 default : da[c.name] = ""; break;
14465 return new this.recordType(Roo.apply(da, d));
14471 * Ext JS Library 1.1.1
14472 * Copyright(c) 2006-2007, Ext JS, LLC.
14474 * Originally Released Under LGPL - original licence link has changed is not relivant.
14477 * <script type="text/javascript">
14481 * @class Roo.data.DataProxy
14482 * @extends Roo.data.Observable
14483 * This class is an abstract base class for implementations which provide retrieval of
14484 * unformatted data objects.<br>
14486 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14487 * (of the appropriate type which knows how to parse the data object) to provide a block of
14488 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14490 * Custom implementations must implement the load method as described in
14491 * {@link Roo.data.HttpProxy#load}.
14493 Roo.data.DataProxy = function(){
14496 * @event beforeload
14497 * Fires before a network request is made to retrieve a data object.
14498 * @param {Object} This DataProxy object.
14499 * @param {Object} params The params parameter to the load function.
14504 * Fires before the load method's callback is called.
14505 * @param {Object} This DataProxy object.
14506 * @param {Object} o The data object.
14507 * @param {Object} arg The callback argument object passed to the load function.
14511 * @event loadexception
14512 * Fires if an Exception occurs during data retrieval.
14513 * @param {Object} This DataProxy object.
14514 * @param {Object} o The data object.
14515 * @param {Object} arg The callback argument object passed to the load function.
14516 * @param {Object} e The Exception.
14518 loadexception : true
14520 Roo.data.DataProxy.superclass.constructor.call(this);
14523 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14526 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14530 * Ext JS Library 1.1.1
14531 * Copyright(c) 2006-2007, Ext JS, LLC.
14533 * Originally Released Under LGPL - original licence link has changed is not relivant.
14536 * <script type="text/javascript">
14539 * @class Roo.data.MemoryProxy
14540 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14541 * to the Reader when its load method is called.
14543 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14545 Roo.data.MemoryProxy = function(data){
14549 Roo.data.MemoryProxy.superclass.constructor.call(this);
14553 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14556 * Load data from the requested source (in this case an in-memory
14557 * data object passed to the constructor), read the data object into
14558 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14559 * process that block using the passed callback.
14560 * @param {Object} params This parameter is not used by the MemoryProxy class.
14561 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14562 * object into a block of Roo.data.Records.
14563 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14564 * The function must be passed <ul>
14565 * <li>The Record block object</li>
14566 * <li>The "arg" argument from the load function</li>
14567 * <li>A boolean success indicator</li>
14569 * @param {Object} scope The scope in which to call the callback
14570 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14572 load : function(params, reader, callback, scope, arg){
14573 params = params || {};
14576 result = reader.readRecords(params.data ? params.data :this.data);
14578 this.fireEvent("loadexception", this, arg, null, e);
14579 callback.call(scope, null, arg, false);
14582 callback.call(scope, result, arg, true);
14586 update : function(params, records){
14591 * Ext JS Library 1.1.1
14592 * Copyright(c) 2006-2007, Ext JS, LLC.
14594 * Originally Released Under LGPL - original licence link has changed is not relivant.
14597 * <script type="text/javascript">
14600 * @class Roo.data.HttpProxy
14601 * @extends Roo.data.DataProxy
14602 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14603 * configured to reference a certain URL.<br><br>
14605 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14606 * from which the running page was served.<br><br>
14608 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14610 * Be aware that to enable the browser to parse an XML document, the server must set
14611 * the Content-Type header in the HTTP response to "text/xml".
14613 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14614 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14615 * will be used to make the request.
14617 Roo.data.HttpProxy = function(conn){
14618 Roo.data.HttpProxy.superclass.constructor.call(this);
14619 // is conn a conn config or a real conn?
14621 this.useAjax = !conn || !conn.events;
14625 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14626 // thse are take from connection...
14629 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14632 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14633 * extra parameters to each request made by this object. (defaults to undefined)
14636 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14637 * to each request made by this object. (defaults to undefined)
14640 * @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)
14643 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14646 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14652 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14656 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14657 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14658 * a finer-grained basis than the DataProxy events.
14660 getConnection : function(){
14661 return this.useAjax ? Roo.Ajax : this.conn;
14665 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14666 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14667 * process that block using the passed callback.
14668 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14669 * for the request to the remote server.
14670 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14671 * object into a block of Roo.data.Records.
14672 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14673 * The function must be passed <ul>
14674 * <li>The Record block object</li>
14675 * <li>The "arg" argument from the load function</li>
14676 * <li>A boolean success indicator</li>
14678 * @param {Object} scope The scope in which to call the callback
14679 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14681 load : function(params, reader, callback, scope, arg){
14682 if(this.fireEvent("beforeload", this, params) !== false){
14684 params : params || {},
14686 callback : callback,
14691 callback : this.loadResponse,
14695 Roo.applyIf(o, this.conn);
14696 if(this.activeRequest){
14697 Roo.Ajax.abort(this.activeRequest);
14699 this.activeRequest = Roo.Ajax.request(o);
14701 this.conn.request(o);
14704 callback.call(scope||this, null, arg, false);
14709 loadResponse : function(o, success, response){
14710 delete this.activeRequest;
14712 this.fireEvent("loadexception", this, o, response);
14713 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14718 result = o.reader.read(response);
14720 this.fireEvent("loadexception", this, o, response, e);
14721 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14725 this.fireEvent("load", this, o, o.request.arg);
14726 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14730 update : function(dataSet){
14735 updateResponse : function(dataSet){
14740 * Ext JS Library 1.1.1
14741 * Copyright(c) 2006-2007, Ext JS, LLC.
14743 * Originally Released Under LGPL - original licence link has changed is not relivant.
14746 * <script type="text/javascript">
14750 * @class Roo.data.ScriptTagProxy
14751 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14752 * other than the originating domain of the running page.<br><br>
14754 * <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
14755 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14757 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14758 * source code that is used as the source inside a <script> tag.<br><br>
14760 * In order for the browser to process the returned data, the server must wrap the data object
14761 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14762 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14763 * depending on whether the callback name was passed:
14766 boolean scriptTag = false;
14767 String cb = request.getParameter("callback");
14770 response.setContentType("text/javascript");
14772 response.setContentType("application/x-json");
14774 Writer out = response.getWriter();
14776 out.write(cb + "(");
14778 out.print(dataBlock.toJsonString());
14785 * @param {Object} config A configuration object.
14787 Roo.data.ScriptTagProxy = function(config){
14788 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14789 Roo.apply(this, config);
14790 this.head = document.getElementsByTagName("head")[0];
14793 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14795 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14797 * @cfg {String} url The URL from which to request the data object.
14800 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14804 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14805 * the server the name of the callback function set up by the load call to process the returned data object.
14806 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14807 * javascript output which calls this named function passing the data object as its only parameter.
14809 callbackParam : "callback",
14811 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14812 * name to the request.
14817 * Load data from the configured URL, read the data object into
14818 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14819 * process that block using the passed callback.
14820 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14821 * for the request to the remote server.
14822 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14823 * object into a block of Roo.data.Records.
14824 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14825 * The function must be passed <ul>
14826 * <li>The Record block object</li>
14827 * <li>The "arg" argument from the load function</li>
14828 * <li>A boolean success indicator</li>
14830 * @param {Object} scope The scope in which to call the callback
14831 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14833 load : function(params, reader, callback, scope, arg){
14834 if(this.fireEvent("beforeload", this, params) !== false){
14836 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14838 var url = this.url;
14839 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14841 url += "&_dc=" + (new Date().getTime());
14843 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14846 cb : "stcCallback"+transId,
14847 scriptId : "stcScript"+transId,
14851 callback : callback,
14857 window[trans.cb] = function(o){
14858 conn.handleResponse(o, trans);
14861 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14863 if(this.autoAbort !== false){
14867 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14869 var script = document.createElement("script");
14870 script.setAttribute("src", url);
14871 script.setAttribute("type", "text/javascript");
14872 script.setAttribute("id", trans.scriptId);
14873 this.head.appendChild(script);
14875 this.trans = trans;
14877 callback.call(scope||this, null, arg, false);
14882 isLoading : function(){
14883 return this.trans ? true : false;
14887 * Abort the current server request.
14889 abort : function(){
14890 if(this.isLoading()){
14891 this.destroyTrans(this.trans);
14896 destroyTrans : function(trans, isLoaded){
14897 this.head.removeChild(document.getElementById(trans.scriptId));
14898 clearTimeout(trans.timeoutId);
14900 window[trans.cb] = undefined;
14902 delete window[trans.cb];
14905 // if hasn't been loaded, wait for load to remove it to prevent script error
14906 window[trans.cb] = function(){
14907 window[trans.cb] = undefined;
14909 delete window[trans.cb];
14916 handleResponse : function(o, trans){
14917 this.trans = false;
14918 this.destroyTrans(trans, true);
14921 result = trans.reader.readRecords(o);
14923 this.fireEvent("loadexception", this, o, trans.arg, e);
14924 trans.callback.call(trans.scope||window, null, trans.arg, false);
14927 this.fireEvent("load", this, o, trans.arg);
14928 trans.callback.call(trans.scope||window, result, trans.arg, true);
14932 handleFailure : function(trans){
14933 this.trans = false;
14934 this.destroyTrans(trans, false);
14935 this.fireEvent("loadexception", this, null, trans.arg);
14936 trans.callback.call(trans.scope||window, null, trans.arg, false);
14940 * Ext JS Library 1.1.1
14941 * Copyright(c) 2006-2007, Ext JS, LLC.
14943 * Originally Released Under LGPL - original licence link has changed is not relivant.
14946 * <script type="text/javascript">
14950 * @class Roo.data.JsonReader
14951 * @extends Roo.data.DataReader
14952 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14953 * based on mappings in a provided Roo.data.Record constructor.
14955 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14956 * in the reply previously.
14961 var RecordDef = Roo.data.Record.create([
14962 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14963 {name: 'occupation'} // This field will use "occupation" as the mapping.
14965 var myReader = new Roo.data.JsonReader({
14966 totalProperty: "results", // The property which contains the total dataset size (optional)
14967 root: "rows", // The property which contains an Array of row objects
14968 id: "id" // The property within each row object that provides an ID for the record (optional)
14972 * This would consume a JSON file like this:
14974 { 'results': 2, 'rows': [
14975 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14976 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14979 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14980 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14981 * paged from the remote server.
14982 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14983 * @cfg {String} root name of the property which contains the Array of row objects.
14984 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14985 * @cfg {Array} fields Array of field definition objects
14987 * Create a new JsonReader
14988 * @param {Object} meta Metadata configuration options
14989 * @param {Object} recordType Either an Array of field definition objects,
14990 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14992 Roo.data.JsonReader = function(meta, recordType){
14995 // set some defaults:
14996 Roo.applyIf(meta, {
14997 totalProperty: 'total',
14998 successProperty : 'success',
15003 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15005 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15007 readerType : 'Json',
15010 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15011 * Used by Store query builder to append _requestMeta to params.
15014 metaFromRemote : false,
15016 * This method is only used by a DataProxy which has retrieved data from a remote server.
15017 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15018 * @return {Object} data A data block which is used by an Roo.data.Store object as
15019 * a cache of Roo.data.Records.
15021 read : function(response){
15022 var json = response.responseText;
15024 var o = /* eval:var:o */ eval("("+json+")");
15026 throw {message: "JsonReader.read: Json object not found"};
15032 this.metaFromRemote = true;
15033 this.meta = o.metaData;
15034 this.recordType = Roo.data.Record.create(o.metaData.fields);
15035 this.onMetaChange(this.meta, this.recordType, o);
15037 return this.readRecords(o);
15040 // private function a store will implement
15041 onMetaChange : function(meta, recordType, o){
15048 simpleAccess: function(obj, subsc) {
15055 getJsonAccessor: function(){
15057 return function(expr) {
15059 return(re.test(expr))
15060 ? new Function("obj", "return obj." + expr)
15065 return Roo.emptyFn;
15070 * Create a data block containing Roo.data.Records from an XML document.
15071 * @param {Object} o An object which contains an Array of row objects in the property specified
15072 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15073 * which contains the total size of the dataset.
15074 * @return {Object} data A data block which is used by an Roo.data.Store object as
15075 * a cache of Roo.data.Records.
15077 readRecords : function(o){
15079 * After any data loads, the raw JSON data is available for further custom processing.
15083 var s = this.meta, Record = this.recordType,
15084 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15086 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15088 if(s.totalProperty) {
15089 this.getTotal = this.getJsonAccessor(s.totalProperty);
15091 if(s.successProperty) {
15092 this.getSuccess = this.getJsonAccessor(s.successProperty);
15094 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15096 var g = this.getJsonAccessor(s.id);
15097 this.getId = function(rec) {
15099 return (r === undefined || r === "") ? null : r;
15102 this.getId = function(){return null;};
15105 for(var jj = 0; jj < fl; jj++){
15107 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15108 this.ef[jj] = this.getJsonAccessor(map);
15112 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15113 if(s.totalProperty){
15114 var vt = parseInt(this.getTotal(o), 10);
15119 if(s.successProperty){
15120 var vs = this.getSuccess(o);
15121 if(vs === false || vs === 'false'){
15126 for(var i = 0; i < c; i++){
15129 var id = this.getId(n);
15130 for(var j = 0; j < fl; j++){
15132 var v = this.ef[j](n);
15134 Roo.log('missing convert for ' + f.name);
15138 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15140 var record = new Record(values, id);
15142 records[i] = record;
15148 totalRecords : totalRecords
15151 // used when loading children.. @see loadDataFromChildren
15152 toLoadData: function(rec)
15154 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15155 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15156 return { data : data, total : data.length };
15161 * Ext JS Library 1.1.1
15162 * Copyright(c) 2006-2007, Ext JS, LLC.
15164 * Originally Released Under LGPL - original licence link has changed is not relivant.
15167 * <script type="text/javascript">
15171 * @class Roo.data.ArrayReader
15172 * @extends Roo.data.DataReader
15173 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15174 * Each element of that Array represents a row of data fields. The
15175 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15176 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15180 var RecordDef = Roo.data.Record.create([
15181 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15182 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15184 var myReader = new Roo.data.ArrayReader({
15185 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15189 * This would consume an Array like this:
15191 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15195 * Create a new JsonReader
15196 * @param {Object} meta Metadata configuration options.
15197 * @param {Object|Array} recordType Either an Array of field definition objects
15199 * @cfg {Array} fields Array of field definition objects
15200 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15201 * as specified to {@link Roo.data.Record#create},
15202 * or an {@link Roo.data.Record} object
15205 * created using {@link Roo.data.Record#create}.
15207 Roo.data.ArrayReader = function(meta, recordType)
15209 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15212 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15215 * Create a data block containing Roo.data.Records from an XML document.
15216 * @param {Object} o An Array of row objects which represents the dataset.
15217 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15218 * a cache of Roo.data.Records.
15220 readRecords : function(o)
15222 var sid = this.meta ? this.meta.id : null;
15223 var recordType = this.recordType, fields = recordType.prototype.fields;
15226 for(var i = 0; i < root.length; i++){
15229 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15230 for(var j = 0, jlen = fields.length; j < jlen; j++){
15231 var f = fields.items[j];
15232 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15233 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15235 values[f.name] = v;
15237 var record = new recordType(values, id);
15239 records[records.length] = record;
15243 totalRecords : records.length
15246 // used when loading children.. @see loadDataFromChildren
15247 toLoadData: function(rec)
15249 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15250 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15261 * @class Roo.bootstrap.ComboBox
15262 * @extends Roo.bootstrap.TriggerField
15263 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15264 * @cfg {Boolean} append (true|false) default false
15265 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15266 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15267 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15268 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15269 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15270 * @cfg {Boolean} animate default true
15271 * @cfg {Boolean} emptyResultText only for touch device
15272 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15273 * @cfg {String} emptyTitle default ''
15274 * @cfg {Number} width fixed with? experimental
15276 * Create a new ComboBox.
15277 * @param {Object} config Configuration options
15279 Roo.bootstrap.ComboBox = function(config){
15280 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15284 * Fires when the dropdown list is expanded
15285 * @param {Roo.bootstrap.ComboBox} combo This combo box
15290 * Fires when the dropdown list is collapsed
15291 * @param {Roo.bootstrap.ComboBox} combo This combo box
15295 * @event beforeselect
15296 * Fires before a list item is selected. Return false to cancel the selection.
15297 * @param {Roo.bootstrap.ComboBox} combo This combo box
15298 * @param {Roo.data.Record} record The data record returned from the underlying store
15299 * @param {Number} index The index of the selected item in the dropdown list
15301 'beforeselect' : true,
15304 * Fires when a list item is selected
15305 * @param {Roo.bootstrap.ComboBox} combo This combo box
15306 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15307 * @param {Number} index The index of the selected item in the dropdown list
15311 * @event beforequery
15312 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15313 * The event object passed has these properties:
15314 * @param {Roo.bootstrap.ComboBox} combo This combo box
15315 * @param {String} query The query
15316 * @param {Boolean} forceAll true to force "all" query
15317 * @param {Boolean} cancel true to cancel the query
15318 * @param {Object} e The query event object
15320 'beforequery': true,
15323 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15324 * @param {Roo.bootstrap.ComboBox} combo This combo box
15329 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15330 * @param {Roo.bootstrap.ComboBox} combo This combo box
15331 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15336 * Fires when the remove value from the combobox array
15337 * @param {Roo.bootstrap.ComboBox} combo This combo box
15341 * @event afterremove
15342 * Fires when the remove value from the combobox array
15343 * @param {Roo.bootstrap.ComboBox} combo This combo box
15345 'afterremove' : true,
15347 * @event specialfilter
15348 * Fires when specialfilter
15349 * @param {Roo.bootstrap.ComboBox} combo This combo box
15351 'specialfilter' : true,
15354 * Fires when tick the element
15355 * @param {Roo.bootstrap.ComboBox} combo This combo box
15359 * @event touchviewdisplay
15360 * Fires when touch view require special display (default is using displayField)
15361 * @param {Roo.bootstrap.ComboBox} combo This combo box
15362 * @param {Object} cfg set html .
15364 'touchviewdisplay' : true
15369 this.tickItems = [];
15371 this.selectedIndex = -1;
15372 if(this.mode == 'local'){
15373 if(config.queryDelay === undefined){
15374 this.queryDelay = 10;
15376 if(config.minChars === undefined){
15382 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15385 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15386 * rendering into an Roo.Editor, defaults to false)
15389 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15390 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15393 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15396 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15397 * the dropdown list (defaults to undefined, with no header element)
15401 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15405 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15407 listWidth: undefined,
15409 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15410 * mode = 'remote' or 'text' if mode = 'local')
15412 displayField: undefined,
15415 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15416 * mode = 'remote' or 'value' if mode = 'local').
15417 * Note: use of a valueField requires the user make a selection
15418 * in order for a value to be mapped.
15420 valueField: undefined,
15422 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15427 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15428 * field's data value (defaults to the underlying DOM element's name)
15430 hiddenName: undefined,
15432 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15436 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15438 selectedClass: 'active',
15441 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15445 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15446 * anchor positions (defaults to 'tl-bl')
15448 listAlign: 'tl-bl?',
15450 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15454 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15455 * query specified by the allQuery config option (defaults to 'query')
15457 triggerAction: 'query',
15459 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15460 * (defaults to 4, does not apply if editable = false)
15464 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15465 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15469 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15470 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15474 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15475 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15479 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15480 * when editable = true (defaults to false)
15482 selectOnFocus:false,
15484 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15486 queryParam: 'query',
15488 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15489 * when mode = 'remote' (defaults to 'Loading...')
15491 loadingText: 'Loading...',
15493 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15497 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15501 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15502 * traditional select (defaults to true)
15506 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15510 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15514 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15515 * listWidth has a higher value)
15519 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15520 * allow the user to set arbitrary text into the field (defaults to false)
15522 forceSelection:false,
15524 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15525 * if typeAhead = true (defaults to 250)
15527 typeAheadDelay : 250,
15529 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15530 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15532 valueNotFoundText : undefined,
15534 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15536 blockFocus : false,
15539 * @cfg {Boolean} disableClear Disable showing of clear button.
15541 disableClear : false,
15543 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15545 alwaysQuery : false,
15548 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15553 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15555 invalidClass : "has-warning",
15558 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15560 validClass : "has-success",
15563 * @cfg {Boolean} specialFilter (true|false) special filter default false
15565 specialFilter : false,
15568 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15570 mobileTouchView : true,
15573 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15575 useNativeIOS : false,
15578 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15580 mobile_restrict_height : false,
15582 ios_options : false,
15594 btnPosition : 'right',
15595 triggerList : true,
15596 showToggleBtn : true,
15598 emptyResultText: 'Empty',
15599 triggerText : 'Select',
15603 // element that contains real text value.. (when hidden is used..)
15605 getAutoCreate : function()
15610 * Render classic select for iso
15613 if(Roo.isIOS && this.useNativeIOS){
15614 cfg = this.getAutoCreateNativeIOS();
15622 if(Roo.isTouch && this.mobileTouchView){
15623 cfg = this.getAutoCreateTouchView();
15630 if(!this.tickable){
15631 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15636 * ComboBox with tickable selections
15639 var align = this.labelAlign || this.parentLabelAlign();
15642 cls : 'form-group roo-combobox-tickable' //input-group
15645 var btn_text_select = '';
15646 var btn_text_done = '';
15647 var btn_text_cancel = '';
15649 if (this.btn_text_show) {
15650 btn_text_select = 'Select';
15651 btn_text_done = 'Done';
15652 btn_text_cancel = 'Cancel';
15657 cls : 'tickable-buttons',
15662 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15663 //html : this.triggerText
15664 html: btn_text_select
15670 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15672 html: btn_text_done
15678 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15680 html: btn_text_cancel
15686 buttons.cn.unshift({
15688 cls: 'roo-select2-search-field-input'
15694 Roo.each(buttons.cn, function(c){
15696 c.cls += ' btn-' + _this.size;
15699 if (_this.disabled) {
15706 style : 'display: contents',
15711 cls: 'form-hidden-field'
15715 cls: 'roo-select2-choices',
15719 cls: 'roo-select2-search-field',
15730 cls: 'roo-select2-container input-group roo-select2-container-multi',
15736 // cls: 'typeahead typeahead-long dropdown-menu',
15737 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15742 if(this.hasFeedback && !this.allowBlank){
15746 cls: 'glyphicon form-control-feedback'
15749 combobox.cn.push(feedback);
15756 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15757 tooltip : 'This field is required'
15759 if (Roo.bootstrap.version == 4) {
15762 style : 'display:none'
15765 if (align ==='left' && this.fieldLabel.length) {
15767 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15774 cls : 'control-label col-form-label',
15775 html : this.fieldLabel
15787 var labelCfg = cfg.cn[1];
15788 var contentCfg = cfg.cn[2];
15791 if(this.indicatorpos == 'right'){
15797 cls : 'control-label col-form-label',
15801 html : this.fieldLabel
15817 labelCfg = cfg.cn[0];
15818 contentCfg = cfg.cn[1];
15822 if(this.labelWidth > 12){
15823 labelCfg.style = "width: " + this.labelWidth + 'px';
15825 if(this.width * 1 > 0){
15826 contentCfg.style = "width: " + this.width + 'px';
15828 if(this.labelWidth < 13 && this.labelmd == 0){
15829 this.labelmd = this.labelWidth;
15832 if(this.labellg > 0){
15833 labelCfg.cls += ' col-lg-' + this.labellg;
15834 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15837 if(this.labelmd > 0){
15838 labelCfg.cls += ' col-md-' + this.labelmd;
15839 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15842 if(this.labelsm > 0){
15843 labelCfg.cls += ' col-sm-' + this.labelsm;
15844 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15847 if(this.labelxs > 0){
15848 labelCfg.cls += ' col-xs-' + this.labelxs;
15849 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15853 } else if ( this.fieldLabel.length) {
15854 // Roo.log(" label");
15859 //cls : 'input-group-addon',
15860 html : this.fieldLabel
15865 if(this.indicatorpos == 'right'){
15869 //cls : 'input-group-addon',
15870 html : this.fieldLabel
15880 // Roo.log(" no label && no align");
15887 ['xs','sm','md','lg'].map(function(size){
15888 if (settings[size]) {
15889 cfg.cls += ' col-' + size + '-' + settings[size];
15897 _initEventsCalled : false,
15900 initEvents: function()
15902 if (this._initEventsCalled) { // as we call render... prevent looping...
15905 this._initEventsCalled = true;
15908 throw "can not find store for combo";
15911 this.indicator = this.indicatorEl();
15913 this.store = Roo.factory(this.store, Roo.data);
15914 this.store.parent = this;
15916 // if we are building from html. then this element is so complex, that we can not really
15917 // use the rendered HTML.
15918 // so we have to trash and replace the previous code.
15919 if (Roo.XComponent.build_from_html) {
15920 // remove this element....
15921 var e = this.el.dom, k=0;
15922 while (e ) { e = e.previousSibling; ++k;}
15927 this.rendered = false;
15929 this.render(this.parent().getChildContainer(true), k);
15932 if(Roo.isIOS && this.useNativeIOS){
15933 this.initIOSView();
15941 if(Roo.isTouch && this.mobileTouchView){
15942 this.initTouchView();
15947 this.initTickableEvents();
15951 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15953 if(this.hiddenName){
15955 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15957 this.hiddenField.dom.value =
15958 this.hiddenValue !== undefined ? this.hiddenValue :
15959 this.value !== undefined ? this.value : '';
15961 // prevent input submission
15962 this.el.dom.removeAttribute('name');
15963 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15968 // this.el.dom.setAttribute('autocomplete', 'off');
15971 var cls = 'x-combo-list';
15973 //this.list = new Roo.Layer({
15974 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15980 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15981 _this.list.setWidth(lw);
15984 this.list.on('mouseover', this.onViewOver, this);
15985 this.list.on('mousemove', this.onViewMove, this);
15986 this.list.on('scroll', this.onViewScroll, this);
15989 this.list.swallowEvent('mousewheel');
15990 this.assetHeight = 0;
15993 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15994 this.assetHeight += this.header.getHeight();
15997 this.innerList = this.list.createChild({cls:cls+'-inner'});
15998 this.innerList.on('mouseover', this.onViewOver, this);
15999 this.innerList.on('mousemove', this.onViewMove, this);
16000 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16002 if(this.allowBlank && !this.pageSize && !this.disableClear){
16003 this.footer = this.list.createChild({cls:cls+'-ft'});
16004 this.pageTb = new Roo.Toolbar(this.footer);
16008 this.footer = this.list.createChild({cls:cls+'-ft'});
16009 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16010 {pageSize: this.pageSize});
16014 if (this.pageTb && this.allowBlank && !this.disableClear) {
16016 this.pageTb.add(new Roo.Toolbar.Fill(), {
16017 cls: 'x-btn-icon x-btn-clear',
16019 handler: function()
16022 _this.clearValue();
16023 _this.onSelect(false, -1);
16028 this.assetHeight += this.footer.getHeight();
16033 this.tpl = Roo.bootstrap.version == 4 ?
16034 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16035 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16038 this.view = new Roo.View(this.list, this.tpl, {
16039 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16041 //this.view.wrapEl.setDisplayed(false);
16042 this.view.on('click', this.onViewClick, this);
16045 this.store.on('beforeload', this.onBeforeLoad, this);
16046 this.store.on('load', this.onLoad, this);
16047 this.store.on('loadexception', this.onLoadException, this);
16049 if(this.resizable){
16050 this.resizer = new Roo.Resizable(this.list, {
16051 pinned:true, handles:'se'
16053 this.resizer.on('resize', function(r, w, h){
16054 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16055 this.listWidth = w;
16056 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16057 this.restrictHeight();
16059 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16062 if(!this.editable){
16063 this.editable = true;
16064 this.setEditable(false);
16069 if (typeof(this.events.add.listeners) != 'undefined') {
16071 this.addicon = this.wrap.createChild(
16072 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16074 this.addicon.on('click', function(e) {
16075 this.fireEvent('add', this);
16078 if (typeof(this.events.edit.listeners) != 'undefined') {
16080 this.editicon = this.wrap.createChild(
16081 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16082 if (this.addicon) {
16083 this.editicon.setStyle('margin-left', '40px');
16085 this.editicon.on('click', function(e) {
16087 // we fire even if inothing is selected..
16088 this.fireEvent('edit', this, this.lastData );
16094 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16095 "up" : function(e){
16096 this.inKeyMode = true;
16100 "down" : function(e){
16101 if(!this.isExpanded()){
16102 this.onTriggerClick();
16104 this.inKeyMode = true;
16109 "enter" : function(e){
16110 // this.onViewClick();
16114 if(this.fireEvent("specialkey", this, e)){
16115 this.onViewClick(false);
16121 "esc" : function(e){
16125 "tab" : function(e){
16128 if(this.fireEvent("specialkey", this, e)){
16129 this.onViewClick(false);
16137 doRelay : function(foo, bar, hname){
16138 if(hname == 'down' || this.scope.isExpanded()){
16139 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16148 this.queryDelay = Math.max(this.queryDelay || 10,
16149 this.mode == 'local' ? 10 : 250);
16152 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16154 if(this.typeAhead){
16155 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16157 if(this.editable !== false){
16158 this.inputEl().on("keyup", this.onKeyUp, this);
16160 if(this.forceSelection){
16161 this.inputEl().on('blur', this.doForce, this);
16165 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16166 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16170 initTickableEvents: function()
16174 if(this.hiddenName){
16176 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16178 this.hiddenField.dom.value =
16179 this.hiddenValue !== undefined ? this.hiddenValue :
16180 this.value !== undefined ? this.value : '';
16182 // prevent input submission
16183 this.el.dom.removeAttribute('name');
16184 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16189 // this.list = this.el.select('ul.dropdown-menu',true).first();
16191 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16192 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16193 if(this.triggerList){
16194 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16197 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16198 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16200 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16201 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16203 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16204 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16206 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16207 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16208 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16211 this.cancelBtn.hide();
16216 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16217 _this.list.setWidth(lw);
16220 this.list.on('mouseover', this.onViewOver, this);
16221 this.list.on('mousemove', this.onViewMove, this);
16223 this.list.on('scroll', this.onViewScroll, this);
16226 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16227 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16230 this.view = new Roo.View(this.list, this.tpl, {
16235 selectedClass: this.selectedClass
16238 //this.view.wrapEl.setDisplayed(false);
16239 this.view.on('click', this.onViewClick, this);
16243 this.store.on('beforeload', this.onBeforeLoad, this);
16244 this.store.on('load', this.onLoad, this);
16245 this.store.on('loadexception', this.onLoadException, this);
16248 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16249 "up" : function(e){
16250 this.inKeyMode = true;
16254 "down" : function(e){
16255 this.inKeyMode = true;
16259 "enter" : function(e){
16260 if(this.fireEvent("specialkey", this, e)){
16261 this.onViewClick(false);
16267 "esc" : function(e){
16268 this.onTickableFooterButtonClick(e, false, false);
16271 "tab" : function(e){
16272 this.fireEvent("specialkey", this, e);
16274 this.onTickableFooterButtonClick(e, false, false);
16281 doRelay : function(e, fn, key){
16282 if(this.scope.isExpanded()){
16283 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16292 this.queryDelay = Math.max(this.queryDelay || 10,
16293 this.mode == 'local' ? 10 : 250);
16296 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16298 if(this.typeAhead){
16299 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16302 if(this.editable !== false){
16303 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16306 this.indicator = this.indicatorEl();
16308 if(this.indicator){
16309 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16310 this.indicator.hide();
16315 onDestroy : function(){
16317 this.view.setStore(null);
16318 this.view.el.removeAllListeners();
16319 this.view.el.remove();
16320 this.view.purgeListeners();
16323 this.list.dom.innerHTML = '';
16327 this.store.un('beforeload', this.onBeforeLoad, this);
16328 this.store.un('load', this.onLoad, this);
16329 this.store.un('loadexception', this.onLoadException, this);
16331 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16335 fireKey : function(e){
16336 if(e.isNavKeyPress() && !this.list.isVisible()){
16337 this.fireEvent("specialkey", this, e);
16342 onResize: function(w, h)
16346 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16348 // if(typeof w != 'number'){
16349 // // we do not handle it!?!?
16352 // var tw = this.trigger.getWidth();
16353 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16354 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16356 // this.inputEl().setWidth( this.adjustWidth('input', x));
16358 // //this.trigger.setStyle('left', x+'px');
16360 // if(this.list && this.listWidth === undefined){
16361 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16362 // this.list.setWidth(lw);
16363 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16371 * Allow or prevent the user from directly editing the field text. If false is passed,
16372 * the user will only be able to select from the items defined in the dropdown list. This method
16373 * is the runtime equivalent of setting the 'editable' config option at config time.
16374 * @param {Boolean} value True to allow the user to directly edit the field text
16376 setEditable : function(value){
16377 if(value == this.editable){
16380 this.editable = value;
16382 this.inputEl().dom.setAttribute('readOnly', true);
16383 this.inputEl().on('mousedown', this.onTriggerClick, this);
16384 this.inputEl().addClass('x-combo-noedit');
16386 this.inputEl().dom.removeAttribute('readOnly');
16387 this.inputEl().un('mousedown', this.onTriggerClick, this);
16388 this.inputEl().removeClass('x-combo-noedit');
16394 onBeforeLoad : function(combo,opts){
16395 if(!this.hasFocus){
16399 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16401 this.restrictHeight();
16402 this.selectedIndex = -1;
16406 onLoad : function(){
16408 this.hasQuery = false;
16410 if(!this.hasFocus){
16414 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16415 this.loading.hide();
16418 if(this.store.getCount() > 0){
16421 this.restrictHeight();
16422 if(this.lastQuery == this.allQuery){
16423 if(this.editable && !this.tickable){
16424 this.inputEl().dom.select();
16428 !this.selectByValue(this.value, true) &&
16431 !this.store.lastOptions ||
16432 typeof(this.store.lastOptions.add) == 'undefined' ||
16433 this.store.lastOptions.add != true
16436 this.select(0, true);
16439 if(this.autoFocus){
16442 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16443 this.taTask.delay(this.typeAheadDelay);
16447 this.onEmptyResults();
16453 onLoadException : function()
16455 this.hasQuery = false;
16457 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16458 this.loading.hide();
16461 if(this.tickable && this.editable){
16466 // only causes errors at present
16467 //Roo.log(this.store.reader.jsonData);
16468 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16470 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16476 onTypeAhead : function(){
16477 if(this.store.getCount() > 0){
16478 var r = this.store.getAt(0);
16479 var newValue = r.data[this.displayField];
16480 var len = newValue.length;
16481 var selStart = this.getRawValue().length;
16483 if(selStart != len){
16484 this.setRawValue(newValue);
16485 this.selectText(selStart, newValue.length);
16491 onSelect : function(record, index){
16493 if(this.fireEvent('beforeselect', this, record, index) !== false){
16495 this.setFromData(index > -1 ? record.data : false);
16498 this.fireEvent('select', this, record, index);
16503 * Returns the currently selected field value or empty string if no value is set.
16504 * @return {String} value The selected value
16506 getValue : function()
16508 if(Roo.isIOS && this.useNativeIOS){
16509 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16513 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16516 if(this.valueField){
16517 return typeof this.value != 'undefined' ? this.value : '';
16519 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16523 getRawValue : function()
16525 if(Roo.isIOS && this.useNativeIOS){
16526 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16529 var v = this.inputEl().getValue();
16535 * Clears any text/value currently set in the field
16537 clearValue : function(){
16539 if(this.hiddenField){
16540 this.hiddenField.dom.value = '';
16543 this.setRawValue('');
16544 this.lastSelectionText = '';
16545 this.lastData = false;
16547 var close = this.closeTriggerEl();
16558 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16559 * will be displayed in the field. If the value does not match the data value of an existing item,
16560 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16561 * Otherwise the field will be blank (although the value will still be set).
16562 * @param {String} value The value to match
16564 setValue : function(v)
16566 if(Roo.isIOS && this.useNativeIOS){
16567 this.setIOSValue(v);
16577 if(this.valueField){
16578 var r = this.findRecord(this.valueField, v);
16580 text = r.data[this.displayField];
16581 }else if(this.valueNotFoundText !== undefined){
16582 text = this.valueNotFoundText;
16585 this.lastSelectionText = text;
16586 if(this.hiddenField){
16587 this.hiddenField.dom.value = v;
16589 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16592 var close = this.closeTriggerEl();
16595 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16601 * @property {Object} the last set data for the element
16606 * Sets the value of the field based on a object which is related to the record format for the store.
16607 * @param {Object} value the value to set as. or false on reset?
16609 setFromData : function(o){
16616 var dv = ''; // display value
16617 var vv = ''; // value value..
16619 if (this.displayField) {
16620 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16622 // this is an error condition!!!
16623 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16626 if(this.valueField){
16627 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16630 var close = this.closeTriggerEl();
16633 if(dv.length || vv * 1 > 0){
16635 this.blockFocus=true;
16641 if(this.hiddenField){
16642 this.hiddenField.dom.value = vv;
16644 this.lastSelectionText = dv;
16645 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16649 // no hidden field.. - we store the value in 'value', but still display
16650 // display field!!!!
16651 this.lastSelectionText = dv;
16652 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16659 reset : function(){
16660 // overridden so that last data is reset..
16667 this.setValue(this.originalValue);
16668 //this.clearInvalid();
16669 this.lastData = false;
16671 this.view.clearSelections();
16677 findRecord : function(prop, value){
16679 if(this.store.getCount() > 0){
16680 this.store.each(function(r){
16681 if(r.data[prop] == value){
16691 getName: function()
16693 // returns hidden if it's set..
16694 if (!this.rendered) {return ''};
16695 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16699 onViewMove : function(e, t){
16700 this.inKeyMode = false;
16704 onViewOver : function(e, t){
16705 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16708 var item = this.view.findItemFromChild(t);
16711 var index = this.view.indexOf(item);
16712 this.select(index, false);
16717 onViewClick : function(view, doFocus, el, e)
16719 var index = this.view.getSelectedIndexes()[0];
16721 var r = this.store.getAt(index);
16725 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16732 Roo.each(this.tickItems, function(v,k){
16734 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16736 _this.tickItems.splice(k, 1);
16738 if(typeof(e) == 'undefined' && view == false){
16739 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16751 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16752 this.tickItems.push(r.data);
16755 if(typeof(e) == 'undefined' && view == false){
16756 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16763 this.onSelect(r, index);
16765 if(doFocus !== false && !this.blockFocus){
16766 this.inputEl().focus();
16771 restrictHeight : function(){
16772 //this.innerList.dom.style.height = '';
16773 //var inner = this.innerList.dom;
16774 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16775 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16776 //this.list.beginUpdate();
16777 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16778 this.list.alignTo(this.inputEl(), this.listAlign);
16779 this.list.alignTo(this.inputEl(), this.listAlign);
16780 //this.list.endUpdate();
16784 onEmptyResults : function(){
16786 if(this.tickable && this.editable){
16787 this.hasFocus = false;
16788 this.restrictHeight();
16796 * Returns true if the dropdown list is expanded, else false.
16798 isExpanded : function(){
16799 return this.list.isVisible();
16803 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16804 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16805 * @param {String} value The data value of the item to select
16806 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16807 * selected item if it is not currently in view (defaults to true)
16808 * @return {Boolean} True if the value matched an item in the list, else false
16810 selectByValue : function(v, scrollIntoView){
16811 if(v !== undefined && v !== null){
16812 var r = this.findRecord(this.valueField || this.displayField, v);
16814 this.select(this.store.indexOf(r), scrollIntoView);
16822 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16823 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16824 * @param {Number} index The zero-based index of the list item to select
16825 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16826 * selected item if it is not currently in view (defaults to true)
16828 select : function(index, scrollIntoView){
16829 this.selectedIndex = index;
16830 this.view.select(index);
16831 if(scrollIntoView !== false){
16832 var el = this.view.getNode(index);
16834 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16837 this.list.scrollChildIntoView(el, false);
16843 selectNext : function(){
16844 var ct = this.store.getCount();
16846 if(this.selectedIndex == -1){
16848 }else if(this.selectedIndex < ct-1){
16849 this.select(this.selectedIndex+1);
16855 selectPrev : function(){
16856 var ct = this.store.getCount();
16858 if(this.selectedIndex == -1){
16860 }else if(this.selectedIndex != 0){
16861 this.select(this.selectedIndex-1);
16867 onKeyUp : function(e){
16868 if(this.editable !== false && !e.isSpecialKey()){
16869 this.lastKey = e.getKey();
16870 this.dqTask.delay(this.queryDelay);
16875 validateBlur : function(){
16876 return !this.list || !this.list.isVisible();
16880 initQuery : function(){
16882 var v = this.getRawValue();
16884 if(this.tickable && this.editable){
16885 v = this.tickableInputEl().getValue();
16892 doForce : function(){
16893 if(this.inputEl().dom.value.length > 0){
16894 this.inputEl().dom.value =
16895 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16901 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16902 * query allowing the query action to be canceled if needed.
16903 * @param {String} query The SQL query to execute
16904 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16905 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16906 * saved in the current store (defaults to false)
16908 doQuery : function(q, forceAll){
16910 if(q === undefined || q === null){
16915 forceAll: forceAll,
16919 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16924 forceAll = qe.forceAll;
16925 if(forceAll === true || (q.length >= this.minChars)){
16927 this.hasQuery = true;
16929 if(this.lastQuery != q || this.alwaysQuery){
16930 this.lastQuery = q;
16931 if(this.mode == 'local'){
16932 this.selectedIndex = -1;
16934 this.store.clearFilter();
16937 if(this.specialFilter){
16938 this.fireEvent('specialfilter', this);
16943 this.store.filter(this.displayField, q);
16946 this.store.fireEvent("datachanged", this.store);
16953 this.store.baseParams[this.queryParam] = q;
16955 var options = {params : this.getParams(q)};
16958 options.add = true;
16959 options.params.start = this.page * this.pageSize;
16962 this.store.load(options);
16965 * this code will make the page width larger, at the beginning, the list not align correctly,
16966 * we should expand the list on onLoad
16967 * so command out it
16972 this.selectedIndex = -1;
16977 this.loadNext = false;
16981 getParams : function(q){
16983 //p[this.queryParam] = q;
16987 p.limit = this.pageSize;
16993 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16995 collapse : function(){
16996 if(!this.isExpanded()){
17002 this.hasFocus = false;
17006 this.cancelBtn.hide();
17007 this.trigger.show();
17010 this.tickableInputEl().dom.value = '';
17011 this.tickableInputEl().blur();
17016 Roo.get(document).un('mousedown', this.collapseIf, this);
17017 Roo.get(document).un('mousewheel', this.collapseIf, this);
17018 if (!this.editable) {
17019 Roo.get(document).un('keydown', this.listKeyPress, this);
17021 this.fireEvent('collapse', this);
17027 collapseIf : function(e){
17028 var in_combo = e.within(this.el);
17029 var in_list = e.within(this.list);
17030 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17032 if (in_combo || in_list || is_list) {
17033 //e.stopPropagation();
17038 this.onTickableFooterButtonClick(e, false, false);
17046 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17048 expand : function(){
17050 if(this.isExpanded() || !this.hasFocus){
17054 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17055 this.list.setWidth(lw);
17061 this.restrictHeight();
17065 this.tickItems = Roo.apply([], this.item);
17068 this.cancelBtn.show();
17069 this.trigger.hide();
17072 this.tickableInputEl().focus();
17077 Roo.get(document).on('mousedown', this.collapseIf, this);
17078 Roo.get(document).on('mousewheel', this.collapseIf, this);
17079 if (!this.editable) {
17080 Roo.get(document).on('keydown', this.listKeyPress, this);
17083 this.fireEvent('expand', this);
17087 // Implements the default empty TriggerField.onTriggerClick function
17088 onTriggerClick : function(e)
17090 Roo.log('trigger click');
17092 if(this.disabled || !this.triggerList){
17097 this.loadNext = false;
17099 if(this.isExpanded()){
17101 if (!this.blockFocus) {
17102 this.inputEl().focus();
17106 this.hasFocus = true;
17107 if(this.triggerAction == 'all') {
17108 this.doQuery(this.allQuery, true);
17110 this.doQuery(this.getRawValue());
17112 if (!this.blockFocus) {
17113 this.inputEl().focus();
17118 onTickableTriggerClick : function(e)
17125 this.loadNext = false;
17126 this.hasFocus = true;
17128 if(this.triggerAction == 'all') {
17129 this.doQuery(this.allQuery, true);
17131 this.doQuery(this.getRawValue());
17135 onSearchFieldClick : function(e)
17137 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17138 this.onTickableFooterButtonClick(e, false, false);
17142 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17147 this.loadNext = false;
17148 this.hasFocus = true;
17150 if(this.triggerAction == 'all') {
17151 this.doQuery(this.allQuery, true);
17153 this.doQuery(this.getRawValue());
17157 listKeyPress : function(e)
17159 //Roo.log('listkeypress');
17160 // scroll to first matching element based on key pres..
17161 if (e.isSpecialKey()) {
17164 var k = String.fromCharCode(e.getKey()).toUpperCase();
17167 var csel = this.view.getSelectedNodes();
17168 var cselitem = false;
17170 var ix = this.view.indexOf(csel[0]);
17171 cselitem = this.store.getAt(ix);
17172 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17178 this.store.each(function(v) {
17180 // start at existing selection.
17181 if (cselitem.id == v.id) {
17187 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17188 match = this.store.indexOf(v);
17194 if (match === false) {
17195 return true; // no more action?
17198 this.view.select(match);
17199 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17200 sn.scrollIntoView(sn.dom.parentNode, false);
17203 onViewScroll : function(e, t){
17205 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){
17209 this.hasQuery = true;
17211 this.loading = this.list.select('.loading', true).first();
17213 if(this.loading === null){
17214 this.list.createChild({
17216 cls: 'loading roo-select2-more-results roo-select2-active',
17217 html: 'Loading more results...'
17220 this.loading = this.list.select('.loading', true).first();
17222 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17224 this.loading.hide();
17227 this.loading.show();
17232 this.loadNext = true;
17234 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17239 addItem : function(o)
17241 var dv = ''; // display value
17243 if (this.displayField) {
17244 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17246 // this is an error condition!!!
17247 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17254 var choice = this.choices.createChild({
17256 cls: 'roo-select2-search-choice',
17265 cls: 'roo-select2-search-choice-close fa fa-times',
17270 }, this.searchField);
17272 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17274 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17282 this.inputEl().dom.value = '';
17287 onRemoveItem : function(e, _self, o)
17289 e.preventDefault();
17291 this.lastItem = Roo.apply([], this.item);
17293 var index = this.item.indexOf(o.data) * 1;
17296 Roo.log('not this item?!');
17300 this.item.splice(index, 1);
17305 this.fireEvent('remove', this, e);
17311 syncValue : function()
17313 if(!this.item.length){
17320 Roo.each(this.item, function(i){
17321 if(_this.valueField){
17322 value.push(i[_this.valueField]);
17329 this.value = value.join(',');
17331 if(this.hiddenField){
17332 this.hiddenField.dom.value = this.value;
17335 this.store.fireEvent("datachanged", this.store);
17340 clearItem : function()
17342 if(!this.multiple){
17348 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17356 if(this.tickable && !Roo.isTouch){
17357 this.view.refresh();
17361 inputEl: function ()
17363 if(Roo.isIOS && this.useNativeIOS){
17364 return this.el.select('select.roo-ios-select', true).first();
17367 if(Roo.isTouch && this.mobileTouchView){
17368 return this.el.select('input.form-control',true).first();
17372 return this.searchField;
17375 return this.el.select('input.form-control',true).first();
17378 onTickableFooterButtonClick : function(e, btn, el)
17380 e.preventDefault();
17382 this.lastItem = Roo.apply([], this.item);
17384 if(btn && btn.name == 'cancel'){
17385 this.tickItems = Roo.apply([], this.item);
17394 Roo.each(this.tickItems, function(o){
17402 validate : function()
17404 if(this.getVisibilityEl().hasClass('hidden')){
17408 var v = this.getRawValue();
17411 v = this.getValue();
17414 if(this.disabled || this.allowBlank || v.length){
17419 this.markInvalid();
17423 tickableInputEl : function()
17425 if(!this.tickable || !this.editable){
17426 return this.inputEl();
17429 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17433 getAutoCreateTouchView : function()
17438 cls: 'form-group' //input-group
17444 type : this.inputType,
17445 cls : 'form-control x-combo-noedit',
17446 autocomplete: 'new-password',
17447 placeholder : this.placeholder || '',
17452 input.name = this.name;
17456 input.cls += ' input-' + this.size;
17459 if (this.disabled) {
17460 input.disabled = true;
17464 cls : 'roo-combobox-wrap',
17471 inputblock.cls += ' input-group';
17473 inputblock.cn.unshift({
17475 cls : 'input-group-addon input-group-prepend input-group-text',
17480 if(this.removable && !this.multiple){
17481 inputblock.cls += ' roo-removable';
17483 inputblock.cn.push({
17486 cls : 'roo-combo-removable-btn close'
17490 if(this.hasFeedback && !this.allowBlank){
17492 inputblock.cls += ' has-feedback';
17494 inputblock.cn.push({
17496 cls: 'glyphicon form-control-feedback'
17503 inputblock.cls += (this.before) ? '' : ' input-group';
17505 inputblock.cn.push({
17507 cls : 'input-group-addon input-group-append input-group-text',
17513 var ibwrap = inputblock;
17518 cls: 'roo-select2-choices',
17522 cls: 'roo-select2-search-field',
17535 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17540 cls: 'form-hidden-field'
17546 if(!this.multiple && this.showToggleBtn){
17552 if (this.caret != false) {
17555 cls: 'fa fa-' + this.caret
17562 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17564 Roo.bootstrap.version == 3 ? caret : '',
17567 cls: 'combobox-clear',
17581 combobox.cls += ' roo-select2-container-multi';
17584 var align = this.labelAlign || this.parentLabelAlign();
17586 if (align ==='left' && this.fieldLabel.length) {
17591 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17592 tooltip : 'This field is required'
17596 cls : 'control-label col-form-label',
17597 html : this.fieldLabel
17601 cls : 'roo-combobox-wrap ',
17608 var labelCfg = cfg.cn[1];
17609 var contentCfg = cfg.cn[2];
17612 if(this.indicatorpos == 'right'){
17617 cls : 'control-label col-form-label',
17621 html : this.fieldLabel
17625 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17626 tooltip : 'This field is required'
17631 cls : "roo-combobox-wrap ",
17639 labelCfg = cfg.cn[0];
17640 contentCfg = cfg.cn[1];
17645 if(this.labelWidth > 12){
17646 labelCfg.style = "width: " + this.labelWidth + 'px';
17649 if(this.labelWidth < 13 && this.labelmd == 0){
17650 this.labelmd = this.labelWidth;
17653 if(this.labellg > 0){
17654 labelCfg.cls += ' col-lg-' + this.labellg;
17655 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17658 if(this.labelmd > 0){
17659 labelCfg.cls += ' col-md-' + this.labelmd;
17660 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17663 if(this.labelsm > 0){
17664 labelCfg.cls += ' col-sm-' + this.labelsm;
17665 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17668 if(this.labelxs > 0){
17669 labelCfg.cls += ' col-xs-' + this.labelxs;
17670 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17674 } else if ( this.fieldLabel.length) {
17678 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17679 tooltip : 'This field is required'
17683 cls : 'control-label',
17684 html : this.fieldLabel
17695 if(this.indicatorpos == 'right'){
17699 cls : 'control-label',
17700 html : this.fieldLabel,
17704 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17705 tooltip : 'This field is required'
17722 var settings = this;
17724 ['xs','sm','md','lg'].map(function(size){
17725 if (settings[size]) {
17726 cfg.cls += ' col-' + size + '-' + settings[size];
17733 initTouchView : function()
17735 this.renderTouchView();
17737 this.touchViewEl.on('scroll', function(){
17738 this.el.dom.scrollTop = 0;
17741 this.originalValue = this.getValue();
17743 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17745 this.inputEl().on("click", this.showTouchView, this);
17746 if (this.triggerEl) {
17747 this.triggerEl.on("click", this.showTouchView, this);
17751 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17752 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17754 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17756 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17757 this.store.on('load', this.onTouchViewLoad, this);
17758 this.store.on('loadexception', this.onTouchViewLoadException, this);
17760 if(this.hiddenName){
17762 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17764 this.hiddenField.dom.value =
17765 this.hiddenValue !== undefined ? this.hiddenValue :
17766 this.value !== undefined ? this.value : '';
17768 this.el.dom.removeAttribute('name');
17769 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17773 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17774 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17777 if(this.removable && !this.multiple){
17778 var close = this.closeTriggerEl();
17780 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17781 close.on('click', this.removeBtnClick, this, close);
17785 * fix the bug in Safari iOS8
17787 this.inputEl().on("focus", function(e){
17788 document.activeElement.blur();
17791 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17798 renderTouchView : function()
17800 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17801 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17803 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17804 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17806 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17807 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17808 this.touchViewBodyEl.setStyle('overflow', 'auto');
17810 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17811 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17813 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17814 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17818 showTouchView : function()
17824 this.touchViewHeaderEl.hide();
17826 if(this.modalTitle.length){
17827 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17828 this.touchViewHeaderEl.show();
17831 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17832 this.touchViewEl.show();
17834 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17836 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17837 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17839 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17841 if(this.modalTitle.length){
17842 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17845 this.touchViewBodyEl.setHeight(bodyHeight);
17849 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17851 this.touchViewEl.addClass(['in','show']);
17854 if(this._touchViewMask){
17855 Roo.get(document.body).addClass("x-body-masked");
17856 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17857 this._touchViewMask.setStyle('z-index', 10000);
17858 this._touchViewMask.addClass('show');
17861 this.doTouchViewQuery();
17865 hideTouchView : function()
17867 this.touchViewEl.removeClass(['in','show']);
17871 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17873 this.touchViewEl.setStyle('display', 'none');
17876 if(this._touchViewMask){
17877 this._touchViewMask.removeClass('show');
17878 Roo.get(document.body).removeClass("x-body-masked");
17882 setTouchViewValue : function()
17889 Roo.each(this.tickItems, function(o){
17894 this.hideTouchView();
17897 doTouchViewQuery : function()
17906 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17910 if(!this.alwaysQuery || this.mode == 'local'){
17911 this.onTouchViewLoad();
17918 onTouchViewBeforeLoad : function(combo,opts)
17924 onTouchViewLoad : function()
17926 if(this.store.getCount() < 1){
17927 this.onTouchViewEmptyResults();
17931 this.clearTouchView();
17933 var rawValue = this.getRawValue();
17935 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17937 this.tickItems = [];
17939 this.store.data.each(function(d, rowIndex){
17940 var row = this.touchViewListGroup.createChild(template);
17942 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17943 row.addClass(d.data.cls);
17946 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17949 html : d.data[this.displayField]
17952 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17953 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17956 row.removeClass('selected');
17957 if(!this.multiple && this.valueField &&
17958 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17961 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17962 row.addClass('selected');
17965 if(this.multiple && this.valueField &&
17966 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17970 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17971 this.tickItems.push(d.data);
17974 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17978 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17980 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17982 if(this.modalTitle.length){
17983 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17986 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17988 if(this.mobile_restrict_height && listHeight < bodyHeight){
17989 this.touchViewBodyEl.setHeight(listHeight);
17994 if(firstChecked && listHeight > bodyHeight){
17995 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18000 onTouchViewLoadException : function()
18002 this.hideTouchView();
18005 onTouchViewEmptyResults : function()
18007 this.clearTouchView();
18009 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18011 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18015 clearTouchView : function()
18017 this.touchViewListGroup.dom.innerHTML = '';
18020 onTouchViewClick : function(e, el, o)
18022 e.preventDefault();
18025 var rowIndex = o.rowIndex;
18027 var r = this.store.getAt(rowIndex);
18029 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18031 if(!this.multiple){
18032 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18033 c.dom.removeAttribute('checked');
18036 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18038 this.setFromData(r.data);
18040 var close = this.closeTriggerEl();
18046 this.hideTouchView();
18048 this.fireEvent('select', this, r, rowIndex);
18053 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18054 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18055 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18059 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18060 this.addItem(r.data);
18061 this.tickItems.push(r.data);
18065 getAutoCreateNativeIOS : function()
18068 cls: 'form-group' //input-group,
18073 cls : 'roo-ios-select'
18077 combobox.name = this.name;
18080 if (this.disabled) {
18081 combobox.disabled = true;
18084 var settings = this;
18086 ['xs','sm','md','lg'].map(function(size){
18087 if (settings[size]) {
18088 cfg.cls += ' col-' + size + '-' + settings[size];
18098 initIOSView : function()
18100 this.store.on('load', this.onIOSViewLoad, this);
18105 onIOSViewLoad : function()
18107 if(this.store.getCount() < 1){
18111 this.clearIOSView();
18113 if(this.allowBlank) {
18115 var default_text = '-- SELECT --';
18117 if(this.placeholder.length){
18118 default_text = this.placeholder;
18121 if(this.emptyTitle.length){
18122 default_text += ' - ' + this.emptyTitle + ' -';
18125 var opt = this.inputEl().createChild({
18128 html : default_text
18132 o[this.valueField] = 0;
18133 o[this.displayField] = default_text;
18135 this.ios_options.push({
18142 this.store.data.each(function(d, rowIndex){
18146 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18147 html = d.data[this.displayField];
18152 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18153 value = d.data[this.valueField];
18162 if(this.value == d.data[this.valueField]){
18163 option['selected'] = true;
18166 var opt = this.inputEl().createChild(option);
18168 this.ios_options.push({
18175 this.inputEl().on('change', function(){
18176 this.fireEvent('select', this);
18181 clearIOSView: function()
18183 this.inputEl().dom.innerHTML = '';
18185 this.ios_options = [];
18188 setIOSValue: function(v)
18192 if(!this.ios_options){
18196 Roo.each(this.ios_options, function(opts){
18198 opts.el.dom.removeAttribute('selected');
18200 if(opts.data[this.valueField] != v){
18204 opts.el.dom.setAttribute('selected', true);
18210 * @cfg {Boolean} grow
18214 * @cfg {Number} growMin
18218 * @cfg {Number} growMax
18227 Roo.apply(Roo.bootstrap.ComboBox, {
18231 cls: 'modal-header',
18253 cls: 'list-group-item',
18257 cls: 'roo-combobox-list-group-item-value'
18261 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18275 listItemCheckbox : {
18277 cls: 'list-group-item',
18281 cls: 'roo-combobox-list-group-item-value'
18285 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18301 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18306 cls: 'modal-footer',
18314 cls: 'col-xs-6 text-left',
18317 cls: 'btn btn-danger roo-touch-view-cancel',
18323 cls: 'col-xs-6 text-right',
18326 cls: 'btn btn-success roo-touch-view-ok',
18337 Roo.apply(Roo.bootstrap.ComboBox, {
18339 touchViewTemplate : {
18341 cls: 'modal fade roo-combobox-touch-view',
18345 cls: 'modal-dialog',
18346 style : 'position:fixed', // we have to fix position....
18350 cls: 'modal-content',
18352 Roo.bootstrap.ComboBox.header,
18353 Roo.bootstrap.ComboBox.body,
18354 Roo.bootstrap.ComboBox.footer
18363 * Ext JS Library 1.1.1
18364 * Copyright(c) 2006-2007, Ext JS, LLC.
18366 * Originally Released Under LGPL - original licence link has changed is not relivant.
18369 * <script type="text/javascript">
18374 * @extends Roo.util.Observable
18375 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18376 * This class also supports single and multi selection modes. <br>
18377 * Create a data model bound view:
18379 var store = new Roo.data.Store(...);
18381 var view = new Roo.View({
18383 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18385 singleSelect: true,
18386 selectedClass: "ydataview-selected",
18390 // listen for node click?
18391 view.on("click", function(vw, index, node, e){
18392 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18396 dataModel.load("foobar.xml");
18398 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18400 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18401 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18403 * Note: old style constructor is still suported (container, template, config)
18406 * Create a new View
18407 * @param {Object} config The config object
18410 Roo.View = function(config, depreciated_tpl, depreciated_config){
18412 this.parent = false;
18414 if (typeof(depreciated_tpl) == 'undefined') {
18415 // new way.. - universal constructor.
18416 Roo.apply(this, config);
18417 this.el = Roo.get(this.el);
18420 this.el = Roo.get(config);
18421 this.tpl = depreciated_tpl;
18422 Roo.apply(this, depreciated_config);
18424 this.wrapEl = this.el.wrap().wrap();
18425 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18428 if(typeof(this.tpl) == "string"){
18429 this.tpl = new Roo.Template(this.tpl);
18431 // support xtype ctors..
18432 this.tpl = new Roo.factory(this.tpl, Roo);
18436 this.tpl.compile();
18441 * @event beforeclick
18442 * Fires before a click is processed. Returns false to cancel the default action.
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
18448 "beforeclick" : true,
18451 * Fires when a template node is 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
18460 * Fires when a template node is double 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
18468 * @event contextmenu
18469 * Fires when a template node is right clicked.
18470 * @param {Roo.View} this
18471 * @param {Number} index The index of the target node
18472 * @param {HTMLElement} node The target node
18473 * @param {Roo.EventObject} e The raw event object
18475 "contextmenu" : true,
18477 * @event selectionchange
18478 * Fires when the selected nodes change.
18479 * @param {Roo.View} this
18480 * @param {Array} selections Array of the selected nodes
18482 "selectionchange" : true,
18485 * @event beforeselect
18486 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18487 * @param {Roo.View} this
18488 * @param {HTMLElement} node The node to be selected
18489 * @param {Array} selections Array of currently selected nodes
18491 "beforeselect" : true,
18493 * @event preparedata
18494 * Fires on every row to render, to allow you to change the data.
18495 * @param {Roo.View} this
18496 * @param {Object} data to be rendered (change this)
18498 "preparedata" : true
18506 "click": this.onClick,
18507 "dblclick": this.onDblClick,
18508 "contextmenu": this.onContextMenu,
18512 this.selections = [];
18514 this.cmp = new Roo.CompositeElementLite([]);
18516 this.store = Roo.factory(this.store, Roo.data);
18517 this.setStore(this.store, true);
18520 if ( this.footer && this.footer.xtype) {
18522 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18524 this.footer.dataSource = this.store;
18525 this.footer.container = fctr;
18526 this.footer = Roo.factory(this.footer, Roo);
18527 fctr.insertFirst(this.el);
18529 // this is a bit insane - as the paging toolbar seems to detach the el..
18530 // dom.parentNode.parentNode.parentNode
18531 // they get detached?
18535 Roo.View.superclass.constructor.call(this);
18540 Roo.extend(Roo.View, Roo.util.Observable, {
18543 * @cfg {Roo.data.Store} store Data store to load data from.
18548 * @cfg {String|Roo.Element} el The container element.
18553 * @cfg {String|Roo.Template} tpl The template used by this View
18557 * @cfg {String} dataName the named area of the template to use as the data area
18558 * Works with domtemplates roo-name="name"
18562 * @cfg {String} selectedClass The css class to add to selected nodes
18564 selectedClass : "x-view-selected",
18566 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18571 * @cfg {String} text to display on mask (default Loading)
18575 * @cfg {Boolean} multiSelect Allow multiple selection
18577 multiSelect : false,
18579 * @cfg {Boolean} singleSelect Allow single selection
18581 singleSelect: false,
18584 * @cfg {Boolean} toggleSelect - selecting
18586 toggleSelect : false,
18589 * @cfg {Boolean} tickable - selecting
18594 * Returns the element this view is bound to.
18595 * @return {Roo.Element}
18597 getEl : function(){
18598 return this.wrapEl;
18604 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18606 refresh : function(){
18607 //Roo.log('refresh');
18610 // if we are using something like 'domtemplate', then
18611 // the what gets used is:
18612 // t.applySubtemplate(NAME, data, wrapping data..)
18613 // the outer template then get' applied with
18614 // the store 'extra data'
18615 // and the body get's added to the
18616 // roo-name="data" node?
18617 // <span class='roo-tpl-{name}'></span> ?????
18621 this.clearSelections();
18622 this.el.update("");
18624 var records = this.store.getRange();
18625 if(records.length < 1) {
18627 // is this valid?? = should it render a template??
18629 this.el.update(this.emptyText);
18633 if (this.dataName) {
18634 this.el.update(t.apply(this.store.meta)); //????
18635 el = this.el.child('.roo-tpl-' + this.dataName);
18638 for(var i = 0, len = records.length; i < len; i++){
18639 var data = this.prepareData(records[i].data, i, records[i]);
18640 this.fireEvent("preparedata", this, data, i, records[i]);
18642 var d = Roo.apply({}, data);
18645 Roo.apply(d, {'roo-id' : Roo.id()});
18649 Roo.each(this.parent.item, function(item){
18650 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18653 Roo.apply(d, {'roo-data-checked' : 'checked'});
18657 html[html.length] = Roo.util.Format.trim(
18659 t.applySubtemplate(this.dataName, d, this.store.meta) :
18666 el.update(html.join(""));
18667 this.nodes = el.dom.childNodes;
18668 this.updateIndexes(0);
18673 * Function to override to reformat the data that is sent to
18674 * the template for each node.
18675 * DEPRICATED - use the preparedata event handler.
18676 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18677 * a JSON object for an UpdateManager bound view).
18679 prepareData : function(data, index, record)
18681 this.fireEvent("preparedata", this, data, index, record);
18685 onUpdate : function(ds, record){
18686 // Roo.log('on update');
18687 this.clearSelections();
18688 var index = this.store.indexOf(record);
18689 var n = this.nodes[index];
18690 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18691 n.parentNode.removeChild(n);
18692 this.updateIndexes(index, index);
18698 onAdd : function(ds, records, index)
18700 //Roo.log(['on Add', ds, records, index] );
18701 this.clearSelections();
18702 if(this.nodes.length == 0){
18706 var n = this.nodes[index];
18707 for(var i = 0, len = records.length; i < len; i++){
18708 var d = this.prepareData(records[i].data, i, records[i]);
18710 this.tpl.insertBefore(n, d);
18713 this.tpl.append(this.el, d);
18716 this.updateIndexes(index);
18719 onRemove : function(ds, record, index){
18720 // Roo.log('onRemove');
18721 this.clearSelections();
18722 var el = this.dataName ?
18723 this.el.child('.roo-tpl-' + this.dataName) :
18726 el.dom.removeChild(this.nodes[index]);
18727 this.updateIndexes(index);
18731 * Refresh an individual node.
18732 * @param {Number} index
18734 refreshNode : function(index){
18735 this.onUpdate(this.store, this.store.getAt(index));
18738 updateIndexes : function(startIndex, endIndex){
18739 var ns = this.nodes;
18740 startIndex = startIndex || 0;
18741 endIndex = endIndex || ns.length - 1;
18742 for(var i = startIndex; i <= endIndex; i++){
18743 ns[i].nodeIndex = i;
18748 * Changes the data store this view uses and refresh the view.
18749 * @param {Store} store
18751 setStore : function(store, initial){
18752 if(!initial && this.store){
18753 this.store.un("datachanged", this.refresh);
18754 this.store.un("add", this.onAdd);
18755 this.store.un("remove", this.onRemove);
18756 this.store.un("update", this.onUpdate);
18757 this.store.un("clear", this.refresh);
18758 this.store.un("beforeload", this.onBeforeLoad);
18759 this.store.un("load", this.onLoad);
18760 this.store.un("loadexception", this.onLoad);
18764 store.on("datachanged", this.refresh, this);
18765 store.on("add", this.onAdd, this);
18766 store.on("remove", this.onRemove, this);
18767 store.on("update", this.onUpdate, this);
18768 store.on("clear", this.refresh, this);
18769 store.on("beforeload", this.onBeforeLoad, this);
18770 store.on("load", this.onLoad, this);
18771 store.on("loadexception", this.onLoad, this);
18779 * onbeforeLoad - masks the loading area.
18782 onBeforeLoad : function(store,opts)
18784 //Roo.log('onBeforeLoad');
18786 this.el.update("");
18788 this.el.mask(this.mask ? this.mask : "Loading" );
18790 onLoad : function ()
18797 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18798 * @param {HTMLElement} node
18799 * @return {HTMLElement} The template node
18801 findItemFromChild : function(node){
18802 var el = this.dataName ?
18803 this.el.child('.roo-tpl-' + this.dataName,true) :
18806 if(!node || node.parentNode == el){
18809 var p = node.parentNode;
18810 while(p && p != el){
18811 if(p.parentNode == el){
18820 onClick : function(e){
18821 var item = this.findItemFromChild(e.getTarget());
18823 var index = this.indexOf(item);
18824 if(this.onItemClick(item, index, e) !== false){
18825 this.fireEvent("click", this, index, item, e);
18828 this.clearSelections();
18833 onContextMenu : function(e){
18834 var item = this.findItemFromChild(e.getTarget());
18836 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18841 onDblClick : function(e){
18842 var item = this.findItemFromChild(e.getTarget());
18844 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18848 onItemClick : function(item, index, e)
18850 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18853 if (this.toggleSelect) {
18854 var m = this.isSelected(item) ? 'unselect' : 'select';
18857 _t[m](item, true, false);
18860 if(this.multiSelect || this.singleSelect){
18861 if(this.multiSelect && e.shiftKey && this.lastSelection){
18862 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18864 this.select(item, this.multiSelect && e.ctrlKey);
18865 this.lastSelection = item;
18868 if(!this.tickable){
18869 e.preventDefault();
18877 * Get the number of selected nodes.
18880 getSelectionCount : function(){
18881 return this.selections.length;
18885 * Get the currently selected nodes.
18886 * @return {Array} An array of HTMLElements
18888 getSelectedNodes : function(){
18889 return this.selections;
18893 * Get the indexes of the selected nodes.
18896 getSelectedIndexes : function(){
18897 var indexes = [], s = this.selections;
18898 for(var i = 0, len = s.length; i < len; i++){
18899 indexes.push(s[i].nodeIndex);
18905 * Clear all selections
18906 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18908 clearSelections : function(suppressEvent){
18909 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18910 this.cmp.elements = this.selections;
18911 this.cmp.removeClass(this.selectedClass);
18912 this.selections = [];
18913 if(!suppressEvent){
18914 this.fireEvent("selectionchange", this, this.selections);
18920 * Returns true if the passed node is selected
18921 * @param {HTMLElement/Number} node The node or node index
18922 * @return {Boolean}
18924 isSelected : function(node){
18925 var s = this.selections;
18929 node = this.getNode(node);
18930 return s.indexOf(node) !== -1;
18935 * @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
18936 * @param {Boolean} keepExisting (optional) true to keep existing selections
18937 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18939 select : function(nodeInfo, keepExisting, suppressEvent){
18940 if(nodeInfo instanceof Array){
18942 this.clearSelections(true);
18944 for(var i = 0, len = nodeInfo.length; i < len; i++){
18945 this.select(nodeInfo[i], true, true);
18949 var node = this.getNode(nodeInfo);
18950 if(!node || this.isSelected(node)){
18951 return; // already selected.
18954 this.clearSelections(true);
18957 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18958 Roo.fly(node).addClass(this.selectedClass);
18959 this.selections.push(node);
18960 if(!suppressEvent){
18961 this.fireEvent("selectionchange", this, this.selections);
18969 * @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
18970 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18971 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18973 unselect : function(nodeInfo, keepExisting, suppressEvent)
18975 if(nodeInfo instanceof Array){
18976 Roo.each(this.selections, function(s) {
18977 this.unselect(s, nodeInfo);
18981 var node = this.getNode(nodeInfo);
18982 if(!node || !this.isSelected(node)){
18983 //Roo.log("not selected");
18984 return; // not selected.
18988 Roo.each(this.selections, function(s) {
18990 Roo.fly(node).removeClass(this.selectedClass);
18997 this.selections= ns;
18998 this.fireEvent("selectionchange", this, this.selections);
19002 * Gets a template node.
19003 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19004 * @return {HTMLElement} The node or null if it wasn't found
19006 getNode : function(nodeInfo){
19007 if(typeof nodeInfo == "string"){
19008 return document.getElementById(nodeInfo);
19009 }else if(typeof nodeInfo == "number"){
19010 return this.nodes[nodeInfo];
19016 * Gets a range template nodes.
19017 * @param {Number} startIndex
19018 * @param {Number} endIndex
19019 * @return {Array} An array of nodes
19021 getNodes : function(start, end){
19022 var ns = this.nodes;
19023 start = start || 0;
19024 end = typeof end == "undefined" ? ns.length - 1 : end;
19027 for(var i = start; i <= end; i++){
19031 for(var i = start; i >= end; i--){
19039 * Finds the index of the passed node
19040 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19041 * @return {Number} The index of the node or -1
19043 indexOf : function(node){
19044 node = this.getNode(node);
19045 if(typeof node.nodeIndex == "number"){
19046 return node.nodeIndex;
19048 var ns = this.nodes;
19049 for(var i = 0, len = ns.length; i < len; i++){
19060 * based on jquery fullcalendar
19064 Roo.bootstrap = Roo.bootstrap || {};
19066 * @class Roo.bootstrap.Calendar
19067 * @extends Roo.bootstrap.Component
19068 * Bootstrap Calendar class
19069 * @cfg {Boolean} loadMask (true|false) default false
19070 * @cfg {Object} header generate the user specific header of the calendar, default false
19073 * Create a new Container
19074 * @param {Object} config The config object
19079 Roo.bootstrap.Calendar = function(config){
19080 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19084 * Fires when a date is selected
19085 * @param {DatePicker} this
19086 * @param {Date} date The selected date
19090 * @event monthchange
19091 * Fires when the displayed month changes
19092 * @param {DatePicker} this
19093 * @param {Date} date The selected month
19095 'monthchange': true,
19097 * @event evententer
19098 * Fires when mouse over an event
19099 * @param {Calendar} this
19100 * @param {event} Event
19102 'evententer': true,
19104 * @event eventleave
19105 * Fires when the mouse leaves an
19106 * @param {Calendar} this
19109 'eventleave': true,
19111 * @event eventclick
19112 * Fires when the mouse click an
19113 * @param {Calendar} this
19122 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19125 * @cfg {Number} startDay
19126 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19134 getAutoCreate : function(){
19137 var fc_button = function(name, corner, style, content ) {
19138 return Roo.apply({},{
19140 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19142 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19145 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19156 style : 'width:100%',
19163 cls : 'fc-header-left',
19165 fc_button('prev', 'left', 'arrow', '‹' ),
19166 fc_button('next', 'right', 'arrow', '›' ),
19167 { tag: 'span', cls: 'fc-header-space' },
19168 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19176 cls : 'fc-header-center',
19180 cls: 'fc-header-title',
19183 html : 'month / year'
19191 cls : 'fc-header-right',
19193 /* fc_button('month', 'left', '', 'month' ),
19194 fc_button('week', '', '', 'week' ),
19195 fc_button('day', 'right', '', 'day' )
19207 header = this.header;
19210 var cal_heads = function() {
19212 // fixme - handle this.
19214 for (var i =0; i < Date.dayNames.length; i++) {
19215 var d = Date.dayNames[i];
19218 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19219 html : d.substring(0,3)
19223 ret[0].cls += ' fc-first';
19224 ret[6].cls += ' fc-last';
19227 var cal_cell = function(n) {
19230 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19235 cls: 'fc-day-number',
19239 cls: 'fc-day-content',
19243 style: 'position: relative;' // height: 17px;
19255 var cal_rows = function() {
19258 for (var r = 0; r < 6; r++) {
19265 for (var i =0; i < Date.dayNames.length; i++) {
19266 var d = Date.dayNames[i];
19267 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19270 row.cn[0].cls+=' fc-first';
19271 row.cn[0].cn[0].style = 'min-height:90px';
19272 row.cn[6].cls+=' fc-last';
19276 ret[0].cls += ' fc-first';
19277 ret[4].cls += ' fc-prev-last';
19278 ret[5].cls += ' fc-last';
19285 cls: 'fc-border-separate',
19286 style : 'width:100%',
19294 cls : 'fc-first fc-last',
19312 cls : 'fc-content',
19313 style : "position: relative;",
19316 cls : 'fc-view fc-view-month fc-grid',
19317 style : 'position: relative',
19318 unselectable : 'on',
19321 cls : 'fc-event-container',
19322 style : 'position:absolute;z-index:8;top:0;left:0;'
19340 initEvents : function()
19343 throw "can not find store for calendar";
19349 style: "text-align:center",
19353 style: "background-color:white;width:50%;margin:250 auto",
19357 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19368 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19370 var size = this.el.select('.fc-content', true).first().getSize();
19371 this.maskEl.setSize(size.width, size.height);
19372 this.maskEl.enableDisplayMode("block");
19373 if(!this.loadMask){
19374 this.maskEl.hide();
19377 this.store = Roo.factory(this.store, Roo.data);
19378 this.store.on('load', this.onLoad, this);
19379 this.store.on('beforeload', this.onBeforeLoad, this);
19383 this.cells = this.el.select('.fc-day',true);
19384 //Roo.log(this.cells);
19385 this.textNodes = this.el.query('.fc-day-number');
19386 this.cells.addClassOnOver('fc-state-hover');
19388 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19389 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19390 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19391 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19393 this.on('monthchange', this.onMonthChange, this);
19395 this.update(new Date().clearTime());
19398 resize : function() {
19399 var sz = this.el.getSize();
19401 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19402 this.el.select('.fc-day-content div',true).setHeight(34);
19407 showPrevMonth : function(e){
19408 this.update(this.activeDate.add("mo", -1));
19410 showToday : function(e){
19411 this.update(new Date().clearTime());
19414 showNextMonth : function(e){
19415 this.update(this.activeDate.add("mo", 1));
19419 showPrevYear : function(){
19420 this.update(this.activeDate.add("y", -1));
19424 showNextYear : function(){
19425 this.update(this.activeDate.add("y", 1));
19430 update : function(date)
19432 var vd = this.activeDate;
19433 this.activeDate = date;
19434 // if(vd && this.el){
19435 // var t = date.getTime();
19436 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19437 // Roo.log('using add remove');
19439 // this.fireEvent('monthchange', this, date);
19441 // this.cells.removeClass("fc-state-highlight");
19442 // this.cells.each(function(c){
19443 // if(c.dateValue == t){
19444 // c.addClass("fc-state-highlight");
19445 // setTimeout(function(){
19446 // try{c.dom.firstChild.focus();}catch(e){}
19456 var days = date.getDaysInMonth();
19458 var firstOfMonth = date.getFirstDateOfMonth();
19459 var startingPos = firstOfMonth.getDay()-this.startDay;
19461 if(startingPos < this.startDay){
19465 var pm = date.add(Date.MONTH, -1);
19466 var prevStart = pm.getDaysInMonth()-startingPos;
19468 this.cells = this.el.select('.fc-day',true);
19469 this.textNodes = this.el.query('.fc-day-number');
19470 this.cells.addClassOnOver('fc-state-hover');
19472 var cells = this.cells.elements;
19473 var textEls = this.textNodes;
19475 Roo.each(cells, function(cell){
19476 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19479 days += startingPos;
19481 // convert everything to numbers so it's fast
19482 var day = 86400000;
19483 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19486 //Roo.log(prevStart);
19488 var today = new Date().clearTime().getTime();
19489 var sel = date.clearTime().getTime();
19490 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19491 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19492 var ddMatch = this.disabledDatesRE;
19493 var ddText = this.disabledDatesText;
19494 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19495 var ddaysText = this.disabledDaysText;
19496 var format = this.format;
19498 var setCellClass = function(cal, cell){
19502 //Roo.log('set Cell Class');
19504 var t = d.getTime();
19508 cell.dateValue = t;
19510 cell.className += " fc-today";
19511 cell.className += " fc-state-highlight";
19512 cell.title = cal.todayText;
19515 // disable highlight in other month..
19516 //cell.className += " fc-state-highlight";
19521 cell.className = " fc-state-disabled";
19522 cell.title = cal.minText;
19526 cell.className = " fc-state-disabled";
19527 cell.title = cal.maxText;
19531 if(ddays.indexOf(d.getDay()) != -1){
19532 cell.title = ddaysText;
19533 cell.className = " fc-state-disabled";
19536 if(ddMatch && format){
19537 var fvalue = d.dateFormat(format);
19538 if(ddMatch.test(fvalue)){
19539 cell.title = ddText.replace("%0", fvalue);
19540 cell.className = " fc-state-disabled";
19544 if (!cell.initialClassName) {
19545 cell.initialClassName = cell.dom.className;
19548 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19553 for(; i < startingPos; i++) {
19554 textEls[i].innerHTML = (++prevStart);
19555 d.setDate(d.getDate()+1);
19557 cells[i].className = "fc-past fc-other-month";
19558 setCellClass(this, cells[i]);
19563 for(; i < days; i++){
19564 intDay = i - startingPos + 1;
19565 textEls[i].innerHTML = (intDay);
19566 d.setDate(d.getDate()+1);
19568 cells[i].className = ''; // "x-date-active";
19569 setCellClass(this, cells[i]);
19573 for(; i < 42; i++) {
19574 textEls[i].innerHTML = (++extraDays);
19575 d.setDate(d.getDate()+1);
19577 cells[i].className = "fc-future fc-other-month";
19578 setCellClass(this, cells[i]);
19581 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19583 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19585 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19586 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19588 if(totalRows != 6){
19589 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19590 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19593 this.fireEvent('monthchange', this, date);
19597 if(!this.internalRender){
19598 var main = this.el.dom.firstChild;
19599 var w = main.offsetWidth;
19600 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19601 Roo.fly(main).setWidth(w);
19602 this.internalRender = true;
19603 // opera does not respect the auto grow header center column
19604 // then, after it gets a width opera refuses to recalculate
19605 // without a second pass
19606 if(Roo.isOpera && !this.secondPass){
19607 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19608 this.secondPass = true;
19609 this.update.defer(10, this, [date]);
19616 findCell : function(dt) {
19617 dt = dt.clearTime().getTime();
19619 this.cells.each(function(c){
19620 //Roo.log("check " +c.dateValue + '?=' + dt);
19621 if(c.dateValue == dt){
19631 findCells : function(ev) {
19632 var s = ev.start.clone().clearTime().getTime();
19634 var e= ev.end.clone().clearTime().getTime();
19637 this.cells.each(function(c){
19638 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19640 if(c.dateValue > e){
19643 if(c.dateValue < s){
19652 // findBestRow: function(cells)
19656 // for (var i =0 ; i < cells.length;i++) {
19657 // ret = Math.max(cells[i].rows || 0,ret);
19664 addItem : function(ev)
19666 // look for vertical location slot in
19667 var cells = this.findCells(ev);
19669 // ev.row = this.findBestRow(cells);
19671 // work out the location.
19675 for(var i =0; i < cells.length; i++) {
19677 cells[i].row = cells[0].row;
19680 cells[i].row = cells[i].row + 1;
19690 if (crow.start.getY() == cells[i].getY()) {
19692 crow.end = cells[i];
19709 cells[0].events.push(ev);
19711 this.calevents.push(ev);
19714 clearEvents: function() {
19716 if(!this.calevents){
19720 Roo.each(this.cells.elements, function(c){
19726 Roo.each(this.calevents, function(e) {
19727 Roo.each(e.els, function(el) {
19728 el.un('mouseenter' ,this.onEventEnter, this);
19729 el.un('mouseleave' ,this.onEventLeave, this);
19734 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19740 renderEvents: function()
19744 this.cells.each(function(c) {
19753 if(c.row != c.events.length){
19754 r = 4 - (4 - (c.row - c.events.length));
19757 c.events = ev.slice(0, r);
19758 c.more = ev.slice(r);
19760 if(c.more.length && c.more.length == 1){
19761 c.events.push(c.more.pop());
19764 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19768 this.cells.each(function(c) {
19770 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19773 for (var e = 0; e < c.events.length; e++){
19774 var ev = c.events[e];
19775 var rows = ev.rows;
19777 for(var i = 0; i < rows.length; i++) {
19779 // how many rows should it span..
19782 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19783 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19785 unselectable : "on",
19788 cls: 'fc-event-inner',
19792 // cls: 'fc-event-time',
19793 // html : cells.length > 1 ? '' : ev.time
19797 cls: 'fc-event-title',
19798 html : String.format('{0}', ev.title)
19805 cls: 'ui-resizable-handle ui-resizable-e',
19806 html : '  '
19813 cfg.cls += ' fc-event-start';
19815 if ((i+1) == rows.length) {
19816 cfg.cls += ' fc-event-end';
19819 var ctr = _this.el.select('.fc-event-container',true).first();
19820 var cg = ctr.createChild(cfg);
19822 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19823 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19825 var r = (c.more.length) ? 1 : 0;
19826 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19827 cg.setWidth(ebox.right - sbox.x -2);
19829 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19830 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19831 cg.on('click', _this.onEventClick, _this, ev);
19842 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19843 style : 'position: absolute',
19844 unselectable : "on",
19847 cls: 'fc-event-inner',
19851 cls: 'fc-event-title',
19859 cls: 'ui-resizable-handle ui-resizable-e',
19860 html : '  '
19866 var ctr = _this.el.select('.fc-event-container',true).first();
19867 var cg = ctr.createChild(cfg);
19869 var sbox = c.select('.fc-day-content',true).first().getBox();
19870 var ebox = c.select('.fc-day-content',true).first().getBox();
19872 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19873 cg.setWidth(ebox.right - sbox.x -2);
19875 cg.on('click', _this.onMoreEventClick, _this, c.more);
19885 onEventEnter: function (e, el,event,d) {
19886 this.fireEvent('evententer', this, el, event);
19889 onEventLeave: function (e, el,event,d) {
19890 this.fireEvent('eventleave', this, el, event);
19893 onEventClick: function (e, el,event,d) {
19894 this.fireEvent('eventclick', this, el, event);
19897 onMonthChange: function () {
19901 onMoreEventClick: function(e, el, more)
19905 this.calpopover.placement = 'right';
19906 this.calpopover.setTitle('More');
19908 this.calpopover.setContent('');
19910 var ctr = this.calpopover.el.select('.popover-content', true).first();
19912 Roo.each(more, function(m){
19914 cls : 'fc-event-hori fc-event-draggable',
19917 var cg = ctr.createChild(cfg);
19919 cg.on('click', _this.onEventClick, _this, m);
19922 this.calpopover.show(el);
19927 onLoad: function ()
19929 this.calevents = [];
19932 if(this.store.getCount() > 0){
19933 this.store.data.each(function(d){
19936 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19937 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19938 time : d.data.start_time,
19939 title : d.data.title,
19940 description : d.data.description,
19941 venue : d.data.venue
19946 this.renderEvents();
19948 if(this.calevents.length && this.loadMask){
19949 this.maskEl.hide();
19953 onBeforeLoad: function()
19955 this.clearEvents();
19957 this.maskEl.show();
19971 * @class Roo.bootstrap.Popover
19972 * @extends Roo.bootstrap.Component
19973 * Bootstrap Popover class
19974 * @cfg {String} html contents of the popover (or false to use children..)
19975 * @cfg {String} title of popover (or false to hide)
19976 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19977 * @cfg {String} trigger click || hover (or false to trigger manually)
19978 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19979 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19980 * - if false and it has a 'parent' then it will be automatically added to that element
19981 * - if string - Roo.get will be called
19982 * @cfg {Number} delay - delay before showing
19985 * Create a new Popover
19986 * @param {Object} config The config object
19989 Roo.bootstrap.Popover = function(config){
19990 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19996 * After the popover show
19998 * @param {Roo.bootstrap.Popover} this
20003 * After the popover hide
20005 * @param {Roo.bootstrap.Popover} this
20011 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20016 placement : 'right',
20017 trigger : 'hover', // hover
20023 can_build_overlaid : false,
20025 maskEl : false, // the mask element
20028 alignEl : false, // when show is called with an element - this get's stored.
20030 getChildContainer : function()
20032 return this.contentEl;
20035 getPopoverHeader : function()
20037 this.title = true; // flag not to hide it..
20038 this.headerEl.addClass('p-0');
20039 return this.headerEl
20043 getAutoCreate : function(){
20046 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20047 style: 'display:block',
20053 cls : 'popover-inner ',
20057 cls: 'popover-title popover-header',
20058 html : this.title === false ? '' : this.title
20061 cls : 'popover-content popover-body ' + (this.cls || ''),
20062 html : this.html || ''
20073 * @param {string} the title
20075 setTitle: function(str)
20079 this.headerEl.dom.innerHTML = str;
20084 * @param {string} the body content
20086 setContent: function(str)
20089 if (this.contentEl) {
20090 this.contentEl.dom.innerHTML = str;
20094 // as it get's added to the bottom of the page.
20095 onRender : function(ct, position)
20097 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20102 var cfg = Roo.apply({}, this.getAutoCreate());
20106 cfg.cls += ' ' + this.cls;
20109 cfg.style = this.style;
20111 //Roo.log("adding to ");
20112 this.el = Roo.get(document.body).createChild(cfg, position);
20113 // Roo.log(this.el);
20116 this.contentEl = this.el.select('.popover-content',true).first();
20117 this.headerEl = this.el.select('.popover-title',true).first();
20120 if(typeof(this.items) != 'undefined'){
20121 var items = this.items;
20124 for(var i =0;i < items.length;i++) {
20125 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20129 this.items = nitems;
20131 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20132 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20139 resizeMask : function()
20141 this.maskEl.setSize(
20142 Roo.lib.Dom.getViewWidth(true),
20143 Roo.lib.Dom.getViewHeight(true)
20147 initEvents : function()
20151 Roo.bootstrap.Popover.register(this);
20154 this.arrowEl = this.el.select('.arrow',true).first();
20155 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20156 this.el.enableDisplayMode('block');
20160 if (this.over === false && !this.parent()) {
20163 if (this.triggers === false) {
20168 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20169 var triggers = this.trigger ? this.trigger.split(' ') : [];
20170 Roo.each(triggers, function(trigger) {
20172 if (trigger == 'click') {
20173 on_el.on('click', this.toggle, this);
20174 } else if (trigger != 'manual') {
20175 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20176 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20178 on_el.on(eventIn ,this.enter, this);
20179 on_el.on(eventOut, this.leave, this);
20189 toggle : function () {
20190 this.hoverState == 'in' ? this.leave() : this.enter();
20193 enter : function () {
20195 clearTimeout(this.timeout);
20197 this.hoverState = 'in';
20199 if (!this.delay || !this.delay.show) {
20204 this.timeout = setTimeout(function () {
20205 if (_t.hoverState == 'in') {
20208 }, this.delay.show)
20211 leave : function() {
20212 clearTimeout(this.timeout);
20214 this.hoverState = 'out';
20216 if (!this.delay || !this.delay.hide) {
20221 this.timeout = setTimeout(function () {
20222 if (_t.hoverState == 'out') {
20225 }, this.delay.hide)
20229 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20230 * @param {string} (left|right|top|bottom) position
20232 show : function (on_el, placement)
20234 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20235 on_el = on_el || false; // default to false
20238 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20239 on_el = this.parent().el;
20240 } else if (this.over) {
20241 on_el = Roo.get(this.over);
20246 this.alignEl = Roo.get( on_el );
20249 this.render(document.body);
20255 if (this.title === false) {
20256 this.headerEl.hide();
20261 this.el.dom.style.display = 'block';
20264 if (this.alignEl) {
20265 this.updatePosition(this.placement, true);
20268 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20269 var es = this.el.getSize();
20270 var x = Roo.lib.Dom.getViewWidth()/2;
20271 var y = Roo.lib.Dom.getViewHeight()/2;
20272 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20277 //var arrow = this.el.select('.arrow',true).first();
20278 //arrow.set(align[2],
20280 this.el.addClass('in');
20284 this.hoverState = 'in';
20287 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20288 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20289 this.maskEl.dom.style.display = 'block';
20290 this.maskEl.addClass('show');
20292 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20294 this.fireEvent('show', this);
20298 * fire this manually after loading a grid in the table for example
20299 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20300 * @param {Boolean} try and move it if we cant get right position.
20302 updatePosition : function(placement, try_move)
20304 // allow for calling with no parameters
20305 placement = placement ? placement : this.placement;
20306 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20308 this.el.removeClass([
20309 'fade','top','bottom', 'left', 'right','in',
20310 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20312 this.el.addClass(placement + ' bs-popover-' + placement);
20314 if (!this.alignEl ) {
20318 switch (placement) {
20320 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20321 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20322 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20323 //normal display... or moved up/down.
20324 this.el.setXY(offset);
20325 var xy = this.alignEl.getAnchorXY('tr', false);
20327 this.arrowEl.setXY(xy);
20330 // continue through...
20331 return this.updatePosition('left', false);
20335 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20336 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20337 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20338 //normal display... or moved up/down.
20339 this.el.setXY(offset);
20340 var xy = this.alignEl.getAnchorXY('tl', false);
20341 xy[0]-=10;xy[1]+=5; // << fix me
20342 this.arrowEl.setXY(xy);
20346 return this.updatePosition('right', false);
20349 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20350 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20351 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20352 //normal display... or moved up/down.
20353 this.el.setXY(offset);
20354 var xy = this.alignEl.getAnchorXY('t', false);
20355 xy[1]-=10; // << fix me
20356 this.arrowEl.setXY(xy);
20360 return this.updatePosition('bottom', false);
20363 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20364 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20365 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20366 //normal display... or moved up/down.
20367 this.el.setXY(offset);
20368 var xy = this.alignEl.getAnchorXY('b', false);
20369 xy[1]+=2; // << fix me
20370 this.arrowEl.setXY(xy);
20374 return this.updatePosition('top', false);
20385 this.el.setXY([0,0]);
20386 this.el.removeClass('in');
20388 this.hoverState = null;
20389 this.maskEl.hide(); // always..
20390 this.fireEvent('hide', this);
20396 Roo.apply(Roo.bootstrap.Popover, {
20399 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20400 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20401 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20402 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20407 clickHander : false,
20410 onMouseDown : function(e)
20412 if (!e.getTarget(".roo-popover")) {
20420 register : function(popup)
20422 if (!Roo.bootstrap.Popover.clickHandler) {
20423 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20425 // hide other popups.
20427 this.popups.push(popup);
20429 hideAll : function()
20431 this.popups.forEach(function(p) {
20439 * Card header - holder for the card header elements.
20444 * @class Roo.bootstrap.PopoverNav
20445 * @extends Roo.bootstrap.NavGroup
20446 * Bootstrap Popover header navigation class
20448 * Create a new Popover Header Navigation
20449 * @param {Object} config The config object
20452 Roo.bootstrap.PopoverNav = function(config){
20453 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20456 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20459 container_method : 'getPopoverHeader'
20477 * @class Roo.bootstrap.Progress
20478 * @extends Roo.bootstrap.Component
20479 * Bootstrap Progress class
20480 * @cfg {Boolean} striped striped of the progress bar
20481 * @cfg {Boolean} active animated of the progress bar
20485 * Create a new Progress
20486 * @param {Object} config The config object
20489 Roo.bootstrap.Progress = function(config){
20490 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20493 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20498 getAutoCreate : function(){
20506 cfg.cls += ' progress-striped';
20510 cfg.cls += ' active';
20529 * @class Roo.bootstrap.ProgressBar
20530 * @extends Roo.bootstrap.Component
20531 * Bootstrap ProgressBar class
20532 * @cfg {Number} aria_valuenow aria-value now
20533 * @cfg {Number} aria_valuemin aria-value min
20534 * @cfg {Number} aria_valuemax aria-value max
20535 * @cfg {String} label label for the progress bar
20536 * @cfg {String} panel (success | info | warning | danger )
20537 * @cfg {String} role role of the progress bar
20538 * @cfg {String} sr_only text
20542 * Create a new ProgressBar
20543 * @param {Object} config The config object
20546 Roo.bootstrap.ProgressBar = function(config){
20547 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20550 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20554 aria_valuemax : 100,
20560 getAutoCreate : function()
20565 cls: 'progress-bar',
20566 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20578 cfg.role = this.role;
20581 if(this.aria_valuenow){
20582 cfg['aria-valuenow'] = this.aria_valuenow;
20585 if(this.aria_valuemin){
20586 cfg['aria-valuemin'] = this.aria_valuemin;
20589 if(this.aria_valuemax){
20590 cfg['aria-valuemax'] = this.aria_valuemax;
20593 if(this.label && !this.sr_only){
20594 cfg.html = this.label;
20598 cfg.cls += ' progress-bar-' + this.panel;
20604 update : function(aria_valuenow)
20606 this.aria_valuenow = aria_valuenow;
20608 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20623 * @class Roo.bootstrap.TabGroup
20624 * @extends Roo.bootstrap.Column
20625 * Bootstrap Column class
20626 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20627 * @cfg {Boolean} carousel true to make the group behave like a carousel
20628 * @cfg {Boolean} bullets show bullets for the panels
20629 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20630 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20631 * @cfg {Boolean} showarrow (true|false) show arrow default true
20634 * Create a new TabGroup
20635 * @param {Object} config The config object
20638 Roo.bootstrap.TabGroup = function(config){
20639 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20641 this.navId = Roo.id();
20644 Roo.bootstrap.TabGroup.register(this);
20648 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20651 transition : false,
20656 slideOnTouch : false,
20659 getAutoCreate : function()
20661 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20663 cfg.cls += ' tab-content';
20665 if (this.carousel) {
20666 cfg.cls += ' carousel slide';
20669 cls : 'carousel-inner',
20673 if(this.bullets && !Roo.isTouch){
20676 cls : 'carousel-bullets',
20680 if(this.bullets_cls){
20681 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20688 cfg.cn[0].cn.push(bullets);
20691 if(this.showarrow){
20692 cfg.cn[0].cn.push({
20694 class : 'carousel-arrow',
20698 class : 'carousel-prev',
20702 class : 'fa fa-chevron-left'
20708 class : 'carousel-next',
20712 class : 'fa fa-chevron-right'
20725 initEvents: function()
20727 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20728 // this.el.on("touchstart", this.onTouchStart, this);
20731 if(this.autoslide){
20734 this.slideFn = window.setInterval(function() {
20735 _this.showPanelNext();
20739 if(this.showarrow){
20740 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20741 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20747 // onTouchStart : function(e, el, o)
20749 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20753 // this.showPanelNext();
20757 getChildContainer : function()
20759 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20763 * register a Navigation item
20764 * @param {Roo.bootstrap.NavItem} the navitem to add
20766 register : function(item)
20768 this.tabs.push( item);
20769 item.navId = this.navId; // not really needed..
20774 getActivePanel : function()
20777 Roo.each(this.tabs, function(t) {
20787 getPanelByName : function(n)
20790 Roo.each(this.tabs, function(t) {
20791 if (t.tabId == n) {
20799 indexOfPanel : function(p)
20802 Roo.each(this.tabs, function(t,i) {
20803 if (t.tabId == p.tabId) {
20812 * show a specific panel
20813 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20814 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20816 showPanel : function (pan)
20818 if(this.transition || typeof(pan) == 'undefined'){
20819 Roo.log("waiting for the transitionend");
20823 if (typeof(pan) == 'number') {
20824 pan = this.tabs[pan];
20827 if (typeof(pan) == 'string') {
20828 pan = this.getPanelByName(pan);
20831 var cur = this.getActivePanel();
20834 Roo.log('pan or acitve pan is undefined');
20838 if (pan.tabId == this.getActivePanel().tabId) {
20842 if (false === cur.fireEvent('beforedeactivate')) {
20846 if(this.bullets > 0 && !Roo.isTouch){
20847 this.setActiveBullet(this.indexOfPanel(pan));
20850 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20852 //class="carousel-item carousel-item-next carousel-item-left"
20854 this.transition = true;
20855 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20856 var lr = dir == 'next' ? 'left' : 'right';
20857 pan.el.addClass(dir); // or prev
20858 pan.el.addClass('carousel-item-' + dir); // or prev
20859 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20860 cur.el.addClass(lr); // or right
20861 pan.el.addClass(lr);
20862 cur.el.addClass('carousel-item-' +lr); // or right
20863 pan.el.addClass('carousel-item-' +lr);
20867 cur.el.on('transitionend', function() {
20868 Roo.log("trans end?");
20870 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20871 pan.setActive(true);
20873 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20874 cur.setActive(false);
20876 _this.transition = false;
20878 }, this, { single: true } );
20883 cur.setActive(false);
20884 pan.setActive(true);
20889 showPanelNext : function()
20891 var i = this.indexOfPanel(this.getActivePanel());
20893 if (i >= this.tabs.length - 1 && !this.autoslide) {
20897 if (i >= this.tabs.length - 1 && this.autoslide) {
20901 this.showPanel(this.tabs[i+1]);
20904 showPanelPrev : function()
20906 var i = this.indexOfPanel(this.getActivePanel());
20908 if (i < 1 && !this.autoslide) {
20912 if (i < 1 && this.autoslide) {
20913 i = this.tabs.length;
20916 this.showPanel(this.tabs[i-1]);
20920 addBullet: function()
20922 if(!this.bullets || Roo.isTouch){
20925 var ctr = this.el.select('.carousel-bullets',true).first();
20926 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20927 var bullet = ctr.createChild({
20928 cls : 'bullet bullet-' + i
20929 },ctr.dom.lastChild);
20934 bullet.on('click', (function(e, el, o, ii, t){
20936 e.preventDefault();
20938 this.showPanel(ii);
20940 if(this.autoslide && this.slideFn){
20941 clearInterval(this.slideFn);
20942 this.slideFn = window.setInterval(function() {
20943 _this.showPanelNext();
20947 }).createDelegate(this, [i, bullet], true));
20952 setActiveBullet : function(i)
20958 Roo.each(this.el.select('.bullet', true).elements, function(el){
20959 el.removeClass('selected');
20962 var bullet = this.el.select('.bullet-' + i, true).first();
20968 bullet.addClass('selected');
20979 Roo.apply(Roo.bootstrap.TabGroup, {
20983 * register a Navigation Group
20984 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20986 register : function(navgrp)
20988 this.groups[navgrp.navId] = navgrp;
20992 * fetch a Navigation Group based on the navigation ID
20993 * if one does not exist , it will get created.
20994 * @param {string} the navgroup to add
20995 * @returns {Roo.bootstrap.NavGroup} the navgroup
20997 get: function(navId) {
20998 if (typeof(this.groups[navId]) == 'undefined') {
20999 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21001 return this.groups[navId] ;
21016 * @class Roo.bootstrap.TabPanel
21017 * @extends Roo.bootstrap.Component
21018 * Bootstrap TabPanel class
21019 * @cfg {Boolean} active panel active
21020 * @cfg {String} html panel content
21021 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21022 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21023 * @cfg {String} href click to link..
21024 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21028 * Create a new TabPanel
21029 * @param {Object} config The config object
21032 Roo.bootstrap.TabPanel = function(config){
21033 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21037 * Fires when the active status changes
21038 * @param {Roo.bootstrap.TabPanel} this
21039 * @param {Boolean} state the new state
21044 * @event beforedeactivate
21045 * Fires before a tab is de-activated - can be used to do validation on a form.
21046 * @param {Roo.bootstrap.TabPanel} this
21047 * @return {Boolean} false if there is an error
21050 'beforedeactivate': true
21053 this.tabId = this.tabId || Roo.id();
21057 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21064 touchSlide : false,
21065 getAutoCreate : function(){
21070 // item is needed for carousel - not sure if it has any effect otherwise
21071 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21072 html: this.html || ''
21076 cfg.cls += ' active';
21080 cfg.tabId = this.tabId;
21088 initEvents: function()
21090 var p = this.parent();
21092 this.navId = this.navId || p.navId;
21094 if (typeof(this.navId) != 'undefined') {
21095 // not really needed.. but just in case.. parent should be a NavGroup.
21096 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21100 var i = tg.tabs.length - 1;
21102 if(this.active && tg.bullets > 0 && i < tg.bullets){
21103 tg.setActiveBullet(i);
21107 this.el.on('click', this.onClick, this);
21109 if(Roo.isTouch && this.touchSlide){
21110 this.el.on("touchstart", this.onTouchStart, this);
21111 this.el.on("touchmove", this.onTouchMove, this);
21112 this.el.on("touchend", this.onTouchEnd, this);
21117 onRender : function(ct, position)
21119 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21122 setActive : function(state)
21124 Roo.log("panel - set active " + this.tabId + "=" + state);
21126 this.active = state;
21128 this.el.removeClass('active');
21130 } else if (!this.el.hasClass('active')) {
21131 this.el.addClass('active');
21134 this.fireEvent('changed', this, state);
21137 onClick : function(e)
21139 e.preventDefault();
21141 if(!this.href.length){
21145 window.location.href = this.href;
21154 onTouchStart : function(e)
21156 this.swiping = false;
21158 this.startX = e.browserEvent.touches[0].clientX;
21159 this.startY = e.browserEvent.touches[0].clientY;
21162 onTouchMove : function(e)
21164 this.swiping = true;
21166 this.endX = e.browserEvent.touches[0].clientX;
21167 this.endY = e.browserEvent.touches[0].clientY;
21170 onTouchEnd : function(e)
21177 var tabGroup = this.parent();
21179 if(this.endX > this.startX){ // swiping right
21180 tabGroup.showPanelPrev();
21184 if(this.startX > this.endX){ // swiping left
21185 tabGroup.showPanelNext();
21204 * @class Roo.bootstrap.DateField
21205 * @extends Roo.bootstrap.Input
21206 * Bootstrap DateField class
21207 * @cfg {Number} weekStart default 0
21208 * @cfg {String} viewMode default empty, (months|years)
21209 * @cfg {String} minViewMode default empty, (months|years)
21210 * @cfg {Number} startDate default -Infinity
21211 * @cfg {Number} endDate default Infinity
21212 * @cfg {Boolean} todayHighlight default false
21213 * @cfg {Boolean} todayBtn default false
21214 * @cfg {Boolean} calendarWeeks default false
21215 * @cfg {Object} daysOfWeekDisabled default empty
21216 * @cfg {Boolean} singleMode default false (true | false)
21218 * @cfg {Boolean} keyboardNavigation default true
21219 * @cfg {String} language default en
21222 * Create a new DateField
21223 * @param {Object} config The config object
21226 Roo.bootstrap.DateField = function(config){
21227 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21231 * Fires when this field show.
21232 * @param {Roo.bootstrap.DateField} this
21233 * @param {Mixed} date The date value
21238 * Fires when this field hide.
21239 * @param {Roo.bootstrap.DateField} this
21240 * @param {Mixed} date The date value
21245 * Fires when select a date.
21246 * @param {Roo.bootstrap.DateField} this
21247 * @param {Mixed} date The date value
21251 * @event beforeselect
21252 * Fires when before select a date.
21253 * @param {Roo.bootstrap.DateField} this
21254 * @param {Mixed} date The date value
21256 beforeselect : true
21260 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21263 * @cfg {String} format
21264 * The default date format string which can be overriden for localization support. The format must be
21265 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21269 * @cfg {String} altFormats
21270 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21271 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21273 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21281 todayHighlight : false,
21287 keyboardNavigation: true,
21289 calendarWeeks: false,
21291 startDate: -Infinity,
21295 daysOfWeekDisabled: [],
21299 singleMode : false,
21301 UTCDate: function()
21303 return new Date(Date.UTC.apply(Date, arguments));
21306 UTCToday: function()
21308 var today = new Date();
21309 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21312 getDate: function() {
21313 var d = this.getUTCDate();
21314 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21317 getUTCDate: function() {
21321 setDate: function(d) {
21322 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21325 setUTCDate: function(d) {
21327 this.setValue(this.formatDate(this.date));
21330 onRender: function(ct, position)
21333 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21335 this.language = this.language || 'en';
21336 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21337 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21339 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21340 this.format = this.format || 'm/d/y';
21341 this.isInline = false;
21342 this.isInput = true;
21343 this.component = this.el.select('.add-on', true).first() || false;
21344 this.component = (this.component && this.component.length === 0) ? false : this.component;
21345 this.hasInput = this.component && this.inputEl().length;
21347 if (typeof(this.minViewMode === 'string')) {
21348 switch (this.minViewMode) {
21350 this.minViewMode = 1;
21353 this.minViewMode = 2;
21356 this.minViewMode = 0;
21361 if (typeof(this.viewMode === 'string')) {
21362 switch (this.viewMode) {
21375 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21377 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21379 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21381 this.picker().on('mousedown', this.onMousedown, this);
21382 this.picker().on('click', this.onClick, this);
21384 this.picker().addClass('datepicker-dropdown');
21386 this.startViewMode = this.viewMode;
21388 if(this.singleMode){
21389 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21390 v.setVisibilityMode(Roo.Element.DISPLAY);
21394 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21395 v.setStyle('width', '189px');
21399 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21400 if(!this.calendarWeeks){
21405 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21406 v.attr('colspan', function(i, val){
21407 return parseInt(val) + 1;
21412 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21414 this.setStartDate(this.startDate);
21415 this.setEndDate(this.endDate);
21417 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21424 if(this.isInline) {
21429 picker : function()
21431 return this.pickerEl;
21432 // return this.el.select('.datepicker', true).first();
21435 fillDow: function()
21437 var dowCnt = this.weekStart;
21446 if(this.calendarWeeks){
21454 while (dowCnt < this.weekStart + 7) {
21458 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21462 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21465 fillMonths: function()
21468 var months = this.picker().select('>.datepicker-months td', true).first();
21470 months.dom.innerHTML = '';
21476 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21479 months.createChild(month);
21486 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;
21488 if (this.date < this.startDate) {
21489 this.viewDate = new Date(this.startDate);
21490 } else if (this.date > this.endDate) {
21491 this.viewDate = new Date(this.endDate);
21493 this.viewDate = new Date(this.date);
21501 var d = new Date(this.viewDate),
21502 year = d.getUTCFullYear(),
21503 month = d.getUTCMonth(),
21504 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21505 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21506 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21507 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21508 currentDate = this.date && this.date.valueOf(),
21509 today = this.UTCToday();
21511 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21513 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21515 // this.picker.select('>tfoot th.today').
21516 // .text(dates[this.language].today)
21517 // .toggle(this.todayBtn !== false);
21519 this.updateNavArrows();
21522 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21524 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21526 prevMonth.setUTCDate(day);
21528 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21530 var nextMonth = new Date(prevMonth);
21532 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21534 nextMonth = nextMonth.valueOf();
21536 var fillMonths = false;
21538 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21540 while(prevMonth.valueOf() <= nextMonth) {
21543 if (prevMonth.getUTCDay() === this.weekStart) {
21545 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21553 if(this.calendarWeeks){
21554 // ISO 8601: First week contains first thursday.
21555 // ISO also states week starts on Monday, but we can be more abstract here.
21557 // Start of current week: based on weekstart/current date
21558 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21559 // Thursday of this week
21560 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21561 // First Thursday of year, year from thursday
21562 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21563 // Calendar week: ms between thursdays, div ms per day, div 7 days
21564 calWeek = (th - yth) / 864e5 / 7 + 1;
21566 fillMonths.cn.push({
21574 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21576 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21579 if (this.todayHighlight &&
21580 prevMonth.getUTCFullYear() == today.getFullYear() &&
21581 prevMonth.getUTCMonth() == today.getMonth() &&
21582 prevMonth.getUTCDate() == today.getDate()) {
21583 clsName += ' today';
21586 if (currentDate && prevMonth.valueOf() === currentDate) {
21587 clsName += ' active';
21590 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21591 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21592 clsName += ' disabled';
21595 fillMonths.cn.push({
21597 cls: 'day ' + clsName,
21598 html: prevMonth.getDate()
21601 prevMonth.setDate(prevMonth.getDate()+1);
21604 var currentYear = this.date && this.date.getUTCFullYear();
21605 var currentMonth = this.date && this.date.getUTCMonth();
21607 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21609 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21610 v.removeClass('active');
21612 if(currentYear === year && k === currentMonth){
21613 v.addClass('active');
21616 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21617 v.addClass('disabled');
21623 year = parseInt(year/10, 10) * 10;
21625 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21627 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21630 for (var i = -1; i < 11; i++) {
21631 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21633 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21641 showMode: function(dir)
21644 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21647 Roo.each(this.picker().select('>div',true).elements, function(v){
21648 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21651 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21656 if(this.isInline) {
21660 this.picker().removeClass(['bottom', 'top']);
21662 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21664 * place to the top of element!
21668 this.picker().addClass('top');
21669 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21674 this.picker().addClass('bottom');
21676 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21679 parseDate : function(value)
21681 if(!value || value instanceof Date){
21684 var v = Date.parseDate(value, this.format);
21685 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21686 v = Date.parseDate(value, 'Y-m-d');
21688 if(!v && this.altFormats){
21689 if(!this.altFormatsArray){
21690 this.altFormatsArray = this.altFormats.split("|");
21692 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21693 v = Date.parseDate(value, this.altFormatsArray[i]);
21699 formatDate : function(date, fmt)
21701 return (!date || !(date instanceof Date)) ?
21702 date : date.dateFormat(fmt || this.format);
21705 onFocus : function()
21707 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21711 onBlur : function()
21713 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21715 var d = this.inputEl().getValue();
21722 showPopup : function()
21724 this.picker().show();
21728 this.fireEvent('showpopup', this, this.date);
21731 hidePopup : function()
21733 if(this.isInline) {
21736 this.picker().hide();
21737 this.viewMode = this.startViewMode;
21740 this.fireEvent('hidepopup', this, this.date);
21744 onMousedown: function(e)
21746 e.stopPropagation();
21747 e.preventDefault();
21752 Roo.bootstrap.DateField.superclass.keyup.call(this);
21756 setValue: function(v)
21758 if(this.fireEvent('beforeselect', this, v) !== false){
21759 var d = new Date(this.parseDate(v) ).clearTime();
21761 if(isNaN(d.getTime())){
21762 this.date = this.viewDate = '';
21763 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21767 v = this.formatDate(d);
21769 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21771 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21775 this.fireEvent('select', this, this.date);
21779 getValue: function()
21781 return this.formatDate(this.date);
21784 fireKey: function(e)
21786 if (!this.picker().isVisible()){
21787 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21793 var dateChanged = false,
21795 newDate, newViewDate;
21800 e.preventDefault();
21804 if (!this.keyboardNavigation) {
21807 dir = e.keyCode == 37 ? -1 : 1;
21810 newDate = this.moveYear(this.date, dir);
21811 newViewDate = this.moveYear(this.viewDate, dir);
21812 } else if (e.shiftKey){
21813 newDate = this.moveMonth(this.date, dir);
21814 newViewDate = this.moveMonth(this.viewDate, dir);
21816 newDate = new Date(this.date);
21817 newDate.setUTCDate(this.date.getUTCDate() + dir);
21818 newViewDate = new Date(this.viewDate);
21819 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21821 if (this.dateWithinRange(newDate)){
21822 this.date = newDate;
21823 this.viewDate = newViewDate;
21824 this.setValue(this.formatDate(this.date));
21826 e.preventDefault();
21827 dateChanged = true;
21832 if (!this.keyboardNavigation) {
21835 dir = e.keyCode == 38 ? -1 : 1;
21837 newDate = this.moveYear(this.date, dir);
21838 newViewDate = this.moveYear(this.viewDate, dir);
21839 } else if (e.shiftKey){
21840 newDate = this.moveMonth(this.date, dir);
21841 newViewDate = this.moveMonth(this.viewDate, dir);
21843 newDate = new Date(this.date);
21844 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21845 newViewDate = new Date(this.viewDate);
21846 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21848 if (this.dateWithinRange(newDate)){
21849 this.date = newDate;
21850 this.viewDate = newViewDate;
21851 this.setValue(this.formatDate(this.date));
21853 e.preventDefault();
21854 dateChanged = true;
21858 this.setValue(this.formatDate(this.date));
21860 e.preventDefault();
21863 this.setValue(this.formatDate(this.date));
21877 onClick: function(e)
21879 e.stopPropagation();
21880 e.preventDefault();
21882 var target = e.getTarget();
21884 if(target.nodeName.toLowerCase() === 'i'){
21885 target = Roo.get(target).dom.parentNode;
21888 var nodeName = target.nodeName;
21889 var className = target.className;
21890 var html = target.innerHTML;
21891 //Roo.log(nodeName);
21893 switch(nodeName.toLowerCase()) {
21895 switch(className) {
21901 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21902 switch(this.viewMode){
21904 this.viewDate = this.moveMonth(this.viewDate, dir);
21908 this.viewDate = this.moveYear(this.viewDate, dir);
21914 var date = new Date();
21915 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21917 this.setValue(this.formatDate(this.date));
21924 if (className.indexOf('disabled') < 0) {
21925 this.viewDate.setUTCDate(1);
21926 if (className.indexOf('month') > -1) {
21927 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21929 var year = parseInt(html, 10) || 0;
21930 this.viewDate.setUTCFullYear(year);
21934 if(this.singleMode){
21935 this.setValue(this.formatDate(this.viewDate));
21946 //Roo.log(className);
21947 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21948 var day = parseInt(html, 10) || 1;
21949 var year = (this.viewDate || new Date()).getUTCFullYear(),
21950 month = (this.viewDate || new Date()).getUTCMonth();
21952 if (className.indexOf('old') > -1) {
21959 } else if (className.indexOf('new') > -1) {
21967 //Roo.log([year,month,day]);
21968 this.date = this.UTCDate(year, month, day,0,0,0,0);
21969 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21971 //Roo.log(this.formatDate(this.date));
21972 this.setValue(this.formatDate(this.date));
21979 setStartDate: function(startDate)
21981 this.startDate = startDate || -Infinity;
21982 if (this.startDate !== -Infinity) {
21983 this.startDate = this.parseDate(this.startDate);
21986 this.updateNavArrows();
21989 setEndDate: function(endDate)
21991 this.endDate = endDate || Infinity;
21992 if (this.endDate !== Infinity) {
21993 this.endDate = this.parseDate(this.endDate);
21996 this.updateNavArrows();
21999 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22001 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22002 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22003 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22005 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22006 return parseInt(d, 10);
22009 this.updateNavArrows();
22012 updateNavArrows: function()
22014 if(this.singleMode){
22018 var d = new Date(this.viewDate),
22019 year = d.getUTCFullYear(),
22020 month = d.getUTCMonth();
22022 Roo.each(this.picker().select('.prev', true).elements, function(v){
22024 switch (this.viewMode) {
22027 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22033 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22040 Roo.each(this.picker().select('.next', true).elements, function(v){
22042 switch (this.viewMode) {
22045 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22051 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22059 moveMonth: function(date, dir)
22064 var new_date = new Date(date.valueOf()),
22065 day = new_date.getUTCDate(),
22066 month = new_date.getUTCMonth(),
22067 mag = Math.abs(dir),
22069 dir = dir > 0 ? 1 : -1;
22072 // If going back one month, make sure month is not current month
22073 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22075 return new_date.getUTCMonth() == month;
22077 // If going forward one month, make sure month is as expected
22078 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22080 return new_date.getUTCMonth() != new_month;
22082 new_month = month + dir;
22083 new_date.setUTCMonth(new_month);
22084 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22085 if (new_month < 0 || new_month > 11) {
22086 new_month = (new_month + 12) % 12;
22089 // For magnitudes >1, move one month at a time...
22090 for (var i=0; i<mag; i++) {
22091 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22092 new_date = this.moveMonth(new_date, dir);
22094 // ...then reset the day, keeping it in the new month
22095 new_month = new_date.getUTCMonth();
22096 new_date.setUTCDate(day);
22098 return new_month != new_date.getUTCMonth();
22101 // Common date-resetting loop -- if date is beyond end of month, make it
22104 new_date.setUTCDate(--day);
22105 new_date.setUTCMonth(new_month);
22110 moveYear: function(date, dir)
22112 return this.moveMonth(date, dir*12);
22115 dateWithinRange: function(date)
22117 return date >= this.startDate && date <= this.endDate;
22123 this.picker().remove();
22126 validateValue : function(value)
22128 if(this.getVisibilityEl().hasClass('hidden')){
22132 if(value.length < 1) {
22133 if(this.allowBlank){
22139 if(value.length < this.minLength){
22142 if(value.length > this.maxLength){
22146 var vt = Roo.form.VTypes;
22147 if(!vt[this.vtype](value, this)){
22151 if(typeof this.validator == "function"){
22152 var msg = this.validator(value);
22158 if(this.regex && !this.regex.test(value)){
22162 if(typeof(this.parseDate(value)) == 'undefined'){
22166 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22170 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22180 this.date = this.viewDate = '';
22182 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22187 Roo.apply(Roo.bootstrap.DateField, {
22198 html: '<i class="fa fa-arrow-left"/>'
22208 html: '<i class="fa fa-arrow-right"/>'
22250 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22251 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22252 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22253 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22254 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22267 navFnc: 'FullYear',
22272 navFnc: 'FullYear',
22277 Roo.apply(Roo.bootstrap.DateField, {
22281 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22285 cls: 'datepicker-days',
22289 cls: 'table-condensed',
22291 Roo.bootstrap.DateField.head,
22295 Roo.bootstrap.DateField.footer
22302 cls: 'datepicker-months',
22306 cls: 'table-condensed',
22308 Roo.bootstrap.DateField.head,
22309 Roo.bootstrap.DateField.content,
22310 Roo.bootstrap.DateField.footer
22317 cls: 'datepicker-years',
22321 cls: 'table-condensed',
22323 Roo.bootstrap.DateField.head,
22324 Roo.bootstrap.DateField.content,
22325 Roo.bootstrap.DateField.footer
22344 * @class Roo.bootstrap.TimeField
22345 * @extends Roo.bootstrap.Input
22346 * Bootstrap DateField class
22350 * Create a new TimeField
22351 * @param {Object} config The config object
22354 Roo.bootstrap.TimeField = function(config){
22355 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22359 * Fires when this field show.
22360 * @param {Roo.bootstrap.DateField} thisthis
22361 * @param {Mixed} date The date value
22366 * Fires when this field hide.
22367 * @param {Roo.bootstrap.DateField} this
22368 * @param {Mixed} date The date value
22373 * Fires when select a date.
22374 * @param {Roo.bootstrap.DateField} this
22375 * @param {Mixed} date The date value
22381 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22384 * @cfg {String} format
22385 * The default time format string which can be overriden for localization support. The format must be
22386 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22390 getAutoCreate : function()
22392 this.after = '<i class="fa far fa-clock"></i>';
22393 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22397 onRender: function(ct, position)
22400 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22402 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22404 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22406 this.pop = this.picker().select('>.datepicker-time',true).first();
22407 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22409 this.picker().on('mousedown', this.onMousedown, this);
22410 this.picker().on('click', this.onClick, this);
22412 this.picker().addClass('datepicker-dropdown');
22417 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22418 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22419 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22420 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22421 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22422 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22426 fireKey: function(e){
22427 if (!this.picker().isVisible()){
22428 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22434 e.preventDefault();
22442 this.onTogglePeriod();
22445 this.onIncrementMinutes();
22448 this.onDecrementMinutes();
22457 onClick: function(e) {
22458 e.stopPropagation();
22459 e.preventDefault();
22462 picker : function()
22464 return this.pickerEl;
22467 fillTime: function()
22469 var time = this.pop.select('tbody', true).first();
22471 time.dom.innerHTML = '';
22486 cls: 'hours-up fa fas fa-chevron-up'
22506 cls: 'minutes-up fa fas fa-chevron-up'
22527 cls: 'timepicker-hour',
22542 cls: 'timepicker-minute',
22557 cls: 'btn btn-primary period',
22579 cls: 'hours-down fa fas fa-chevron-down'
22599 cls: 'minutes-down fa fas fa-chevron-down'
22617 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22624 var hours = this.time.getHours();
22625 var minutes = this.time.getMinutes();
22638 hours = hours - 12;
22642 hours = '0' + hours;
22646 minutes = '0' + minutes;
22649 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22650 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22651 this.pop.select('button', true).first().dom.innerHTML = period;
22657 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22659 var cls = ['bottom'];
22661 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22668 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22672 //this.picker().setXY(20000,20000);
22673 this.picker().addClass(cls.join('-'));
22677 Roo.each(cls, function(c){
22682 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22683 //_this.picker().setTop(_this.inputEl().getHeight());
22687 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22689 //_this.picker().setTop(0 - _this.picker().getHeight());
22694 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22698 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22706 onFocus : function()
22708 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22712 onBlur : function()
22714 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22720 this.picker().show();
22725 this.fireEvent('show', this, this.date);
22730 this.picker().hide();
22733 this.fireEvent('hide', this, this.date);
22736 setTime : function()
22739 this.setValue(this.time.format(this.format));
22741 this.fireEvent('select', this, this.date);
22746 onMousedown: function(e){
22747 e.stopPropagation();
22748 e.preventDefault();
22751 onIncrementHours: function()
22753 Roo.log('onIncrementHours');
22754 this.time = this.time.add(Date.HOUR, 1);
22759 onDecrementHours: function()
22761 Roo.log('onDecrementHours');
22762 this.time = this.time.add(Date.HOUR, -1);
22766 onIncrementMinutes: function()
22768 Roo.log('onIncrementMinutes');
22769 this.time = this.time.add(Date.MINUTE, 1);
22773 onDecrementMinutes: function()
22775 Roo.log('onDecrementMinutes');
22776 this.time = this.time.add(Date.MINUTE, -1);
22780 onTogglePeriod: function()
22782 Roo.log('onTogglePeriod');
22783 this.time = this.time.add(Date.HOUR, 12);
22791 Roo.apply(Roo.bootstrap.TimeField, {
22795 cls: 'datepicker dropdown-menu',
22799 cls: 'datepicker-time',
22803 cls: 'table-condensed',
22832 cls: 'btn btn-info ok',
22860 * @class Roo.bootstrap.MonthField
22861 * @extends Roo.bootstrap.Input
22862 * Bootstrap MonthField class
22864 * @cfg {String} language default en
22867 * Create a new MonthField
22868 * @param {Object} config The config object
22871 Roo.bootstrap.MonthField = function(config){
22872 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22877 * Fires when this field show.
22878 * @param {Roo.bootstrap.MonthField} this
22879 * @param {Mixed} date The date value
22884 * Fires when this field hide.
22885 * @param {Roo.bootstrap.MonthField} this
22886 * @param {Mixed} date The date value
22891 * Fires when select a date.
22892 * @param {Roo.bootstrap.MonthField} this
22893 * @param {String} oldvalue The old value
22894 * @param {String} newvalue The new value
22900 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22902 onRender: function(ct, position)
22905 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22907 this.language = this.language || 'en';
22908 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22909 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22911 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22912 this.isInline = false;
22913 this.isInput = true;
22914 this.component = this.el.select('.add-on', true).first() || false;
22915 this.component = (this.component && this.component.length === 0) ? false : this.component;
22916 this.hasInput = this.component && this.inputEL().length;
22918 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22920 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22922 this.picker().on('mousedown', this.onMousedown, this);
22923 this.picker().on('click', this.onClick, this);
22925 this.picker().addClass('datepicker-dropdown');
22927 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22928 v.setStyle('width', '189px');
22935 if(this.isInline) {
22941 setValue: function(v, suppressEvent)
22943 var o = this.getValue();
22945 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22949 if(suppressEvent !== true){
22950 this.fireEvent('select', this, o, v);
22955 getValue: function()
22960 onClick: function(e)
22962 e.stopPropagation();
22963 e.preventDefault();
22965 var target = e.getTarget();
22967 if(target.nodeName.toLowerCase() === 'i'){
22968 target = Roo.get(target).dom.parentNode;
22971 var nodeName = target.nodeName;
22972 var className = target.className;
22973 var html = target.innerHTML;
22975 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22979 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22981 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22987 picker : function()
22989 return this.pickerEl;
22992 fillMonths: function()
22995 var months = this.picker().select('>.datepicker-months td', true).first();
22997 months.dom.innerHTML = '';
23003 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23006 months.createChild(month);
23015 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23016 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23019 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23020 e.removeClass('active');
23022 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23023 e.addClass('active');
23030 if(this.isInline) {
23034 this.picker().removeClass(['bottom', 'top']);
23036 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23038 * place to the top of element!
23042 this.picker().addClass('top');
23043 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23048 this.picker().addClass('bottom');
23050 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23053 onFocus : function()
23055 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23059 onBlur : function()
23061 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23063 var d = this.inputEl().getValue();
23072 this.picker().show();
23073 this.picker().select('>.datepicker-months', true).first().show();
23077 this.fireEvent('show', this, this.date);
23082 if(this.isInline) {
23085 this.picker().hide();
23086 this.fireEvent('hide', this, this.date);
23090 onMousedown: function(e)
23092 e.stopPropagation();
23093 e.preventDefault();
23098 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23102 fireKey: function(e)
23104 if (!this.picker().isVisible()){
23105 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23116 e.preventDefault();
23120 dir = e.keyCode == 37 ? -1 : 1;
23122 this.vIndex = this.vIndex + dir;
23124 if(this.vIndex < 0){
23128 if(this.vIndex > 11){
23132 if(isNaN(this.vIndex)){
23136 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23142 dir = e.keyCode == 38 ? -1 : 1;
23144 this.vIndex = this.vIndex + dir * 4;
23146 if(this.vIndex < 0){
23150 if(this.vIndex > 11){
23154 if(isNaN(this.vIndex)){
23158 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23163 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23164 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23168 e.preventDefault();
23171 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23172 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23188 this.picker().remove();
23193 Roo.apply(Roo.bootstrap.MonthField, {
23212 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23213 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23218 Roo.apply(Roo.bootstrap.MonthField, {
23222 cls: 'datepicker dropdown-menu roo-dynamic',
23226 cls: 'datepicker-months',
23230 cls: 'table-condensed',
23232 Roo.bootstrap.DateField.content
23252 * @class Roo.bootstrap.CheckBox
23253 * @extends Roo.bootstrap.Input
23254 * Bootstrap CheckBox class
23256 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23257 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23258 * @cfg {String} boxLabel The text that appears beside the checkbox
23259 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23260 * @cfg {Boolean} checked initnal the element
23261 * @cfg {Boolean} inline inline the element (default false)
23262 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23263 * @cfg {String} tooltip label tooltip
23266 * Create a new CheckBox
23267 * @param {Object} config The config object
23270 Roo.bootstrap.CheckBox = function(config){
23271 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23276 * Fires when the element is checked or unchecked.
23277 * @param {Roo.bootstrap.CheckBox} this This input
23278 * @param {Boolean} checked The new checked value
23283 * Fires when the element is click.
23284 * @param {Roo.bootstrap.CheckBox} this This input
23291 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23293 inputType: 'checkbox',
23302 // checkbox success does not make any sense really..
23307 getAutoCreate : function()
23309 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23315 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23318 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23324 type : this.inputType,
23325 value : this.inputValue,
23326 cls : 'roo-' + this.inputType, //'form-box',
23327 placeholder : this.placeholder || ''
23331 if(this.inputType != 'radio'){
23335 cls : 'roo-hidden-value',
23336 value : this.checked ? this.inputValue : this.valueOff
23341 if (this.weight) { // Validity check?
23342 cfg.cls += " " + this.inputType + "-" + this.weight;
23345 if (this.disabled) {
23346 input.disabled=true;
23350 input.checked = this.checked;
23355 input.name = this.name;
23357 if(this.inputType != 'radio'){
23358 hidden.name = this.name;
23359 input.name = '_hidden_' + this.name;
23364 input.cls += ' input-' + this.size;
23369 ['xs','sm','md','lg'].map(function(size){
23370 if (settings[size]) {
23371 cfg.cls += ' col-' + size + '-' + settings[size];
23375 var inputblock = input;
23377 if (this.before || this.after) {
23380 cls : 'input-group',
23385 inputblock.cn.push({
23387 cls : 'input-group-addon',
23392 inputblock.cn.push(input);
23394 if(this.inputType != 'radio'){
23395 inputblock.cn.push(hidden);
23399 inputblock.cn.push({
23401 cls : 'input-group-addon',
23407 var boxLabelCfg = false;
23413 //'for': id, // box label is handled by onclick - so no for...
23415 html: this.boxLabel
23418 boxLabelCfg.tooltip = this.tooltip;
23424 if (align ==='left' && this.fieldLabel.length) {
23425 // Roo.log("left and has label");
23430 cls : 'control-label',
23431 html : this.fieldLabel
23442 cfg.cn[1].cn.push(boxLabelCfg);
23445 if(this.labelWidth > 12){
23446 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23449 if(this.labelWidth < 13 && this.labelmd == 0){
23450 this.labelmd = this.labelWidth;
23453 if(this.labellg > 0){
23454 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23455 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23458 if(this.labelmd > 0){
23459 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23460 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23463 if(this.labelsm > 0){
23464 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23465 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23468 if(this.labelxs > 0){
23469 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23470 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23473 } else if ( this.fieldLabel.length) {
23474 // Roo.log(" label");
23478 tag: this.boxLabel ? 'span' : 'label',
23480 cls: 'control-label box-input-label',
23481 //cls : 'input-group-addon',
23482 html : this.fieldLabel
23489 cfg.cn.push(boxLabelCfg);
23494 // Roo.log(" no label && no align");
23495 cfg.cn = [ inputblock ] ;
23497 cfg.cn.push(boxLabelCfg);
23505 if(this.inputType != 'radio'){
23506 cfg.cn.push(hidden);
23514 * return the real input element.
23516 inputEl: function ()
23518 return this.el.select('input.roo-' + this.inputType,true).first();
23520 hiddenEl: function ()
23522 return this.el.select('input.roo-hidden-value',true).first();
23525 labelEl: function()
23527 return this.el.select('label.control-label',true).first();
23529 /* depricated... */
23533 return this.labelEl();
23536 boxLabelEl: function()
23538 return this.el.select('label.box-label',true).first();
23541 initEvents : function()
23543 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23545 this.inputEl().on('click', this.onClick, this);
23547 if (this.boxLabel) {
23548 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23551 this.startValue = this.getValue();
23554 Roo.bootstrap.CheckBox.register(this);
23558 onClick : function(e)
23560 if(this.fireEvent('click', this, e) !== false){
23561 this.setChecked(!this.checked);
23566 setChecked : function(state,suppressEvent)
23568 this.startValue = this.getValue();
23570 if(this.inputType == 'radio'){
23572 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23573 e.dom.checked = false;
23576 this.inputEl().dom.checked = true;
23578 this.inputEl().dom.value = this.inputValue;
23580 if(suppressEvent !== true){
23581 this.fireEvent('check', this, true);
23589 this.checked = state;
23591 this.inputEl().dom.checked = state;
23594 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23596 if(suppressEvent !== true){
23597 this.fireEvent('check', this, state);
23603 getValue : function()
23605 if(this.inputType == 'radio'){
23606 return this.getGroupValue();
23609 return this.hiddenEl().dom.value;
23613 getGroupValue : function()
23615 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23619 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23622 setValue : function(v,suppressEvent)
23624 if(this.inputType == 'radio'){
23625 this.setGroupValue(v, suppressEvent);
23629 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23634 setGroupValue : function(v, suppressEvent)
23636 this.startValue = this.getValue();
23638 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23639 e.dom.checked = false;
23641 if(e.dom.value == v){
23642 e.dom.checked = true;
23646 if(suppressEvent !== true){
23647 this.fireEvent('check', this, true);
23655 validate : function()
23657 if(this.getVisibilityEl().hasClass('hidden')){
23663 (this.inputType == 'radio' && this.validateRadio()) ||
23664 (this.inputType == 'checkbox' && this.validateCheckbox())
23670 this.markInvalid();
23674 validateRadio : function()
23676 if(this.getVisibilityEl().hasClass('hidden')){
23680 if(this.allowBlank){
23686 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23687 if(!e.dom.checked){
23699 validateCheckbox : function()
23702 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23703 //return (this.getValue() == this.inputValue) ? true : false;
23706 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23714 for(var i in group){
23715 if(group[i].el.isVisible(true)){
23723 for(var i in group){
23728 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23735 * Mark this field as valid
23737 markValid : function()
23741 this.fireEvent('valid', this);
23743 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23746 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23753 if(this.inputType == 'radio'){
23754 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23755 var fg = e.findParent('.form-group', false, true);
23756 if (Roo.bootstrap.version == 3) {
23757 fg.removeClass([_this.invalidClass, _this.validClass]);
23758 fg.addClass(_this.validClass);
23760 fg.removeClass(['is-valid', 'is-invalid']);
23761 fg.addClass('is-valid');
23769 var fg = this.el.findParent('.form-group', false, true);
23770 if (Roo.bootstrap.version == 3) {
23771 fg.removeClass([this.invalidClass, this.validClass]);
23772 fg.addClass(this.validClass);
23774 fg.removeClass(['is-valid', 'is-invalid']);
23775 fg.addClass('is-valid');
23780 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23786 for(var i in group){
23787 var fg = group[i].el.findParent('.form-group', false, true);
23788 if (Roo.bootstrap.version == 3) {
23789 fg.removeClass([this.invalidClass, this.validClass]);
23790 fg.addClass(this.validClass);
23792 fg.removeClass(['is-valid', 'is-invalid']);
23793 fg.addClass('is-valid');
23799 * Mark this field as invalid
23800 * @param {String} msg The validation message
23802 markInvalid : function(msg)
23804 if(this.allowBlank){
23810 this.fireEvent('invalid', this, msg);
23812 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23815 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23819 label.markInvalid();
23822 if(this.inputType == 'radio'){
23824 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23825 var fg = e.findParent('.form-group', false, true);
23826 if (Roo.bootstrap.version == 3) {
23827 fg.removeClass([_this.invalidClass, _this.validClass]);
23828 fg.addClass(_this.invalidClass);
23830 fg.removeClass(['is-invalid', 'is-valid']);
23831 fg.addClass('is-invalid');
23839 var fg = this.el.findParent('.form-group', false, true);
23840 if (Roo.bootstrap.version == 3) {
23841 fg.removeClass([_this.invalidClass, _this.validClass]);
23842 fg.addClass(_this.invalidClass);
23844 fg.removeClass(['is-invalid', 'is-valid']);
23845 fg.addClass('is-invalid');
23850 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23856 for(var i in group){
23857 var fg = group[i].el.findParent('.form-group', false, true);
23858 if (Roo.bootstrap.version == 3) {
23859 fg.removeClass([_this.invalidClass, _this.validClass]);
23860 fg.addClass(_this.invalidClass);
23862 fg.removeClass(['is-invalid', 'is-valid']);
23863 fg.addClass('is-invalid');
23869 clearInvalid : function()
23871 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23873 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23875 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23877 if (label && label.iconEl) {
23878 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23879 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23883 disable : function()
23885 if(this.inputType != 'radio'){
23886 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23893 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23894 _this.getActionEl().addClass(this.disabledClass);
23895 e.dom.disabled = true;
23899 this.disabled = true;
23900 this.fireEvent("disable", this);
23904 enable : function()
23906 if(this.inputType != 'radio'){
23907 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23914 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23915 _this.getActionEl().removeClass(this.disabledClass);
23916 e.dom.disabled = false;
23920 this.disabled = false;
23921 this.fireEvent("enable", this);
23925 setBoxLabel : function(v)
23930 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23936 Roo.apply(Roo.bootstrap.CheckBox, {
23941 * register a CheckBox Group
23942 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23944 register : function(checkbox)
23946 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23947 this.groups[checkbox.groupId] = {};
23950 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23954 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23958 * fetch a CheckBox Group based on the group ID
23959 * @param {string} the group ID
23960 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23962 get: function(groupId) {
23963 if (typeof(this.groups[groupId]) == 'undefined') {
23967 return this.groups[groupId] ;
23980 * @class Roo.bootstrap.Radio
23981 * @extends Roo.bootstrap.Component
23982 * Bootstrap Radio class
23983 * @cfg {String} boxLabel - the label associated
23984 * @cfg {String} value - the value of radio
23987 * Create a new Radio
23988 * @param {Object} config The config object
23990 Roo.bootstrap.Radio = function(config){
23991 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23995 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24001 getAutoCreate : function()
24005 cls : 'form-group radio',
24010 html : this.boxLabel
24018 initEvents : function()
24020 this.parent().register(this);
24022 this.el.on('click', this.onClick, this);
24026 onClick : function(e)
24028 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24029 this.setChecked(true);
24033 setChecked : function(state, suppressEvent)
24035 this.parent().setValue(this.value, suppressEvent);
24039 setBoxLabel : function(v)
24044 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24059 * @class Roo.bootstrap.SecurePass
24060 * @extends Roo.bootstrap.Input
24061 * Bootstrap SecurePass class
24065 * Create a new SecurePass
24066 * @param {Object} config The config object
24069 Roo.bootstrap.SecurePass = function (config) {
24070 // these go here, so the translation tool can replace them..
24072 PwdEmpty: "Please type a password, and then retype it to confirm.",
24073 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24074 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24075 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24076 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24077 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24078 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24079 TooWeak: "Your password is Too Weak."
24081 this.meterLabel = "Password strength:";
24082 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24083 this.meterClass = [
24084 "roo-password-meter-tooweak",
24085 "roo-password-meter-weak",
24086 "roo-password-meter-medium",
24087 "roo-password-meter-strong",
24088 "roo-password-meter-grey"
24093 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24096 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24098 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24100 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24101 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24102 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24103 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24104 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24105 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24106 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24116 * @cfg {String/Object} Label for the strength meter (defaults to
24117 * 'Password strength:')
24122 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24123 * ['Weak', 'Medium', 'Strong'])
24126 pwdStrengths: false,
24139 initEvents: function ()
24141 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24143 if (this.el.is('input[type=password]') && Roo.isSafari) {
24144 this.el.on('keydown', this.SafariOnKeyDown, this);
24147 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24150 onRender: function (ct, position)
24152 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24153 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24154 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24156 this.trigger.createChild({
24161 cls: 'roo-password-meter-grey col-xs-12',
24164 //width: this.meterWidth + 'px'
24168 cls: 'roo-password-meter-text'
24174 if (this.hideTrigger) {
24175 this.trigger.setDisplayed(false);
24177 this.setSize(this.width || '', this.height || '');
24180 onDestroy: function ()
24182 if (this.trigger) {
24183 this.trigger.removeAllListeners();
24184 this.trigger.remove();
24187 this.wrap.remove();
24189 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24192 checkStrength: function ()
24194 var pwd = this.inputEl().getValue();
24195 if (pwd == this._lastPwd) {
24200 if (this.ClientSideStrongPassword(pwd)) {
24202 } else if (this.ClientSideMediumPassword(pwd)) {
24204 } else if (this.ClientSideWeakPassword(pwd)) {
24210 Roo.log('strength1: ' + strength);
24212 //var pm = this.trigger.child('div/div/div').dom;
24213 var pm = this.trigger.child('div/div');
24214 pm.removeClass(this.meterClass);
24215 pm.addClass(this.meterClass[strength]);
24218 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24220 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24222 this._lastPwd = pwd;
24226 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24228 this._lastPwd = '';
24230 var pm = this.trigger.child('div/div');
24231 pm.removeClass(this.meterClass);
24232 pm.addClass('roo-password-meter-grey');
24235 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24238 this.inputEl().dom.type='password';
24241 validateValue: function (value)
24243 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24246 if (value.length == 0) {
24247 if (this.allowBlank) {
24248 this.clearInvalid();
24252 this.markInvalid(this.errors.PwdEmpty);
24253 this.errorMsg = this.errors.PwdEmpty;
24261 if (!value.match(/[\x21-\x7e]+/)) {
24262 this.markInvalid(this.errors.PwdBadChar);
24263 this.errorMsg = this.errors.PwdBadChar;
24266 if (value.length < 6) {
24267 this.markInvalid(this.errors.PwdShort);
24268 this.errorMsg = this.errors.PwdShort;
24271 if (value.length > 16) {
24272 this.markInvalid(this.errors.PwdLong);
24273 this.errorMsg = this.errors.PwdLong;
24277 if (this.ClientSideStrongPassword(value)) {
24279 } else if (this.ClientSideMediumPassword(value)) {
24281 } else if (this.ClientSideWeakPassword(value)) {
24288 if (strength < 2) {
24289 //this.markInvalid(this.errors.TooWeak);
24290 this.errorMsg = this.errors.TooWeak;
24295 console.log('strength2: ' + strength);
24297 //var pm = this.trigger.child('div/div/div').dom;
24299 var pm = this.trigger.child('div/div');
24300 pm.removeClass(this.meterClass);
24301 pm.addClass(this.meterClass[strength]);
24303 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24305 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24307 this.errorMsg = '';
24311 CharacterSetChecks: function (type)
24314 this.fResult = false;
24317 isctype: function (character, type)
24320 case this.kCapitalLetter:
24321 if (character >= 'A' && character <= 'Z') {
24326 case this.kSmallLetter:
24327 if (character >= 'a' && character <= 'z') {
24333 if (character >= '0' && character <= '9') {
24338 case this.kPunctuation:
24339 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24350 IsLongEnough: function (pwd, size)
24352 return !(pwd == null || isNaN(size) || pwd.length < size);
24355 SpansEnoughCharacterSets: function (word, nb)
24357 if (!this.IsLongEnough(word, nb))
24362 var characterSetChecks = new Array(
24363 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24364 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24367 for (var index = 0; index < word.length; ++index) {
24368 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24369 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24370 characterSetChecks[nCharSet].fResult = true;
24377 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24378 if (characterSetChecks[nCharSet].fResult) {
24383 if (nCharSets < nb) {
24389 ClientSideStrongPassword: function (pwd)
24391 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24394 ClientSideMediumPassword: function (pwd)
24396 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24399 ClientSideWeakPassword: function (pwd)
24401 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24404 })//<script type="text/javascript">
24407 * Based Ext JS Library 1.1.1
24408 * Copyright(c) 2006-2007, Ext JS, LLC.
24414 * @class Roo.HtmlEditorCore
24415 * @extends Roo.Component
24416 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24418 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24421 Roo.HtmlEditorCore = function(config){
24424 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24429 * @event initialize
24430 * Fires when the editor is fully initialized (including the iframe)
24431 * @param {Roo.HtmlEditorCore} this
24436 * Fires when the editor is first receives the focus. Any insertion must wait
24437 * until after this event.
24438 * @param {Roo.HtmlEditorCore} this
24442 * @event beforesync
24443 * Fires before the textarea is updated with content from the editor iframe. Return false
24444 * to cancel the sync.
24445 * @param {Roo.HtmlEditorCore} this
24446 * @param {String} html
24450 * @event beforepush
24451 * Fires before the iframe editor is updated with content from the textarea. Return false
24452 * to cancel the push.
24453 * @param {Roo.HtmlEditorCore} this
24454 * @param {String} html
24459 * Fires when the textarea is updated with content from the editor iframe.
24460 * @param {Roo.HtmlEditorCore} this
24461 * @param {String} html
24466 * Fires when the iframe editor is updated with content from the textarea.
24467 * @param {Roo.HtmlEditorCore} this
24468 * @param {String} html
24473 * @event editorevent
24474 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24475 * @param {Roo.HtmlEditorCore} this
24481 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24483 // defaults : white / black...
24484 this.applyBlacklists();
24491 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24495 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24501 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24506 * @cfg {Number} height (in pixels)
24510 * @cfg {Number} width (in pixels)
24515 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24518 stylesheets: false,
24523 // private properties
24524 validationEvent : false,
24526 initialized : false,
24528 sourceEditMode : false,
24529 onFocus : Roo.emptyFn,
24531 hideMode:'offsets',
24535 // blacklist + whitelisted elements..
24542 * Protected method that will not generally be called directly. It
24543 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24544 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24546 getDocMarkup : function(){
24550 // inherit styels from page...??
24551 if (this.stylesheets === false) {
24553 Roo.get(document.head).select('style').each(function(node) {
24554 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24557 Roo.get(document.head).select('link').each(function(node) {
24558 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24561 } else if (!this.stylesheets.length) {
24563 st = '<style type="text/css">' +
24564 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24567 for (var i in this.stylesheets) {
24568 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24573 st += '<style type="text/css">' +
24574 'IMG { cursor: pointer } ' +
24577 var cls = 'roo-htmleditor-body';
24579 if(this.bodyCls.length){
24580 cls += ' ' + this.bodyCls;
24583 return '<html><head>' + st +
24584 //<style type="text/css">' +
24585 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24587 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24591 onRender : function(ct, position)
24594 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24595 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24598 this.el.dom.style.border = '0 none';
24599 this.el.dom.setAttribute('tabIndex', -1);
24600 this.el.addClass('x-hidden hide');
24604 if(Roo.isIE){ // fix IE 1px bogus margin
24605 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24609 this.frameId = Roo.id();
24613 var iframe = this.owner.wrap.createChild({
24615 cls: 'form-control', // bootstrap..
24617 name: this.frameId,
24618 frameBorder : 'no',
24619 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24624 this.iframe = iframe.dom;
24626 this.assignDocWin();
24628 this.doc.designMode = 'on';
24631 this.doc.write(this.getDocMarkup());
24635 var task = { // must defer to wait for browser to be ready
24637 //console.log("run task?" + this.doc.readyState);
24638 this.assignDocWin();
24639 if(this.doc.body || this.doc.readyState == 'complete'){
24641 this.doc.designMode="on";
24645 Roo.TaskMgr.stop(task);
24646 this.initEditor.defer(10, this);
24653 Roo.TaskMgr.start(task);
24658 onResize : function(w, h)
24660 Roo.log('resize: ' +w + ',' + h );
24661 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24665 if(typeof w == 'number'){
24667 this.iframe.style.width = w + 'px';
24669 if(typeof h == 'number'){
24671 this.iframe.style.height = h + 'px';
24673 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24680 * Toggles the editor between standard and source edit mode.
24681 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24683 toggleSourceEdit : function(sourceEditMode){
24685 this.sourceEditMode = sourceEditMode === true;
24687 if(this.sourceEditMode){
24689 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24692 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24693 //this.iframe.className = '';
24696 //this.setSize(this.owner.wrap.getSize());
24697 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24704 * Protected method that will not generally be called directly. If you need/want
24705 * custom HTML cleanup, this is the method you should override.
24706 * @param {String} html The HTML to be cleaned
24707 * return {String} The cleaned HTML
24709 cleanHtml : function(html){
24710 html = String(html);
24711 if(html.length > 5){
24712 if(Roo.isSafari){ // strip safari nonsense
24713 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24716 if(html == ' '){
24723 * HTML Editor -> Textarea
24724 * Protected method that will not generally be called directly. Syncs the contents
24725 * of the editor iframe with the textarea.
24727 syncValue : function(){
24728 if(this.initialized){
24729 var bd = (this.doc.body || this.doc.documentElement);
24730 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24731 var html = bd.innerHTML;
24733 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24734 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24736 html = '<div style="'+m[0]+'">' + html + '</div>';
24739 html = this.cleanHtml(html);
24740 // fix up the special chars.. normaly like back quotes in word...
24741 // however we do not want to do this with chinese..
24742 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24744 var cc = match.charCodeAt();
24746 // Get the character value, handling surrogate pairs
24747 if (match.length == 2) {
24748 // It's a surrogate pair, calculate the Unicode code point
24749 var high = match.charCodeAt(0) - 0xD800;
24750 var low = match.charCodeAt(1) - 0xDC00;
24751 cc = (high * 0x400) + low + 0x10000;
24753 (cc >= 0x4E00 && cc < 0xA000 ) ||
24754 (cc >= 0x3400 && cc < 0x4E00 ) ||
24755 (cc >= 0xf900 && cc < 0xfb00 )
24760 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24761 return "&#" + cc + ";";
24768 if(this.owner.fireEvent('beforesync', this, html) !== false){
24769 this.el.dom.value = html;
24770 this.owner.fireEvent('sync', this, html);
24776 * Protected method that will not generally be called directly. Pushes the value of the textarea
24777 * into the iframe editor.
24779 pushValue : function(){
24780 if(this.initialized){
24781 var v = this.el.dom.value.trim();
24783 // if(v.length < 1){
24787 if(this.owner.fireEvent('beforepush', this, v) !== false){
24788 var d = (this.doc.body || this.doc.documentElement);
24790 this.cleanUpPaste();
24791 this.el.dom.value = d.innerHTML;
24792 this.owner.fireEvent('push', this, v);
24798 deferFocus : function(){
24799 this.focus.defer(10, this);
24803 focus : function(){
24804 if(this.win && !this.sourceEditMode){
24811 assignDocWin: function()
24813 var iframe = this.iframe;
24816 this.doc = iframe.contentWindow.document;
24817 this.win = iframe.contentWindow;
24819 // if (!Roo.get(this.frameId)) {
24822 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24823 // this.win = Roo.get(this.frameId).dom.contentWindow;
24825 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24829 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24830 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24835 initEditor : function(){
24836 //console.log("INIT EDITOR");
24837 this.assignDocWin();
24841 this.doc.designMode="on";
24843 this.doc.write(this.getDocMarkup());
24846 var dbody = (this.doc.body || this.doc.documentElement);
24847 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24848 // this copies styles from the containing element into thsi one..
24849 // not sure why we need all of this..
24850 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24852 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24853 //ss['background-attachment'] = 'fixed'; // w3c
24854 dbody.bgProperties = 'fixed'; // ie
24855 //Roo.DomHelper.applyStyles(dbody, ss);
24856 Roo.EventManager.on(this.doc, {
24857 //'mousedown': this.onEditorEvent,
24858 'mouseup': this.onEditorEvent,
24859 'dblclick': this.onEditorEvent,
24860 'click': this.onEditorEvent,
24861 'keyup': this.onEditorEvent,
24866 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24868 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24869 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24871 this.initialized = true;
24873 this.owner.fireEvent('initialize', this);
24878 onDestroy : function(){
24884 //for (var i =0; i < this.toolbars.length;i++) {
24885 // // fixme - ask toolbars for heights?
24886 // this.toolbars[i].onDestroy();
24889 //this.wrap.dom.innerHTML = '';
24890 //this.wrap.remove();
24895 onFirstFocus : function(){
24897 this.assignDocWin();
24900 this.activated = true;
24903 if(Roo.isGecko){ // prevent silly gecko errors
24905 var s = this.win.getSelection();
24906 if(!s.focusNode || s.focusNode.nodeType != 3){
24907 var r = s.getRangeAt(0);
24908 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24913 this.execCmd('useCSS', true);
24914 this.execCmd('styleWithCSS', false);
24917 this.owner.fireEvent('activate', this);
24921 adjustFont: function(btn){
24922 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24923 //if(Roo.isSafari){ // safari
24926 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24927 if(Roo.isSafari){ // safari
24928 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24929 v = (v < 10) ? 10 : v;
24930 v = (v > 48) ? 48 : v;
24931 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24936 v = Math.max(1, v+adjust);
24938 this.execCmd('FontSize', v );
24941 onEditorEvent : function(e)
24943 this.owner.fireEvent('editorevent', this, e);
24944 // this.updateToolbar();
24945 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24948 insertTag : function(tg)
24950 // could be a bit smarter... -> wrap the current selected tRoo..
24951 if (tg.toLowerCase() == 'span' ||
24952 tg.toLowerCase() == 'code' ||
24953 tg.toLowerCase() == 'sup' ||
24954 tg.toLowerCase() == 'sub'
24957 range = this.createRange(this.getSelection());
24958 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24959 wrappingNode.appendChild(range.extractContents());
24960 range.insertNode(wrappingNode);
24967 this.execCmd("formatblock", tg);
24971 insertText : function(txt)
24975 var range = this.createRange();
24976 range.deleteContents();
24977 //alert(Sender.getAttribute('label'));
24979 range.insertNode(this.doc.createTextNode(txt));
24985 * Executes a Midas editor command on the editor document and performs necessary focus and
24986 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24987 * @param {String} cmd The Midas command
24988 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24990 relayCmd : function(cmd, value){
24992 this.execCmd(cmd, value);
24993 this.owner.fireEvent('editorevent', this);
24994 //this.updateToolbar();
24995 this.owner.deferFocus();
24999 * Executes a Midas editor command directly on the editor document.
25000 * For visual commands, you should use {@link #relayCmd} instead.
25001 * <b>This should only be called after the editor is initialized.</b>
25002 * @param {String} cmd The Midas command
25003 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25005 execCmd : function(cmd, value){
25006 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25013 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25015 * @param {String} text | dom node..
25017 insertAtCursor : function(text)
25020 if(!this.activated){
25026 var r = this.doc.selection.createRange();
25037 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25041 // from jquery ui (MIT licenced)
25043 var win = this.win;
25045 if (win.getSelection && win.getSelection().getRangeAt) {
25046 range = win.getSelection().getRangeAt(0);
25047 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25048 range.insertNode(node);
25049 } else if (win.document.selection && win.document.selection.createRange) {
25050 // no firefox support
25051 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25052 win.document.selection.createRange().pasteHTML(txt);
25054 // no firefox support
25055 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25056 this.execCmd('InsertHTML', txt);
25065 mozKeyPress : function(e){
25067 var c = e.getCharCode(), cmd;
25070 c = String.fromCharCode(c).toLowerCase();
25084 this.cleanUpPaste.defer(100, this);
25092 e.preventDefault();
25100 fixKeys : function(){ // load time branching for fastest keydown performance
25102 return function(e){
25103 var k = e.getKey(), r;
25106 r = this.doc.selection.createRange();
25109 r.pasteHTML('    ');
25116 r = this.doc.selection.createRange();
25118 var target = r.parentElement();
25119 if(!target || target.tagName.toLowerCase() != 'li'){
25121 r.pasteHTML('<br />');
25127 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25128 this.cleanUpPaste.defer(100, this);
25134 }else if(Roo.isOpera){
25135 return function(e){
25136 var k = e.getKey();
25140 this.execCmd('InsertHTML','    ');
25143 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25144 this.cleanUpPaste.defer(100, this);
25149 }else if(Roo.isSafari){
25150 return function(e){
25151 var k = e.getKey();
25155 this.execCmd('InsertText','\t');
25159 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25160 this.cleanUpPaste.defer(100, this);
25168 getAllAncestors: function()
25170 var p = this.getSelectedNode();
25173 a.push(p); // push blank onto stack..
25174 p = this.getParentElement();
25178 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25182 a.push(this.doc.body);
25186 lastSelNode : false,
25189 getSelection : function()
25191 this.assignDocWin();
25192 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25195 getSelectedNode: function()
25197 // this may only work on Gecko!!!
25199 // should we cache this!!!!
25204 var range = this.createRange(this.getSelection()).cloneRange();
25207 var parent = range.parentElement();
25209 var testRange = range.duplicate();
25210 testRange.moveToElementText(parent);
25211 if (testRange.inRange(range)) {
25214 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25217 parent = parent.parentElement;
25222 // is ancestor a text element.
25223 var ac = range.commonAncestorContainer;
25224 if (ac.nodeType == 3) {
25225 ac = ac.parentNode;
25228 var ar = ac.childNodes;
25231 var other_nodes = [];
25232 var has_other_nodes = false;
25233 for (var i=0;i<ar.length;i++) {
25234 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25237 // fullly contained node.
25239 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25244 // probably selected..
25245 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25246 other_nodes.push(ar[i]);
25250 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25255 has_other_nodes = true;
25257 if (!nodes.length && other_nodes.length) {
25258 nodes= other_nodes;
25260 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25266 createRange: function(sel)
25268 // this has strange effects when using with
25269 // top toolbar - not sure if it's a great idea.
25270 //this.editor.contentWindow.focus();
25271 if (typeof sel != "undefined") {
25273 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25275 return this.doc.createRange();
25278 return this.doc.createRange();
25281 getParentElement: function()
25284 this.assignDocWin();
25285 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25287 var range = this.createRange(sel);
25290 var p = range.commonAncestorContainer;
25291 while (p.nodeType == 3) { // text node
25302 * Range intersection.. the hard stuff...
25306 * [ -- selected range --- ]
25310 * if end is before start or hits it. fail.
25311 * if start is after end or hits it fail.
25313 * if either hits (but other is outside. - then it's not
25319 // @see http://www.thismuchiknow.co.uk/?p=64.
25320 rangeIntersectsNode : function(range, node)
25322 var nodeRange = node.ownerDocument.createRange();
25324 nodeRange.selectNode(node);
25326 nodeRange.selectNodeContents(node);
25329 var rangeStartRange = range.cloneRange();
25330 rangeStartRange.collapse(true);
25332 var rangeEndRange = range.cloneRange();
25333 rangeEndRange.collapse(false);
25335 var nodeStartRange = nodeRange.cloneRange();
25336 nodeStartRange.collapse(true);
25338 var nodeEndRange = nodeRange.cloneRange();
25339 nodeEndRange.collapse(false);
25341 return rangeStartRange.compareBoundaryPoints(
25342 Range.START_TO_START, nodeEndRange) == -1 &&
25343 rangeEndRange.compareBoundaryPoints(
25344 Range.START_TO_START, nodeStartRange) == 1;
25348 rangeCompareNode : function(range, node)
25350 var nodeRange = node.ownerDocument.createRange();
25352 nodeRange.selectNode(node);
25354 nodeRange.selectNodeContents(node);
25358 range.collapse(true);
25360 nodeRange.collapse(true);
25362 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25363 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25365 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25367 var nodeIsBefore = ss == 1;
25368 var nodeIsAfter = ee == -1;
25370 if (nodeIsBefore && nodeIsAfter) {
25373 if (!nodeIsBefore && nodeIsAfter) {
25374 return 1; //right trailed.
25377 if (nodeIsBefore && !nodeIsAfter) {
25378 return 2; // left trailed.
25384 // private? - in a new class?
25385 cleanUpPaste : function()
25387 // cleans up the whole document..
25388 Roo.log('cleanuppaste');
25390 this.cleanUpChildren(this.doc.body);
25391 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25392 if (clean != this.doc.body.innerHTML) {
25393 this.doc.body.innerHTML = clean;
25398 cleanWordChars : function(input) {// change the chars to hex code
25399 var he = Roo.HtmlEditorCore;
25401 var output = input;
25402 Roo.each(he.swapCodes, function(sw) {
25403 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25405 output = output.replace(swapper, sw[1]);
25412 cleanUpChildren : function (n)
25414 if (!n.childNodes.length) {
25417 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25418 this.cleanUpChild(n.childNodes[i]);
25425 cleanUpChild : function (node)
25428 //console.log(node);
25429 if (node.nodeName == "#text") {
25430 // clean up silly Windows -- stuff?
25433 if (node.nodeName == "#comment") {
25434 node.parentNode.removeChild(node);
25435 // clean up silly Windows -- stuff?
25438 var lcname = node.tagName.toLowerCase();
25439 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25440 // whitelist of tags..
25442 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25444 node.parentNode.removeChild(node);
25449 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25451 // spans with no attributes - just remove them..
25452 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25453 remove_keep_children = true;
25456 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25457 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25459 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25460 // remove_keep_children = true;
25463 if (remove_keep_children) {
25464 this.cleanUpChildren(node);
25465 // inserts everything just before this node...
25466 while (node.childNodes.length) {
25467 var cn = node.childNodes[0];
25468 node.removeChild(cn);
25469 node.parentNode.insertBefore(cn, node);
25471 node.parentNode.removeChild(node);
25475 if (!node.attributes || !node.attributes.length) {
25480 this.cleanUpChildren(node);
25484 function cleanAttr(n,v)
25487 if (v.match(/^\./) || v.match(/^\//)) {
25490 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25493 if (v.match(/^#/)) {
25496 if (v.match(/^\{/)) { // allow template editing.
25499 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25500 node.removeAttribute(n);
25504 var cwhite = this.cwhite;
25505 var cblack = this.cblack;
25507 function cleanStyle(n,v)
25509 if (v.match(/expression/)) { //XSS?? should we even bother..
25510 node.removeAttribute(n);
25514 var parts = v.split(/;/);
25517 Roo.each(parts, function(p) {
25518 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25522 var l = p.split(':').shift().replace(/\s+/g,'');
25523 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25525 if ( cwhite.length && cblack.indexOf(l) > -1) {
25526 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25527 //node.removeAttribute(n);
25531 // only allow 'c whitelisted system attributes'
25532 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25533 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25534 //node.removeAttribute(n);
25544 if (clean.length) {
25545 node.setAttribute(n, clean.join(';'));
25547 node.removeAttribute(n);
25553 for (var i = node.attributes.length-1; i > -1 ; i--) {
25554 var a = node.attributes[i];
25557 if (a.name.toLowerCase().substr(0,2)=='on') {
25558 node.removeAttribute(a.name);
25561 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25562 node.removeAttribute(a.name);
25565 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25566 cleanAttr(a.name,a.value); // fixme..
25569 if (a.name == 'style') {
25570 cleanStyle(a.name,a.value);
25573 /// clean up MS crap..
25574 // tecnically this should be a list of valid class'es..
25577 if (a.name == 'class') {
25578 if (a.value.match(/^Mso/)) {
25579 node.removeAttribute('class');
25582 if (a.value.match(/^body$/)) {
25583 node.removeAttribute('class');
25594 this.cleanUpChildren(node);
25600 * Clean up MS wordisms...
25602 cleanWord : function(node)
25605 this.cleanWord(this.doc.body);
25610 node.nodeName == 'SPAN' &&
25611 !node.hasAttributes() &&
25612 node.childNodes.length == 1 &&
25613 node.firstChild.nodeName == "#text"
25615 var textNode = node.firstChild;
25616 node.removeChild(textNode);
25617 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25618 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25620 node.parentNode.insertBefore(textNode, node);
25621 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25622 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25624 node.parentNode.removeChild(node);
25627 if (node.nodeName == "#text") {
25628 // clean up silly Windows -- stuff?
25631 if (node.nodeName == "#comment") {
25632 node.parentNode.removeChild(node);
25633 // clean up silly Windows -- stuff?
25637 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25638 node.parentNode.removeChild(node);
25641 //Roo.log(node.tagName);
25642 // remove - but keep children..
25643 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25644 //Roo.log('-- removed');
25645 while (node.childNodes.length) {
25646 var cn = node.childNodes[0];
25647 node.removeChild(cn);
25648 node.parentNode.insertBefore(cn, node);
25649 // move node to parent - and clean it..
25650 this.cleanWord(cn);
25652 node.parentNode.removeChild(node);
25653 /// no need to iterate chidlren = it's got none..
25654 //this.iterateChildren(node, this.cleanWord);
25658 if (node.className.length) {
25660 var cn = node.className.split(/\W+/);
25662 Roo.each(cn, function(cls) {
25663 if (cls.match(/Mso[a-zA-Z]+/)) {
25668 node.className = cna.length ? cna.join(' ') : '';
25670 node.removeAttribute("class");
25674 if (node.hasAttribute("lang")) {
25675 node.removeAttribute("lang");
25678 if (node.hasAttribute("style")) {
25680 var styles = node.getAttribute("style").split(";");
25682 Roo.each(styles, function(s) {
25683 if (!s.match(/:/)) {
25686 var kv = s.split(":");
25687 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25690 // what ever is left... we allow.
25693 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25694 if (!nstyle.length) {
25695 node.removeAttribute('style');
25698 this.iterateChildren(node, this.cleanWord);
25704 * iterateChildren of a Node, calling fn each time, using this as the scole..
25705 * @param {DomNode} node node to iterate children of.
25706 * @param {Function} fn method of this class to call on each item.
25708 iterateChildren : function(node, fn)
25710 if (!node.childNodes.length) {
25713 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25714 fn.call(this, node.childNodes[i])
25720 * cleanTableWidths.
25722 * Quite often pasting from word etc.. results in tables with column and widths.
25723 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25726 cleanTableWidths : function(node)
25731 this.cleanTableWidths(this.doc.body);
25736 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25739 Roo.log(node.tagName);
25740 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25741 this.iterateChildren(node, this.cleanTableWidths);
25744 if (node.hasAttribute('width')) {
25745 node.removeAttribute('width');
25749 if (node.hasAttribute("style")) {
25752 var styles = node.getAttribute("style").split(";");
25754 Roo.each(styles, function(s) {
25755 if (!s.match(/:/)) {
25758 var kv = s.split(":");
25759 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25762 // what ever is left... we allow.
25765 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25766 if (!nstyle.length) {
25767 node.removeAttribute('style');
25771 this.iterateChildren(node, this.cleanTableWidths);
25779 domToHTML : function(currentElement, depth, nopadtext) {
25781 depth = depth || 0;
25782 nopadtext = nopadtext || false;
25784 if (!currentElement) {
25785 return this.domToHTML(this.doc.body);
25788 //Roo.log(currentElement);
25790 var allText = false;
25791 var nodeName = currentElement.nodeName;
25792 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25794 if (nodeName == '#text') {
25796 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25801 if (nodeName != 'BODY') {
25804 // Prints the node tagName, such as <A>, <IMG>, etc
25807 for(i = 0; i < currentElement.attributes.length;i++) {
25809 var aname = currentElement.attributes.item(i).name;
25810 if (!currentElement.attributes.item(i).value.length) {
25813 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25816 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25825 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25828 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25833 // Traverse the tree
25835 var currentElementChild = currentElement.childNodes.item(i);
25836 var allText = true;
25837 var innerHTML = '';
25839 while (currentElementChild) {
25840 // Formatting code (indent the tree so it looks nice on the screen)
25841 var nopad = nopadtext;
25842 if (lastnode == 'SPAN') {
25846 if (currentElementChild.nodeName == '#text') {
25847 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25848 toadd = nopadtext ? toadd : toadd.trim();
25849 if (!nopad && toadd.length > 80) {
25850 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25852 innerHTML += toadd;
25855 currentElementChild = currentElement.childNodes.item(i);
25861 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25863 // Recursively traverse the tree structure of the child node
25864 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25865 lastnode = currentElementChild.nodeName;
25867 currentElementChild=currentElement.childNodes.item(i);
25873 // The remaining code is mostly for formatting the tree
25874 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25879 ret+= "</"+tagName+">";
25885 applyBlacklists : function()
25887 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25888 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25892 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25893 if (b.indexOf(tag) > -1) {
25896 this.white.push(tag);
25900 Roo.each(w, function(tag) {
25901 if (b.indexOf(tag) > -1) {
25904 if (this.white.indexOf(tag) > -1) {
25907 this.white.push(tag);
25912 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25913 if (w.indexOf(tag) > -1) {
25916 this.black.push(tag);
25920 Roo.each(b, function(tag) {
25921 if (w.indexOf(tag) > -1) {
25924 if (this.black.indexOf(tag) > -1) {
25927 this.black.push(tag);
25932 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25933 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25937 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25938 if (b.indexOf(tag) > -1) {
25941 this.cwhite.push(tag);
25945 Roo.each(w, function(tag) {
25946 if (b.indexOf(tag) > -1) {
25949 if (this.cwhite.indexOf(tag) > -1) {
25952 this.cwhite.push(tag);
25957 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25958 if (w.indexOf(tag) > -1) {
25961 this.cblack.push(tag);
25965 Roo.each(b, function(tag) {
25966 if (w.indexOf(tag) > -1) {
25969 if (this.cblack.indexOf(tag) > -1) {
25972 this.cblack.push(tag);
25977 setStylesheets : function(stylesheets)
25979 if(typeof(stylesheets) == 'string'){
25980 Roo.get(this.iframe.contentDocument.head).createChild({
25982 rel : 'stylesheet',
25991 Roo.each(stylesheets, function(s) {
25996 Roo.get(_this.iframe.contentDocument.head).createChild({
25998 rel : 'stylesheet',
26007 removeStylesheets : function()
26011 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26016 setStyle : function(style)
26018 Roo.get(this.iframe.contentDocument.head).createChild({
26027 // hide stuff that is not compatible
26041 * @event specialkey
26045 * @cfg {String} fieldClass @hide
26048 * @cfg {String} focusClass @hide
26051 * @cfg {String} autoCreate @hide
26054 * @cfg {String} inputType @hide
26057 * @cfg {String} invalidClass @hide
26060 * @cfg {String} invalidText @hide
26063 * @cfg {String} msgFx @hide
26066 * @cfg {String} validateOnBlur @hide
26070 Roo.HtmlEditorCore.white = [
26071 'area', 'br', 'img', 'input', 'hr', 'wbr',
26073 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26074 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26075 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26076 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26077 'table', 'ul', 'xmp',
26079 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26082 'dir', 'menu', 'ol', 'ul', 'dl',
26088 Roo.HtmlEditorCore.black = [
26089 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26091 'base', 'basefont', 'bgsound', 'blink', 'body',
26092 'frame', 'frameset', 'head', 'html', 'ilayer',
26093 'iframe', 'layer', 'link', 'meta', 'object',
26094 'script', 'style' ,'title', 'xml' // clean later..
26096 Roo.HtmlEditorCore.clean = [
26097 'script', 'style', 'title', 'xml'
26099 Roo.HtmlEditorCore.remove = [
26104 Roo.HtmlEditorCore.ablack = [
26108 Roo.HtmlEditorCore.aclean = [
26109 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26113 Roo.HtmlEditorCore.pwhite= [
26114 'http', 'https', 'mailto'
26117 // white listed style attributes.
26118 Roo.HtmlEditorCore.cwhite= [
26119 // 'text-align', /// default is to allow most things..
26125 // black listed style attributes.
26126 Roo.HtmlEditorCore.cblack= [
26127 // 'font-size' -- this can be set by the project
26131 Roo.HtmlEditorCore.swapCodes =[
26132 [ 8211, "–" ],
26133 [ 8212, "—" ],
26150 * @class Roo.bootstrap.HtmlEditor
26151 * @extends Roo.bootstrap.TextArea
26152 * Bootstrap HtmlEditor class
26155 * Create a new HtmlEditor
26156 * @param {Object} config The config object
26159 Roo.bootstrap.HtmlEditor = function(config){
26160 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26161 if (!this.toolbars) {
26162 this.toolbars = [];
26165 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26168 * @event initialize
26169 * Fires when the editor is fully initialized (including the iframe)
26170 * @param {HtmlEditor} this
26175 * Fires when the editor is first receives the focus. Any insertion must wait
26176 * until after this event.
26177 * @param {HtmlEditor} this
26181 * @event beforesync
26182 * Fires before the textarea is updated with content from the editor iframe. Return false
26183 * to cancel the sync.
26184 * @param {HtmlEditor} this
26185 * @param {String} html
26189 * @event beforepush
26190 * Fires before the iframe editor is updated with content from the textarea. Return false
26191 * to cancel the push.
26192 * @param {HtmlEditor} this
26193 * @param {String} html
26198 * Fires when the textarea is updated with content from the editor iframe.
26199 * @param {HtmlEditor} this
26200 * @param {String} html
26205 * Fires when the iframe editor is updated with content from the textarea.
26206 * @param {HtmlEditor} this
26207 * @param {String} html
26211 * @event editmodechange
26212 * Fires when the editor switches edit modes
26213 * @param {HtmlEditor} this
26214 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26216 editmodechange: true,
26218 * @event editorevent
26219 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26220 * @param {HtmlEditor} this
26224 * @event firstfocus
26225 * Fires when on first focus - needed by toolbars..
26226 * @param {HtmlEditor} this
26231 * Auto save the htmlEditor value as a file into Events
26232 * @param {HtmlEditor} this
26236 * @event savedpreview
26237 * preview the saved version of htmlEditor
26238 * @param {HtmlEditor} this
26245 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26249 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26254 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26259 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26264 * @cfg {Number} height (in pixels)
26268 * @cfg {Number} width (in pixels)
26273 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26276 stylesheets: false,
26281 // private properties
26282 validationEvent : false,
26284 initialized : false,
26287 onFocus : Roo.emptyFn,
26289 hideMode:'offsets',
26291 tbContainer : false,
26295 toolbarContainer :function() {
26296 return this.wrap.select('.x-html-editor-tb',true).first();
26300 * Protected method that will not generally be called directly. It
26301 * is called when the editor creates its toolbar. Override this method if you need to
26302 * add custom toolbar buttons.
26303 * @param {HtmlEditor} editor
26305 createToolbar : function(){
26306 Roo.log('renewing');
26307 Roo.log("create toolbars");
26309 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26310 this.toolbars[0].render(this.toolbarContainer());
26314 // if (!editor.toolbars || !editor.toolbars.length) {
26315 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26318 // for (var i =0 ; i < editor.toolbars.length;i++) {
26319 // editor.toolbars[i] = Roo.factory(
26320 // typeof(editor.toolbars[i]) == 'string' ?
26321 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26322 // Roo.bootstrap.HtmlEditor);
26323 // editor.toolbars[i].init(editor);
26329 onRender : function(ct, position)
26331 // Roo.log("Call onRender: " + this.xtype);
26333 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26335 this.wrap = this.inputEl().wrap({
26336 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26339 this.editorcore.onRender(ct, position);
26341 if (this.resizable) {
26342 this.resizeEl = new Roo.Resizable(this.wrap, {
26346 minHeight : this.height,
26347 height: this.height,
26348 handles : this.resizable,
26351 resize : function(r, w, h) {
26352 _t.onResize(w,h); // -something
26358 this.createToolbar(this);
26361 if(!this.width && this.resizable){
26362 this.setSize(this.wrap.getSize());
26364 if (this.resizeEl) {
26365 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26366 // should trigger onReize..
26372 onResize : function(w, h)
26374 Roo.log('resize: ' +w + ',' + h );
26375 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26379 if(this.inputEl() ){
26380 if(typeof w == 'number'){
26381 var aw = w - this.wrap.getFrameWidth('lr');
26382 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26385 if(typeof h == 'number'){
26386 var tbh = -11; // fixme it needs to tool bar size!
26387 for (var i =0; i < this.toolbars.length;i++) {
26388 // fixme - ask toolbars for heights?
26389 tbh += this.toolbars[i].el.getHeight();
26390 //if (this.toolbars[i].footer) {
26391 // tbh += this.toolbars[i].footer.el.getHeight();
26399 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26400 ah -= 5; // knock a few pixes off for look..
26401 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26405 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26406 this.editorcore.onResize(ew,eh);
26411 * Toggles the editor between standard and source edit mode.
26412 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26414 toggleSourceEdit : function(sourceEditMode)
26416 this.editorcore.toggleSourceEdit(sourceEditMode);
26418 if(this.editorcore.sourceEditMode){
26419 Roo.log('editor - showing textarea');
26422 // Roo.log(this.syncValue());
26424 this.inputEl().removeClass(['hide', 'x-hidden']);
26425 this.inputEl().dom.removeAttribute('tabIndex');
26426 this.inputEl().focus();
26428 Roo.log('editor - hiding textarea');
26430 // Roo.log(this.pushValue());
26433 this.inputEl().addClass(['hide', 'x-hidden']);
26434 this.inputEl().dom.setAttribute('tabIndex', -1);
26435 //this.deferFocus();
26438 if(this.resizable){
26439 this.setSize(this.wrap.getSize());
26442 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26445 // private (for BoxComponent)
26446 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26448 // private (for BoxComponent)
26449 getResizeEl : function(){
26453 // private (for BoxComponent)
26454 getPositionEl : function(){
26459 initEvents : function(){
26460 this.originalValue = this.getValue();
26464 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26467 // markInvalid : Roo.emptyFn,
26469 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26472 // clearInvalid : Roo.emptyFn,
26474 setValue : function(v){
26475 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26476 this.editorcore.pushValue();
26481 deferFocus : function(){
26482 this.focus.defer(10, this);
26486 focus : function(){
26487 this.editorcore.focus();
26493 onDestroy : function(){
26499 for (var i =0; i < this.toolbars.length;i++) {
26500 // fixme - ask toolbars for heights?
26501 this.toolbars[i].onDestroy();
26504 this.wrap.dom.innerHTML = '';
26505 this.wrap.remove();
26510 onFirstFocus : function(){
26511 //Roo.log("onFirstFocus");
26512 this.editorcore.onFirstFocus();
26513 for (var i =0; i < this.toolbars.length;i++) {
26514 this.toolbars[i].onFirstFocus();
26520 syncValue : function()
26522 this.editorcore.syncValue();
26525 pushValue : function()
26527 this.editorcore.pushValue();
26531 // hide stuff that is not compatible
26545 * @event specialkey
26549 * @cfg {String} fieldClass @hide
26552 * @cfg {String} focusClass @hide
26555 * @cfg {String} autoCreate @hide
26558 * @cfg {String} inputType @hide
26562 * @cfg {String} invalidText @hide
26565 * @cfg {String} msgFx @hide
26568 * @cfg {String} validateOnBlur @hide
26577 Roo.namespace('Roo.bootstrap.htmleditor');
26579 * @class Roo.bootstrap.HtmlEditorToolbar1
26585 new Roo.bootstrap.HtmlEditor({
26588 new Roo.bootstrap.HtmlEditorToolbar1({
26589 disable : { fonts: 1 , format: 1, ..., ... , ...],
26595 * @cfg {Object} disable List of elements to disable..
26596 * @cfg {Array} btns List of additional buttons.
26600 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26603 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26606 Roo.apply(this, config);
26608 // default disabled, based on 'good practice'..
26609 this.disable = this.disable || {};
26610 Roo.applyIf(this.disable, {
26613 specialElements : true
26615 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26617 this.editor = config.editor;
26618 this.editorcore = config.editor.editorcore;
26620 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26622 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26623 // dont call parent... till later.
26625 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26630 editorcore : false,
26635 "h1","h2","h3","h4","h5","h6",
26637 "abbr", "acronym", "address", "cite", "samp", "var",
26641 onRender : function(ct, position)
26643 // Roo.log("Call onRender: " + this.xtype);
26645 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26647 this.el.dom.style.marginBottom = '0';
26649 var editorcore = this.editorcore;
26650 var editor= this.editor;
26653 var btn = function(id,cmd , toggle, handler, html){
26655 var event = toggle ? 'toggle' : 'click';
26660 xns: Roo.bootstrap,
26664 enableToggle:toggle !== false,
26666 pressed : toggle ? false : null,
26669 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26670 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26676 // var cb_box = function...
26681 xns: Roo.bootstrap,
26686 xns: Roo.bootstrap,
26690 Roo.each(this.formats, function(f) {
26691 style.menu.items.push({
26693 xns: Roo.bootstrap,
26694 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26699 editorcore.insertTag(this.tagname);
26706 children.push(style);
26708 btn('bold',false,true);
26709 btn('italic',false,true);
26710 btn('align-left', 'justifyleft',true);
26711 btn('align-center', 'justifycenter',true);
26712 btn('align-right' , 'justifyright',true);
26713 btn('link', false, false, function(btn) {
26714 //Roo.log("create link?");
26715 var url = prompt(this.createLinkText, this.defaultLinkValue);
26716 if(url && url != 'http:/'+'/'){
26717 this.editorcore.relayCmd('createlink', url);
26720 btn('list','insertunorderedlist',true);
26721 btn('pencil', false,true, function(btn){
26723 this.toggleSourceEdit(btn.pressed);
26726 if (this.editor.btns.length > 0) {
26727 for (var i = 0; i<this.editor.btns.length; i++) {
26728 children.push(this.editor.btns[i]);
26736 xns: Roo.bootstrap,
26741 xns: Roo.bootstrap,
26746 cog.menu.items.push({
26748 xns: Roo.bootstrap,
26749 html : Clean styles,
26754 editorcore.insertTag(this.tagname);
26763 this.xtype = 'NavSimplebar';
26765 for(var i=0;i< children.length;i++) {
26767 this.buttons.add(this.addxtypeChild(children[i]));
26771 editor.on('editorevent', this.updateToolbar, this);
26773 onBtnClick : function(id)
26775 this.editorcore.relayCmd(id);
26776 this.editorcore.focus();
26780 * Protected method that will not generally be called directly. It triggers
26781 * a toolbar update by reading the markup state of the current selection in the editor.
26783 updateToolbar: function(){
26785 if(!this.editorcore.activated){
26786 this.editor.onFirstFocus(); // is this neeed?
26790 var btns = this.buttons;
26791 var doc = this.editorcore.doc;
26792 btns.get('bold').setActive(doc.queryCommandState('bold'));
26793 btns.get('italic').setActive(doc.queryCommandState('italic'));
26794 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26796 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26797 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26798 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26800 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26801 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26804 var ans = this.editorcore.getAllAncestors();
26805 if (this.formatCombo) {
26808 var store = this.formatCombo.store;
26809 this.formatCombo.setValue("");
26810 for (var i =0; i < ans.length;i++) {
26811 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26813 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26821 // hides menus... - so this cant be on a menu...
26822 Roo.bootstrap.MenuMgr.hideAll();
26824 Roo.bootstrap.MenuMgr.hideAll();
26825 //this.editorsyncValue();
26827 onFirstFocus: function() {
26828 this.buttons.each(function(item){
26832 toggleSourceEdit : function(sourceEditMode){
26835 if(sourceEditMode){
26836 Roo.log("disabling buttons");
26837 this.buttons.each( function(item){
26838 if(item.cmd != 'pencil'){
26844 Roo.log("enabling buttons");
26845 if(this.editorcore.initialized){
26846 this.buttons.each( function(item){
26852 Roo.log("calling toggole on editor");
26853 // tell the editor that it's been pressed..
26854 this.editor.toggleSourceEdit(sourceEditMode);
26868 * @class Roo.bootstrap.Markdown
26869 * @extends Roo.bootstrap.TextArea
26870 * Bootstrap Showdown editable area
26871 * @cfg {string} content
26874 * Create a new Showdown
26877 Roo.bootstrap.Markdown = function(config){
26878 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26882 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26886 initEvents : function()
26889 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26890 this.markdownEl = this.el.createChild({
26891 cls : 'roo-markdown-area'
26893 this.inputEl().addClass('d-none');
26894 if (this.getValue() == '') {
26895 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26898 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26900 this.markdownEl.on('click', this.toggleTextEdit, this);
26901 this.on('blur', this.toggleTextEdit, this);
26902 this.on('specialkey', this.resizeTextArea, this);
26905 toggleTextEdit : function()
26907 var sh = this.markdownEl.getHeight();
26908 this.inputEl().addClass('d-none');
26909 this.markdownEl.addClass('d-none');
26910 if (!this.editing) {
26912 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26913 this.inputEl().removeClass('d-none');
26914 this.inputEl().focus();
26915 this.editing = true;
26918 // show showdown...
26919 this.updateMarkdown();
26920 this.markdownEl.removeClass('d-none');
26921 this.editing = false;
26924 updateMarkdown : function()
26926 if (this.getValue() == '') {
26927 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26931 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26934 resizeTextArea: function () {
26937 Roo.log([sh, this.getValue().split("\n").length * 30]);
26938 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26940 setValue : function(val)
26942 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26943 if (!this.editing) {
26944 this.updateMarkdown();
26950 if (!this.editing) {
26951 this.toggleTextEdit();
26959 * @class Roo.bootstrap.Table.AbstractSelectionModel
26960 * @extends Roo.util.Observable
26961 * Abstract base class for grid SelectionModels. It provides the interface that should be
26962 * implemented by descendant classes. This class should not be directly instantiated.
26965 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26966 this.locked = false;
26967 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26971 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26972 /** @ignore Called by the grid automatically. Do not call directly. */
26973 init : function(grid){
26979 * Locks the selections.
26982 this.locked = true;
26986 * Unlocks the selections.
26988 unlock : function(){
26989 this.locked = false;
26993 * Returns true if the selections are locked.
26994 * @return {Boolean}
26996 isLocked : function(){
26997 return this.locked;
27001 initEvents : function ()
27007 * @extends Roo.bootstrap.Table.AbstractSelectionModel
27008 * @class Roo.bootstrap.Table.RowSelectionModel
27009 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27010 * It supports multiple selections and keyboard selection/navigation.
27012 * @param {Object} config
27015 Roo.bootstrap.Table.RowSelectionModel = function(config){
27016 Roo.apply(this, config);
27017 this.selections = new Roo.util.MixedCollection(false, function(o){
27022 this.lastActive = false;
27026 * @event selectionchange
27027 * Fires when the selection changes
27028 * @param {SelectionModel} this
27030 "selectionchange" : true,
27032 * @event afterselectionchange
27033 * Fires after the selection changes (eg. by key press or clicking)
27034 * @param {SelectionModel} this
27036 "afterselectionchange" : true,
27038 * @event beforerowselect
27039 * Fires when a row is selected being selected, return false to cancel.
27040 * @param {SelectionModel} this
27041 * @param {Number} rowIndex The selected index
27042 * @param {Boolean} keepExisting False if other selections will be cleared
27044 "beforerowselect" : true,
27047 * Fires when a row is selected.
27048 * @param {SelectionModel} this
27049 * @param {Number} rowIndex The selected index
27050 * @param {Roo.data.Record} r The record
27052 "rowselect" : true,
27054 * @event rowdeselect
27055 * Fires when a row is deselected.
27056 * @param {SelectionModel} this
27057 * @param {Number} rowIndex The selected index
27059 "rowdeselect" : true
27061 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27062 this.locked = false;
27065 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27067 * @cfg {Boolean} singleSelect
27068 * True to allow selection of only one row at a time (defaults to false)
27070 singleSelect : false,
27073 initEvents : function()
27076 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27077 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27078 //}else{ // allow click to work like normal
27079 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27081 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27082 this.grid.on("rowclick", this.handleMouseDown, this);
27084 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27085 "up" : function(e){
27087 this.selectPrevious(e.shiftKey);
27088 }else if(this.last !== false && this.lastActive !== false){
27089 var last = this.last;
27090 this.selectRange(this.last, this.lastActive-1);
27091 this.grid.getView().focusRow(this.lastActive);
27092 if(last !== false){
27096 this.selectFirstRow();
27098 this.fireEvent("afterselectionchange", this);
27100 "down" : function(e){
27102 this.selectNext(e.shiftKey);
27103 }else if(this.last !== false && this.lastActive !== false){
27104 var last = this.last;
27105 this.selectRange(this.last, this.lastActive+1);
27106 this.grid.getView().focusRow(this.lastActive);
27107 if(last !== false){
27111 this.selectFirstRow();
27113 this.fireEvent("afterselectionchange", this);
27117 this.grid.store.on('load', function(){
27118 this.selections.clear();
27121 var view = this.grid.view;
27122 view.on("refresh", this.onRefresh, this);
27123 view.on("rowupdated", this.onRowUpdated, this);
27124 view.on("rowremoved", this.onRemove, this);
27129 onRefresh : function()
27131 var ds = this.grid.store, i, v = this.grid.view;
27132 var s = this.selections;
27133 s.each(function(r){
27134 if((i = ds.indexOfId(r.id)) != -1){
27143 onRemove : function(v, index, r){
27144 this.selections.remove(r);
27148 onRowUpdated : function(v, index, r){
27149 if(this.isSelected(r)){
27150 v.onRowSelect(index);
27156 * @param {Array} records The records to select
27157 * @param {Boolean} keepExisting (optional) True to keep existing selections
27159 selectRecords : function(records, keepExisting)
27162 this.clearSelections();
27164 var ds = this.grid.store;
27165 for(var i = 0, len = records.length; i < len; i++){
27166 this.selectRow(ds.indexOf(records[i]), true);
27171 * Gets the number of selected rows.
27174 getCount : function(){
27175 return this.selections.length;
27179 * Selects the first row in the grid.
27181 selectFirstRow : function(){
27186 * Select the last row.
27187 * @param {Boolean} keepExisting (optional) True to keep existing selections
27189 selectLastRow : function(keepExisting){
27190 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27191 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27195 * Selects the row immediately following the last selected row.
27196 * @param {Boolean} keepExisting (optional) True to keep existing selections
27198 selectNext : function(keepExisting)
27200 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27201 this.selectRow(this.last+1, keepExisting);
27202 this.grid.getView().focusRow(this.last);
27207 * Selects the row that precedes the last selected row.
27208 * @param {Boolean} keepExisting (optional) True to keep existing selections
27210 selectPrevious : function(keepExisting){
27212 this.selectRow(this.last-1, keepExisting);
27213 this.grid.getView().focusRow(this.last);
27218 * Returns the selected records
27219 * @return {Array} Array of selected records
27221 getSelections : function(){
27222 return [].concat(this.selections.items);
27226 * Returns the first selected record.
27229 getSelected : function(){
27230 return this.selections.itemAt(0);
27235 * Clears all selections.
27237 clearSelections : function(fast)
27243 var ds = this.grid.store;
27244 var s = this.selections;
27245 s.each(function(r){
27246 this.deselectRow(ds.indexOfId(r.id));
27250 this.selections.clear();
27257 * Selects all rows.
27259 selectAll : function(){
27263 this.selections.clear();
27264 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27265 this.selectRow(i, true);
27270 * Returns True if there is a selection.
27271 * @return {Boolean}
27273 hasSelection : function(){
27274 return this.selections.length > 0;
27278 * Returns True if the specified row is selected.
27279 * @param {Number/Record} record The record or index of the record to check
27280 * @return {Boolean}
27282 isSelected : function(index){
27283 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27284 return (r && this.selections.key(r.id) ? true : false);
27288 * Returns True if the specified record id is selected.
27289 * @param {String} id The id of record to check
27290 * @return {Boolean}
27292 isIdSelected : function(id){
27293 return (this.selections.key(id) ? true : false);
27298 handleMouseDBClick : function(e, t){
27302 handleMouseDown : function(e, t)
27304 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27305 if(this.isLocked() || rowIndex < 0 ){
27308 if(e.shiftKey && this.last !== false){
27309 var last = this.last;
27310 this.selectRange(last, rowIndex, e.ctrlKey);
27311 this.last = last; // reset the last
27315 var isSelected = this.isSelected(rowIndex);
27316 //Roo.log("select row:" + rowIndex);
27318 this.deselectRow(rowIndex);
27320 this.selectRow(rowIndex, true);
27324 if(e.button !== 0 && isSelected){
27325 alert('rowIndex 2: ' + rowIndex);
27326 view.focusRow(rowIndex);
27327 }else if(e.ctrlKey && isSelected){
27328 this.deselectRow(rowIndex);
27329 }else if(!isSelected){
27330 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27331 view.focusRow(rowIndex);
27335 this.fireEvent("afterselectionchange", this);
27338 handleDragableRowClick : function(grid, rowIndex, e)
27340 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27341 this.selectRow(rowIndex, false);
27342 grid.view.focusRow(rowIndex);
27343 this.fireEvent("afterselectionchange", this);
27348 * Selects multiple rows.
27349 * @param {Array} rows Array of the indexes of the row to select
27350 * @param {Boolean} keepExisting (optional) True to keep existing selections
27352 selectRows : function(rows, keepExisting){
27354 this.clearSelections();
27356 for(var i = 0, len = rows.length; i < len; i++){
27357 this.selectRow(rows[i], true);
27362 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27363 * @param {Number} startRow The index of the first row in the range
27364 * @param {Number} endRow The index of the last row in the range
27365 * @param {Boolean} keepExisting (optional) True to retain existing selections
27367 selectRange : function(startRow, endRow, keepExisting){
27372 this.clearSelections();
27374 if(startRow <= endRow){
27375 for(var i = startRow; i <= endRow; i++){
27376 this.selectRow(i, true);
27379 for(var i = startRow; i >= endRow; i--){
27380 this.selectRow(i, true);
27386 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27387 * @param {Number} startRow The index of the first row in the range
27388 * @param {Number} endRow The index of the last row in the range
27390 deselectRange : function(startRow, endRow, preventViewNotify){
27394 for(var i = startRow; i <= endRow; i++){
27395 this.deselectRow(i, preventViewNotify);
27401 * @param {Number} row The index of the row to select
27402 * @param {Boolean} keepExisting (optional) True to keep existing selections
27404 selectRow : function(index, keepExisting, preventViewNotify)
27406 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27409 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27410 if(!keepExisting || this.singleSelect){
27411 this.clearSelections();
27414 var r = this.grid.store.getAt(index);
27415 //console.log('selectRow - record id :' + r.id);
27417 this.selections.add(r);
27418 this.last = this.lastActive = index;
27419 if(!preventViewNotify){
27420 var proxy = new Roo.Element(
27421 this.grid.getRowDom(index)
27423 proxy.addClass('bg-info info');
27425 this.fireEvent("rowselect", this, index, r);
27426 this.fireEvent("selectionchange", this);
27432 * @param {Number} row The index of the row to deselect
27434 deselectRow : function(index, preventViewNotify)
27439 if(this.last == index){
27442 if(this.lastActive == index){
27443 this.lastActive = false;
27446 var r = this.grid.store.getAt(index);
27451 this.selections.remove(r);
27452 //.console.log('deselectRow - record id :' + r.id);
27453 if(!preventViewNotify){
27455 var proxy = new Roo.Element(
27456 this.grid.getRowDom(index)
27458 proxy.removeClass('bg-info info');
27460 this.fireEvent("rowdeselect", this, index);
27461 this.fireEvent("selectionchange", this);
27465 restoreLast : function(){
27467 this.last = this._last;
27472 acceptsNav : function(row, col, cm){
27473 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27477 onEditorKey : function(field, e){
27478 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27483 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27485 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27487 }else if(k == e.ENTER && !e.ctrlKey){
27491 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27493 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27495 }else if(k == e.ESC){
27499 g.startEditing(newCell[0], newCell[1]);
27505 * Ext JS Library 1.1.1
27506 * Copyright(c) 2006-2007, Ext JS, LLC.
27508 * Originally Released Under LGPL - original licence link has changed is not relivant.
27511 * <script type="text/javascript">
27515 * @class Roo.bootstrap.PagingToolbar
27516 * @extends Roo.bootstrap.NavSimplebar
27517 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27519 * Create a new PagingToolbar
27520 * @param {Object} config The config object
27521 * @param {Roo.data.Store} store
27523 Roo.bootstrap.PagingToolbar = function(config)
27525 // old args format still supported... - xtype is prefered..
27526 // created from xtype...
27528 this.ds = config.dataSource;
27530 if (config.store && !this.ds) {
27531 this.store= Roo.factory(config.store, Roo.data);
27532 this.ds = this.store;
27533 this.ds.xmodule = this.xmodule || false;
27536 this.toolbarItems = [];
27537 if (config.items) {
27538 this.toolbarItems = config.items;
27541 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27546 this.bind(this.ds);
27549 if (Roo.bootstrap.version == 4) {
27550 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27552 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27557 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27559 * @cfg {Roo.data.Store} dataSource
27560 * The underlying data store providing the paged data
27563 * @cfg {String/HTMLElement/Element} container
27564 * container The id or element that will contain the toolbar
27567 * @cfg {Boolean} displayInfo
27568 * True to display the displayMsg (defaults to false)
27571 * @cfg {Number} pageSize
27572 * The number of records to display per page (defaults to 20)
27576 * @cfg {String} displayMsg
27577 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27579 displayMsg : 'Displaying {0} - {1} of {2}',
27581 * @cfg {String} emptyMsg
27582 * The message to display when no records are found (defaults to "No data to display")
27584 emptyMsg : 'No data to display',
27586 * Customizable piece of the default paging text (defaults to "Page")
27589 beforePageText : "Page",
27591 * Customizable piece of the default paging text (defaults to "of %0")
27594 afterPageText : "of {0}",
27596 * Customizable piece of the default paging text (defaults to "First Page")
27599 firstText : "First Page",
27601 * Customizable piece of the default paging text (defaults to "Previous Page")
27604 prevText : "Previous Page",
27606 * Customizable piece of the default paging text (defaults to "Next Page")
27609 nextText : "Next Page",
27611 * Customizable piece of the default paging text (defaults to "Last Page")
27614 lastText : "Last Page",
27616 * Customizable piece of the default paging text (defaults to "Refresh")
27619 refreshText : "Refresh",
27623 onRender : function(ct, position)
27625 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27626 this.navgroup.parentId = this.id;
27627 this.navgroup.onRender(this.el, null);
27628 // add the buttons to the navgroup
27630 if(this.displayInfo){
27631 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27632 this.displayEl = this.el.select('.x-paging-info', true).first();
27633 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27634 // this.displayEl = navel.el.select('span',true).first();
27640 Roo.each(_this.buttons, function(e){ // this might need to use render????
27641 Roo.factory(e).render(_this.el);
27645 Roo.each(_this.toolbarItems, function(e) {
27646 _this.navgroup.addItem(e);
27650 this.first = this.navgroup.addItem({
27651 tooltip: this.firstText,
27652 cls: "prev btn-outline-secondary",
27653 html : ' <i class="fa fa-step-backward"></i>',
27655 preventDefault: true,
27656 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27659 this.prev = this.navgroup.addItem({
27660 tooltip: this.prevText,
27661 cls: "prev btn-outline-secondary",
27662 html : ' <i class="fa fa-backward"></i>',
27664 preventDefault: true,
27665 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27667 //this.addSeparator();
27670 var field = this.navgroup.addItem( {
27672 cls : 'x-paging-position btn-outline-secondary',
27674 html : this.beforePageText +
27675 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27676 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27679 this.field = field.el.select('input', true).first();
27680 this.field.on("keydown", this.onPagingKeydown, this);
27681 this.field.on("focus", function(){this.dom.select();});
27684 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27685 //this.field.setHeight(18);
27686 //this.addSeparator();
27687 this.next = this.navgroup.addItem({
27688 tooltip: this.nextText,
27689 cls: "next btn-outline-secondary",
27690 html : ' <i class="fa fa-forward"></i>',
27692 preventDefault: true,
27693 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27695 this.last = this.navgroup.addItem({
27696 tooltip: this.lastText,
27697 html : ' <i class="fa fa-step-forward"></i>',
27698 cls: "next btn-outline-secondary",
27700 preventDefault: true,
27701 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27703 //this.addSeparator();
27704 this.loading = this.navgroup.addItem({
27705 tooltip: this.refreshText,
27706 cls: "btn-outline-secondary",
27707 html : ' <i class="fa fa-refresh"></i>',
27708 preventDefault: true,
27709 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27715 updateInfo : function(){
27716 if(this.displayEl){
27717 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27718 var msg = count == 0 ?
27722 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27724 this.displayEl.update(msg);
27729 onLoad : function(ds, r, o)
27731 this.cursor = o.params && o.params.start ? o.params.start : 0;
27733 var d = this.getPageData(),
27738 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27739 this.field.dom.value = ap;
27740 this.first.setDisabled(ap == 1);
27741 this.prev.setDisabled(ap == 1);
27742 this.next.setDisabled(ap == ps);
27743 this.last.setDisabled(ap == ps);
27744 this.loading.enable();
27749 getPageData : function(){
27750 var total = this.ds.getTotalCount();
27753 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27754 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27759 onLoadError : function(){
27760 this.loading.enable();
27764 onPagingKeydown : function(e){
27765 var k = e.getKey();
27766 var d = this.getPageData();
27768 var v = this.field.dom.value, pageNum;
27769 if(!v || isNaN(pageNum = parseInt(v, 10))){
27770 this.field.dom.value = d.activePage;
27773 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27774 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27777 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))
27779 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27780 this.field.dom.value = pageNum;
27781 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27784 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27786 var v = this.field.dom.value, pageNum;
27787 var increment = (e.shiftKey) ? 10 : 1;
27788 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27791 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27792 this.field.dom.value = d.activePage;
27795 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27797 this.field.dom.value = parseInt(v, 10) + increment;
27798 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27799 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27806 beforeLoad : function(){
27808 this.loading.disable();
27813 onClick : function(which){
27822 ds.load({params:{start: 0, limit: this.pageSize}});
27825 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27828 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27831 var total = ds.getTotalCount();
27832 var extra = total % this.pageSize;
27833 var lastStart = extra ? (total - extra) : total-this.pageSize;
27834 ds.load({params:{start: lastStart, limit: this.pageSize}});
27837 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27843 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27844 * @param {Roo.data.Store} store The data store to unbind
27846 unbind : function(ds){
27847 ds.un("beforeload", this.beforeLoad, this);
27848 ds.un("load", this.onLoad, this);
27849 ds.un("loadexception", this.onLoadError, this);
27850 ds.un("remove", this.updateInfo, this);
27851 ds.un("add", this.updateInfo, this);
27852 this.ds = undefined;
27856 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27857 * @param {Roo.data.Store} store The data store to bind
27859 bind : function(ds){
27860 ds.on("beforeload", this.beforeLoad, this);
27861 ds.on("load", this.onLoad, this);
27862 ds.on("loadexception", this.onLoadError, this);
27863 ds.on("remove", this.updateInfo, this);
27864 ds.on("add", this.updateInfo, this);
27875 * @class Roo.bootstrap.MessageBar
27876 * @extends Roo.bootstrap.Component
27877 * Bootstrap MessageBar class
27878 * @cfg {String} html contents of the MessageBar
27879 * @cfg {String} weight (info | success | warning | danger) default info
27880 * @cfg {String} beforeClass insert the bar before the given class
27881 * @cfg {Boolean} closable (true | false) default false
27882 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27885 * Create a new Element
27886 * @param {Object} config The config object
27889 Roo.bootstrap.MessageBar = function(config){
27890 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27893 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27899 beforeClass: 'bootstrap-sticky-wrap',
27901 getAutoCreate : function(){
27905 cls: 'alert alert-dismissable alert-' + this.weight,
27910 html: this.html || ''
27916 cfg.cls += ' alert-messages-fixed';
27930 onRender : function(ct, position)
27932 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27935 var cfg = Roo.apply({}, this.getAutoCreate());
27939 cfg.cls += ' ' + this.cls;
27942 cfg.style = this.style;
27944 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27946 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27949 this.el.select('>button.close').on('click', this.hide, this);
27955 if (!this.rendered) {
27961 this.fireEvent('show', this);
27967 if (!this.rendered) {
27973 this.fireEvent('hide', this);
27976 update : function()
27978 // var e = this.el.dom.firstChild;
27980 // if(this.closable){
27981 // e = e.nextSibling;
27984 // e.data = this.html || '';
27986 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28002 * @class Roo.bootstrap.Graph
28003 * @extends Roo.bootstrap.Component
28004 * Bootstrap Graph class
28008 @cfg {String} graphtype bar | vbar | pie
28009 @cfg {number} g_x coodinator | centre x (pie)
28010 @cfg {number} g_y coodinator | centre y (pie)
28011 @cfg {number} g_r radius (pie)
28012 @cfg {number} g_height height of the chart (respected by all elements in the set)
28013 @cfg {number} g_width width of the chart (respected by all elements in the set)
28014 @cfg {Object} title The title of the chart
28017 -opts (object) options for the chart
28019 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28020 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28022 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.
28023 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28025 o stretch (boolean)
28027 -opts (object) options for the pie
28030 o startAngle (number)
28031 o endAngle (number)
28035 * Create a new Input
28036 * @param {Object} config The config object
28039 Roo.bootstrap.Graph = function(config){
28040 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28046 * The img click event for the img.
28047 * @param {Roo.EventObject} e
28053 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28064 //g_colors: this.colors,
28071 getAutoCreate : function(){
28082 onRender : function(ct,position){
28085 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28087 if (typeof(Raphael) == 'undefined') {
28088 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28092 this.raphael = Raphael(this.el.dom);
28094 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28095 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28096 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28097 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28099 r.text(160, 10, "Single Series Chart").attr(txtattr);
28100 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28101 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28102 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28104 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28105 r.barchart(330, 10, 300, 220, data1);
28106 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28107 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28110 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28111 // r.barchart(30, 30, 560, 250, xdata, {
28112 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28113 // axis : "0 0 1 1",
28114 // axisxlabels : xdata
28115 // //yvalues : cols,
28118 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28120 // this.load(null,xdata,{
28121 // axis : "0 0 1 1",
28122 // axisxlabels : xdata
28127 load : function(graphtype,xdata,opts)
28129 this.raphael.clear();
28131 graphtype = this.graphtype;
28136 var r = this.raphael,
28137 fin = function () {
28138 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28140 fout = function () {
28141 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28143 pfin = function() {
28144 this.sector.stop();
28145 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28148 this.label[0].stop();
28149 this.label[0].attr({ r: 7.5 });
28150 this.label[1].attr({ "font-weight": 800 });
28153 pfout = function() {
28154 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28157 this.label[0].animate({ r: 5 }, 500, "bounce");
28158 this.label[1].attr({ "font-weight": 400 });
28164 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28167 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28170 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28171 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28173 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28180 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28185 setTitle: function(o)
28190 initEvents: function() {
28193 this.el.on('click', this.onClick, this);
28197 onClick : function(e)
28199 Roo.log('img onclick');
28200 this.fireEvent('click', this, e);
28212 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28215 * @class Roo.bootstrap.dash.NumberBox
28216 * @extends Roo.bootstrap.Component
28217 * Bootstrap NumberBox class
28218 * @cfg {String} headline Box headline
28219 * @cfg {String} content Box content
28220 * @cfg {String} icon Box icon
28221 * @cfg {String} footer Footer text
28222 * @cfg {String} fhref Footer href
28225 * Create a new NumberBox
28226 * @param {Object} config The config object
28230 Roo.bootstrap.dash.NumberBox = function(config){
28231 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28235 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28244 getAutoCreate : function(){
28248 cls : 'small-box ',
28256 cls : 'roo-headline',
28257 html : this.headline
28261 cls : 'roo-content',
28262 html : this.content
28276 cls : 'ion ' + this.icon
28285 cls : 'small-box-footer',
28286 href : this.fhref || '#',
28290 cfg.cn.push(footer);
28297 onRender : function(ct,position){
28298 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28305 setHeadline: function (value)
28307 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28310 setFooter: function (value, href)
28312 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28315 this.el.select('a.small-box-footer',true).first().attr('href', href);
28320 setContent: function (value)
28322 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28325 initEvents: function()
28339 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28342 * @class Roo.bootstrap.dash.TabBox
28343 * @extends Roo.bootstrap.Component
28344 * Bootstrap TabBox class
28345 * @cfg {String} title Title of the TabBox
28346 * @cfg {String} icon Icon of the TabBox
28347 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28348 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28351 * Create a new TabBox
28352 * @param {Object} config The config object
28356 Roo.bootstrap.dash.TabBox = function(config){
28357 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28362 * When a pane is added
28363 * @param {Roo.bootstrap.dash.TabPane} pane
28367 * @event activatepane
28368 * When a pane is activated
28369 * @param {Roo.bootstrap.dash.TabPane} pane
28371 "activatepane" : true
28379 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28384 tabScrollable : false,
28386 getChildContainer : function()
28388 return this.el.select('.tab-content', true).first();
28391 getAutoCreate : function(){
28395 cls: 'pull-left header',
28403 cls: 'fa ' + this.icon
28409 cls: 'nav nav-tabs pull-right',
28415 if(this.tabScrollable){
28422 cls: 'nav nav-tabs pull-right',
28433 cls: 'nav-tabs-custom',
28438 cls: 'tab-content no-padding',
28446 initEvents : function()
28448 //Roo.log('add add pane handler');
28449 this.on('addpane', this.onAddPane, this);
28452 * Updates the box title
28453 * @param {String} html to set the title to.
28455 setTitle : function(value)
28457 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28459 onAddPane : function(pane)
28461 this.panes.push(pane);
28462 //Roo.log('addpane');
28464 // tabs are rendere left to right..
28465 if(!this.showtabs){
28469 var ctr = this.el.select('.nav-tabs', true).first();
28472 var existing = ctr.select('.nav-tab',true);
28473 var qty = existing.getCount();;
28476 var tab = ctr.createChild({
28478 cls : 'nav-tab' + (qty ? '' : ' active'),
28486 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28489 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28491 pane.el.addClass('active');
28496 onTabClick : function(ev,un,ob,pane)
28498 //Roo.log('tab - prev default');
28499 ev.preventDefault();
28502 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28503 pane.tab.addClass('active');
28504 //Roo.log(pane.title);
28505 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28506 // technically we should have a deactivate event.. but maybe add later.
28507 // and it should not de-activate the selected tab...
28508 this.fireEvent('activatepane', pane);
28509 pane.el.addClass('active');
28510 pane.fireEvent('activate');
28515 getActivePane : function()
28518 Roo.each(this.panes, function(p) {
28519 if(p.el.hasClass('active')){
28540 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28542 * @class Roo.bootstrap.TabPane
28543 * @extends Roo.bootstrap.Component
28544 * Bootstrap TabPane class
28545 * @cfg {Boolean} active (false | true) Default false
28546 * @cfg {String} title title of panel
28550 * Create a new TabPane
28551 * @param {Object} config The config object
28554 Roo.bootstrap.dash.TabPane = function(config){
28555 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28561 * When a pane is activated
28562 * @param {Roo.bootstrap.dash.TabPane} pane
28569 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28574 // the tabBox that this is attached to.
28577 getAutoCreate : function()
28585 cfg.cls += ' active';
28590 initEvents : function()
28592 //Roo.log('trigger add pane handler');
28593 this.parent().fireEvent('addpane', this)
28597 * Updates the tab title
28598 * @param {String} html to set the title to.
28600 setTitle: function(str)
28606 this.tab.select('a', true).first().dom.innerHTML = str;
28623 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28626 * @class Roo.bootstrap.menu.Menu
28627 * @extends Roo.bootstrap.Component
28628 * Bootstrap Menu class - container for Menu
28629 * @cfg {String} html Text of the menu
28630 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28631 * @cfg {String} icon Font awesome icon
28632 * @cfg {String} pos Menu align to (top | bottom) default bottom
28636 * Create a new Menu
28637 * @param {Object} config The config object
28641 Roo.bootstrap.menu.Menu = function(config){
28642 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28646 * @event beforeshow
28647 * Fires before this menu is displayed
28648 * @param {Roo.bootstrap.menu.Menu} this
28652 * @event beforehide
28653 * Fires before this menu is hidden
28654 * @param {Roo.bootstrap.menu.Menu} this
28659 * Fires after this menu is displayed
28660 * @param {Roo.bootstrap.menu.Menu} this
28665 * Fires after this menu is hidden
28666 * @param {Roo.bootstrap.menu.Menu} this
28671 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28672 * @param {Roo.bootstrap.menu.Menu} this
28673 * @param {Roo.EventObject} e
28680 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28684 weight : 'default',
28689 getChildContainer : function() {
28690 if(this.isSubMenu){
28694 return this.el.select('ul.dropdown-menu', true).first();
28697 getAutoCreate : function()
28702 cls : 'roo-menu-text',
28710 cls : 'fa ' + this.icon
28721 cls : 'dropdown-button btn btn-' + this.weight,
28726 cls : 'dropdown-toggle btn btn-' + this.weight,
28736 cls : 'dropdown-menu'
28742 if(this.pos == 'top'){
28743 cfg.cls += ' dropup';
28746 if(this.isSubMenu){
28749 cls : 'dropdown-menu'
28756 onRender : function(ct, position)
28758 this.isSubMenu = ct.hasClass('dropdown-submenu');
28760 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28763 initEvents : function()
28765 if(this.isSubMenu){
28769 this.hidden = true;
28771 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28772 this.triggerEl.on('click', this.onTriggerPress, this);
28774 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28775 this.buttonEl.on('click', this.onClick, this);
28781 if(this.isSubMenu){
28785 return this.el.select('ul.dropdown-menu', true).first();
28788 onClick : function(e)
28790 this.fireEvent("click", this, e);
28793 onTriggerPress : function(e)
28795 if (this.isVisible()) {
28802 isVisible : function(){
28803 return !this.hidden;
28808 this.fireEvent("beforeshow", this);
28810 this.hidden = false;
28811 this.el.addClass('open');
28813 Roo.get(document).on("mouseup", this.onMouseUp, this);
28815 this.fireEvent("show", this);
28822 this.fireEvent("beforehide", this);
28824 this.hidden = true;
28825 this.el.removeClass('open');
28827 Roo.get(document).un("mouseup", this.onMouseUp);
28829 this.fireEvent("hide", this);
28832 onMouseUp : function()
28846 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28849 * @class Roo.bootstrap.menu.Item
28850 * @extends Roo.bootstrap.Component
28851 * Bootstrap MenuItem class
28852 * @cfg {Boolean} submenu (true | false) default false
28853 * @cfg {String} html text of the item
28854 * @cfg {String} href the link
28855 * @cfg {Boolean} disable (true | false) default false
28856 * @cfg {Boolean} preventDefault (true | false) default true
28857 * @cfg {String} icon Font awesome icon
28858 * @cfg {String} pos Submenu align to (left | right) default right
28862 * Create a new Item
28863 * @param {Object} config The config object
28867 Roo.bootstrap.menu.Item = function(config){
28868 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28872 * Fires when the mouse is hovering over this menu
28873 * @param {Roo.bootstrap.menu.Item} this
28874 * @param {Roo.EventObject} e
28879 * Fires when the mouse exits this menu
28880 * @param {Roo.bootstrap.menu.Item} this
28881 * @param {Roo.EventObject} e
28887 * The raw click event for the entire grid.
28888 * @param {Roo.EventObject} e
28894 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28899 preventDefault: true,
28904 getAutoCreate : function()
28909 cls : 'roo-menu-item-text',
28917 cls : 'fa ' + this.icon
28926 href : this.href || '#',
28933 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28937 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28939 if(this.pos == 'left'){
28940 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28947 initEvents : function()
28949 this.el.on('mouseover', this.onMouseOver, this);
28950 this.el.on('mouseout', this.onMouseOut, this);
28952 this.el.select('a', true).first().on('click', this.onClick, this);
28956 onClick : function(e)
28958 if(this.preventDefault){
28959 e.preventDefault();
28962 this.fireEvent("click", this, e);
28965 onMouseOver : function(e)
28967 if(this.submenu && this.pos == 'left'){
28968 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28971 this.fireEvent("mouseover", this, e);
28974 onMouseOut : function(e)
28976 this.fireEvent("mouseout", this, e);
28988 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28991 * @class Roo.bootstrap.menu.Separator
28992 * @extends Roo.bootstrap.Component
28993 * Bootstrap Separator class
28996 * Create a new Separator
28997 * @param {Object} config The config object
29001 Roo.bootstrap.menu.Separator = function(config){
29002 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29005 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29007 getAutoCreate : function(){
29010 cls: 'dropdown-divider divider'
29028 * @class Roo.bootstrap.Tooltip
29029 * Bootstrap Tooltip class
29030 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29031 * to determine which dom element triggers the tooltip.
29033 * It needs to add support for additional attributes like tooltip-position
29036 * Create a new Toolti
29037 * @param {Object} config The config object
29040 Roo.bootstrap.Tooltip = function(config){
29041 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29043 this.alignment = Roo.bootstrap.Tooltip.alignment;
29045 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29046 this.alignment = config.alignment;
29051 Roo.apply(Roo.bootstrap.Tooltip, {
29053 * @function init initialize tooltip monitoring.
29057 currentTip : false,
29058 currentRegion : false,
29064 Roo.get(document).on('mouseover', this.enter ,this);
29065 Roo.get(document).on('mouseout', this.leave, this);
29068 this.currentTip = new Roo.bootstrap.Tooltip();
29071 enter : function(ev)
29073 var dom = ev.getTarget();
29075 //Roo.log(['enter',dom]);
29076 var el = Roo.fly(dom);
29077 if (this.currentEl) {
29079 //Roo.log(this.currentEl);
29080 //Roo.log(this.currentEl.contains(dom));
29081 if (this.currentEl == el) {
29084 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29090 if (this.currentTip.el) {
29091 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29095 if(!el || el.dom == document){
29101 if (!el.attr('tooltip')) {
29102 pel = el.findParent("[tooltip]");
29104 bindEl = Roo.get(pel);
29110 // you can not look for children, as if el is the body.. then everythign is the child..
29111 if (!pel && !el.attr('tooltip')) { //
29112 if (!el.select("[tooltip]").elements.length) {
29115 // is the mouse over this child...?
29116 bindEl = el.select("[tooltip]").first();
29117 var xy = ev.getXY();
29118 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29119 //Roo.log("not in region.");
29122 //Roo.log("child element over..");
29125 this.currentEl = el;
29126 this.currentTip.bind(bindEl);
29127 this.currentRegion = Roo.lib.Region.getRegion(dom);
29128 this.currentTip.enter();
29131 leave : function(ev)
29133 var dom = ev.getTarget();
29134 //Roo.log(['leave',dom]);
29135 if (!this.currentEl) {
29140 if (dom != this.currentEl.dom) {
29143 var xy = ev.getXY();
29144 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29147 // only activate leave if mouse cursor is outside... bounding box..
29152 if (this.currentTip) {
29153 this.currentTip.leave();
29155 //Roo.log('clear currentEl');
29156 this.currentEl = false;
29161 'left' : ['r-l', [-2,0], 'right'],
29162 'right' : ['l-r', [2,0], 'left'],
29163 'bottom' : ['t-b', [0,2], 'top'],
29164 'top' : [ 'b-t', [0,-2], 'bottom']
29170 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29175 delay : null, // can be { show : 300 , hide: 500}
29179 hoverState : null, //???
29181 placement : 'bottom',
29185 getAutoCreate : function(){
29192 cls : 'tooltip-arrow arrow'
29195 cls : 'tooltip-inner'
29202 bind : function(el)
29207 initEvents : function()
29209 this.arrowEl = this.el.select('.arrow', true).first();
29210 this.innerEl = this.el.select('.tooltip-inner', true).first();
29213 enter : function () {
29215 if (this.timeout != null) {
29216 clearTimeout(this.timeout);
29219 this.hoverState = 'in';
29220 //Roo.log("enter - show");
29221 if (!this.delay || !this.delay.show) {
29226 this.timeout = setTimeout(function () {
29227 if (_t.hoverState == 'in') {
29230 }, this.delay.show);
29234 clearTimeout(this.timeout);
29236 this.hoverState = 'out';
29237 if (!this.delay || !this.delay.hide) {
29243 this.timeout = setTimeout(function () {
29244 //Roo.log("leave - timeout");
29246 if (_t.hoverState == 'out') {
29248 Roo.bootstrap.Tooltip.currentEl = false;
29253 show : function (msg)
29256 this.render(document.body);
29259 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29261 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29263 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29265 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29266 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29268 var placement = typeof this.placement == 'function' ?
29269 this.placement.call(this, this.el, on_el) :
29272 var autoToken = /\s?auto?\s?/i;
29273 var autoPlace = autoToken.test(placement);
29275 placement = placement.replace(autoToken, '') || 'top';
29279 //this.el.setXY([0,0]);
29281 //this.el.dom.style.display='block';
29283 //this.el.appendTo(on_el);
29285 var p = this.getPosition();
29286 var box = this.el.getBox();
29292 var align = this.alignment[placement];
29294 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29296 if(placement == 'top' || placement == 'bottom'){
29298 placement = 'right';
29301 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29302 placement = 'left';
29305 var scroll = Roo.select('body', true).first().getScroll();
29307 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29311 align = this.alignment[placement];
29313 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29317 var elems = document.getElementsByTagName('div');
29318 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29319 for (var i = 0; i < elems.length; i++) {
29320 var zindex = Number.parseInt(
29321 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29324 if (zindex > highest) {
29331 this.el.dom.style.zIndex = highest;
29333 this.el.alignTo(this.bindEl, align[0],align[1]);
29334 //var arrow = this.el.select('.arrow',true).first();
29335 //arrow.set(align[2],
29337 this.el.addClass(placement);
29338 this.el.addClass("bs-tooltip-"+ placement);
29340 this.el.addClass('in fade show');
29342 this.hoverState = null;
29344 if (this.el.hasClass('fade')) {
29359 //this.el.setXY([0,0]);
29360 this.el.removeClass(['show', 'in']);
29376 * @class Roo.bootstrap.LocationPicker
29377 * @extends Roo.bootstrap.Component
29378 * Bootstrap LocationPicker class
29379 * @cfg {Number} latitude Position when init default 0
29380 * @cfg {Number} longitude Position when init default 0
29381 * @cfg {Number} zoom default 15
29382 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29383 * @cfg {Boolean} mapTypeControl default false
29384 * @cfg {Boolean} disableDoubleClickZoom default false
29385 * @cfg {Boolean} scrollwheel default true
29386 * @cfg {Boolean} streetViewControl default false
29387 * @cfg {Number} radius default 0
29388 * @cfg {String} locationName
29389 * @cfg {Boolean} draggable default true
29390 * @cfg {Boolean} enableAutocomplete default false
29391 * @cfg {Boolean} enableReverseGeocode default true
29392 * @cfg {String} markerTitle
29395 * Create a new LocationPicker
29396 * @param {Object} config The config object
29400 Roo.bootstrap.LocationPicker = function(config){
29402 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29407 * Fires when the picker initialized.
29408 * @param {Roo.bootstrap.LocationPicker} this
29409 * @param {Google Location} location
29413 * @event positionchanged
29414 * Fires when the picker position changed.
29415 * @param {Roo.bootstrap.LocationPicker} this
29416 * @param {Google Location} location
29418 positionchanged : true,
29421 * Fires when the map resize.
29422 * @param {Roo.bootstrap.LocationPicker} this
29427 * Fires when the map show.
29428 * @param {Roo.bootstrap.LocationPicker} this
29433 * Fires when the map hide.
29434 * @param {Roo.bootstrap.LocationPicker} this
29439 * Fires when click the map.
29440 * @param {Roo.bootstrap.LocationPicker} this
29441 * @param {Map event} e
29445 * @event mapRightClick
29446 * Fires when right click the map.
29447 * @param {Roo.bootstrap.LocationPicker} this
29448 * @param {Map event} e
29450 mapRightClick : true,
29452 * @event markerClick
29453 * Fires when click the marker.
29454 * @param {Roo.bootstrap.LocationPicker} this
29455 * @param {Map event} e
29457 markerClick : true,
29459 * @event markerRightClick
29460 * Fires when right click the marker.
29461 * @param {Roo.bootstrap.LocationPicker} this
29462 * @param {Map event} e
29464 markerRightClick : true,
29466 * @event OverlayViewDraw
29467 * Fires when OverlayView Draw
29468 * @param {Roo.bootstrap.LocationPicker} this
29470 OverlayViewDraw : true,
29472 * @event OverlayViewOnAdd
29473 * Fires when OverlayView Draw
29474 * @param {Roo.bootstrap.LocationPicker} this
29476 OverlayViewOnAdd : true,
29478 * @event OverlayViewOnRemove
29479 * Fires when OverlayView Draw
29480 * @param {Roo.bootstrap.LocationPicker} this
29482 OverlayViewOnRemove : true,
29484 * @event OverlayViewShow
29485 * Fires when OverlayView Draw
29486 * @param {Roo.bootstrap.LocationPicker} this
29487 * @param {Pixel} cpx
29489 OverlayViewShow : true,
29491 * @event OverlayViewHide
29492 * Fires when OverlayView Draw
29493 * @param {Roo.bootstrap.LocationPicker} this
29495 OverlayViewHide : true,
29497 * @event loadexception
29498 * Fires when load google lib failed.
29499 * @param {Roo.bootstrap.LocationPicker} this
29501 loadexception : true
29506 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29508 gMapContext: false,
29514 mapTypeControl: false,
29515 disableDoubleClickZoom: false,
29517 streetViewControl: false,
29521 enableAutocomplete: false,
29522 enableReverseGeocode: true,
29525 getAutoCreate: function()
29530 cls: 'roo-location-picker'
29536 initEvents: function(ct, position)
29538 if(!this.el.getWidth() || this.isApplied()){
29542 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29547 initial: function()
29549 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29550 this.fireEvent('loadexception', this);
29554 if(!this.mapTypeId){
29555 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29558 this.gMapContext = this.GMapContext();
29560 this.initOverlayView();
29562 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29566 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29567 _this.setPosition(_this.gMapContext.marker.position);
29570 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29571 _this.fireEvent('mapClick', this, event);
29575 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29576 _this.fireEvent('mapRightClick', this, event);
29580 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29581 _this.fireEvent('markerClick', this, event);
29585 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29586 _this.fireEvent('markerRightClick', this, event);
29590 this.setPosition(this.gMapContext.location);
29592 this.fireEvent('initial', this, this.gMapContext.location);
29595 initOverlayView: function()
29599 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29603 _this.fireEvent('OverlayViewDraw', _this);
29608 _this.fireEvent('OverlayViewOnAdd', _this);
29611 onRemove: function()
29613 _this.fireEvent('OverlayViewOnRemove', _this);
29616 show: function(cpx)
29618 _this.fireEvent('OverlayViewShow', _this, cpx);
29623 _this.fireEvent('OverlayViewHide', _this);
29629 fromLatLngToContainerPixel: function(event)
29631 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29634 isApplied: function()
29636 return this.getGmapContext() == false ? false : true;
29639 getGmapContext: function()
29641 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29644 GMapContext: function()
29646 var position = new google.maps.LatLng(this.latitude, this.longitude);
29648 var _map = new google.maps.Map(this.el.dom, {
29651 mapTypeId: this.mapTypeId,
29652 mapTypeControl: this.mapTypeControl,
29653 disableDoubleClickZoom: this.disableDoubleClickZoom,
29654 scrollwheel: this.scrollwheel,
29655 streetViewControl: this.streetViewControl,
29656 locationName: this.locationName,
29657 draggable: this.draggable,
29658 enableAutocomplete: this.enableAutocomplete,
29659 enableReverseGeocode: this.enableReverseGeocode
29662 var _marker = new google.maps.Marker({
29663 position: position,
29665 title: this.markerTitle,
29666 draggable: this.draggable
29673 location: position,
29674 radius: this.radius,
29675 locationName: this.locationName,
29676 addressComponents: {
29677 formatted_address: null,
29678 addressLine1: null,
29679 addressLine2: null,
29681 streetNumber: null,
29685 stateOrProvince: null
29688 domContainer: this.el.dom,
29689 geodecoder: new google.maps.Geocoder()
29693 drawCircle: function(center, radius, options)
29695 if (this.gMapContext.circle != null) {
29696 this.gMapContext.circle.setMap(null);
29700 options = Roo.apply({}, options, {
29701 strokeColor: "#0000FF",
29702 strokeOpacity: .35,
29704 fillColor: "#0000FF",
29708 options.map = this.gMapContext.map;
29709 options.radius = radius;
29710 options.center = center;
29711 this.gMapContext.circle = new google.maps.Circle(options);
29712 return this.gMapContext.circle;
29718 setPosition: function(location)
29720 this.gMapContext.location = location;
29721 this.gMapContext.marker.setPosition(location);
29722 this.gMapContext.map.panTo(location);
29723 this.drawCircle(location, this.gMapContext.radius, {});
29727 if (this.gMapContext.settings.enableReverseGeocode) {
29728 this.gMapContext.geodecoder.geocode({
29729 latLng: this.gMapContext.location
29730 }, function(results, status) {
29732 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29733 _this.gMapContext.locationName = results[0].formatted_address;
29734 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29736 _this.fireEvent('positionchanged', this, location);
29743 this.fireEvent('positionchanged', this, location);
29748 google.maps.event.trigger(this.gMapContext.map, "resize");
29750 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29752 this.fireEvent('resize', this);
29755 setPositionByLatLng: function(latitude, longitude)
29757 this.setPosition(new google.maps.LatLng(latitude, longitude));
29760 getCurrentPosition: function()
29763 latitude: this.gMapContext.location.lat(),
29764 longitude: this.gMapContext.location.lng()
29768 getAddressName: function()
29770 return this.gMapContext.locationName;
29773 getAddressComponents: function()
29775 return this.gMapContext.addressComponents;
29778 address_component_from_google_geocode: function(address_components)
29782 for (var i = 0; i < address_components.length; i++) {
29783 var component = address_components[i];
29784 if (component.types.indexOf("postal_code") >= 0) {
29785 result.postalCode = component.short_name;
29786 } else if (component.types.indexOf("street_number") >= 0) {
29787 result.streetNumber = component.short_name;
29788 } else if (component.types.indexOf("route") >= 0) {
29789 result.streetName = component.short_name;
29790 } else if (component.types.indexOf("neighborhood") >= 0) {
29791 result.city = component.short_name;
29792 } else if (component.types.indexOf("locality") >= 0) {
29793 result.city = component.short_name;
29794 } else if (component.types.indexOf("sublocality") >= 0) {
29795 result.district = component.short_name;
29796 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29797 result.stateOrProvince = component.short_name;
29798 } else if (component.types.indexOf("country") >= 0) {
29799 result.country = component.short_name;
29803 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29804 result.addressLine2 = "";
29808 setZoomLevel: function(zoom)
29810 this.gMapContext.map.setZoom(zoom);
29823 this.fireEvent('show', this);
29834 this.fireEvent('hide', this);
29839 Roo.apply(Roo.bootstrap.LocationPicker, {
29841 OverlayView : function(map, options)
29843 options = options || {};
29850 * @class Roo.bootstrap.Alert
29851 * @extends Roo.bootstrap.Component
29852 * Bootstrap Alert class - shows an alert area box
29854 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29855 Enter a valid email address
29858 * @cfg {String} title The title of alert
29859 * @cfg {String} html The content of alert
29860 * @cfg {String} weight ( success | info | warning | danger )
29861 * @cfg {String} fa font-awesomeicon
29862 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29863 * @cfg {Boolean} close true to show a x closer
29867 * Create a new alert
29868 * @param {Object} config The config object
29872 Roo.bootstrap.Alert = function(config){
29873 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29877 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29883 faicon: false, // BC
29887 getAutoCreate : function()
29899 style : this.close ? '' : 'display:none'
29903 cls : 'roo-alert-icon'
29908 cls : 'roo-alert-title',
29913 cls : 'roo-alert-text',
29920 cfg.cn[0].cls += ' fa ' + this.faicon;
29923 cfg.cn[0].cls += ' fa ' + this.fa;
29927 cfg.cls += ' alert-' + this.weight;
29933 initEvents: function()
29935 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29936 this.titleEl = this.el.select('.roo-alert-title',true).first();
29937 this.iconEl = this.el.select('.roo-alert-icon',true).first();
29938 if (this.seconds > 0) {
29939 this.hide.defer(this.seconds, this);
29943 setTitle : function(str)
29945 this.titleEl.dom.innerHTML = str;
29948 setText : function(str)
29950 this.titleEl.dom.innerHTML = str;
29953 setWeight : function(weight)
29956 this.el.removeClass('alert-' + this.weight);
29959 this.weight = weight;
29961 this.el.addClass('alert-' + this.weight);
29964 setIcon : function(icon)
29967 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29970 this.faicon = icon;
29972 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
29993 * @class Roo.bootstrap.UploadCropbox
29994 * @extends Roo.bootstrap.Component
29995 * Bootstrap UploadCropbox class
29996 * @cfg {String} emptyText show when image has been loaded
29997 * @cfg {String} rotateNotify show when image too small to rotate
29998 * @cfg {Number} errorTimeout default 3000
29999 * @cfg {Number} minWidth default 300
30000 * @cfg {Number} minHeight default 300
30001 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30002 * @cfg {Boolean} isDocument (true|false) default false
30003 * @cfg {String} url action url
30004 * @cfg {String} paramName default 'imageUpload'
30005 * @cfg {String} method default POST
30006 * @cfg {Boolean} loadMask (true|false) default true
30007 * @cfg {Boolean} loadingText default 'Loading...'
30010 * Create a new UploadCropbox
30011 * @param {Object} config The config object
30014 Roo.bootstrap.UploadCropbox = function(config){
30015 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30019 * @event beforeselectfile
30020 * Fire before select file
30021 * @param {Roo.bootstrap.UploadCropbox} this
30023 "beforeselectfile" : true,
30026 * Fire after initEvent
30027 * @param {Roo.bootstrap.UploadCropbox} this
30032 * Fire after initEvent
30033 * @param {Roo.bootstrap.UploadCropbox} this
30034 * @param {String} data
30039 * Fire when preparing the file data
30040 * @param {Roo.bootstrap.UploadCropbox} this
30041 * @param {Object} file
30046 * Fire when get exception
30047 * @param {Roo.bootstrap.UploadCropbox} this
30048 * @param {XMLHttpRequest} xhr
30050 "exception" : true,
30052 * @event beforeloadcanvas
30053 * Fire before load the canvas
30054 * @param {Roo.bootstrap.UploadCropbox} this
30055 * @param {String} src
30057 "beforeloadcanvas" : true,
30060 * Fire when trash image
30061 * @param {Roo.bootstrap.UploadCropbox} this
30066 * Fire when download the image
30067 * @param {Roo.bootstrap.UploadCropbox} this
30071 * @event footerbuttonclick
30072 * Fire when footerbuttonclick
30073 * @param {Roo.bootstrap.UploadCropbox} this
30074 * @param {String} type
30076 "footerbuttonclick" : true,
30080 * @param {Roo.bootstrap.UploadCropbox} this
30085 * Fire when rotate the image
30086 * @param {Roo.bootstrap.UploadCropbox} this
30087 * @param {String} pos
30092 * Fire when inspect the file
30093 * @param {Roo.bootstrap.UploadCropbox} this
30094 * @param {Object} file
30099 * Fire when xhr upload the file
30100 * @param {Roo.bootstrap.UploadCropbox} this
30101 * @param {Object} data
30106 * Fire when arrange the file data
30107 * @param {Roo.bootstrap.UploadCropbox} this
30108 * @param {Object} formData
30113 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30116 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30118 emptyText : 'Click to upload image',
30119 rotateNotify : 'Image is too small to rotate',
30120 errorTimeout : 3000,
30134 cropType : 'image/jpeg',
30136 canvasLoaded : false,
30137 isDocument : false,
30139 paramName : 'imageUpload',
30141 loadingText : 'Loading...',
30144 getAutoCreate : function()
30148 cls : 'roo-upload-cropbox',
30152 cls : 'roo-upload-cropbox-selector',
30157 cls : 'roo-upload-cropbox-body',
30158 style : 'cursor:pointer',
30162 cls : 'roo-upload-cropbox-preview'
30166 cls : 'roo-upload-cropbox-thumb'
30170 cls : 'roo-upload-cropbox-empty-notify',
30171 html : this.emptyText
30175 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30176 html : this.rotateNotify
30182 cls : 'roo-upload-cropbox-footer',
30185 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30195 onRender : function(ct, position)
30197 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30199 if (this.buttons.length) {
30201 Roo.each(this.buttons, function(bb) {
30203 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30205 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30211 this.maskEl = this.el;
30215 initEvents : function()
30217 this.urlAPI = (window.createObjectURL && window) ||
30218 (window.URL && URL.revokeObjectURL && URL) ||
30219 (window.webkitURL && webkitURL);
30221 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30222 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30224 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30225 this.selectorEl.hide();
30227 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30228 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30230 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30231 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30232 this.thumbEl.hide();
30234 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30235 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30237 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30238 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30239 this.errorEl.hide();
30241 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30242 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30243 this.footerEl.hide();
30245 this.setThumbBoxSize();
30251 this.fireEvent('initial', this);
30258 window.addEventListener("resize", function() { _this.resize(); } );
30260 this.bodyEl.on('click', this.beforeSelectFile, this);
30263 this.bodyEl.on('touchstart', this.onTouchStart, this);
30264 this.bodyEl.on('touchmove', this.onTouchMove, this);
30265 this.bodyEl.on('touchend', this.onTouchEnd, this);
30269 this.bodyEl.on('mousedown', this.onMouseDown, this);
30270 this.bodyEl.on('mousemove', this.onMouseMove, this);
30271 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30272 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30273 Roo.get(document).on('mouseup', this.onMouseUp, this);
30276 this.selectorEl.on('change', this.onFileSelected, this);
30282 this.baseScale = 1;
30284 this.baseRotate = 1;
30285 this.dragable = false;
30286 this.pinching = false;
30289 this.cropData = false;
30290 this.notifyEl.dom.innerHTML = this.emptyText;
30292 this.selectorEl.dom.value = '';
30296 resize : function()
30298 if(this.fireEvent('resize', this) != false){
30299 this.setThumbBoxPosition();
30300 this.setCanvasPosition();
30304 onFooterButtonClick : function(e, el, o, type)
30307 case 'rotate-left' :
30308 this.onRotateLeft(e);
30310 case 'rotate-right' :
30311 this.onRotateRight(e);
30314 this.beforeSelectFile(e);
30329 this.fireEvent('footerbuttonclick', this, type);
30332 beforeSelectFile : function(e)
30334 e.preventDefault();
30336 if(this.fireEvent('beforeselectfile', this) != false){
30337 this.selectorEl.dom.click();
30341 onFileSelected : function(e)
30343 e.preventDefault();
30345 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30349 var file = this.selectorEl.dom.files[0];
30351 if(this.fireEvent('inspect', this, file) != false){
30352 this.prepare(file);
30357 trash : function(e)
30359 this.fireEvent('trash', this);
30362 download : function(e)
30364 this.fireEvent('download', this);
30367 loadCanvas : function(src)
30369 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30373 this.imageEl = document.createElement('img');
30377 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30379 this.imageEl.src = src;
30383 onLoadCanvas : function()
30385 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30386 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30388 this.bodyEl.un('click', this.beforeSelectFile, this);
30390 this.notifyEl.hide();
30391 this.thumbEl.show();
30392 this.footerEl.show();
30394 this.baseRotateLevel();
30396 if(this.isDocument){
30397 this.setThumbBoxSize();
30400 this.setThumbBoxPosition();
30402 this.baseScaleLevel();
30408 this.canvasLoaded = true;
30411 this.maskEl.unmask();
30416 setCanvasPosition : function()
30418 if(!this.canvasEl){
30422 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30423 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30425 this.previewEl.setLeft(pw);
30426 this.previewEl.setTop(ph);
30430 onMouseDown : function(e)
30434 this.dragable = true;
30435 this.pinching = false;
30437 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30438 this.dragable = false;
30442 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30443 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30447 onMouseMove : function(e)
30451 if(!this.canvasLoaded){
30455 if (!this.dragable){
30459 var minX = Math.ceil(this.thumbEl.getLeft(true));
30460 var minY = Math.ceil(this.thumbEl.getTop(true));
30462 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30463 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30465 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30466 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30468 x = x - this.mouseX;
30469 y = y - this.mouseY;
30471 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30472 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30474 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30475 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30477 this.previewEl.setLeft(bgX);
30478 this.previewEl.setTop(bgY);
30480 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30481 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30484 onMouseUp : function(e)
30488 this.dragable = false;
30491 onMouseWheel : function(e)
30495 this.startScale = this.scale;
30497 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30499 if(!this.zoomable()){
30500 this.scale = this.startScale;
30509 zoomable : function()
30511 var minScale = this.thumbEl.getWidth() / this.minWidth;
30513 if(this.minWidth < this.minHeight){
30514 minScale = this.thumbEl.getHeight() / this.minHeight;
30517 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30518 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30522 (this.rotate == 0 || this.rotate == 180) &&
30524 width > this.imageEl.OriginWidth ||
30525 height > this.imageEl.OriginHeight ||
30526 (width < this.minWidth && height < this.minHeight)
30534 (this.rotate == 90 || this.rotate == 270) &&
30536 width > this.imageEl.OriginWidth ||
30537 height > this.imageEl.OriginHeight ||
30538 (width < this.minHeight && height < this.minWidth)
30545 !this.isDocument &&
30546 (this.rotate == 0 || this.rotate == 180) &&
30548 width < this.minWidth ||
30549 width > this.imageEl.OriginWidth ||
30550 height < this.minHeight ||
30551 height > this.imageEl.OriginHeight
30558 !this.isDocument &&
30559 (this.rotate == 90 || this.rotate == 270) &&
30561 width < this.minHeight ||
30562 width > this.imageEl.OriginWidth ||
30563 height < this.minWidth ||
30564 height > this.imageEl.OriginHeight
30574 onRotateLeft : function(e)
30576 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30578 var minScale = this.thumbEl.getWidth() / this.minWidth;
30580 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30581 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30583 this.startScale = this.scale;
30585 while (this.getScaleLevel() < minScale){
30587 this.scale = this.scale + 1;
30589 if(!this.zoomable()){
30594 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30595 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30600 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30607 this.scale = this.startScale;
30609 this.onRotateFail();
30614 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30616 if(this.isDocument){
30617 this.setThumbBoxSize();
30618 this.setThumbBoxPosition();
30619 this.setCanvasPosition();
30624 this.fireEvent('rotate', this, 'left');
30628 onRotateRight : function(e)
30630 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30632 var minScale = this.thumbEl.getWidth() / this.minWidth;
30634 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30635 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30637 this.startScale = this.scale;
30639 while (this.getScaleLevel() < minScale){
30641 this.scale = this.scale + 1;
30643 if(!this.zoomable()){
30648 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30649 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30654 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30661 this.scale = this.startScale;
30663 this.onRotateFail();
30668 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30670 if(this.isDocument){
30671 this.setThumbBoxSize();
30672 this.setThumbBoxPosition();
30673 this.setCanvasPosition();
30678 this.fireEvent('rotate', this, 'right');
30681 onRotateFail : function()
30683 this.errorEl.show(true);
30687 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30692 this.previewEl.dom.innerHTML = '';
30694 var canvasEl = document.createElement("canvas");
30696 var contextEl = canvasEl.getContext("2d");
30698 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30699 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30700 var center = this.imageEl.OriginWidth / 2;
30702 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30703 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30704 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30705 center = this.imageEl.OriginHeight / 2;
30708 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30710 contextEl.translate(center, center);
30711 contextEl.rotate(this.rotate * Math.PI / 180);
30713 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30715 this.canvasEl = document.createElement("canvas");
30717 this.contextEl = this.canvasEl.getContext("2d");
30719 switch (this.rotate) {
30722 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30723 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30725 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30730 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30731 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30733 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30734 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);
30738 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30743 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30744 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30746 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30747 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);
30751 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30756 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30757 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30759 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30760 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30764 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30771 this.previewEl.appendChild(this.canvasEl);
30773 this.setCanvasPosition();
30778 if(!this.canvasLoaded){
30782 var imageCanvas = document.createElement("canvas");
30784 var imageContext = imageCanvas.getContext("2d");
30786 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30787 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30789 var center = imageCanvas.width / 2;
30791 imageContext.translate(center, center);
30793 imageContext.rotate(this.rotate * Math.PI / 180);
30795 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30797 var canvas = document.createElement("canvas");
30799 var context = canvas.getContext("2d");
30801 canvas.width = this.minWidth;
30802 canvas.height = this.minHeight;
30804 switch (this.rotate) {
30807 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30808 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30810 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30811 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30813 var targetWidth = this.minWidth - 2 * x;
30814 var targetHeight = this.minHeight - 2 * y;
30818 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30819 scale = targetWidth / width;
30822 if(x > 0 && y == 0){
30823 scale = targetHeight / height;
30826 if(x > 0 && y > 0){
30827 scale = targetWidth / width;
30829 if(width < height){
30830 scale = targetHeight / height;
30834 context.scale(scale, scale);
30836 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30837 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30839 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30840 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30842 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30847 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30848 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30850 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30851 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30853 var targetWidth = this.minWidth - 2 * x;
30854 var targetHeight = this.minHeight - 2 * y;
30858 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30859 scale = targetWidth / width;
30862 if(x > 0 && y == 0){
30863 scale = targetHeight / height;
30866 if(x > 0 && y > 0){
30867 scale = targetWidth / width;
30869 if(width < height){
30870 scale = targetHeight / height;
30874 context.scale(scale, scale);
30876 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30877 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30879 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30880 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30882 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30884 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30889 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30890 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30892 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30893 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30895 var targetWidth = this.minWidth - 2 * x;
30896 var targetHeight = this.minHeight - 2 * y;
30900 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30901 scale = targetWidth / width;
30904 if(x > 0 && y == 0){
30905 scale = targetHeight / height;
30908 if(x > 0 && y > 0){
30909 scale = targetWidth / width;
30911 if(width < height){
30912 scale = targetHeight / height;
30916 context.scale(scale, scale);
30918 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30919 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30921 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30922 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30924 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30925 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30927 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30932 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30933 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30935 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30936 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30938 var targetWidth = this.minWidth - 2 * x;
30939 var targetHeight = this.minHeight - 2 * y;
30943 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30944 scale = targetWidth / width;
30947 if(x > 0 && y == 0){
30948 scale = targetHeight / height;
30951 if(x > 0 && y > 0){
30952 scale = targetWidth / width;
30954 if(width < height){
30955 scale = targetHeight / height;
30959 context.scale(scale, scale);
30961 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30962 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30964 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30965 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30967 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30969 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30976 this.cropData = canvas.toDataURL(this.cropType);
30978 if(this.fireEvent('crop', this, this.cropData) !== false){
30979 this.process(this.file, this.cropData);
30986 setThumbBoxSize : function()
30990 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30991 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30992 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30994 this.minWidth = width;
30995 this.minHeight = height;
30997 if(this.rotate == 90 || this.rotate == 270){
30998 this.minWidth = height;
30999 this.minHeight = width;
31004 width = Math.ceil(this.minWidth * height / this.minHeight);
31006 if(this.minWidth > this.minHeight){
31008 height = Math.ceil(this.minHeight * width / this.minWidth);
31011 this.thumbEl.setStyle({
31012 width : width + 'px',
31013 height : height + 'px'
31020 setThumbBoxPosition : function()
31022 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31023 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31025 this.thumbEl.setLeft(x);
31026 this.thumbEl.setTop(y);
31030 baseRotateLevel : function()
31032 this.baseRotate = 1;
31035 typeof(this.exif) != 'undefined' &&
31036 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31037 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31039 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31042 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31046 baseScaleLevel : function()
31050 if(this.isDocument){
31052 if(this.baseRotate == 6 || this.baseRotate == 8){
31054 height = this.thumbEl.getHeight();
31055 this.baseScale = height / this.imageEl.OriginWidth;
31057 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31058 width = this.thumbEl.getWidth();
31059 this.baseScale = width / this.imageEl.OriginHeight;
31065 height = this.thumbEl.getHeight();
31066 this.baseScale = height / this.imageEl.OriginHeight;
31068 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31069 width = this.thumbEl.getWidth();
31070 this.baseScale = width / this.imageEl.OriginWidth;
31076 if(this.baseRotate == 6 || this.baseRotate == 8){
31078 width = this.thumbEl.getHeight();
31079 this.baseScale = width / this.imageEl.OriginHeight;
31081 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31082 height = this.thumbEl.getWidth();
31083 this.baseScale = height / this.imageEl.OriginHeight;
31086 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31087 height = this.thumbEl.getWidth();
31088 this.baseScale = height / this.imageEl.OriginHeight;
31090 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31091 width = this.thumbEl.getHeight();
31092 this.baseScale = width / this.imageEl.OriginWidth;
31099 width = this.thumbEl.getWidth();
31100 this.baseScale = width / this.imageEl.OriginWidth;
31102 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31103 height = this.thumbEl.getHeight();
31104 this.baseScale = height / this.imageEl.OriginHeight;
31107 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31109 height = this.thumbEl.getHeight();
31110 this.baseScale = height / this.imageEl.OriginHeight;
31112 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31113 width = this.thumbEl.getWidth();
31114 this.baseScale = width / this.imageEl.OriginWidth;
31122 getScaleLevel : function()
31124 return this.baseScale * Math.pow(1.1, this.scale);
31127 onTouchStart : function(e)
31129 if(!this.canvasLoaded){
31130 this.beforeSelectFile(e);
31134 var touches = e.browserEvent.touches;
31140 if(touches.length == 1){
31141 this.onMouseDown(e);
31145 if(touches.length != 2){
31151 for(var i = 0, finger; finger = touches[i]; i++){
31152 coords.push(finger.pageX, finger.pageY);
31155 var x = Math.pow(coords[0] - coords[2], 2);
31156 var y = Math.pow(coords[1] - coords[3], 2);
31158 this.startDistance = Math.sqrt(x + y);
31160 this.startScale = this.scale;
31162 this.pinching = true;
31163 this.dragable = false;
31167 onTouchMove : function(e)
31169 if(!this.pinching && !this.dragable){
31173 var touches = e.browserEvent.touches;
31180 this.onMouseMove(e);
31186 for(var i = 0, finger; finger = touches[i]; i++){
31187 coords.push(finger.pageX, finger.pageY);
31190 var x = Math.pow(coords[0] - coords[2], 2);
31191 var y = Math.pow(coords[1] - coords[3], 2);
31193 this.endDistance = Math.sqrt(x + y);
31195 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31197 if(!this.zoomable()){
31198 this.scale = this.startScale;
31206 onTouchEnd : function(e)
31208 this.pinching = false;
31209 this.dragable = false;
31213 process : function(file, crop)
31216 this.maskEl.mask(this.loadingText);
31219 this.xhr = new XMLHttpRequest();
31221 file.xhr = this.xhr;
31223 this.xhr.open(this.method, this.url, true);
31226 "Accept": "application/json",
31227 "Cache-Control": "no-cache",
31228 "X-Requested-With": "XMLHttpRequest"
31231 for (var headerName in headers) {
31232 var headerValue = headers[headerName];
31234 this.xhr.setRequestHeader(headerName, headerValue);
31240 this.xhr.onload = function()
31242 _this.xhrOnLoad(_this.xhr);
31245 this.xhr.onerror = function()
31247 _this.xhrOnError(_this.xhr);
31250 var formData = new FormData();
31252 formData.append('returnHTML', 'NO');
31255 formData.append('crop', crop);
31258 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31259 formData.append(this.paramName, file, file.name);
31262 if(typeof(file.filename) != 'undefined'){
31263 formData.append('filename', file.filename);
31266 if(typeof(file.mimetype) != 'undefined'){
31267 formData.append('mimetype', file.mimetype);
31270 if(this.fireEvent('arrange', this, formData) != false){
31271 this.xhr.send(formData);
31275 xhrOnLoad : function(xhr)
31278 this.maskEl.unmask();
31281 if (xhr.readyState !== 4) {
31282 this.fireEvent('exception', this, xhr);
31286 var response = Roo.decode(xhr.responseText);
31288 if(!response.success){
31289 this.fireEvent('exception', this, xhr);
31293 var response = Roo.decode(xhr.responseText);
31295 this.fireEvent('upload', this, response);
31299 xhrOnError : function()
31302 this.maskEl.unmask();
31305 Roo.log('xhr on error');
31307 var response = Roo.decode(xhr.responseText);
31313 prepare : function(file)
31316 this.maskEl.mask(this.loadingText);
31322 if(typeof(file) === 'string'){
31323 this.loadCanvas(file);
31327 if(!file || !this.urlAPI){
31332 this.cropType = file.type;
31336 if(this.fireEvent('prepare', this, this.file) != false){
31338 var reader = new FileReader();
31340 reader.onload = function (e) {
31341 if (e.target.error) {
31342 Roo.log(e.target.error);
31346 var buffer = e.target.result,
31347 dataView = new DataView(buffer),
31349 maxOffset = dataView.byteLength - 4,
31353 if (dataView.getUint16(0) === 0xffd8) {
31354 while (offset < maxOffset) {
31355 markerBytes = dataView.getUint16(offset);
31357 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31358 markerLength = dataView.getUint16(offset + 2) + 2;
31359 if (offset + markerLength > dataView.byteLength) {
31360 Roo.log('Invalid meta data: Invalid segment size.');
31364 if(markerBytes == 0xffe1){
31365 _this.parseExifData(
31372 offset += markerLength;
31382 var url = _this.urlAPI.createObjectURL(_this.file);
31384 _this.loadCanvas(url);
31389 reader.readAsArrayBuffer(this.file);
31395 parseExifData : function(dataView, offset, length)
31397 var tiffOffset = offset + 10,
31401 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31402 // No Exif data, might be XMP data instead
31406 // Check for the ASCII code for "Exif" (0x45786966):
31407 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31408 // No Exif data, might be XMP data instead
31411 if (tiffOffset + 8 > dataView.byteLength) {
31412 Roo.log('Invalid Exif data: Invalid segment size.');
31415 // Check for the two null bytes:
31416 if (dataView.getUint16(offset + 8) !== 0x0000) {
31417 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31420 // Check the byte alignment:
31421 switch (dataView.getUint16(tiffOffset)) {
31423 littleEndian = true;
31426 littleEndian = false;
31429 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31432 // Check for the TIFF tag marker (0x002A):
31433 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31434 Roo.log('Invalid Exif data: Missing TIFF marker.');
31437 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31438 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31440 this.parseExifTags(
31443 tiffOffset + dirOffset,
31448 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31453 if (dirOffset + 6 > dataView.byteLength) {
31454 Roo.log('Invalid Exif data: Invalid directory offset.');
31457 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31458 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31459 if (dirEndOffset + 4 > dataView.byteLength) {
31460 Roo.log('Invalid Exif data: Invalid directory size.');
31463 for (i = 0; i < tagsNumber; i += 1) {
31467 dirOffset + 2 + 12 * i, // tag offset
31471 // Return the offset to the next directory:
31472 return dataView.getUint32(dirEndOffset, littleEndian);
31475 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31477 var tag = dataView.getUint16(offset, littleEndian);
31479 this.exif[tag] = this.getExifValue(
31483 dataView.getUint16(offset + 2, littleEndian), // tag type
31484 dataView.getUint32(offset + 4, littleEndian), // tag length
31489 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31491 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31500 Roo.log('Invalid Exif data: Invalid tag type.');
31504 tagSize = tagType.size * length;
31505 // Determine if the value is contained in the dataOffset bytes,
31506 // or if the value at the dataOffset is a pointer to the actual data:
31507 dataOffset = tagSize > 4 ?
31508 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31509 if (dataOffset + tagSize > dataView.byteLength) {
31510 Roo.log('Invalid Exif data: Invalid data offset.');
31513 if (length === 1) {
31514 return tagType.getValue(dataView, dataOffset, littleEndian);
31517 for (i = 0; i < length; i += 1) {
31518 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31521 if (tagType.ascii) {
31523 // Concatenate the chars:
31524 for (i = 0; i < values.length; i += 1) {
31526 // Ignore the terminating NULL byte(s):
31527 if (c === '\u0000') {
31539 Roo.apply(Roo.bootstrap.UploadCropbox, {
31541 'Orientation': 0x0112
31545 1: 0, //'top-left',
31547 3: 180, //'bottom-right',
31548 // 4: 'bottom-left',
31550 6: 90, //'right-top',
31551 // 7: 'right-bottom',
31552 8: 270 //'left-bottom'
31556 // byte, 8-bit unsigned int:
31558 getValue: function (dataView, dataOffset) {
31559 return dataView.getUint8(dataOffset);
31563 // ascii, 8-bit byte:
31565 getValue: function (dataView, dataOffset) {
31566 return String.fromCharCode(dataView.getUint8(dataOffset));
31571 // short, 16 bit int:
31573 getValue: function (dataView, dataOffset, littleEndian) {
31574 return dataView.getUint16(dataOffset, littleEndian);
31578 // long, 32 bit int:
31580 getValue: function (dataView, dataOffset, littleEndian) {
31581 return dataView.getUint32(dataOffset, littleEndian);
31585 // rational = two long values, first is numerator, second is denominator:
31587 getValue: function (dataView, dataOffset, littleEndian) {
31588 return dataView.getUint32(dataOffset, littleEndian) /
31589 dataView.getUint32(dataOffset + 4, littleEndian);
31593 // slong, 32 bit signed int:
31595 getValue: function (dataView, dataOffset, littleEndian) {
31596 return dataView.getInt32(dataOffset, littleEndian);
31600 // srational, two slongs, first is numerator, second is denominator:
31602 getValue: function (dataView, dataOffset, littleEndian) {
31603 return dataView.getInt32(dataOffset, littleEndian) /
31604 dataView.getInt32(dataOffset + 4, littleEndian);
31614 cls : 'btn-group roo-upload-cropbox-rotate-left',
31615 action : 'rotate-left',
31619 cls : 'btn btn-default',
31620 html : '<i class="fa fa-undo"></i>'
31626 cls : 'btn-group roo-upload-cropbox-picture',
31627 action : 'picture',
31631 cls : 'btn btn-default',
31632 html : '<i class="fa fa-picture-o"></i>'
31638 cls : 'btn-group roo-upload-cropbox-rotate-right',
31639 action : 'rotate-right',
31643 cls : 'btn btn-default',
31644 html : '<i class="fa fa-repeat"></i>'
31652 cls : 'btn-group roo-upload-cropbox-rotate-left',
31653 action : 'rotate-left',
31657 cls : 'btn btn-default',
31658 html : '<i class="fa fa-undo"></i>'
31664 cls : 'btn-group roo-upload-cropbox-download',
31665 action : 'download',
31669 cls : 'btn btn-default',
31670 html : '<i class="fa fa-download"></i>'
31676 cls : 'btn-group roo-upload-cropbox-crop',
31681 cls : 'btn btn-default',
31682 html : '<i class="fa fa-crop"></i>'
31688 cls : 'btn-group roo-upload-cropbox-trash',
31693 cls : 'btn btn-default',
31694 html : '<i class="fa fa-trash"></i>'
31700 cls : 'btn-group roo-upload-cropbox-rotate-right',
31701 action : 'rotate-right',
31705 cls : 'btn btn-default',
31706 html : '<i class="fa fa-repeat"></i>'
31714 cls : 'btn-group roo-upload-cropbox-rotate-left',
31715 action : 'rotate-left',
31719 cls : 'btn btn-default',
31720 html : '<i class="fa fa-undo"></i>'
31726 cls : 'btn-group roo-upload-cropbox-rotate-right',
31727 action : 'rotate-right',
31731 cls : 'btn btn-default',
31732 html : '<i class="fa fa-repeat"></i>'
31745 * @class Roo.bootstrap.DocumentManager
31746 * @extends Roo.bootstrap.Component
31747 * Bootstrap DocumentManager class
31748 * @cfg {String} paramName default 'imageUpload'
31749 * @cfg {String} toolTipName default 'filename'
31750 * @cfg {String} method default POST
31751 * @cfg {String} url action url
31752 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31753 * @cfg {Boolean} multiple multiple upload default true
31754 * @cfg {Number} thumbSize default 300
31755 * @cfg {String} fieldLabel
31756 * @cfg {Number} labelWidth default 4
31757 * @cfg {String} labelAlign (left|top) default left
31758 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31759 * @cfg {Number} labellg set the width of label (1-12)
31760 * @cfg {Number} labelmd set the width of label (1-12)
31761 * @cfg {Number} labelsm set the width of label (1-12)
31762 * @cfg {Number} labelxs set the width of label (1-12)
31765 * Create a new DocumentManager
31766 * @param {Object} config The config object
31769 Roo.bootstrap.DocumentManager = function(config){
31770 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31773 this.delegates = [];
31778 * Fire when initial the DocumentManager
31779 * @param {Roo.bootstrap.DocumentManager} this
31784 * inspect selected file
31785 * @param {Roo.bootstrap.DocumentManager} this
31786 * @param {File} file
31791 * Fire when xhr load exception
31792 * @param {Roo.bootstrap.DocumentManager} this
31793 * @param {XMLHttpRequest} xhr
31795 "exception" : true,
31797 * @event afterupload
31798 * Fire when xhr load exception
31799 * @param {Roo.bootstrap.DocumentManager} this
31800 * @param {XMLHttpRequest} xhr
31802 "afterupload" : true,
31805 * prepare the form data
31806 * @param {Roo.bootstrap.DocumentManager} this
31807 * @param {Object} formData
31812 * Fire when remove the file
31813 * @param {Roo.bootstrap.DocumentManager} this
31814 * @param {Object} file
31819 * Fire after refresh the file
31820 * @param {Roo.bootstrap.DocumentManager} this
31825 * Fire after click the image
31826 * @param {Roo.bootstrap.DocumentManager} this
31827 * @param {Object} file
31832 * Fire when upload a image and editable set to true
31833 * @param {Roo.bootstrap.DocumentManager} this
31834 * @param {Object} file
31838 * @event beforeselectfile
31839 * Fire before select file
31840 * @param {Roo.bootstrap.DocumentManager} this
31842 "beforeselectfile" : true,
31845 * Fire before process file
31846 * @param {Roo.bootstrap.DocumentManager} this
31847 * @param {Object} file
31851 * @event previewrendered
31852 * Fire when preview rendered
31853 * @param {Roo.bootstrap.DocumentManager} this
31854 * @param {Object} file
31856 "previewrendered" : true,
31859 "previewResize" : true
31864 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31873 paramName : 'imageUpload',
31874 toolTipName : 'filename',
31877 labelAlign : 'left',
31887 getAutoCreate : function()
31889 var managerWidget = {
31891 cls : 'roo-document-manager',
31895 cls : 'roo-document-manager-selector',
31900 cls : 'roo-document-manager-uploader',
31904 cls : 'roo-document-manager-upload-btn',
31905 html : '<i class="fa fa-plus"></i>'
31916 cls : 'column col-md-12',
31921 if(this.fieldLabel.length){
31926 cls : 'column col-md-12',
31927 html : this.fieldLabel
31931 cls : 'column col-md-12',
31936 if(this.labelAlign == 'left'){
31941 html : this.fieldLabel
31950 if(this.labelWidth > 12){
31951 content[0].style = "width: " + this.labelWidth + 'px';
31954 if(this.labelWidth < 13 && this.labelmd == 0){
31955 this.labelmd = this.labelWidth;
31958 if(this.labellg > 0){
31959 content[0].cls += ' col-lg-' + this.labellg;
31960 content[1].cls += ' col-lg-' + (12 - this.labellg);
31963 if(this.labelmd > 0){
31964 content[0].cls += ' col-md-' + this.labelmd;
31965 content[1].cls += ' col-md-' + (12 - this.labelmd);
31968 if(this.labelsm > 0){
31969 content[0].cls += ' col-sm-' + this.labelsm;
31970 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31973 if(this.labelxs > 0){
31974 content[0].cls += ' col-xs-' + this.labelxs;
31975 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31983 cls : 'row clearfix',
31991 initEvents : function()
31993 this.managerEl = this.el.select('.roo-document-manager', true).first();
31994 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31996 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31997 this.selectorEl.hide();
32000 this.selectorEl.attr('multiple', 'multiple');
32003 this.selectorEl.on('change', this.onFileSelected, this);
32005 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32006 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32008 this.uploader.on('click', this.onUploaderClick, this);
32010 this.renderProgressDialog();
32014 window.addEventListener("resize", function() { _this.refresh(); } );
32016 this.fireEvent('initial', this);
32019 renderProgressDialog : function()
32023 this.progressDialog = new Roo.bootstrap.Modal({
32024 cls : 'roo-document-manager-progress-dialog',
32025 allow_close : false,
32036 btnclick : function() {
32037 _this.uploadCancel();
32043 this.progressDialog.render(Roo.get(document.body));
32045 this.progress = new Roo.bootstrap.Progress({
32046 cls : 'roo-document-manager-progress',
32051 this.progress.render(this.progressDialog.getChildContainer());
32053 this.progressBar = new Roo.bootstrap.ProgressBar({
32054 cls : 'roo-document-manager-progress-bar',
32057 aria_valuemax : 12,
32061 this.progressBar.render(this.progress.getChildContainer());
32064 onUploaderClick : function(e)
32066 e.preventDefault();
32068 if(this.fireEvent('beforeselectfile', this) != false){
32069 this.selectorEl.dom.click();
32074 onFileSelected : function(e)
32076 e.preventDefault();
32078 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32082 Roo.each(this.selectorEl.dom.files, function(file){
32083 if(this.fireEvent('inspect', this, file) != false){
32084 this.files.push(file);
32094 this.selectorEl.dom.value = '';
32096 if(!this.files || !this.files.length){
32100 if(this.boxes > 0 && this.files.length > this.boxes){
32101 this.files = this.files.slice(0, this.boxes);
32104 this.uploader.show();
32106 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32107 this.uploader.hide();
32116 Roo.each(this.files, function(file){
32118 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32119 var f = this.renderPreview(file);
32124 if(file.type.indexOf('image') != -1){
32125 this.delegates.push(
32127 _this.process(file);
32128 }).createDelegate(this)
32136 _this.process(file);
32137 }).createDelegate(this)
32142 this.files = files;
32144 this.delegates = this.delegates.concat(docs);
32146 if(!this.delegates.length){
32151 this.progressBar.aria_valuemax = this.delegates.length;
32158 arrange : function()
32160 if(!this.delegates.length){
32161 this.progressDialog.hide();
32166 var delegate = this.delegates.shift();
32168 this.progressDialog.show();
32170 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32172 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32177 refresh : function()
32179 this.uploader.show();
32181 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32182 this.uploader.hide();
32185 Roo.isTouch ? this.closable(false) : this.closable(true);
32187 this.fireEvent('refresh', this);
32190 onRemove : function(e, el, o)
32192 e.preventDefault();
32194 this.fireEvent('remove', this, o);
32198 remove : function(o)
32202 Roo.each(this.files, function(file){
32203 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32212 this.files = files;
32219 Roo.each(this.files, function(file){
32224 file.target.remove();
32233 onClick : function(e, el, o)
32235 e.preventDefault();
32237 this.fireEvent('click', this, o);
32241 closable : function(closable)
32243 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32245 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32257 xhrOnLoad : function(xhr)
32259 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32263 if (xhr.readyState !== 4) {
32265 this.fireEvent('exception', this, xhr);
32269 var response = Roo.decode(xhr.responseText);
32271 if(!response.success){
32273 this.fireEvent('exception', this, xhr);
32277 var file = this.renderPreview(response.data);
32279 this.files.push(file);
32283 this.fireEvent('afterupload', this, xhr);
32287 xhrOnError : function(xhr)
32289 Roo.log('xhr on error');
32291 var response = Roo.decode(xhr.responseText);
32298 process : function(file)
32300 if(this.fireEvent('process', this, file) !== false){
32301 if(this.editable && file.type.indexOf('image') != -1){
32302 this.fireEvent('edit', this, file);
32306 this.uploadStart(file, false);
32313 uploadStart : function(file, crop)
32315 this.xhr = new XMLHttpRequest();
32317 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32322 file.xhr = this.xhr;
32324 this.managerEl.createChild({
32326 cls : 'roo-document-manager-loading',
32330 tooltip : file.name,
32331 cls : 'roo-document-manager-thumb',
32332 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32338 this.xhr.open(this.method, this.url, true);
32341 "Accept": "application/json",
32342 "Cache-Control": "no-cache",
32343 "X-Requested-With": "XMLHttpRequest"
32346 for (var headerName in headers) {
32347 var headerValue = headers[headerName];
32349 this.xhr.setRequestHeader(headerName, headerValue);
32355 this.xhr.onload = function()
32357 _this.xhrOnLoad(_this.xhr);
32360 this.xhr.onerror = function()
32362 _this.xhrOnError(_this.xhr);
32365 var formData = new FormData();
32367 formData.append('returnHTML', 'NO');
32370 formData.append('crop', crop);
32373 formData.append(this.paramName, file, file.name);
32380 if(this.fireEvent('prepare', this, formData, options) != false){
32382 if(options.manually){
32386 this.xhr.send(formData);
32390 this.uploadCancel();
32393 uploadCancel : function()
32399 this.delegates = [];
32401 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32408 renderPreview : function(file)
32410 if(typeof(file.target) != 'undefined' && file.target){
32414 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32416 var previewEl = this.managerEl.createChild({
32418 cls : 'roo-document-manager-preview',
32422 tooltip : file[this.toolTipName],
32423 cls : 'roo-document-manager-thumb',
32424 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32429 html : '<i class="fa fa-times-circle"></i>'
32434 var close = previewEl.select('button.close', true).first();
32436 close.on('click', this.onRemove, this, file);
32438 file.target = previewEl;
32440 var image = previewEl.select('img', true).first();
32444 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32446 image.on('click', this.onClick, this, file);
32448 this.fireEvent('previewrendered', this, file);
32454 onPreviewLoad : function(file, image)
32456 if(typeof(file.target) == 'undefined' || !file.target){
32460 var width = image.dom.naturalWidth || image.dom.width;
32461 var height = image.dom.naturalHeight || image.dom.height;
32463 if(!this.previewResize) {
32467 if(width > height){
32468 file.target.addClass('wide');
32472 file.target.addClass('tall');
32477 uploadFromSource : function(file, crop)
32479 this.xhr = new XMLHttpRequest();
32481 this.managerEl.createChild({
32483 cls : 'roo-document-manager-loading',
32487 tooltip : file.name,
32488 cls : 'roo-document-manager-thumb',
32489 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32495 this.xhr.open(this.method, this.url, true);
32498 "Accept": "application/json",
32499 "Cache-Control": "no-cache",
32500 "X-Requested-With": "XMLHttpRequest"
32503 for (var headerName in headers) {
32504 var headerValue = headers[headerName];
32506 this.xhr.setRequestHeader(headerName, headerValue);
32512 this.xhr.onload = function()
32514 _this.xhrOnLoad(_this.xhr);
32517 this.xhr.onerror = function()
32519 _this.xhrOnError(_this.xhr);
32522 var formData = new FormData();
32524 formData.append('returnHTML', 'NO');
32526 formData.append('crop', crop);
32528 if(typeof(file.filename) != 'undefined'){
32529 formData.append('filename', file.filename);
32532 if(typeof(file.mimetype) != 'undefined'){
32533 formData.append('mimetype', file.mimetype);
32538 if(this.fireEvent('prepare', this, formData) != false){
32539 this.xhr.send(formData);
32549 * @class Roo.bootstrap.DocumentViewer
32550 * @extends Roo.bootstrap.Component
32551 * Bootstrap DocumentViewer class
32552 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32553 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32556 * Create a new DocumentViewer
32557 * @param {Object} config The config object
32560 Roo.bootstrap.DocumentViewer = function(config){
32561 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32566 * Fire after initEvent
32567 * @param {Roo.bootstrap.DocumentViewer} this
32573 * @param {Roo.bootstrap.DocumentViewer} this
32578 * Fire after download button
32579 * @param {Roo.bootstrap.DocumentViewer} this
32584 * Fire after trash button
32585 * @param {Roo.bootstrap.DocumentViewer} this
32592 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32594 showDownload : true,
32598 getAutoCreate : function()
32602 cls : 'roo-document-viewer',
32606 cls : 'roo-document-viewer-body',
32610 cls : 'roo-document-viewer-thumb',
32614 cls : 'roo-document-viewer-image'
32622 cls : 'roo-document-viewer-footer',
32625 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32629 cls : 'btn-group roo-document-viewer-download',
32633 cls : 'btn btn-default',
32634 html : '<i class="fa fa-download"></i>'
32640 cls : 'btn-group roo-document-viewer-trash',
32644 cls : 'btn btn-default',
32645 html : '<i class="fa fa-trash"></i>'
32658 initEvents : function()
32660 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32661 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32663 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32664 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32666 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32667 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32669 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32670 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32672 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32673 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32675 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32676 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32678 this.bodyEl.on('click', this.onClick, this);
32679 this.downloadBtn.on('click', this.onDownload, this);
32680 this.trashBtn.on('click', this.onTrash, this);
32682 this.downloadBtn.hide();
32683 this.trashBtn.hide();
32685 if(this.showDownload){
32686 this.downloadBtn.show();
32689 if(this.showTrash){
32690 this.trashBtn.show();
32693 if(!this.showDownload && !this.showTrash) {
32694 this.footerEl.hide();
32699 initial : function()
32701 this.fireEvent('initial', this);
32705 onClick : function(e)
32707 e.preventDefault();
32709 this.fireEvent('click', this);
32712 onDownload : function(e)
32714 e.preventDefault();
32716 this.fireEvent('download', this);
32719 onTrash : function(e)
32721 e.preventDefault();
32723 this.fireEvent('trash', this);
32735 * @class Roo.bootstrap.NavProgressBar
32736 * @extends Roo.bootstrap.Component
32737 * Bootstrap NavProgressBar class
32740 * Create a new nav progress bar
32741 * @param {Object} config The config object
32744 Roo.bootstrap.NavProgressBar = function(config){
32745 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32747 this.bullets = this.bullets || [];
32749 // Roo.bootstrap.NavProgressBar.register(this);
32753 * Fires when the active item changes
32754 * @param {Roo.bootstrap.NavProgressBar} this
32755 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32756 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32763 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32768 getAutoCreate : function()
32770 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32774 cls : 'roo-navigation-bar-group',
32778 cls : 'roo-navigation-top-bar'
32782 cls : 'roo-navigation-bullets-bar',
32786 cls : 'roo-navigation-bar'
32793 cls : 'roo-navigation-bottom-bar'
32803 initEvents: function()
32808 onRender : function(ct, position)
32810 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32812 if(this.bullets.length){
32813 Roo.each(this.bullets, function(b){
32822 addItem : function(cfg)
32824 var item = new Roo.bootstrap.NavProgressItem(cfg);
32826 item.parentId = this.id;
32827 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32830 var top = new Roo.bootstrap.Element({
32832 cls : 'roo-navigation-bar-text'
32835 var bottom = new Roo.bootstrap.Element({
32837 cls : 'roo-navigation-bar-text'
32840 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32841 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32843 var topText = new Roo.bootstrap.Element({
32845 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32848 var bottomText = new Roo.bootstrap.Element({
32850 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32853 topText.onRender(top.el, null);
32854 bottomText.onRender(bottom.el, null);
32857 item.bottomEl = bottom;
32860 this.barItems.push(item);
32865 getActive : function()
32867 var active = false;
32869 Roo.each(this.barItems, function(v){
32871 if (!v.isActive()) {
32883 setActiveItem : function(item)
32887 Roo.each(this.barItems, function(v){
32888 if (v.rid == item.rid) {
32892 if (v.isActive()) {
32893 v.setActive(false);
32898 item.setActive(true);
32900 this.fireEvent('changed', this, item, prev);
32903 getBarItem: function(rid)
32907 Roo.each(this.barItems, function(e) {
32908 if (e.rid != rid) {
32919 indexOfItem : function(item)
32923 Roo.each(this.barItems, function(v, i){
32925 if (v.rid != item.rid) {
32936 setActiveNext : function()
32938 var i = this.indexOfItem(this.getActive());
32940 if (i > this.barItems.length) {
32944 this.setActiveItem(this.barItems[i+1]);
32947 setActivePrev : function()
32949 var i = this.indexOfItem(this.getActive());
32955 this.setActiveItem(this.barItems[i-1]);
32958 format : function()
32960 if(!this.barItems.length){
32964 var width = 100 / this.barItems.length;
32966 Roo.each(this.barItems, function(i){
32967 i.el.setStyle('width', width + '%');
32968 i.topEl.el.setStyle('width', width + '%');
32969 i.bottomEl.el.setStyle('width', width + '%');
32978 * Nav Progress Item
32983 * @class Roo.bootstrap.NavProgressItem
32984 * @extends Roo.bootstrap.Component
32985 * Bootstrap NavProgressItem class
32986 * @cfg {String} rid the reference id
32987 * @cfg {Boolean} active (true|false) Is item active default false
32988 * @cfg {Boolean} disabled (true|false) Is item active default false
32989 * @cfg {String} html
32990 * @cfg {String} position (top|bottom) text position default bottom
32991 * @cfg {String} icon show icon instead of number
32994 * Create a new NavProgressItem
32995 * @param {Object} config The config object
32997 Roo.bootstrap.NavProgressItem = function(config){
32998 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33003 * The raw click event for the entire grid.
33004 * @param {Roo.bootstrap.NavProgressItem} this
33005 * @param {Roo.EventObject} e
33012 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33018 position : 'bottom',
33021 getAutoCreate : function()
33023 var iconCls = 'roo-navigation-bar-item-icon';
33025 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33029 cls: 'roo-navigation-bar-item',
33039 cfg.cls += ' active';
33042 cfg.cls += ' disabled';
33048 disable : function()
33050 this.setDisabled(true);
33053 enable : function()
33055 this.setDisabled(false);
33058 initEvents: function()
33060 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33062 this.iconEl.on('click', this.onClick, this);
33065 onClick : function(e)
33067 e.preventDefault();
33073 if(this.fireEvent('click', this, e) === false){
33077 this.parent().setActiveItem(this);
33080 isActive: function ()
33082 return this.active;
33085 setActive : function(state)
33087 if(this.active == state){
33091 this.active = state;
33094 this.el.addClass('active');
33098 this.el.removeClass('active');
33103 setDisabled : function(state)
33105 if(this.disabled == state){
33109 this.disabled = state;
33112 this.el.addClass('disabled');
33116 this.el.removeClass('disabled');
33119 tooltipEl : function()
33121 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33134 * @class Roo.bootstrap.FieldLabel
33135 * @extends Roo.bootstrap.Component
33136 * Bootstrap FieldLabel class
33137 * @cfg {String} html contents of the element
33138 * @cfg {String} tag tag of the element default label
33139 * @cfg {String} cls class of the element
33140 * @cfg {String} target label target
33141 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33142 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33143 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33144 * @cfg {String} iconTooltip default "This field is required"
33145 * @cfg {String} indicatorpos (left|right) default left
33148 * Create a new FieldLabel
33149 * @param {Object} config The config object
33152 Roo.bootstrap.FieldLabel = function(config){
33153 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33158 * Fires after the field has been marked as invalid.
33159 * @param {Roo.form.FieldLabel} this
33160 * @param {String} msg The validation message
33165 * Fires after the field has been validated with no errors.
33166 * @param {Roo.form.FieldLabel} this
33172 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33179 invalidClass : 'has-warning',
33180 validClass : 'has-success',
33181 iconTooltip : 'This field is required',
33182 indicatorpos : 'left',
33184 getAutoCreate : function(){
33187 if (!this.allowBlank) {
33193 cls : 'roo-bootstrap-field-label ' + this.cls,
33198 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33199 tooltip : this.iconTooltip
33208 if(this.indicatorpos == 'right'){
33211 cls : 'roo-bootstrap-field-label ' + this.cls,
33220 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33221 tooltip : this.iconTooltip
33230 initEvents: function()
33232 Roo.bootstrap.Element.superclass.initEvents.call(this);
33234 this.indicator = this.indicatorEl();
33236 if(this.indicator){
33237 this.indicator.removeClass('visible');
33238 this.indicator.addClass('invisible');
33241 Roo.bootstrap.FieldLabel.register(this);
33244 indicatorEl : function()
33246 var indicator = this.el.select('i.roo-required-indicator',true).first();
33257 * Mark this field as valid
33259 markValid : function()
33261 if(this.indicator){
33262 this.indicator.removeClass('visible');
33263 this.indicator.addClass('invisible');
33265 if (Roo.bootstrap.version == 3) {
33266 this.el.removeClass(this.invalidClass);
33267 this.el.addClass(this.validClass);
33269 this.el.removeClass('is-invalid');
33270 this.el.addClass('is-valid');
33274 this.fireEvent('valid', this);
33278 * Mark this field as invalid
33279 * @param {String} msg The validation message
33281 markInvalid : function(msg)
33283 if(this.indicator){
33284 this.indicator.removeClass('invisible');
33285 this.indicator.addClass('visible');
33287 if (Roo.bootstrap.version == 3) {
33288 this.el.removeClass(this.validClass);
33289 this.el.addClass(this.invalidClass);
33291 this.el.removeClass('is-valid');
33292 this.el.addClass('is-invalid');
33296 this.fireEvent('invalid', this, msg);
33302 Roo.apply(Roo.bootstrap.FieldLabel, {
33307 * register a FieldLabel Group
33308 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33310 register : function(label)
33312 if(this.groups.hasOwnProperty(label.target)){
33316 this.groups[label.target] = label;
33320 * fetch a FieldLabel Group based on the target
33321 * @param {string} target
33322 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33324 get: function(target) {
33325 if (typeof(this.groups[target]) == 'undefined') {
33329 return this.groups[target] ;
33338 * page DateSplitField.
33344 * @class Roo.bootstrap.DateSplitField
33345 * @extends Roo.bootstrap.Component
33346 * Bootstrap DateSplitField class
33347 * @cfg {string} fieldLabel - the label associated
33348 * @cfg {Number} labelWidth set the width of label (0-12)
33349 * @cfg {String} labelAlign (top|left)
33350 * @cfg {Boolean} dayAllowBlank (true|false) default false
33351 * @cfg {Boolean} monthAllowBlank (true|false) default false
33352 * @cfg {Boolean} yearAllowBlank (true|false) default false
33353 * @cfg {string} dayPlaceholder
33354 * @cfg {string} monthPlaceholder
33355 * @cfg {string} yearPlaceholder
33356 * @cfg {string} dayFormat default 'd'
33357 * @cfg {string} monthFormat default 'm'
33358 * @cfg {string} yearFormat default 'Y'
33359 * @cfg {Number} labellg set the width of label (1-12)
33360 * @cfg {Number} labelmd set the width of label (1-12)
33361 * @cfg {Number} labelsm set the width of label (1-12)
33362 * @cfg {Number} labelxs set the width of label (1-12)
33366 * Create a new DateSplitField
33367 * @param {Object} config The config object
33370 Roo.bootstrap.DateSplitField = function(config){
33371 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33377 * getting the data of years
33378 * @param {Roo.bootstrap.DateSplitField} this
33379 * @param {Object} years
33384 * getting the data of days
33385 * @param {Roo.bootstrap.DateSplitField} this
33386 * @param {Object} days
33391 * Fires after the field has been marked as invalid.
33392 * @param {Roo.form.Field} this
33393 * @param {String} msg The validation message
33398 * Fires after the field has been validated with no errors.
33399 * @param {Roo.form.Field} this
33405 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33408 labelAlign : 'top',
33410 dayAllowBlank : false,
33411 monthAllowBlank : false,
33412 yearAllowBlank : false,
33413 dayPlaceholder : '',
33414 monthPlaceholder : '',
33415 yearPlaceholder : '',
33419 isFormField : true,
33425 getAutoCreate : function()
33429 cls : 'row roo-date-split-field-group',
33434 cls : 'form-hidden-field roo-date-split-field-group-value',
33440 var labelCls = 'col-md-12';
33441 var contentCls = 'col-md-4';
33443 if(this.fieldLabel){
33447 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33451 html : this.fieldLabel
33456 if(this.labelAlign == 'left'){
33458 if(this.labelWidth > 12){
33459 label.style = "width: " + this.labelWidth + 'px';
33462 if(this.labelWidth < 13 && this.labelmd == 0){
33463 this.labelmd = this.labelWidth;
33466 if(this.labellg > 0){
33467 labelCls = ' col-lg-' + this.labellg;
33468 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33471 if(this.labelmd > 0){
33472 labelCls = ' col-md-' + this.labelmd;
33473 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33476 if(this.labelsm > 0){
33477 labelCls = ' col-sm-' + this.labelsm;
33478 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33481 if(this.labelxs > 0){
33482 labelCls = ' col-xs-' + this.labelxs;
33483 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33487 label.cls += ' ' + labelCls;
33489 cfg.cn.push(label);
33492 Roo.each(['day', 'month', 'year'], function(t){
33495 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33502 inputEl: function ()
33504 return this.el.select('.roo-date-split-field-group-value', true).first();
33507 onRender : function(ct, position)
33511 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33513 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33515 this.dayField = new Roo.bootstrap.ComboBox({
33516 allowBlank : this.dayAllowBlank,
33517 alwaysQuery : true,
33518 displayField : 'value',
33521 forceSelection : true,
33523 placeholder : this.dayPlaceholder,
33524 selectOnFocus : true,
33525 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33526 triggerAction : 'all',
33528 valueField : 'value',
33529 store : new Roo.data.SimpleStore({
33530 data : (function() {
33532 _this.fireEvent('days', _this, days);
33535 fields : [ 'value' ]
33538 select : function (_self, record, index)
33540 _this.setValue(_this.getValue());
33545 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33547 this.monthField = new Roo.bootstrap.MonthField({
33548 after : '<i class=\"fa fa-calendar\"></i>',
33549 allowBlank : this.monthAllowBlank,
33550 placeholder : this.monthPlaceholder,
33553 render : function (_self)
33555 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33556 e.preventDefault();
33560 select : function (_self, oldvalue, newvalue)
33562 _this.setValue(_this.getValue());
33567 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33569 this.yearField = new Roo.bootstrap.ComboBox({
33570 allowBlank : this.yearAllowBlank,
33571 alwaysQuery : true,
33572 displayField : 'value',
33575 forceSelection : true,
33577 placeholder : this.yearPlaceholder,
33578 selectOnFocus : true,
33579 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33580 triggerAction : 'all',
33582 valueField : 'value',
33583 store : new Roo.data.SimpleStore({
33584 data : (function() {
33586 _this.fireEvent('years', _this, years);
33589 fields : [ 'value' ]
33592 select : function (_self, record, index)
33594 _this.setValue(_this.getValue());
33599 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33602 setValue : function(v, format)
33604 this.inputEl.dom.value = v;
33606 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33608 var d = Date.parseDate(v, f);
33615 this.setDay(d.format(this.dayFormat));
33616 this.setMonth(d.format(this.monthFormat));
33617 this.setYear(d.format(this.yearFormat));
33624 setDay : function(v)
33626 this.dayField.setValue(v);
33627 this.inputEl.dom.value = this.getValue();
33632 setMonth : function(v)
33634 this.monthField.setValue(v, true);
33635 this.inputEl.dom.value = this.getValue();
33640 setYear : function(v)
33642 this.yearField.setValue(v);
33643 this.inputEl.dom.value = this.getValue();
33648 getDay : function()
33650 return this.dayField.getValue();
33653 getMonth : function()
33655 return this.monthField.getValue();
33658 getYear : function()
33660 return this.yearField.getValue();
33663 getValue : function()
33665 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33667 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33677 this.inputEl.dom.value = '';
33682 validate : function()
33684 var d = this.dayField.validate();
33685 var m = this.monthField.validate();
33686 var y = this.yearField.validate();
33691 (!this.dayAllowBlank && !d) ||
33692 (!this.monthAllowBlank && !m) ||
33693 (!this.yearAllowBlank && !y)
33698 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33707 this.markInvalid();
33712 markValid : function()
33715 var label = this.el.select('label', true).first();
33716 var icon = this.el.select('i.fa-star', true).first();
33722 this.fireEvent('valid', this);
33726 * Mark this field as invalid
33727 * @param {String} msg The validation message
33729 markInvalid : function(msg)
33732 var label = this.el.select('label', true).first();
33733 var icon = this.el.select('i.fa-star', true).first();
33735 if(label && !icon){
33736 this.el.select('.roo-date-split-field-label', true).createChild({
33738 cls : 'text-danger fa fa-lg fa-star',
33739 tooltip : 'This field is required',
33740 style : 'margin-right:5px;'
33744 this.fireEvent('invalid', this, msg);
33747 clearInvalid : function()
33749 var label = this.el.select('label', true).first();
33750 var icon = this.el.select('i.fa-star', true).first();
33756 this.fireEvent('valid', this);
33759 getName: function()
33769 * http://masonry.desandro.com
33771 * The idea is to render all the bricks based on vertical width...
33773 * The original code extends 'outlayer' - we might need to use that....
33779 * @class Roo.bootstrap.LayoutMasonry
33780 * @extends Roo.bootstrap.Component
33781 * Bootstrap Layout Masonry class
33784 * Create a new Element
33785 * @param {Object} config The config object
33788 Roo.bootstrap.LayoutMasonry = function(config){
33790 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33794 Roo.bootstrap.LayoutMasonry.register(this);
33800 * Fire after layout the items
33801 * @param {Roo.bootstrap.LayoutMasonry} this
33802 * @param {Roo.EventObject} e
33809 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33812 * @cfg {Boolean} isLayoutInstant = no animation?
33814 isLayoutInstant : false, // needed?
33817 * @cfg {Number} boxWidth width of the columns
33822 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33827 * @cfg {Number} padWidth padding below box..
33832 * @cfg {Number} gutter gutter width..
33837 * @cfg {Number} maxCols maximum number of columns
33843 * @cfg {Boolean} isAutoInitial defalut true
33845 isAutoInitial : true,
33850 * @cfg {Boolean} isHorizontal defalut false
33852 isHorizontal : false,
33854 currentSize : null,
33860 bricks: null, //CompositeElement
33864 _isLayoutInited : false,
33866 // isAlternative : false, // only use for vertical layout...
33869 * @cfg {Number} alternativePadWidth padding below box..
33871 alternativePadWidth : 50,
33873 selectedBrick : [],
33875 getAutoCreate : function(){
33877 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33881 cls: 'blog-masonary-wrapper ' + this.cls,
33883 cls : 'mas-boxes masonary'
33890 getChildContainer: function( )
33892 if (this.boxesEl) {
33893 return this.boxesEl;
33896 this.boxesEl = this.el.select('.mas-boxes').first();
33898 return this.boxesEl;
33902 initEvents : function()
33906 if(this.isAutoInitial){
33907 Roo.log('hook children rendered');
33908 this.on('childrenrendered', function() {
33909 Roo.log('children rendered');
33915 initial : function()
33917 this.selectedBrick = [];
33919 this.currentSize = this.el.getBox(true);
33921 Roo.EventManager.onWindowResize(this.resize, this);
33923 if(!this.isAutoInitial){
33931 //this.layout.defer(500,this);
33935 resize : function()
33937 var cs = this.el.getBox(true);
33940 this.currentSize.width == cs.width &&
33941 this.currentSize.x == cs.x &&
33942 this.currentSize.height == cs.height &&
33943 this.currentSize.y == cs.y
33945 Roo.log("no change in with or X or Y");
33949 this.currentSize = cs;
33955 layout : function()
33957 this._resetLayout();
33959 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33961 this.layoutItems( isInstant );
33963 this._isLayoutInited = true;
33965 this.fireEvent('layout', this);
33969 _resetLayout : function()
33971 if(this.isHorizontal){
33972 this.horizontalMeasureColumns();
33976 this.verticalMeasureColumns();
33980 verticalMeasureColumns : function()
33982 this.getContainerWidth();
33984 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33985 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33989 var boxWidth = this.boxWidth + this.padWidth;
33991 if(this.containerWidth < this.boxWidth){
33992 boxWidth = this.containerWidth
33995 var containerWidth = this.containerWidth;
33997 var cols = Math.floor(containerWidth / boxWidth);
33999 this.cols = Math.max( cols, 1 );
34001 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34003 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34005 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34007 this.colWidth = boxWidth + avail - this.padWidth;
34009 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34010 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34013 horizontalMeasureColumns : function()
34015 this.getContainerWidth();
34017 var boxWidth = this.boxWidth;
34019 if(this.containerWidth < boxWidth){
34020 boxWidth = this.containerWidth;
34023 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34025 this.el.setHeight(boxWidth);
34029 getContainerWidth : function()
34031 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34034 layoutItems : function( isInstant )
34036 Roo.log(this.bricks);
34038 var items = Roo.apply([], this.bricks);
34040 if(this.isHorizontal){
34041 this._horizontalLayoutItems( items , isInstant );
34045 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34046 // this._verticalAlternativeLayoutItems( items , isInstant );
34050 this._verticalLayoutItems( items , isInstant );
34054 _verticalLayoutItems : function ( items , isInstant)
34056 if ( !items || !items.length ) {
34061 ['xs', 'xs', 'xs', 'tall'],
34062 ['xs', 'xs', 'tall'],
34063 ['xs', 'xs', 'sm'],
34064 ['xs', 'xs', 'xs'],
34070 ['sm', 'xs', 'xs'],
34074 ['tall', 'xs', 'xs', 'xs'],
34075 ['tall', 'xs', 'xs'],
34087 Roo.each(items, function(item, k){
34089 switch (item.size) {
34090 // these layouts take up a full box,
34101 boxes.push([item]);
34124 var filterPattern = function(box, length)
34132 var pattern = box.slice(0, length);
34136 Roo.each(pattern, function(i){
34137 format.push(i.size);
34140 Roo.each(standard, function(s){
34142 if(String(s) != String(format)){
34151 if(!match && length == 1){
34156 filterPattern(box, length - 1);
34160 queue.push(pattern);
34162 box = box.slice(length, box.length);
34164 filterPattern(box, 4);
34170 Roo.each(boxes, function(box, k){
34176 if(box.length == 1){
34181 filterPattern(box, 4);
34185 this._processVerticalLayoutQueue( queue, isInstant );
34189 // _verticalAlternativeLayoutItems : function( items , isInstant )
34191 // if ( !items || !items.length ) {
34195 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34199 _horizontalLayoutItems : function ( items , isInstant)
34201 if ( !items || !items.length || items.length < 3) {
34207 var eItems = items.slice(0, 3);
34209 items = items.slice(3, items.length);
34212 ['xs', 'xs', 'xs', 'wide'],
34213 ['xs', 'xs', 'wide'],
34214 ['xs', 'xs', 'sm'],
34215 ['xs', 'xs', 'xs'],
34221 ['sm', 'xs', 'xs'],
34225 ['wide', 'xs', 'xs', 'xs'],
34226 ['wide', 'xs', 'xs'],
34239 Roo.each(items, function(item, k){
34241 switch (item.size) {
34252 boxes.push([item]);
34276 var filterPattern = function(box, length)
34284 var pattern = box.slice(0, length);
34288 Roo.each(pattern, function(i){
34289 format.push(i.size);
34292 Roo.each(standard, function(s){
34294 if(String(s) != String(format)){
34303 if(!match && length == 1){
34308 filterPattern(box, length - 1);
34312 queue.push(pattern);
34314 box = box.slice(length, box.length);
34316 filterPattern(box, 4);
34322 Roo.each(boxes, function(box, k){
34328 if(box.length == 1){
34333 filterPattern(box, 4);
34340 var pos = this.el.getBox(true);
34344 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34346 var hit_end = false;
34348 Roo.each(queue, function(box){
34352 Roo.each(box, function(b){
34354 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34364 Roo.each(box, function(b){
34366 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34369 mx = Math.max(mx, b.x);
34373 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34377 Roo.each(box, function(b){
34379 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34393 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34396 /** Sets position of item in DOM
34397 * @param {Element} item
34398 * @param {Number} x - horizontal position
34399 * @param {Number} y - vertical position
34400 * @param {Boolean} isInstant - disables transitions
34402 _processVerticalLayoutQueue : function( queue, isInstant )
34404 var pos = this.el.getBox(true);
34409 for (var i = 0; i < this.cols; i++){
34413 Roo.each(queue, function(box, k){
34415 var col = k % this.cols;
34417 Roo.each(box, function(b,kk){
34419 b.el.position('absolute');
34421 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34422 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34424 if(b.size == 'md-left' || b.size == 'md-right'){
34425 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34426 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34429 b.el.setWidth(width);
34430 b.el.setHeight(height);
34432 b.el.select('iframe',true).setSize(width,height);
34436 for (var i = 0; i < this.cols; i++){
34438 if(maxY[i] < maxY[col]){
34443 col = Math.min(col, i);
34447 x = pos.x + col * (this.colWidth + this.padWidth);
34451 var positions = [];
34453 switch (box.length){
34455 positions = this.getVerticalOneBoxColPositions(x, y, box);
34458 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34461 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34464 positions = this.getVerticalFourBoxColPositions(x, y, box);
34470 Roo.each(box, function(b,kk){
34472 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34474 var sz = b.el.getSize();
34476 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34484 for (var i = 0; i < this.cols; i++){
34485 mY = Math.max(mY, maxY[i]);
34488 this.el.setHeight(mY - pos.y);
34492 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34494 // var pos = this.el.getBox(true);
34497 // var maxX = pos.right;
34499 // var maxHeight = 0;
34501 // Roo.each(items, function(item, k){
34505 // item.el.position('absolute');
34507 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34509 // item.el.setWidth(width);
34511 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34513 // item.el.setHeight(height);
34516 // item.el.setXY([x, y], isInstant ? false : true);
34518 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34521 // y = y + height + this.alternativePadWidth;
34523 // maxHeight = maxHeight + height + this.alternativePadWidth;
34527 // this.el.setHeight(maxHeight);
34531 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34533 var pos = this.el.getBox(true);
34538 var maxX = pos.right;
34540 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34542 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34544 Roo.each(queue, function(box, k){
34546 Roo.each(box, function(b, kk){
34548 b.el.position('absolute');
34550 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34551 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34553 if(b.size == 'md-left' || b.size == 'md-right'){
34554 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34555 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34558 b.el.setWidth(width);
34559 b.el.setHeight(height);
34567 var positions = [];
34569 switch (box.length){
34571 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34574 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34577 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34580 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34586 Roo.each(box, function(b,kk){
34588 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34590 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34598 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34600 Roo.each(eItems, function(b,k){
34602 b.size = (k == 0) ? 'sm' : 'xs';
34603 b.x = (k == 0) ? 2 : 1;
34604 b.y = (k == 0) ? 2 : 1;
34606 b.el.position('absolute');
34608 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34610 b.el.setWidth(width);
34612 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34614 b.el.setHeight(height);
34618 var positions = [];
34621 x : maxX - this.unitWidth * 2 - this.gutter,
34626 x : maxX - this.unitWidth,
34627 y : minY + (this.unitWidth + this.gutter) * 2
34631 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34635 Roo.each(eItems, function(b,k){
34637 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34643 getVerticalOneBoxColPositions : function(x, y, box)
34647 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34649 if(box[0].size == 'md-left'){
34653 if(box[0].size == 'md-right'){
34658 x : x + (this.unitWidth + this.gutter) * rand,
34665 getVerticalTwoBoxColPositions : function(x, y, box)
34669 if(box[0].size == 'xs'){
34673 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34677 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34691 x : x + (this.unitWidth + this.gutter) * 2,
34692 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34699 getVerticalThreeBoxColPositions : function(x, y, box)
34703 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34711 x : x + (this.unitWidth + this.gutter) * 1,
34716 x : x + (this.unitWidth + this.gutter) * 2,
34724 if(box[0].size == 'xs' && box[1].size == 'xs'){
34733 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34737 x : x + (this.unitWidth + this.gutter) * 1,
34751 x : x + (this.unitWidth + this.gutter) * 2,
34756 x : x + (this.unitWidth + this.gutter) * 2,
34757 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34764 getVerticalFourBoxColPositions : function(x, y, box)
34768 if(box[0].size == 'xs'){
34777 y : y + (this.unitHeight + this.gutter) * 1
34782 y : y + (this.unitHeight + this.gutter) * 2
34786 x : x + (this.unitWidth + this.gutter) * 1,
34800 x : x + (this.unitWidth + this.gutter) * 2,
34805 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34806 y : y + (this.unitHeight + this.gutter) * 1
34810 x : x + (this.unitWidth + this.gutter) * 2,
34811 y : y + (this.unitWidth + this.gutter) * 2
34818 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34822 if(box[0].size == 'md-left'){
34824 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34831 if(box[0].size == 'md-right'){
34833 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34834 y : minY + (this.unitWidth + this.gutter) * 1
34840 var rand = Math.floor(Math.random() * (4 - box[0].y));
34843 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34844 y : minY + (this.unitWidth + this.gutter) * rand
34851 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34855 if(box[0].size == 'xs'){
34858 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34863 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34864 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34872 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34877 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34878 y : minY + (this.unitWidth + this.gutter) * 2
34885 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34889 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34892 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34897 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34898 y : minY + (this.unitWidth + this.gutter) * 1
34902 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34903 y : minY + (this.unitWidth + this.gutter) * 2
34910 if(box[0].size == 'xs' && box[1].size == 'xs'){
34913 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34918 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34923 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34924 y : minY + (this.unitWidth + this.gutter) * 1
34932 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34937 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34938 y : minY + (this.unitWidth + this.gutter) * 2
34942 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34943 y : minY + (this.unitWidth + this.gutter) * 2
34950 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34954 if(box[0].size == 'xs'){
34957 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34962 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34967 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),
34972 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34973 y : minY + (this.unitWidth + this.gutter) * 1
34981 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34986 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34987 y : minY + (this.unitWidth + this.gutter) * 2
34991 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34992 y : minY + (this.unitWidth + this.gutter) * 2
34996 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),
34997 y : minY + (this.unitWidth + this.gutter) * 2
35005 * remove a Masonry Brick
35006 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35008 removeBrick : function(brick_id)
35014 for (var i = 0; i<this.bricks.length; i++) {
35015 if (this.bricks[i].id == brick_id) {
35016 this.bricks.splice(i,1);
35017 this.el.dom.removeChild(Roo.get(brick_id).dom);
35024 * adds a Masonry Brick
35025 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35027 addBrick : function(cfg)
35029 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35030 //this.register(cn);
35031 cn.parentId = this.id;
35032 cn.render(this.el);
35037 * register a Masonry Brick
35038 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35041 register : function(brick)
35043 this.bricks.push(brick);
35044 brick.masonryId = this.id;
35048 * clear all the Masonry Brick
35050 clearAll : function()
35053 //this.getChildContainer().dom.innerHTML = "";
35054 this.el.dom.innerHTML = '';
35057 getSelected : function()
35059 if (!this.selectedBrick) {
35063 return this.selectedBrick;
35067 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35071 * register a Masonry Layout
35072 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35075 register : function(layout)
35077 this.groups[layout.id] = layout;
35080 * fetch a Masonry Layout based on the masonry layout ID
35081 * @param {string} the masonry layout to add
35082 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35085 get: function(layout_id) {
35086 if (typeof(this.groups[layout_id]) == 'undefined') {
35089 return this.groups[layout_id] ;
35101 * http://masonry.desandro.com
35103 * The idea is to render all the bricks based on vertical width...
35105 * The original code extends 'outlayer' - we might need to use that....
35111 * @class Roo.bootstrap.LayoutMasonryAuto
35112 * @extends Roo.bootstrap.Component
35113 * Bootstrap Layout Masonry class
35116 * Create a new Element
35117 * @param {Object} config The config object
35120 Roo.bootstrap.LayoutMasonryAuto = function(config){
35121 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35124 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35127 * @cfg {Boolean} isFitWidth - resize the width..
35129 isFitWidth : false, // options..
35131 * @cfg {Boolean} isOriginLeft = left align?
35133 isOriginLeft : true,
35135 * @cfg {Boolean} isOriginTop = top align?
35137 isOriginTop : false,
35139 * @cfg {Boolean} isLayoutInstant = no animation?
35141 isLayoutInstant : false, // needed?
35143 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35145 isResizingContainer : true,
35147 * @cfg {Number} columnWidth width of the columns
35153 * @cfg {Number} maxCols maximum number of columns
35158 * @cfg {Number} padHeight padding below box..
35164 * @cfg {Boolean} isAutoInitial defalut true
35167 isAutoInitial : true,
35173 initialColumnWidth : 0,
35174 currentSize : null,
35176 colYs : null, // array.
35183 bricks: null, //CompositeElement
35184 cols : 0, // array?
35185 // element : null, // wrapped now this.el
35186 _isLayoutInited : null,
35189 getAutoCreate : function(){
35193 cls: 'blog-masonary-wrapper ' + this.cls,
35195 cls : 'mas-boxes masonary'
35202 getChildContainer: function( )
35204 if (this.boxesEl) {
35205 return this.boxesEl;
35208 this.boxesEl = this.el.select('.mas-boxes').first();
35210 return this.boxesEl;
35214 initEvents : function()
35218 if(this.isAutoInitial){
35219 Roo.log('hook children rendered');
35220 this.on('childrenrendered', function() {
35221 Roo.log('children rendered');
35228 initial : function()
35230 this.reloadItems();
35232 this.currentSize = this.el.getBox(true);
35234 /// was window resize... - let's see if this works..
35235 Roo.EventManager.onWindowResize(this.resize, this);
35237 if(!this.isAutoInitial){
35242 this.layout.defer(500,this);
35245 reloadItems: function()
35247 this.bricks = this.el.select('.masonry-brick', true);
35249 this.bricks.each(function(b) {
35250 //Roo.log(b.getSize());
35251 if (!b.attr('originalwidth')) {
35252 b.attr('originalwidth', b.getSize().width);
35257 Roo.log(this.bricks.elements.length);
35260 resize : function()
35263 var cs = this.el.getBox(true);
35265 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35266 Roo.log("no change in with or X");
35269 this.currentSize = cs;
35273 layout : function()
35276 this._resetLayout();
35277 //this._manageStamps();
35279 // don't animate first layout
35280 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35281 this.layoutItems( isInstant );
35283 // flag for initalized
35284 this._isLayoutInited = true;
35287 layoutItems : function( isInstant )
35289 //var items = this._getItemsForLayout( this.items );
35290 // original code supports filtering layout items.. we just ignore it..
35292 this._layoutItems( this.bricks , isInstant );
35294 this._postLayout();
35296 _layoutItems : function ( items , isInstant)
35298 //this.fireEvent( 'layout', this, items );
35301 if ( !items || !items.elements.length ) {
35302 // no items, emit event with empty array
35307 items.each(function(item) {
35308 Roo.log("layout item");
35310 // get x/y object from method
35311 var position = this._getItemLayoutPosition( item );
35313 position.item = item;
35314 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35315 queue.push( position );
35318 this._processLayoutQueue( queue );
35320 /** Sets position of item in DOM
35321 * @param {Element} item
35322 * @param {Number} x - horizontal position
35323 * @param {Number} y - vertical position
35324 * @param {Boolean} isInstant - disables transitions
35326 _processLayoutQueue : function( queue )
35328 for ( var i=0, len = queue.length; i < len; i++ ) {
35329 var obj = queue[i];
35330 obj.item.position('absolute');
35331 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35337 * Any logic you want to do after each layout,
35338 * i.e. size the container
35340 _postLayout : function()
35342 this.resizeContainer();
35345 resizeContainer : function()
35347 if ( !this.isResizingContainer ) {
35350 var size = this._getContainerSize();
35352 this.el.setSize(size.width,size.height);
35353 this.boxesEl.setSize(size.width,size.height);
35359 _resetLayout : function()
35361 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35362 this.colWidth = this.el.getWidth();
35363 //this.gutter = this.el.getWidth();
35365 this.measureColumns();
35371 this.colYs.push( 0 );
35377 measureColumns : function()
35379 this.getContainerWidth();
35380 // if columnWidth is 0, default to outerWidth of first item
35381 if ( !this.columnWidth ) {
35382 var firstItem = this.bricks.first();
35383 Roo.log(firstItem);
35384 this.columnWidth = this.containerWidth;
35385 if (firstItem && firstItem.attr('originalwidth') ) {
35386 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35388 // columnWidth fall back to item of first element
35389 Roo.log("set column width?");
35390 this.initialColumnWidth = this.columnWidth ;
35392 // if first elem has no width, default to size of container
35397 if (this.initialColumnWidth) {
35398 this.columnWidth = this.initialColumnWidth;
35403 // column width is fixed at the top - however if container width get's smaller we should
35406 // this bit calcs how man columns..
35408 var columnWidth = this.columnWidth += this.gutter;
35410 // calculate columns
35411 var containerWidth = this.containerWidth + this.gutter;
35413 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35414 // fix rounding errors, typically with gutters
35415 var excess = columnWidth - containerWidth % columnWidth;
35418 // if overshoot is less than a pixel, round up, otherwise floor it
35419 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35420 cols = Math[ mathMethod ]( cols );
35421 this.cols = Math.max( cols, 1 );
35422 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35424 // padding positioning..
35425 var totalColWidth = this.cols * this.columnWidth;
35426 var padavail = this.containerWidth - totalColWidth;
35427 // so for 2 columns - we need 3 'pads'
35429 var padNeeded = (1+this.cols) * this.padWidth;
35431 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35433 this.columnWidth += padExtra
35434 //this.padWidth = Math.floor(padavail / ( this.cols));
35436 // adjust colum width so that padding is fixed??
35438 // we have 3 columns ... total = width * 3
35439 // we have X left over... that should be used by
35441 //if (this.expandC) {
35449 getContainerWidth : function()
35451 /* // container is parent if fit width
35452 var container = this.isFitWidth ? this.element.parentNode : this.element;
35453 // check that this.size and size are there
35454 // IE8 triggers resize on body size change, so they might not be
35456 var size = getSize( container ); //FIXME
35457 this.containerWidth = size && size.innerWidth; //FIXME
35460 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35464 _getItemLayoutPosition : function( item ) // what is item?
35466 // we resize the item to our columnWidth..
35468 item.setWidth(this.columnWidth);
35469 item.autoBoxAdjust = false;
35471 var sz = item.getSize();
35473 // how many columns does this brick span
35474 var remainder = this.containerWidth % this.columnWidth;
35476 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35477 // round if off by 1 pixel, otherwise use ceil
35478 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35479 colSpan = Math.min( colSpan, this.cols );
35481 // normally this should be '1' as we dont' currently allow multi width columns..
35483 var colGroup = this._getColGroup( colSpan );
35484 // get the minimum Y value from the columns
35485 var minimumY = Math.min.apply( Math, colGroup );
35486 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35488 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35490 // position the brick
35492 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35493 y: this.currentSize.y + minimumY + this.padHeight
35497 // apply setHeight to necessary columns
35498 var setHeight = minimumY + sz.height + this.padHeight;
35499 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35501 var setSpan = this.cols + 1 - colGroup.length;
35502 for ( var i = 0; i < setSpan; i++ ) {
35503 this.colYs[ shortColIndex + i ] = setHeight ;
35510 * @param {Number} colSpan - number of columns the element spans
35511 * @returns {Array} colGroup
35513 _getColGroup : function( colSpan )
35515 if ( colSpan < 2 ) {
35516 // if brick spans only one column, use all the column Ys
35521 // how many different places could this brick fit horizontally
35522 var groupCount = this.cols + 1 - colSpan;
35523 // for each group potential horizontal position
35524 for ( var i = 0; i < groupCount; i++ ) {
35525 // make an array of colY values for that one group
35526 var groupColYs = this.colYs.slice( i, i + colSpan );
35527 // and get the max value of the array
35528 colGroup[i] = Math.max.apply( Math, groupColYs );
35533 _manageStamp : function( stamp )
35535 var stampSize = stamp.getSize();
35536 var offset = stamp.getBox();
35537 // get the columns that this stamp affects
35538 var firstX = this.isOriginLeft ? offset.x : offset.right;
35539 var lastX = firstX + stampSize.width;
35540 var firstCol = Math.floor( firstX / this.columnWidth );
35541 firstCol = Math.max( 0, firstCol );
35543 var lastCol = Math.floor( lastX / this.columnWidth );
35544 // lastCol should not go over if multiple of columnWidth #425
35545 lastCol -= lastX % this.columnWidth ? 0 : 1;
35546 lastCol = Math.min( this.cols - 1, lastCol );
35548 // set colYs to bottom of the stamp
35549 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35552 for ( var i = firstCol; i <= lastCol; i++ ) {
35553 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35558 _getContainerSize : function()
35560 this.maxY = Math.max.apply( Math, this.colYs );
35565 if ( this.isFitWidth ) {
35566 size.width = this._getContainerFitWidth();
35572 _getContainerFitWidth : function()
35574 var unusedCols = 0;
35575 // count unused columns
35578 if ( this.colYs[i] !== 0 ) {
35583 // fit container to columns that have been used
35584 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35587 needsResizeLayout : function()
35589 var previousWidth = this.containerWidth;
35590 this.getContainerWidth();
35591 return previousWidth !== this.containerWidth;
35606 * @class Roo.bootstrap.MasonryBrick
35607 * @extends Roo.bootstrap.Component
35608 * Bootstrap MasonryBrick class
35611 * Create a new MasonryBrick
35612 * @param {Object} config The config object
35615 Roo.bootstrap.MasonryBrick = function(config){
35617 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35619 Roo.bootstrap.MasonryBrick.register(this);
35625 * When a MasonryBrick is clcik
35626 * @param {Roo.bootstrap.MasonryBrick} this
35627 * @param {Roo.EventObject} e
35633 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35636 * @cfg {String} title
35640 * @cfg {String} html
35644 * @cfg {String} bgimage
35648 * @cfg {String} videourl
35652 * @cfg {String} cls
35656 * @cfg {String} href
35660 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35665 * @cfg {String} placetitle (center|bottom)
35670 * @cfg {Boolean} isFitContainer defalut true
35672 isFitContainer : true,
35675 * @cfg {Boolean} preventDefault defalut false
35677 preventDefault : false,
35680 * @cfg {Boolean} inverse defalut false
35682 maskInverse : false,
35684 getAutoCreate : function()
35686 if(!this.isFitContainer){
35687 return this.getSplitAutoCreate();
35690 var cls = 'masonry-brick masonry-brick-full';
35692 if(this.href.length){
35693 cls += ' masonry-brick-link';
35696 if(this.bgimage.length){
35697 cls += ' masonry-brick-image';
35700 if(this.maskInverse){
35701 cls += ' mask-inverse';
35704 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35705 cls += ' enable-mask';
35709 cls += ' masonry-' + this.size + '-brick';
35712 if(this.placetitle.length){
35714 switch (this.placetitle) {
35716 cls += ' masonry-center-title';
35719 cls += ' masonry-bottom-title';
35726 if(!this.html.length && !this.bgimage.length){
35727 cls += ' masonry-center-title';
35730 if(!this.html.length && this.bgimage.length){
35731 cls += ' masonry-bottom-title';
35736 cls += ' ' + this.cls;
35740 tag: (this.href.length) ? 'a' : 'div',
35745 cls: 'masonry-brick-mask'
35749 cls: 'masonry-brick-paragraph',
35755 if(this.href.length){
35756 cfg.href = this.href;
35759 var cn = cfg.cn[1].cn;
35761 if(this.title.length){
35764 cls: 'masonry-brick-title',
35769 if(this.html.length){
35772 cls: 'masonry-brick-text',
35777 if (!this.title.length && !this.html.length) {
35778 cfg.cn[1].cls += ' hide';
35781 if(this.bgimage.length){
35784 cls: 'masonry-brick-image-view',
35789 if(this.videourl.length){
35790 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35791 // youtube support only?
35794 cls: 'masonry-brick-image-view',
35797 allowfullscreen : true
35805 getSplitAutoCreate : function()
35807 var cls = 'masonry-brick masonry-brick-split';
35809 if(this.href.length){
35810 cls += ' masonry-brick-link';
35813 if(this.bgimage.length){
35814 cls += ' masonry-brick-image';
35818 cls += ' masonry-' + this.size + '-brick';
35821 switch (this.placetitle) {
35823 cls += ' masonry-center-title';
35826 cls += ' masonry-bottom-title';
35829 if(!this.bgimage.length){
35830 cls += ' masonry-center-title';
35833 if(this.bgimage.length){
35834 cls += ' masonry-bottom-title';
35840 cls += ' ' + this.cls;
35844 tag: (this.href.length) ? 'a' : 'div',
35849 cls: 'masonry-brick-split-head',
35853 cls: 'masonry-brick-paragraph',
35860 cls: 'masonry-brick-split-body',
35866 if(this.href.length){
35867 cfg.href = this.href;
35870 if(this.title.length){
35871 cfg.cn[0].cn[0].cn.push({
35873 cls: 'masonry-brick-title',
35878 if(this.html.length){
35879 cfg.cn[1].cn.push({
35881 cls: 'masonry-brick-text',
35886 if(this.bgimage.length){
35887 cfg.cn[0].cn.push({
35889 cls: 'masonry-brick-image-view',
35894 if(this.videourl.length){
35895 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35896 // youtube support only?
35897 cfg.cn[0].cn.cn.push({
35899 cls: 'masonry-brick-image-view',
35902 allowfullscreen : true
35909 initEvents: function()
35911 switch (this.size) {
35944 this.el.on('touchstart', this.onTouchStart, this);
35945 this.el.on('touchmove', this.onTouchMove, this);
35946 this.el.on('touchend', this.onTouchEnd, this);
35947 this.el.on('contextmenu', this.onContextMenu, this);
35949 this.el.on('mouseenter' ,this.enter, this);
35950 this.el.on('mouseleave', this.leave, this);
35951 this.el.on('click', this.onClick, this);
35954 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35955 this.parent().bricks.push(this);
35960 onClick: function(e, el)
35962 var time = this.endTimer - this.startTimer;
35963 // Roo.log(e.preventDefault());
35966 e.preventDefault();
35971 if(!this.preventDefault){
35975 e.preventDefault();
35977 if (this.activeClass != '') {
35978 this.selectBrick();
35981 this.fireEvent('click', this, e);
35984 enter: function(e, el)
35986 e.preventDefault();
35988 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35992 if(this.bgimage.length && this.html.length){
35993 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35997 leave: function(e, el)
35999 e.preventDefault();
36001 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36005 if(this.bgimage.length && this.html.length){
36006 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36010 onTouchStart: function(e, el)
36012 // e.preventDefault();
36014 this.touchmoved = false;
36016 if(!this.isFitContainer){
36020 if(!this.bgimage.length || !this.html.length){
36024 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36026 this.timer = new Date().getTime();
36030 onTouchMove: function(e, el)
36032 this.touchmoved = true;
36035 onContextMenu : function(e,el)
36037 e.preventDefault();
36038 e.stopPropagation();
36042 onTouchEnd: function(e, el)
36044 // e.preventDefault();
36046 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36053 if(!this.bgimage.length || !this.html.length){
36055 if(this.href.length){
36056 window.location.href = this.href;
36062 if(!this.isFitContainer){
36066 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36068 window.location.href = this.href;
36071 //selection on single brick only
36072 selectBrick : function() {
36074 if (!this.parentId) {
36078 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36079 var index = m.selectedBrick.indexOf(this.id);
36082 m.selectedBrick.splice(index,1);
36083 this.el.removeClass(this.activeClass);
36087 for(var i = 0; i < m.selectedBrick.length; i++) {
36088 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36089 b.el.removeClass(b.activeClass);
36092 m.selectedBrick = [];
36094 m.selectedBrick.push(this.id);
36095 this.el.addClass(this.activeClass);
36099 isSelected : function(){
36100 return this.el.hasClass(this.activeClass);
36105 Roo.apply(Roo.bootstrap.MasonryBrick, {
36108 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36110 * register a Masonry Brick
36111 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36114 register : function(brick)
36116 //this.groups[brick.id] = brick;
36117 this.groups.add(brick.id, brick);
36120 * fetch a masonry brick based on the masonry brick ID
36121 * @param {string} the masonry brick to add
36122 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36125 get: function(brick_id)
36127 // if (typeof(this.groups[brick_id]) == 'undefined') {
36130 // return this.groups[brick_id] ;
36132 if(this.groups.key(brick_id)) {
36133 return this.groups.key(brick_id);
36151 * @class Roo.bootstrap.Brick
36152 * @extends Roo.bootstrap.Component
36153 * Bootstrap Brick class
36156 * Create a new Brick
36157 * @param {Object} config The config object
36160 Roo.bootstrap.Brick = function(config){
36161 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36167 * When a Brick is click
36168 * @param {Roo.bootstrap.Brick} this
36169 * @param {Roo.EventObject} e
36175 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36178 * @cfg {String} title
36182 * @cfg {String} html
36186 * @cfg {String} bgimage
36190 * @cfg {String} cls
36194 * @cfg {String} href
36198 * @cfg {String} video
36202 * @cfg {Boolean} square
36206 getAutoCreate : function()
36208 var cls = 'roo-brick';
36210 if(this.href.length){
36211 cls += ' roo-brick-link';
36214 if(this.bgimage.length){
36215 cls += ' roo-brick-image';
36218 if(!this.html.length && !this.bgimage.length){
36219 cls += ' roo-brick-center-title';
36222 if(!this.html.length && this.bgimage.length){
36223 cls += ' roo-brick-bottom-title';
36227 cls += ' ' + this.cls;
36231 tag: (this.href.length) ? 'a' : 'div',
36236 cls: 'roo-brick-paragraph',
36242 if(this.href.length){
36243 cfg.href = this.href;
36246 var cn = cfg.cn[0].cn;
36248 if(this.title.length){
36251 cls: 'roo-brick-title',
36256 if(this.html.length){
36259 cls: 'roo-brick-text',
36266 if(this.bgimage.length){
36269 cls: 'roo-brick-image-view',
36277 initEvents: function()
36279 if(this.title.length || this.html.length){
36280 this.el.on('mouseenter' ,this.enter, this);
36281 this.el.on('mouseleave', this.leave, this);
36284 Roo.EventManager.onWindowResize(this.resize, this);
36286 if(this.bgimage.length){
36287 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36288 this.imageEl.on('load', this.onImageLoad, this);
36295 onImageLoad : function()
36300 resize : function()
36302 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36304 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36306 if(this.bgimage.length){
36307 var image = this.el.select('.roo-brick-image-view', true).first();
36309 image.setWidth(paragraph.getWidth());
36312 image.setHeight(paragraph.getWidth());
36315 this.el.setHeight(image.getHeight());
36316 paragraph.setHeight(image.getHeight());
36322 enter: function(e, el)
36324 e.preventDefault();
36326 if(this.bgimage.length){
36327 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36328 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36332 leave: function(e, el)
36334 e.preventDefault();
36336 if(this.bgimage.length){
36337 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36338 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36353 * @class Roo.bootstrap.NumberField
36354 * @extends Roo.bootstrap.Input
36355 * Bootstrap NumberField class
36361 * Create a new NumberField
36362 * @param {Object} config The config object
36365 Roo.bootstrap.NumberField = function(config){
36366 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36369 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36372 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36374 allowDecimals : true,
36376 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36378 decimalSeparator : ".",
36380 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36382 decimalPrecision : 2,
36384 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36386 allowNegative : true,
36389 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36393 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36395 minValue : Number.NEGATIVE_INFINITY,
36397 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36399 maxValue : Number.MAX_VALUE,
36401 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36403 minText : "The minimum value for this field is {0}",
36405 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36407 maxText : "The maximum value for this field is {0}",
36409 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36410 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36412 nanText : "{0} is not a valid number",
36414 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36416 thousandsDelimiter : false,
36418 * @cfg {String} valueAlign alignment of value
36420 valueAlign : "left",
36422 getAutoCreate : function()
36424 var hiddenInput = {
36428 cls: 'hidden-number-input'
36432 hiddenInput.name = this.name;
36437 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36439 this.name = hiddenInput.name;
36441 if(cfg.cn.length > 0) {
36442 cfg.cn.push(hiddenInput);
36449 initEvents : function()
36451 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36453 var allowed = "0123456789";
36455 if(this.allowDecimals){
36456 allowed += this.decimalSeparator;
36459 if(this.allowNegative){
36463 if(this.thousandsDelimiter) {
36467 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36469 var keyPress = function(e){
36471 var k = e.getKey();
36473 var c = e.getCharCode();
36476 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36477 allowed.indexOf(String.fromCharCode(c)) === -1
36483 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36487 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36492 this.el.on("keypress", keyPress, this);
36495 validateValue : function(value)
36498 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36502 var num = this.parseValue(value);
36505 this.markInvalid(String.format(this.nanText, value));
36509 if(num < this.minValue){
36510 this.markInvalid(String.format(this.minText, this.minValue));
36514 if(num > this.maxValue){
36515 this.markInvalid(String.format(this.maxText, this.maxValue));
36522 getValue : function()
36524 var v = this.hiddenEl().getValue();
36526 return this.fixPrecision(this.parseValue(v));
36529 parseValue : function(value)
36531 if(this.thousandsDelimiter) {
36533 r = new RegExp(",", "g");
36534 value = value.replace(r, "");
36537 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36538 return isNaN(value) ? '' : value;
36541 fixPrecision : function(value)
36543 if(this.thousandsDelimiter) {
36545 r = new RegExp(",", "g");
36546 value = value.replace(r, "");
36549 var nan = isNaN(value);
36551 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36552 return nan ? '' : value;
36554 return parseFloat(value).toFixed(this.decimalPrecision);
36557 setValue : function(v)
36559 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36565 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36567 this.inputEl().dom.value = (v == '') ? '' :
36568 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36570 if(!this.allowZero && v === '0') {
36571 this.hiddenEl().dom.value = '';
36572 this.inputEl().dom.value = '';
36579 decimalPrecisionFcn : function(v)
36581 return Math.floor(v);
36584 beforeBlur : function()
36586 var v = this.parseValue(this.getRawValue());
36588 if(v || v === 0 || v === ''){
36593 hiddenEl : function()
36595 return this.el.select('input.hidden-number-input',true).first();
36607 * @class Roo.bootstrap.DocumentSlider
36608 * @extends Roo.bootstrap.Component
36609 * Bootstrap DocumentSlider class
36612 * Create a new DocumentViewer
36613 * @param {Object} config The config object
36616 Roo.bootstrap.DocumentSlider = function(config){
36617 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36624 * Fire after initEvent
36625 * @param {Roo.bootstrap.DocumentSlider} this
36630 * Fire after update
36631 * @param {Roo.bootstrap.DocumentSlider} this
36637 * @param {Roo.bootstrap.DocumentSlider} this
36643 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36649 getAutoCreate : function()
36653 cls : 'roo-document-slider',
36657 cls : 'roo-document-slider-header',
36661 cls : 'roo-document-slider-header-title'
36667 cls : 'roo-document-slider-body',
36671 cls : 'roo-document-slider-prev',
36675 cls : 'fa fa-chevron-left'
36681 cls : 'roo-document-slider-thumb',
36685 cls : 'roo-document-slider-image'
36691 cls : 'roo-document-slider-next',
36695 cls : 'fa fa-chevron-right'
36707 initEvents : function()
36709 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36710 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36712 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36713 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36715 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36716 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36718 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36719 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36721 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36722 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36724 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36725 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36727 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36728 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36730 this.thumbEl.on('click', this.onClick, this);
36732 this.prevIndicator.on('click', this.prev, this);
36734 this.nextIndicator.on('click', this.next, this);
36738 initial : function()
36740 if(this.files.length){
36741 this.indicator = 1;
36745 this.fireEvent('initial', this);
36748 update : function()
36750 this.imageEl.attr('src', this.files[this.indicator - 1]);
36752 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36754 this.prevIndicator.show();
36756 if(this.indicator == 1){
36757 this.prevIndicator.hide();
36760 this.nextIndicator.show();
36762 if(this.indicator == this.files.length){
36763 this.nextIndicator.hide();
36766 this.thumbEl.scrollTo('top');
36768 this.fireEvent('update', this);
36771 onClick : function(e)
36773 e.preventDefault();
36775 this.fireEvent('click', this);
36780 e.preventDefault();
36782 this.indicator = Math.max(1, this.indicator - 1);
36789 e.preventDefault();
36791 this.indicator = Math.min(this.files.length, this.indicator + 1);
36805 * @class Roo.bootstrap.RadioSet
36806 * @extends Roo.bootstrap.Input
36807 * Bootstrap RadioSet class
36808 * @cfg {String} indicatorpos (left|right) default left
36809 * @cfg {Boolean} inline (true|false) inline the element (default true)
36810 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36812 * Create a new RadioSet
36813 * @param {Object} config The config object
36816 Roo.bootstrap.RadioSet = function(config){
36818 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36822 Roo.bootstrap.RadioSet.register(this);
36827 * Fires when the element is checked or unchecked.
36828 * @param {Roo.bootstrap.RadioSet} this This radio
36829 * @param {Roo.bootstrap.Radio} item The checked item
36834 * Fires when the element is click.
36835 * @param {Roo.bootstrap.RadioSet} this This radio set
36836 * @param {Roo.bootstrap.Radio} item The checked item
36837 * @param {Roo.EventObject} e The event object
36844 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36852 indicatorpos : 'left',
36854 getAutoCreate : function()
36858 cls : 'roo-radio-set-label',
36862 html : this.fieldLabel
36866 if (Roo.bootstrap.version == 3) {
36869 if(this.indicatorpos == 'left'){
36872 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36873 tooltip : 'This field is required'
36878 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36879 tooltip : 'This field is required'
36885 cls : 'roo-radio-set-items'
36888 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36890 if (align === 'left' && this.fieldLabel.length) {
36893 cls : "roo-radio-set-right",
36899 if(this.labelWidth > 12){
36900 label.style = "width: " + this.labelWidth + 'px';
36903 if(this.labelWidth < 13 && this.labelmd == 0){
36904 this.labelmd = this.labelWidth;
36907 if(this.labellg > 0){
36908 label.cls += ' col-lg-' + this.labellg;
36909 items.cls += ' col-lg-' + (12 - this.labellg);
36912 if(this.labelmd > 0){
36913 label.cls += ' col-md-' + this.labelmd;
36914 items.cls += ' col-md-' + (12 - this.labelmd);
36917 if(this.labelsm > 0){
36918 label.cls += ' col-sm-' + this.labelsm;
36919 items.cls += ' col-sm-' + (12 - this.labelsm);
36922 if(this.labelxs > 0){
36923 label.cls += ' col-xs-' + this.labelxs;
36924 items.cls += ' col-xs-' + (12 - this.labelxs);
36930 cls : 'roo-radio-set',
36934 cls : 'roo-radio-set-input',
36937 value : this.value ? this.value : ''
36944 if(this.weight.length){
36945 cfg.cls += ' roo-radio-' + this.weight;
36949 cfg.cls += ' roo-radio-set-inline';
36953 ['xs','sm','md','lg'].map(function(size){
36954 if (settings[size]) {
36955 cfg.cls += ' col-' + size + '-' + settings[size];
36963 initEvents : function()
36965 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36966 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36968 if(!this.fieldLabel.length){
36969 this.labelEl.hide();
36972 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36973 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36975 this.indicator = this.indicatorEl();
36977 if(this.indicator){
36978 this.indicator.addClass('invisible');
36981 this.originalValue = this.getValue();
36985 inputEl: function ()
36987 return this.el.select('.roo-radio-set-input', true).first();
36990 getChildContainer : function()
36992 return this.itemsEl;
36995 register : function(item)
36997 this.radioes.push(item);
37001 validate : function()
37003 if(this.getVisibilityEl().hasClass('hidden')){
37009 Roo.each(this.radioes, function(i){
37018 if(this.allowBlank) {
37022 if(this.disabled || valid){
37027 this.markInvalid();
37032 markValid : function()
37034 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37035 this.indicatorEl().removeClass('visible');
37036 this.indicatorEl().addClass('invisible');
37040 if (Roo.bootstrap.version == 3) {
37041 this.el.removeClass([this.invalidClass, this.validClass]);
37042 this.el.addClass(this.validClass);
37044 this.el.removeClass(['is-invalid','is-valid']);
37045 this.el.addClass(['is-valid']);
37047 this.fireEvent('valid', this);
37050 markInvalid : function(msg)
37052 if(this.allowBlank || this.disabled){
37056 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37057 this.indicatorEl().removeClass('invisible');
37058 this.indicatorEl().addClass('visible');
37060 if (Roo.bootstrap.version == 3) {
37061 this.el.removeClass([this.invalidClass, this.validClass]);
37062 this.el.addClass(this.invalidClass);
37064 this.el.removeClass(['is-invalid','is-valid']);
37065 this.el.addClass(['is-invalid']);
37068 this.fireEvent('invalid', this, msg);
37072 setValue : function(v, suppressEvent)
37074 if(this.value === v){
37081 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37084 Roo.each(this.radioes, function(i){
37086 i.el.removeClass('checked');
37089 Roo.each(this.radioes, function(i){
37091 if(i.value === v || i.value.toString() === v.toString()){
37093 i.el.addClass('checked');
37095 if(suppressEvent !== true){
37096 this.fireEvent('check', this, i);
37107 clearInvalid : function(){
37109 if(!this.el || this.preventMark){
37113 this.el.removeClass([this.invalidClass]);
37115 this.fireEvent('valid', this);
37120 Roo.apply(Roo.bootstrap.RadioSet, {
37124 register : function(set)
37126 this.groups[set.name] = set;
37129 get: function(name)
37131 if (typeof(this.groups[name]) == 'undefined') {
37135 return this.groups[name] ;
37141 * Ext JS Library 1.1.1
37142 * Copyright(c) 2006-2007, Ext JS, LLC.
37144 * Originally Released Under LGPL - original licence link has changed is not relivant.
37147 * <script type="text/javascript">
37152 * @class Roo.bootstrap.SplitBar
37153 * @extends Roo.util.Observable
37154 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37158 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37159 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37160 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37161 split.minSize = 100;
37162 split.maxSize = 600;
37163 split.animate = true;
37164 split.on('moved', splitterMoved);
37167 * Create a new SplitBar
37168 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37169 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37170 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37171 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37172 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37173 position of the SplitBar).
37175 Roo.bootstrap.SplitBar = function(cfg){
37180 // dragElement : elm
37181 // resizingElement: el,
37183 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37184 // placement : Roo.bootstrap.SplitBar.LEFT ,
37185 // existingProxy ???
37188 this.el = Roo.get(cfg.dragElement, true);
37189 this.el.dom.unselectable = "on";
37191 this.resizingEl = Roo.get(cfg.resizingElement, true);
37195 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37196 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37199 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37202 * The minimum size of the resizing element. (Defaults to 0)
37208 * The maximum size of the resizing element. (Defaults to 2000)
37211 this.maxSize = 2000;
37214 * Whether to animate the transition to the new size
37217 this.animate = false;
37220 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37223 this.useShim = false;
37228 if(!cfg.existingProxy){
37230 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37232 this.proxy = Roo.get(cfg.existingProxy).dom;
37235 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37238 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37241 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37244 this.dragSpecs = {};
37247 * @private The adapter to use to positon and resize elements
37249 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37250 this.adapter.init(this);
37252 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37254 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37255 this.el.addClass("roo-splitbar-h");
37258 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37259 this.el.addClass("roo-splitbar-v");
37265 * Fires when the splitter is moved (alias for {@link #event-moved})
37266 * @param {Roo.bootstrap.SplitBar} this
37267 * @param {Number} newSize the new width or height
37272 * Fires when the splitter is moved
37273 * @param {Roo.bootstrap.SplitBar} this
37274 * @param {Number} newSize the new width or height
37278 * @event beforeresize
37279 * Fires before the splitter is dragged
37280 * @param {Roo.bootstrap.SplitBar} this
37282 "beforeresize" : true,
37284 "beforeapply" : true
37287 Roo.util.Observable.call(this);
37290 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37291 onStartProxyDrag : function(x, y){
37292 this.fireEvent("beforeresize", this);
37294 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37296 o.enableDisplayMode("block");
37297 // all splitbars share the same overlay
37298 Roo.bootstrap.SplitBar.prototype.overlay = o;
37300 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37301 this.overlay.show();
37302 Roo.get(this.proxy).setDisplayed("block");
37303 var size = this.adapter.getElementSize(this);
37304 this.activeMinSize = this.getMinimumSize();;
37305 this.activeMaxSize = this.getMaximumSize();;
37306 var c1 = size - this.activeMinSize;
37307 var c2 = Math.max(this.activeMaxSize - size, 0);
37308 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37309 this.dd.resetConstraints();
37310 this.dd.setXConstraint(
37311 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37312 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37314 this.dd.setYConstraint(0, 0);
37316 this.dd.resetConstraints();
37317 this.dd.setXConstraint(0, 0);
37318 this.dd.setYConstraint(
37319 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37320 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37323 this.dragSpecs.startSize = size;
37324 this.dragSpecs.startPoint = [x, y];
37325 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37329 * @private Called after the drag operation by the DDProxy
37331 onEndProxyDrag : function(e){
37332 Roo.get(this.proxy).setDisplayed(false);
37333 var endPoint = Roo.lib.Event.getXY(e);
37335 this.overlay.hide();
37338 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37339 newSize = this.dragSpecs.startSize +
37340 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37341 endPoint[0] - this.dragSpecs.startPoint[0] :
37342 this.dragSpecs.startPoint[0] - endPoint[0]
37345 newSize = this.dragSpecs.startSize +
37346 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37347 endPoint[1] - this.dragSpecs.startPoint[1] :
37348 this.dragSpecs.startPoint[1] - endPoint[1]
37351 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37352 if(newSize != this.dragSpecs.startSize){
37353 if(this.fireEvent('beforeapply', this, newSize) !== false){
37354 this.adapter.setElementSize(this, newSize);
37355 this.fireEvent("moved", this, newSize);
37356 this.fireEvent("resize", this, newSize);
37362 * Get the adapter this SplitBar uses
37363 * @return The adapter object
37365 getAdapter : function(){
37366 return this.adapter;
37370 * Set the adapter this SplitBar uses
37371 * @param {Object} adapter A SplitBar adapter object
37373 setAdapter : function(adapter){
37374 this.adapter = adapter;
37375 this.adapter.init(this);
37379 * Gets the minimum size for the resizing element
37380 * @return {Number} The minimum size
37382 getMinimumSize : function(){
37383 return this.minSize;
37387 * Sets the minimum size for the resizing element
37388 * @param {Number} minSize The minimum size
37390 setMinimumSize : function(minSize){
37391 this.minSize = minSize;
37395 * Gets the maximum size for the resizing element
37396 * @return {Number} The maximum size
37398 getMaximumSize : function(){
37399 return this.maxSize;
37403 * Sets the maximum size for the resizing element
37404 * @param {Number} maxSize The maximum size
37406 setMaximumSize : function(maxSize){
37407 this.maxSize = maxSize;
37411 * Sets the initialize size for the resizing element
37412 * @param {Number} size The initial size
37414 setCurrentSize : function(size){
37415 var oldAnimate = this.animate;
37416 this.animate = false;
37417 this.adapter.setElementSize(this, size);
37418 this.animate = oldAnimate;
37422 * Destroy this splitbar.
37423 * @param {Boolean} removeEl True to remove the element
37425 destroy : function(removeEl){
37427 this.shim.remove();
37430 this.proxy.parentNode.removeChild(this.proxy);
37438 * @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.
37440 Roo.bootstrap.SplitBar.createProxy = function(dir){
37441 var proxy = new Roo.Element(document.createElement("div"));
37442 proxy.unselectable();
37443 var cls = 'roo-splitbar-proxy';
37444 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37445 document.body.appendChild(proxy.dom);
37450 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37451 * Default Adapter. It assumes the splitter and resizing element are not positioned
37452 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37454 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37457 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37458 // do nothing for now
37459 init : function(s){
37463 * Called before drag operations to get the current size of the resizing element.
37464 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37466 getElementSize : function(s){
37467 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37468 return s.resizingEl.getWidth();
37470 return s.resizingEl.getHeight();
37475 * Called after drag operations to set the size of the resizing element.
37476 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37477 * @param {Number} newSize The new size to set
37478 * @param {Function} onComplete A function to be invoked when resizing is complete
37480 setElementSize : function(s, newSize, onComplete){
37481 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37483 s.resizingEl.setWidth(newSize);
37485 onComplete(s, newSize);
37488 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37493 s.resizingEl.setHeight(newSize);
37495 onComplete(s, newSize);
37498 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37505 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37506 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37507 * Adapter that moves the splitter element to align with the resized sizing element.
37508 * Used with an absolute positioned SplitBar.
37509 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37510 * document.body, make sure you assign an id to the body element.
37512 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37513 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37514 this.container = Roo.get(container);
37517 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37518 init : function(s){
37519 this.basic.init(s);
37522 getElementSize : function(s){
37523 return this.basic.getElementSize(s);
37526 setElementSize : function(s, newSize, onComplete){
37527 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37530 moveSplitter : function(s){
37531 var yes = Roo.bootstrap.SplitBar;
37532 switch(s.placement){
37534 s.el.setX(s.resizingEl.getRight());
37537 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37540 s.el.setY(s.resizingEl.getBottom());
37543 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37550 * Orientation constant - Create a vertical SplitBar
37554 Roo.bootstrap.SplitBar.VERTICAL = 1;
37557 * Orientation constant - Create a horizontal SplitBar
37561 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37564 * Placement constant - The resizing element is to the left of the splitter element
37568 Roo.bootstrap.SplitBar.LEFT = 1;
37571 * Placement constant - The resizing element is to the right of the splitter element
37575 Roo.bootstrap.SplitBar.RIGHT = 2;
37578 * Placement constant - The resizing element is positioned above the splitter element
37582 Roo.bootstrap.SplitBar.TOP = 3;
37585 * Placement constant - The resizing element is positioned under splitter element
37589 Roo.bootstrap.SplitBar.BOTTOM = 4;
37590 Roo.namespace("Roo.bootstrap.layout");/*
37592 * Ext JS Library 1.1.1
37593 * Copyright(c) 2006-2007, Ext JS, LLC.
37595 * Originally Released Under LGPL - original licence link has changed is not relivant.
37598 * <script type="text/javascript">
37602 * @class Roo.bootstrap.layout.Manager
37603 * @extends Roo.bootstrap.Component
37604 * Base class for layout managers.
37606 Roo.bootstrap.layout.Manager = function(config)
37608 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37614 /** false to disable window resize monitoring @type Boolean */
37615 this.monitorWindowResize = true;
37620 * Fires when a layout is performed.
37621 * @param {Roo.LayoutManager} this
37625 * @event regionresized
37626 * Fires when the user resizes a region.
37627 * @param {Roo.LayoutRegion} region The resized region
37628 * @param {Number} newSize The new size (width for east/west, height for north/south)
37630 "regionresized" : true,
37632 * @event regioncollapsed
37633 * Fires when a region is collapsed.
37634 * @param {Roo.LayoutRegion} region The collapsed region
37636 "regioncollapsed" : true,
37638 * @event regionexpanded
37639 * Fires when a region is expanded.
37640 * @param {Roo.LayoutRegion} region The expanded region
37642 "regionexpanded" : true
37644 this.updating = false;
37647 this.el = Roo.get(config.el);
37653 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37658 monitorWindowResize : true,
37664 onRender : function(ct, position)
37667 this.el = Roo.get(ct);
37670 //this.fireEvent('render',this);
37674 initEvents: function()
37678 // ie scrollbar fix
37679 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37680 document.body.scroll = "no";
37681 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37682 this.el.position('relative');
37684 this.id = this.el.id;
37685 this.el.addClass("roo-layout-container");
37686 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37687 if(this.el.dom != document.body ) {
37688 this.el.on('resize', this.layout,this);
37689 this.el.on('show', this.layout,this);
37695 * Returns true if this layout is currently being updated
37696 * @return {Boolean}
37698 isUpdating : function(){
37699 return this.updating;
37703 * Suspend the LayoutManager from doing auto-layouts while
37704 * making multiple add or remove calls
37706 beginUpdate : function(){
37707 this.updating = true;
37711 * Restore auto-layouts and optionally disable the manager from performing a layout
37712 * @param {Boolean} noLayout true to disable a layout update
37714 endUpdate : function(noLayout){
37715 this.updating = false;
37721 layout: function(){
37725 onRegionResized : function(region, newSize){
37726 this.fireEvent("regionresized", region, newSize);
37730 onRegionCollapsed : function(region){
37731 this.fireEvent("regioncollapsed", region);
37734 onRegionExpanded : function(region){
37735 this.fireEvent("regionexpanded", region);
37739 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37740 * performs box-model adjustments.
37741 * @return {Object} The size as an object {width: (the width), height: (the height)}
37743 getViewSize : function()
37746 if(this.el.dom != document.body){
37747 size = this.el.getSize();
37749 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37751 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37752 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37757 * Returns the Element this layout is bound to.
37758 * @return {Roo.Element}
37760 getEl : function(){
37765 * Returns the specified region.
37766 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37767 * @return {Roo.LayoutRegion}
37769 getRegion : function(target){
37770 return this.regions[target.toLowerCase()];
37773 onWindowResize : function(){
37774 if(this.monitorWindowResize){
37781 * Ext JS Library 1.1.1
37782 * Copyright(c) 2006-2007, Ext JS, LLC.
37784 * Originally Released Under LGPL - original licence link has changed is not relivant.
37787 * <script type="text/javascript">
37790 * @class Roo.bootstrap.layout.Border
37791 * @extends Roo.bootstrap.layout.Manager
37792 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37793 * please see: examples/bootstrap/nested.html<br><br>
37795 <b>The container the layout is rendered into can be either the body element or any other element.
37796 If it is not the body element, the container needs to either be an absolute positioned element,
37797 or you will need to add "position:relative" to the css of the container. You will also need to specify
37798 the container size if it is not the body element.</b>
37801 * Create a new Border
37802 * @param {Object} config Configuration options
37804 Roo.bootstrap.layout.Border = function(config){
37805 config = config || {};
37806 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37810 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37811 if(config[region]){
37812 config[region].region = region;
37813 this.addRegion(config[region]);
37819 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37821 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37823 parent : false, // this might point to a 'nest' or a ???
37826 * Creates and adds a new region if it doesn't already exist.
37827 * @param {String} target The target region key (north, south, east, west or center).
37828 * @param {Object} config The regions config object
37829 * @return {BorderLayoutRegion} The new region
37831 addRegion : function(config)
37833 if(!this.regions[config.region]){
37834 var r = this.factory(config);
37835 this.bindRegion(r);
37837 return this.regions[config.region];
37841 bindRegion : function(r){
37842 this.regions[r.config.region] = r;
37844 r.on("visibilitychange", this.layout, this);
37845 r.on("paneladded", this.layout, this);
37846 r.on("panelremoved", this.layout, this);
37847 r.on("invalidated", this.layout, this);
37848 r.on("resized", this.onRegionResized, this);
37849 r.on("collapsed", this.onRegionCollapsed, this);
37850 r.on("expanded", this.onRegionExpanded, this);
37854 * Performs a layout update.
37856 layout : function()
37858 if(this.updating) {
37862 // render all the rebions if they have not been done alreayd?
37863 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37864 if(this.regions[region] && !this.regions[region].bodyEl){
37865 this.regions[region].onRender(this.el)
37869 var size = this.getViewSize();
37870 var w = size.width;
37871 var h = size.height;
37876 //var x = 0, y = 0;
37878 var rs = this.regions;
37879 var north = rs["north"];
37880 var south = rs["south"];
37881 var west = rs["west"];
37882 var east = rs["east"];
37883 var center = rs["center"];
37884 //if(this.hideOnLayout){ // not supported anymore
37885 //c.el.setStyle("display", "none");
37887 if(north && north.isVisible()){
37888 var b = north.getBox();
37889 var m = north.getMargins();
37890 b.width = w - (m.left+m.right);
37893 centerY = b.height + b.y + m.bottom;
37894 centerH -= centerY;
37895 north.updateBox(this.safeBox(b));
37897 if(south && south.isVisible()){
37898 var b = south.getBox();
37899 var m = south.getMargins();
37900 b.width = w - (m.left+m.right);
37902 var totalHeight = (b.height + m.top + m.bottom);
37903 b.y = h - totalHeight + m.top;
37904 centerH -= totalHeight;
37905 south.updateBox(this.safeBox(b));
37907 if(west && west.isVisible()){
37908 var b = west.getBox();
37909 var m = west.getMargins();
37910 b.height = centerH - (m.top+m.bottom);
37912 b.y = centerY + m.top;
37913 var totalWidth = (b.width + m.left + m.right);
37914 centerX += totalWidth;
37915 centerW -= totalWidth;
37916 west.updateBox(this.safeBox(b));
37918 if(east && east.isVisible()){
37919 var b = east.getBox();
37920 var m = east.getMargins();
37921 b.height = centerH - (m.top+m.bottom);
37922 var totalWidth = (b.width + m.left + m.right);
37923 b.x = w - totalWidth + m.left;
37924 b.y = centerY + m.top;
37925 centerW -= totalWidth;
37926 east.updateBox(this.safeBox(b));
37929 var m = center.getMargins();
37931 x: centerX + m.left,
37932 y: centerY + m.top,
37933 width: centerW - (m.left+m.right),
37934 height: centerH - (m.top+m.bottom)
37936 //if(this.hideOnLayout){
37937 //center.el.setStyle("display", "block");
37939 center.updateBox(this.safeBox(centerBox));
37942 this.fireEvent("layout", this);
37946 safeBox : function(box){
37947 box.width = Math.max(0, box.width);
37948 box.height = Math.max(0, box.height);
37953 * Adds a ContentPanel (or subclass) to this layout.
37954 * @param {String} target The target region key (north, south, east, west or center).
37955 * @param {Roo.ContentPanel} panel The panel to add
37956 * @return {Roo.ContentPanel} The added panel
37958 add : function(target, panel){
37960 target = target.toLowerCase();
37961 return this.regions[target].add(panel);
37965 * Remove a ContentPanel (or subclass) to this layout.
37966 * @param {String} target The target region key (north, south, east, west or center).
37967 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37968 * @return {Roo.ContentPanel} The removed panel
37970 remove : function(target, panel){
37971 target = target.toLowerCase();
37972 return this.regions[target].remove(panel);
37976 * Searches all regions for a panel with the specified id
37977 * @param {String} panelId
37978 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37980 findPanel : function(panelId){
37981 var rs = this.regions;
37982 for(var target in rs){
37983 if(typeof rs[target] != "function"){
37984 var p = rs[target].getPanel(panelId);
37994 * Searches all regions for a panel with the specified id and activates (shows) it.
37995 * @param {String/ContentPanel} panelId The panels id or the panel itself
37996 * @return {Roo.ContentPanel} The shown panel or null
37998 showPanel : function(panelId) {
37999 var rs = this.regions;
38000 for(var target in rs){
38001 var r = rs[target];
38002 if(typeof r != "function"){
38003 if(r.hasPanel(panelId)){
38004 return r.showPanel(panelId);
38012 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38013 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38016 restoreState : function(provider){
38018 provider = Roo.state.Manager;
38020 var sm = new Roo.LayoutStateManager();
38021 sm.init(this, provider);
38027 * Adds a xtype elements to the layout.
38031 xtype : 'ContentPanel',
38038 xtype : 'NestedLayoutPanel',
38044 items : [ ... list of content panels or nested layout panels.. ]
38048 * @param {Object} cfg Xtype definition of item to add.
38050 addxtype : function(cfg)
38052 // basically accepts a pannel...
38053 // can accept a layout region..!?!?
38054 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38057 // theory? children can only be panels??
38059 //if (!cfg.xtype.match(/Panel$/)) {
38064 if (typeof(cfg.region) == 'undefined') {
38065 Roo.log("Failed to add Panel, region was not set");
38069 var region = cfg.region;
38075 xitems = cfg.items;
38080 if ( region == 'center') {
38081 Roo.log("Center: " + cfg.title);
38087 case 'Content': // ContentPanel (el, cfg)
38088 case 'Scroll': // ContentPanel (el, cfg)
38090 cfg.autoCreate = cfg.autoCreate || true;
38091 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38093 // var el = this.el.createChild();
38094 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38097 this.add(region, ret);
38101 case 'TreePanel': // our new panel!
38102 cfg.el = this.el.createChild();
38103 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38104 this.add(region, ret);
38109 // create a new Layout (which is a Border Layout...
38111 var clayout = cfg.layout;
38112 clayout.el = this.el.createChild();
38113 clayout.items = clayout.items || [];
38117 // replace this exitems with the clayout ones..
38118 xitems = clayout.items;
38120 // force background off if it's in center...
38121 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38122 cfg.background = false;
38124 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38127 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38128 //console.log('adding nested layout panel ' + cfg.toSource());
38129 this.add(region, ret);
38130 nb = {}; /// find first...
38135 // needs grid and region
38137 //var el = this.getRegion(region).el.createChild();
38139 *var el = this.el.createChild();
38140 // create the grid first...
38141 cfg.grid.container = el;
38142 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38145 if (region == 'center' && this.active ) {
38146 cfg.background = false;
38149 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38151 this.add(region, ret);
38153 if (cfg.background) {
38154 // render grid on panel activation (if panel background)
38155 ret.on('activate', function(gp) {
38156 if (!gp.grid.rendered) {
38157 // gp.grid.render(el);
38161 // cfg.grid.render(el);
38167 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38168 // it was the old xcomponent building that caused this before.
38169 // espeically if border is the top element in the tree.
38179 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38181 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38182 this.add(region, ret);
38186 throw "Can not add '" + cfg.xtype + "' to Border";
38192 this.beginUpdate();
38196 Roo.each(xitems, function(i) {
38197 region = nb && i.region ? i.region : false;
38199 var add = ret.addxtype(i);
38202 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38203 if (!i.background) {
38204 abn[region] = nb[region] ;
38211 // make the last non-background panel active..
38212 //if (nb) { Roo.log(abn); }
38215 for(var r in abn) {
38216 region = this.getRegion(r);
38218 // tried using nb[r], but it does not work..
38220 region.showPanel(abn[r]);
38231 factory : function(cfg)
38234 var validRegions = Roo.bootstrap.layout.Border.regions;
38236 var target = cfg.region;
38239 var r = Roo.bootstrap.layout;
38243 return new r.North(cfg);
38245 return new r.South(cfg);
38247 return new r.East(cfg);
38249 return new r.West(cfg);
38251 return new r.Center(cfg);
38253 throw 'Layout region "'+target+'" not supported.';
38260 * Ext JS Library 1.1.1
38261 * Copyright(c) 2006-2007, Ext JS, LLC.
38263 * Originally Released Under LGPL - original licence link has changed is not relivant.
38266 * <script type="text/javascript">
38270 * @class Roo.bootstrap.layout.Basic
38271 * @extends Roo.util.Observable
38272 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38273 * and does not have a titlebar, tabs or any other features. All it does is size and position
38274 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38275 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38276 * @cfg {string} region the region that it inhabits..
38277 * @cfg {bool} skipConfig skip config?
38281 Roo.bootstrap.layout.Basic = function(config){
38283 this.mgr = config.mgr;
38285 this.position = config.region;
38287 var skipConfig = config.skipConfig;
38291 * @scope Roo.BasicLayoutRegion
38295 * @event beforeremove
38296 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38297 * @param {Roo.LayoutRegion} this
38298 * @param {Roo.ContentPanel} panel The panel
38299 * @param {Object} e The cancel event object
38301 "beforeremove" : true,
38303 * @event invalidated
38304 * Fires when the layout for this region is changed.
38305 * @param {Roo.LayoutRegion} this
38307 "invalidated" : true,
38309 * @event visibilitychange
38310 * Fires when this region is shown or hidden
38311 * @param {Roo.LayoutRegion} this
38312 * @param {Boolean} visibility true or false
38314 "visibilitychange" : true,
38316 * @event paneladded
38317 * Fires when a panel is added.
38318 * @param {Roo.LayoutRegion} this
38319 * @param {Roo.ContentPanel} panel The panel
38321 "paneladded" : true,
38323 * @event panelremoved
38324 * Fires when a panel is removed.
38325 * @param {Roo.LayoutRegion} this
38326 * @param {Roo.ContentPanel} panel The panel
38328 "panelremoved" : true,
38330 * @event beforecollapse
38331 * Fires when this region before collapse.
38332 * @param {Roo.LayoutRegion} this
38334 "beforecollapse" : true,
38337 * Fires when this region is collapsed.
38338 * @param {Roo.LayoutRegion} this
38340 "collapsed" : true,
38343 * Fires when this region is expanded.
38344 * @param {Roo.LayoutRegion} this
38349 * Fires when this region is slid into view.
38350 * @param {Roo.LayoutRegion} this
38352 "slideshow" : true,
38355 * Fires when this region slides out of view.
38356 * @param {Roo.LayoutRegion} this
38358 "slidehide" : true,
38360 * @event panelactivated
38361 * Fires when a panel is activated.
38362 * @param {Roo.LayoutRegion} this
38363 * @param {Roo.ContentPanel} panel The activated panel
38365 "panelactivated" : true,
38368 * Fires when the user resizes this region.
38369 * @param {Roo.LayoutRegion} this
38370 * @param {Number} newSize The new size (width for east/west, height for north/south)
38374 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38375 this.panels = new Roo.util.MixedCollection();
38376 this.panels.getKey = this.getPanelId.createDelegate(this);
38378 this.activePanel = null;
38379 // ensure listeners are added...
38381 if (config.listeners || config.events) {
38382 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38383 listeners : config.listeners || {},
38384 events : config.events || {}
38388 if(skipConfig !== true){
38389 this.applyConfig(config);
38393 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38395 getPanelId : function(p){
38399 applyConfig : function(config){
38400 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38401 this.config = config;
38406 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38407 * the width, for horizontal (north, south) the height.
38408 * @param {Number} newSize The new width or height
38410 resizeTo : function(newSize){
38411 var el = this.el ? this.el :
38412 (this.activePanel ? this.activePanel.getEl() : null);
38414 switch(this.position){
38417 el.setWidth(newSize);
38418 this.fireEvent("resized", this, newSize);
38422 el.setHeight(newSize);
38423 this.fireEvent("resized", this, newSize);
38429 getBox : function(){
38430 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38433 getMargins : function(){
38434 return this.margins;
38437 updateBox : function(box){
38439 var el = this.activePanel.getEl();
38440 el.dom.style.left = box.x + "px";
38441 el.dom.style.top = box.y + "px";
38442 this.activePanel.setSize(box.width, box.height);
38446 * Returns the container element for this region.
38447 * @return {Roo.Element}
38449 getEl : function(){
38450 return this.activePanel;
38454 * Returns true if this region is currently visible.
38455 * @return {Boolean}
38457 isVisible : function(){
38458 return this.activePanel ? true : false;
38461 setActivePanel : function(panel){
38462 panel = this.getPanel(panel);
38463 if(this.activePanel && this.activePanel != panel){
38464 this.activePanel.setActiveState(false);
38465 this.activePanel.getEl().setLeftTop(-10000,-10000);
38467 this.activePanel = panel;
38468 panel.setActiveState(true);
38470 panel.setSize(this.box.width, this.box.height);
38472 this.fireEvent("panelactivated", this, panel);
38473 this.fireEvent("invalidated");
38477 * Show the specified panel.
38478 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38479 * @return {Roo.ContentPanel} The shown panel or null
38481 showPanel : function(panel){
38482 panel = this.getPanel(panel);
38484 this.setActivePanel(panel);
38490 * Get the active panel for this region.
38491 * @return {Roo.ContentPanel} The active panel or null
38493 getActivePanel : function(){
38494 return this.activePanel;
38498 * Add the passed ContentPanel(s)
38499 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38500 * @return {Roo.ContentPanel} The panel added (if only one was added)
38502 add : function(panel){
38503 if(arguments.length > 1){
38504 for(var i = 0, len = arguments.length; i < len; i++) {
38505 this.add(arguments[i]);
38509 if(this.hasPanel(panel)){
38510 this.showPanel(panel);
38513 var el = panel.getEl();
38514 if(el.dom.parentNode != this.mgr.el.dom){
38515 this.mgr.el.dom.appendChild(el.dom);
38517 if(panel.setRegion){
38518 panel.setRegion(this);
38520 this.panels.add(panel);
38521 el.setStyle("position", "absolute");
38522 if(!panel.background){
38523 this.setActivePanel(panel);
38524 if(this.config.initialSize && this.panels.getCount()==1){
38525 this.resizeTo(this.config.initialSize);
38528 this.fireEvent("paneladded", this, panel);
38533 * Returns true if the panel is in this region.
38534 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38535 * @return {Boolean}
38537 hasPanel : function(panel){
38538 if(typeof panel == "object"){ // must be panel obj
38539 panel = panel.getId();
38541 return this.getPanel(panel) ? true : false;
38545 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38546 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38547 * @param {Boolean} preservePanel Overrides the config preservePanel option
38548 * @return {Roo.ContentPanel} The panel that was removed
38550 remove : function(panel, preservePanel){
38551 panel = this.getPanel(panel);
38556 this.fireEvent("beforeremove", this, panel, e);
38557 if(e.cancel === true){
38560 var panelId = panel.getId();
38561 this.panels.removeKey(panelId);
38566 * Returns the panel specified or null if it's not in this region.
38567 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38568 * @return {Roo.ContentPanel}
38570 getPanel : function(id){
38571 if(typeof id == "object"){ // must be panel obj
38574 return this.panels.get(id);
38578 * Returns this regions position (north/south/east/west/center).
38581 getPosition: function(){
38582 return this.position;
38586 * Ext JS Library 1.1.1
38587 * Copyright(c) 2006-2007, Ext JS, LLC.
38589 * Originally Released Under LGPL - original licence link has changed is not relivant.
38592 * <script type="text/javascript">
38596 * @class Roo.bootstrap.layout.Region
38597 * @extends Roo.bootstrap.layout.Basic
38598 * This class represents a region in a layout manager.
38600 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38601 * @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})
38602 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38603 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38604 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38605 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38606 * @cfg {String} title The title for the region (overrides panel titles)
38607 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38608 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38609 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38610 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38611 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38612 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38613 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38614 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38615 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38616 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38618 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38619 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38620 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38621 * @cfg {Number} width For East/West panels
38622 * @cfg {Number} height For North/South panels
38623 * @cfg {Boolean} split To show the splitter
38624 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38626 * @cfg {string} cls Extra CSS classes to add to region
38628 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38629 * @cfg {string} region the region that it inhabits..
38632 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38633 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38635 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38636 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38637 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38639 Roo.bootstrap.layout.Region = function(config)
38641 this.applyConfig(config);
38643 var mgr = config.mgr;
38644 var pos = config.region;
38645 config.skipConfig = true;
38646 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38649 this.onRender(mgr.el);
38652 this.visible = true;
38653 this.collapsed = false;
38654 this.unrendered_panels = [];
38657 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38659 position: '', // set by wrapper (eg. north/south etc..)
38660 unrendered_panels : null, // unrendered panels.
38662 tabPosition : false,
38664 mgr: false, // points to 'Border'
38667 createBody : function(){
38668 /** This region's body element
38669 * @type Roo.Element */
38670 this.bodyEl = this.el.createChild({
38672 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38676 onRender: function(ctr, pos)
38678 var dh = Roo.DomHelper;
38679 /** This region's container element
38680 * @type Roo.Element */
38681 this.el = dh.append(ctr.dom, {
38683 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38685 /** This region's title element
38686 * @type Roo.Element */
38688 this.titleEl = dh.append(this.el.dom, {
38690 unselectable: "on",
38691 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38693 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38694 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38698 this.titleEl.enableDisplayMode();
38699 /** This region's title text element
38700 * @type HTMLElement */
38701 this.titleTextEl = this.titleEl.dom.firstChild;
38702 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38704 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38705 this.closeBtn.enableDisplayMode();
38706 this.closeBtn.on("click", this.closeClicked, this);
38707 this.closeBtn.hide();
38709 this.createBody(this.config);
38710 if(this.config.hideWhenEmpty){
38712 this.on("paneladded", this.validateVisibility, this);
38713 this.on("panelremoved", this.validateVisibility, this);
38715 if(this.autoScroll){
38716 this.bodyEl.setStyle("overflow", "auto");
38718 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38720 //if(c.titlebar !== false){
38721 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38722 this.titleEl.hide();
38724 this.titleEl.show();
38725 if(this.config.title){
38726 this.titleTextEl.innerHTML = this.config.title;
38730 if(this.config.collapsed){
38731 this.collapse(true);
38733 if(this.config.hidden){
38737 if (this.unrendered_panels && this.unrendered_panels.length) {
38738 for (var i =0;i< this.unrendered_panels.length; i++) {
38739 this.add(this.unrendered_panels[i]);
38741 this.unrendered_panels = null;
38747 applyConfig : function(c)
38750 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38751 var dh = Roo.DomHelper;
38752 if(c.titlebar !== false){
38753 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38754 this.collapseBtn.on("click", this.collapse, this);
38755 this.collapseBtn.enableDisplayMode();
38757 if(c.showPin === true || this.showPin){
38758 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38759 this.stickBtn.enableDisplayMode();
38760 this.stickBtn.on("click", this.expand, this);
38761 this.stickBtn.hide();
38766 /** This region's collapsed element
38767 * @type Roo.Element */
38770 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38771 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38774 if(c.floatable !== false){
38775 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38776 this.collapsedEl.on("click", this.collapseClick, this);
38779 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38780 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38781 id: "message", unselectable: "on", style:{"float":"left"}});
38782 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38784 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38785 this.expandBtn.on("click", this.expand, this);
38789 if(this.collapseBtn){
38790 this.collapseBtn.setVisible(c.collapsible == true);
38793 this.cmargins = c.cmargins || this.cmargins ||
38794 (this.position == "west" || this.position == "east" ?
38795 {top: 0, left: 2, right:2, bottom: 0} :
38796 {top: 2, left: 0, right:0, bottom: 2});
38798 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38801 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38803 this.autoScroll = c.autoScroll || false;
38808 this.duration = c.duration || .30;
38809 this.slideDuration = c.slideDuration || .45;
38814 * Returns true if this region is currently visible.
38815 * @return {Boolean}
38817 isVisible : function(){
38818 return this.visible;
38822 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38823 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38825 //setCollapsedTitle : function(title){
38826 // title = title || " ";
38827 // if(this.collapsedTitleTextEl){
38828 // this.collapsedTitleTextEl.innerHTML = title;
38832 getBox : function(){
38834 // if(!this.collapsed){
38835 b = this.el.getBox(false, true);
38837 // b = this.collapsedEl.getBox(false, true);
38842 getMargins : function(){
38843 return this.margins;
38844 //return this.collapsed ? this.cmargins : this.margins;
38847 highlight : function(){
38848 this.el.addClass("x-layout-panel-dragover");
38851 unhighlight : function(){
38852 this.el.removeClass("x-layout-panel-dragover");
38855 updateBox : function(box)
38857 if (!this.bodyEl) {
38858 return; // not rendered yet..
38862 if(!this.collapsed){
38863 this.el.dom.style.left = box.x + "px";
38864 this.el.dom.style.top = box.y + "px";
38865 this.updateBody(box.width, box.height);
38867 this.collapsedEl.dom.style.left = box.x + "px";
38868 this.collapsedEl.dom.style.top = box.y + "px";
38869 this.collapsedEl.setSize(box.width, box.height);
38872 this.tabs.autoSizeTabs();
38876 updateBody : function(w, h)
38879 this.el.setWidth(w);
38880 w -= this.el.getBorderWidth("rl");
38881 if(this.config.adjustments){
38882 w += this.config.adjustments[0];
38885 if(h !== null && h > 0){
38886 this.el.setHeight(h);
38887 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38888 h -= this.el.getBorderWidth("tb");
38889 if(this.config.adjustments){
38890 h += this.config.adjustments[1];
38892 this.bodyEl.setHeight(h);
38894 h = this.tabs.syncHeight(h);
38897 if(this.panelSize){
38898 w = w !== null ? w : this.panelSize.width;
38899 h = h !== null ? h : this.panelSize.height;
38901 if(this.activePanel){
38902 var el = this.activePanel.getEl();
38903 w = w !== null ? w : el.getWidth();
38904 h = h !== null ? h : el.getHeight();
38905 this.panelSize = {width: w, height: h};
38906 this.activePanel.setSize(w, h);
38908 if(Roo.isIE && this.tabs){
38909 this.tabs.el.repaint();
38914 * Returns the container element for this region.
38915 * @return {Roo.Element}
38917 getEl : function(){
38922 * Hides this region.
38925 //if(!this.collapsed){
38926 this.el.dom.style.left = "-2000px";
38929 // this.collapsedEl.dom.style.left = "-2000px";
38930 // this.collapsedEl.hide();
38932 this.visible = false;
38933 this.fireEvent("visibilitychange", this, false);
38937 * Shows this region if it was previously hidden.
38940 //if(!this.collapsed){
38943 // this.collapsedEl.show();
38945 this.visible = true;
38946 this.fireEvent("visibilitychange", this, true);
38949 closeClicked : function(){
38950 if(this.activePanel){
38951 this.remove(this.activePanel);
38955 collapseClick : function(e){
38957 e.stopPropagation();
38960 e.stopPropagation();
38966 * Collapses this region.
38967 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38970 collapse : function(skipAnim, skipCheck = false){
38971 if(this.collapsed) {
38975 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38977 this.collapsed = true;
38979 this.split.el.hide();
38981 if(this.config.animate && skipAnim !== true){
38982 this.fireEvent("invalidated", this);
38983 this.animateCollapse();
38985 this.el.setLocation(-20000,-20000);
38987 this.collapsedEl.show();
38988 this.fireEvent("collapsed", this);
38989 this.fireEvent("invalidated", this);
38995 animateCollapse : function(){
39000 * Expands this region if it was previously collapsed.
39001 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39002 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39005 expand : function(e, skipAnim){
39007 e.stopPropagation();
39009 if(!this.collapsed || this.el.hasActiveFx()) {
39013 this.afterSlideIn();
39016 this.collapsed = false;
39017 if(this.config.animate && skipAnim !== true){
39018 this.animateExpand();
39022 this.split.el.show();
39024 this.collapsedEl.setLocation(-2000,-2000);
39025 this.collapsedEl.hide();
39026 this.fireEvent("invalidated", this);
39027 this.fireEvent("expanded", this);
39031 animateExpand : function(){
39035 initTabs : function()
39037 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39039 var ts = new Roo.bootstrap.panel.Tabs({
39040 el: this.bodyEl.dom,
39042 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39043 disableTooltips: this.config.disableTabTips,
39044 toolbar : this.config.toolbar
39047 if(this.config.hideTabs){
39048 ts.stripWrap.setDisplayed(false);
39051 ts.resizeTabs = this.config.resizeTabs === true;
39052 ts.minTabWidth = this.config.minTabWidth || 40;
39053 ts.maxTabWidth = this.config.maxTabWidth || 250;
39054 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39055 ts.monitorResize = false;
39056 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39057 ts.bodyEl.addClass('roo-layout-tabs-body');
39058 this.panels.each(this.initPanelAsTab, this);
39061 initPanelAsTab : function(panel){
39062 var ti = this.tabs.addTab(
39066 this.config.closeOnTab && panel.isClosable(),
39069 if(panel.tabTip !== undefined){
39070 ti.setTooltip(panel.tabTip);
39072 ti.on("activate", function(){
39073 this.setActivePanel(panel);
39076 if(this.config.closeOnTab){
39077 ti.on("beforeclose", function(t, e){
39079 this.remove(panel);
39083 panel.tabItem = ti;
39088 updatePanelTitle : function(panel, title)
39090 if(this.activePanel == panel){
39091 this.updateTitle(title);
39094 var ti = this.tabs.getTab(panel.getEl().id);
39096 if(panel.tabTip !== undefined){
39097 ti.setTooltip(panel.tabTip);
39102 updateTitle : function(title){
39103 if(this.titleTextEl && !this.config.title){
39104 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39108 setActivePanel : function(panel)
39110 panel = this.getPanel(panel);
39111 if(this.activePanel && this.activePanel != panel){
39112 if(this.activePanel.setActiveState(false) === false){
39116 this.activePanel = panel;
39117 panel.setActiveState(true);
39118 if(this.panelSize){
39119 panel.setSize(this.panelSize.width, this.panelSize.height);
39122 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39124 this.updateTitle(panel.getTitle());
39126 this.fireEvent("invalidated", this);
39128 this.fireEvent("panelactivated", this, panel);
39132 * Shows the specified panel.
39133 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39134 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39136 showPanel : function(panel)
39138 panel = this.getPanel(panel);
39141 var tab = this.tabs.getTab(panel.getEl().id);
39142 if(tab.isHidden()){
39143 this.tabs.unhideTab(tab.id);
39147 this.setActivePanel(panel);
39154 * Get the active panel for this region.
39155 * @return {Roo.ContentPanel} The active panel or null
39157 getActivePanel : function(){
39158 return this.activePanel;
39161 validateVisibility : function(){
39162 if(this.panels.getCount() < 1){
39163 this.updateTitle(" ");
39164 this.closeBtn.hide();
39167 if(!this.isVisible()){
39174 * Adds the passed ContentPanel(s) to this region.
39175 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39176 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39178 add : function(panel)
39180 if(arguments.length > 1){
39181 for(var i = 0, len = arguments.length; i < len; i++) {
39182 this.add(arguments[i]);
39187 // if we have not been rendered yet, then we can not really do much of this..
39188 if (!this.bodyEl) {
39189 this.unrendered_panels.push(panel);
39196 if(this.hasPanel(panel)){
39197 this.showPanel(panel);
39200 panel.setRegion(this);
39201 this.panels.add(panel);
39202 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39203 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39204 // and hide them... ???
39205 this.bodyEl.dom.appendChild(panel.getEl().dom);
39206 if(panel.background !== true){
39207 this.setActivePanel(panel);
39209 this.fireEvent("paneladded", this, panel);
39216 this.initPanelAsTab(panel);
39220 if(panel.background !== true){
39221 this.tabs.activate(panel.getEl().id);
39223 this.fireEvent("paneladded", this, panel);
39228 * Hides the tab for the specified panel.
39229 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39231 hidePanel : function(panel){
39232 if(this.tabs && (panel = this.getPanel(panel))){
39233 this.tabs.hideTab(panel.getEl().id);
39238 * Unhides the tab for a previously hidden panel.
39239 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39241 unhidePanel : function(panel){
39242 if(this.tabs && (panel = this.getPanel(panel))){
39243 this.tabs.unhideTab(panel.getEl().id);
39247 clearPanels : function(){
39248 while(this.panels.getCount() > 0){
39249 this.remove(this.panels.first());
39254 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39255 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39256 * @param {Boolean} preservePanel Overrides the config preservePanel option
39257 * @return {Roo.ContentPanel} The panel that was removed
39259 remove : function(panel, preservePanel)
39261 panel = this.getPanel(panel);
39266 this.fireEvent("beforeremove", this, panel, e);
39267 if(e.cancel === true){
39270 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39271 var panelId = panel.getId();
39272 this.panels.removeKey(panelId);
39274 document.body.appendChild(panel.getEl().dom);
39277 this.tabs.removeTab(panel.getEl().id);
39278 }else if (!preservePanel){
39279 this.bodyEl.dom.removeChild(panel.getEl().dom);
39281 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39282 var p = this.panels.first();
39283 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39284 tempEl.appendChild(p.getEl().dom);
39285 this.bodyEl.update("");
39286 this.bodyEl.dom.appendChild(p.getEl().dom);
39288 this.updateTitle(p.getTitle());
39290 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39291 this.setActivePanel(p);
39293 panel.setRegion(null);
39294 if(this.activePanel == panel){
39295 this.activePanel = null;
39297 if(this.config.autoDestroy !== false && preservePanel !== true){
39298 try{panel.destroy();}catch(e){}
39300 this.fireEvent("panelremoved", this, panel);
39305 * Returns the TabPanel component used by this region
39306 * @return {Roo.TabPanel}
39308 getTabs : function(){
39312 createTool : function(parentEl, className){
39313 var btn = Roo.DomHelper.append(parentEl, {
39315 cls: "x-layout-tools-button",
39318 cls: "roo-layout-tools-button-inner " + className,
39322 btn.addClassOnOver("roo-layout-tools-button-over");
39327 * Ext JS Library 1.1.1
39328 * Copyright(c) 2006-2007, Ext JS, LLC.
39330 * Originally Released Under LGPL - original licence link has changed is not relivant.
39333 * <script type="text/javascript">
39339 * @class Roo.SplitLayoutRegion
39340 * @extends Roo.LayoutRegion
39341 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39343 Roo.bootstrap.layout.Split = function(config){
39344 this.cursor = config.cursor;
39345 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39348 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39350 splitTip : "Drag to resize.",
39351 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39352 useSplitTips : false,
39354 applyConfig : function(config){
39355 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39358 onRender : function(ctr,pos) {
39360 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39361 if(!this.config.split){
39366 var splitEl = Roo.DomHelper.append(ctr.dom, {
39368 id: this.el.id + "-split",
39369 cls: "roo-layout-split roo-layout-split-"+this.position,
39372 /** The SplitBar for this region
39373 * @type Roo.SplitBar */
39374 // does not exist yet...
39375 Roo.log([this.position, this.orientation]);
39377 this.split = new Roo.bootstrap.SplitBar({
39378 dragElement : splitEl,
39379 resizingElement: this.el,
39380 orientation : this.orientation
39383 this.split.on("moved", this.onSplitMove, this);
39384 this.split.useShim = this.config.useShim === true;
39385 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39386 if(this.useSplitTips){
39387 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39389 //if(config.collapsible){
39390 // this.split.el.on("dblclick", this.collapse, this);
39393 if(typeof this.config.minSize != "undefined"){
39394 this.split.minSize = this.config.minSize;
39396 if(typeof this.config.maxSize != "undefined"){
39397 this.split.maxSize = this.config.maxSize;
39399 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39400 this.hideSplitter();
39405 getHMaxSize : function(){
39406 var cmax = this.config.maxSize || 10000;
39407 var center = this.mgr.getRegion("center");
39408 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39411 getVMaxSize : function(){
39412 var cmax = this.config.maxSize || 10000;
39413 var center = this.mgr.getRegion("center");
39414 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39417 onSplitMove : function(split, newSize){
39418 this.fireEvent("resized", this, newSize);
39422 * Returns the {@link Roo.SplitBar} for this region.
39423 * @return {Roo.SplitBar}
39425 getSplitBar : function(){
39430 this.hideSplitter();
39431 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39434 hideSplitter : function(){
39436 this.split.el.setLocation(-2000,-2000);
39437 this.split.el.hide();
39443 this.split.el.show();
39445 Roo.bootstrap.layout.Split.superclass.show.call(this);
39448 beforeSlide: function(){
39449 if(Roo.isGecko){// firefox overflow auto bug workaround
39450 this.bodyEl.clip();
39452 this.tabs.bodyEl.clip();
39454 if(this.activePanel){
39455 this.activePanel.getEl().clip();
39457 if(this.activePanel.beforeSlide){
39458 this.activePanel.beforeSlide();
39464 afterSlide : function(){
39465 if(Roo.isGecko){// firefox overflow auto bug workaround
39466 this.bodyEl.unclip();
39468 this.tabs.bodyEl.unclip();
39470 if(this.activePanel){
39471 this.activePanel.getEl().unclip();
39472 if(this.activePanel.afterSlide){
39473 this.activePanel.afterSlide();
39479 initAutoHide : function(){
39480 if(this.autoHide !== false){
39481 if(!this.autoHideHd){
39482 var st = new Roo.util.DelayedTask(this.slideIn, this);
39483 this.autoHideHd = {
39484 "mouseout": function(e){
39485 if(!e.within(this.el, true)){
39489 "mouseover" : function(e){
39495 this.el.on(this.autoHideHd);
39499 clearAutoHide : function(){
39500 if(this.autoHide !== false){
39501 this.el.un("mouseout", this.autoHideHd.mouseout);
39502 this.el.un("mouseover", this.autoHideHd.mouseover);
39506 clearMonitor : function(){
39507 Roo.get(document).un("click", this.slideInIf, this);
39510 // these names are backwards but not changed for compat
39511 slideOut : function(){
39512 if(this.isSlid || this.el.hasActiveFx()){
39515 this.isSlid = true;
39516 if(this.collapseBtn){
39517 this.collapseBtn.hide();
39519 this.closeBtnState = this.closeBtn.getStyle('display');
39520 this.closeBtn.hide();
39522 this.stickBtn.show();
39525 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39526 this.beforeSlide();
39527 this.el.setStyle("z-index", 10001);
39528 this.el.slideIn(this.getSlideAnchor(), {
39529 callback: function(){
39531 this.initAutoHide();
39532 Roo.get(document).on("click", this.slideInIf, this);
39533 this.fireEvent("slideshow", this);
39540 afterSlideIn : function(){
39541 this.clearAutoHide();
39542 this.isSlid = false;
39543 this.clearMonitor();
39544 this.el.setStyle("z-index", "");
39545 if(this.collapseBtn){
39546 this.collapseBtn.show();
39548 this.closeBtn.setStyle('display', this.closeBtnState);
39550 this.stickBtn.hide();
39552 this.fireEvent("slidehide", this);
39555 slideIn : function(cb){
39556 if(!this.isSlid || this.el.hasActiveFx()){
39560 this.isSlid = false;
39561 this.beforeSlide();
39562 this.el.slideOut(this.getSlideAnchor(), {
39563 callback: function(){
39564 this.el.setLeftTop(-10000, -10000);
39566 this.afterSlideIn();
39574 slideInIf : function(e){
39575 if(!e.within(this.el)){
39580 animateCollapse : function(){
39581 this.beforeSlide();
39582 this.el.setStyle("z-index", 20000);
39583 var anchor = this.getSlideAnchor();
39584 this.el.slideOut(anchor, {
39585 callback : function(){
39586 this.el.setStyle("z-index", "");
39587 this.collapsedEl.slideIn(anchor, {duration:.3});
39589 this.el.setLocation(-10000,-10000);
39591 this.fireEvent("collapsed", this);
39598 animateExpand : function(){
39599 this.beforeSlide();
39600 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39601 this.el.setStyle("z-index", 20000);
39602 this.collapsedEl.hide({
39605 this.el.slideIn(this.getSlideAnchor(), {
39606 callback : function(){
39607 this.el.setStyle("z-index", "");
39610 this.split.el.show();
39612 this.fireEvent("invalidated", this);
39613 this.fireEvent("expanded", this);
39641 getAnchor : function(){
39642 return this.anchors[this.position];
39645 getCollapseAnchor : function(){
39646 return this.canchors[this.position];
39649 getSlideAnchor : function(){
39650 return this.sanchors[this.position];
39653 getAlignAdj : function(){
39654 var cm = this.cmargins;
39655 switch(this.position){
39671 getExpandAdj : function(){
39672 var c = this.collapsedEl, cm = this.cmargins;
39673 switch(this.position){
39675 return [-(cm.right+c.getWidth()+cm.left), 0];
39678 return [cm.right+c.getWidth()+cm.left, 0];
39681 return [0, -(cm.top+cm.bottom+c.getHeight())];
39684 return [0, cm.top+cm.bottom+c.getHeight()];
39690 * Ext JS Library 1.1.1
39691 * Copyright(c) 2006-2007, Ext JS, LLC.
39693 * Originally Released Under LGPL - original licence link has changed is not relivant.
39696 * <script type="text/javascript">
39699 * These classes are private internal classes
39701 Roo.bootstrap.layout.Center = function(config){
39702 config.region = "center";
39703 Roo.bootstrap.layout.Region.call(this, config);
39704 this.visible = true;
39705 this.minWidth = config.minWidth || 20;
39706 this.minHeight = config.minHeight || 20;
39709 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39711 // center panel can't be hidden
39715 // center panel can't be hidden
39718 getMinWidth: function(){
39719 return this.minWidth;
39722 getMinHeight: function(){
39723 return this.minHeight;
39737 Roo.bootstrap.layout.North = function(config)
39739 config.region = 'north';
39740 config.cursor = 'n-resize';
39742 Roo.bootstrap.layout.Split.call(this, config);
39746 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39747 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39748 this.split.el.addClass("roo-layout-split-v");
39750 //var size = config.initialSize || config.height;
39751 //if(this.el && typeof size != "undefined"){
39752 // this.el.setHeight(size);
39755 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39757 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39760 onRender : function(ctr, pos)
39762 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39763 var size = this.config.initialSize || this.config.height;
39764 if(this.el && typeof size != "undefined"){
39765 this.el.setHeight(size);
39770 getBox : function(){
39771 if(this.collapsed){
39772 return this.collapsedEl.getBox();
39774 var box = this.el.getBox();
39776 box.height += this.split.el.getHeight();
39781 updateBox : function(box){
39782 if(this.split && !this.collapsed){
39783 box.height -= this.split.el.getHeight();
39784 this.split.el.setLeft(box.x);
39785 this.split.el.setTop(box.y+box.height);
39786 this.split.el.setWidth(box.width);
39788 if(this.collapsed){
39789 this.updateBody(box.width, null);
39791 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39799 Roo.bootstrap.layout.South = function(config){
39800 config.region = 'south';
39801 config.cursor = 's-resize';
39802 Roo.bootstrap.layout.Split.call(this, config);
39804 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39805 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39806 this.split.el.addClass("roo-layout-split-v");
39811 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39812 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39814 onRender : function(ctr, pos)
39816 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39817 var size = this.config.initialSize || this.config.height;
39818 if(this.el && typeof size != "undefined"){
39819 this.el.setHeight(size);
39824 getBox : function(){
39825 if(this.collapsed){
39826 return this.collapsedEl.getBox();
39828 var box = this.el.getBox();
39830 var sh = this.split.el.getHeight();
39837 updateBox : function(box){
39838 if(this.split && !this.collapsed){
39839 var sh = this.split.el.getHeight();
39842 this.split.el.setLeft(box.x);
39843 this.split.el.setTop(box.y-sh);
39844 this.split.el.setWidth(box.width);
39846 if(this.collapsed){
39847 this.updateBody(box.width, null);
39849 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39853 Roo.bootstrap.layout.East = function(config){
39854 config.region = "east";
39855 config.cursor = "e-resize";
39856 Roo.bootstrap.layout.Split.call(this, config);
39858 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39859 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39860 this.split.el.addClass("roo-layout-split-h");
39864 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39865 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39867 onRender : function(ctr, pos)
39869 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39870 var size = this.config.initialSize || this.config.width;
39871 if(this.el && typeof size != "undefined"){
39872 this.el.setWidth(size);
39877 getBox : function(){
39878 if(this.collapsed){
39879 return this.collapsedEl.getBox();
39881 var box = this.el.getBox();
39883 var sw = this.split.el.getWidth();
39890 updateBox : function(box){
39891 if(this.split && !this.collapsed){
39892 var sw = this.split.el.getWidth();
39894 this.split.el.setLeft(box.x);
39895 this.split.el.setTop(box.y);
39896 this.split.el.setHeight(box.height);
39899 if(this.collapsed){
39900 this.updateBody(null, box.height);
39902 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39906 Roo.bootstrap.layout.West = function(config){
39907 config.region = "west";
39908 config.cursor = "w-resize";
39910 Roo.bootstrap.layout.Split.call(this, config);
39912 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39913 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39914 this.split.el.addClass("roo-layout-split-h");
39918 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39919 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39921 onRender: function(ctr, pos)
39923 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39924 var size = this.config.initialSize || this.config.width;
39925 if(typeof size != "undefined"){
39926 this.el.setWidth(size);
39930 getBox : function(){
39931 if(this.collapsed){
39932 return this.collapsedEl.getBox();
39934 var box = this.el.getBox();
39935 if (box.width == 0) {
39936 box.width = this.config.width; // kludge?
39939 box.width += this.split.el.getWidth();
39944 updateBox : function(box){
39945 if(this.split && !this.collapsed){
39946 var sw = this.split.el.getWidth();
39948 this.split.el.setLeft(box.x+box.width);
39949 this.split.el.setTop(box.y);
39950 this.split.el.setHeight(box.height);
39952 if(this.collapsed){
39953 this.updateBody(null, box.height);
39955 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39957 });Roo.namespace("Roo.bootstrap.panel");/*
39959 * Ext JS Library 1.1.1
39960 * Copyright(c) 2006-2007, Ext JS, LLC.
39962 * Originally Released Under LGPL - original licence link has changed is not relivant.
39965 * <script type="text/javascript">
39968 * @class Roo.ContentPanel
39969 * @extends Roo.util.Observable
39970 * A basic ContentPanel element.
39971 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39972 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39973 * @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
39974 * @cfg {Boolean} closable True if the panel can be closed/removed
39975 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39976 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39977 * @cfg {Toolbar} toolbar A toolbar for this panel
39978 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39979 * @cfg {String} title The title for this panel
39980 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39981 * @cfg {String} url Calls {@link #setUrl} with this value
39982 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39983 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39984 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39985 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39986 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39987 * @cfg {Boolean} badges render the badges
39988 * @cfg {String} cls extra classes to use
39989 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39992 * Create a new ContentPanel.
39993 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39994 * @param {String/Object} config A string to set only the title or a config object
39995 * @param {String} content (optional) Set the HTML content for this panel
39996 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39998 Roo.bootstrap.panel.Content = function( config){
40000 this.tpl = config.tpl || false;
40002 var el = config.el;
40003 var content = config.content;
40005 if(config.autoCreate){ // xtype is available if this is called from factory
40008 this.el = Roo.get(el);
40009 if(!this.el && config && config.autoCreate){
40010 if(typeof config.autoCreate == "object"){
40011 if(!config.autoCreate.id){
40012 config.autoCreate.id = config.id||el;
40014 this.el = Roo.DomHelper.append(document.body,
40015 config.autoCreate, true);
40019 cls: (config.cls || '') +
40020 (config.background ? ' bg-' + config.background : '') +
40021 " roo-layout-inactive-content",
40024 if (config.iframe) {
40028 style : 'border: 0px',
40029 src : 'about:blank'
40035 elcfg.html = config.html;
40039 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40040 if (config.iframe) {
40041 this.iframeEl = this.el.select('iframe',true).first();
40046 this.closable = false;
40047 this.loaded = false;
40048 this.active = false;
40051 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40053 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40055 this.wrapEl = this.el; //this.el.wrap();
40057 if (config.toolbar.items) {
40058 ti = config.toolbar.items ;
40059 delete config.toolbar.items ;
40063 this.toolbar.render(this.wrapEl, 'before');
40064 for(var i =0;i < ti.length;i++) {
40065 // Roo.log(['add child', items[i]]);
40066 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40068 this.toolbar.items = nitems;
40069 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40070 delete config.toolbar;
40074 // xtype created footer. - not sure if will work as we normally have to render first..
40075 if (this.footer && !this.footer.el && this.footer.xtype) {
40076 if (!this.wrapEl) {
40077 this.wrapEl = this.el.wrap();
40080 this.footer.container = this.wrapEl.createChild();
40082 this.footer = Roo.factory(this.footer, Roo);
40087 if(typeof config == "string"){
40088 this.title = config;
40090 Roo.apply(this, config);
40094 this.resizeEl = Roo.get(this.resizeEl, true);
40096 this.resizeEl = this.el;
40098 // handle view.xtype
40106 * Fires when this panel is activated.
40107 * @param {Roo.ContentPanel} this
40111 * @event deactivate
40112 * Fires when this panel is activated.
40113 * @param {Roo.ContentPanel} this
40115 "deactivate" : true,
40119 * Fires when this panel is resized if fitToFrame is true.
40120 * @param {Roo.ContentPanel} this
40121 * @param {Number} width The width after any component adjustments
40122 * @param {Number} height The height after any component adjustments
40128 * Fires when this tab is created
40129 * @param {Roo.ContentPanel} this
40140 if(this.autoScroll && !this.iframe){
40141 this.resizeEl.setStyle("overflow", "auto");
40143 // fix randome scrolling
40144 //this.el.on('scroll', function() {
40145 // Roo.log('fix random scolling');
40146 // this.scrollTo('top',0);
40149 content = content || this.content;
40151 this.setContent(content);
40153 if(config && config.url){
40154 this.setUrl(this.url, this.params, this.loadOnce);
40159 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40161 if (this.view && typeof(this.view.xtype) != 'undefined') {
40162 this.view.el = this.el.appendChild(document.createElement("div"));
40163 this.view = Roo.factory(this.view);
40164 this.view.render && this.view.render(false, '');
40168 this.fireEvent('render', this);
40171 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40181 setRegion : function(region){
40182 this.region = region;
40183 this.setActiveClass(region && !this.background);
40187 setActiveClass: function(state)
40190 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40191 this.el.setStyle('position','relative');
40193 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40194 this.el.setStyle('position', 'absolute');
40199 * Returns the toolbar for this Panel if one was configured.
40200 * @return {Roo.Toolbar}
40202 getToolbar : function(){
40203 return this.toolbar;
40206 setActiveState : function(active)
40208 this.active = active;
40209 this.setActiveClass(active);
40211 if(this.fireEvent("deactivate", this) === false){
40216 this.fireEvent("activate", this);
40220 * Updates this panel's element (not for iframe)
40221 * @param {String} content The new content
40222 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40224 setContent : function(content, loadScripts){
40229 this.el.update(content, loadScripts);
40232 ignoreResize : function(w, h){
40233 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40236 this.lastSize = {width: w, height: h};
40241 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40242 * @return {Roo.UpdateManager} The UpdateManager
40244 getUpdateManager : function(){
40248 return this.el.getUpdateManager();
40251 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40252 * Does not work with IFRAME contents
40253 * @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:
40256 url: "your-url.php",
40257 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40258 callback: yourFunction,
40259 scope: yourObject, //(optional scope)
40262 text: "Loading...",
40268 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40269 * 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.
40270 * @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}
40271 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40272 * @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.
40273 * @return {Roo.ContentPanel} this
40281 var um = this.el.getUpdateManager();
40282 um.update.apply(um, arguments);
40288 * 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.
40289 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40290 * @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)
40291 * @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)
40292 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40294 setUrl : function(url, params, loadOnce){
40296 this.iframeEl.dom.src = url;
40300 if(this.refreshDelegate){
40301 this.removeListener("activate", this.refreshDelegate);
40303 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40304 this.on("activate", this.refreshDelegate);
40305 return this.el.getUpdateManager();
40308 _handleRefresh : function(url, params, loadOnce){
40309 if(!loadOnce || !this.loaded){
40310 var updater = this.el.getUpdateManager();
40311 updater.update(url, params, this._setLoaded.createDelegate(this));
40315 _setLoaded : function(){
40316 this.loaded = true;
40320 * Returns this panel's id
40323 getId : function(){
40328 * Returns this panel's element - used by regiosn to add.
40329 * @return {Roo.Element}
40331 getEl : function(){
40332 return this.wrapEl || this.el;
40337 adjustForComponents : function(width, height)
40339 //Roo.log('adjustForComponents ');
40340 if(this.resizeEl != this.el){
40341 width -= this.el.getFrameWidth('lr');
40342 height -= this.el.getFrameWidth('tb');
40345 var te = this.toolbar.getEl();
40346 te.setWidth(width);
40347 height -= te.getHeight();
40350 var te = this.footer.getEl();
40351 te.setWidth(width);
40352 height -= te.getHeight();
40356 if(this.adjustments){
40357 width += this.adjustments[0];
40358 height += this.adjustments[1];
40360 return {"width": width, "height": height};
40363 setSize : function(width, height){
40364 if(this.fitToFrame && !this.ignoreResize(width, height)){
40365 if(this.fitContainer && this.resizeEl != this.el){
40366 this.el.setSize(width, height);
40368 var size = this.adjustForComponents(width, height);
40370 this.iframeEl.setSize(width,height);
40373 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40374 this.fireEvent('resize', this, size.width, size.height);
40381 * Returns this panel's title
40384 getTitle : function(){
40386 if (typeof(this.title) != 'object') {
40391 for (var k in this.title) {
40392 if (!this.title.hasOwnProperty(k)) {
40396 if (k.indexOf('-') >= 0) {
40397 var s = k.split('-');
40398 for (var i = 0; i<s.length; i++) {
40399 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40402 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40409 * Set this panel's title
40410 * @param {String} title
40412 setTitle : function(title){
40413 this.title = title;
40415 this.region.updatePanelTitle(this, title);
40420 * Returns true is this panel was configured to be closable
40421 * @return {Boolean}
40423 isClosable : function(){
40424 return this.closable;
40427 beforeSlide : function(){
40429 this.resizeEl.clip();
40432 afterSlide : function(){
40434 this.resizeEl.unclip();
40438 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40439 * Will fail silently if the {@link #setUrl} method has not been called.
40440 * This does not activate the panel, just updates its content.
40442 refresh : function(){
40443 if(this.refreshDelegate){
40444 this.loaded = false;
40445 this.refreshDelegate();
40450 * Destroys this panel
40452 destroy : function(){
40453 this.el.removeAllListeners();
40454 var tempEl = document.createElement("span");
40455 tempEl.appendChild(this.el.dom);
40456 tempEl.innerHTML = "";
40462 * form - if the content panel contains a form - this is a reference to it.
40463 * @type {Roo.form.Form}
40467 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40468 * This contains a reference to it.
40474 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40484 * @param {Object} cfg Xtype definition of item to add.
40488 getChildContainer: function () {
40489 return this.getEl();
40494 var ret = new Roo.factory(cfg);
40499 if (cfg.xtype.match(/^Form$/)) {
40502 //if (this.footer) {
40503 // el = this.footer.container.insertSibling(false, 'before');
40505 el = this.el.createChild();
40508 this.form = new Roo.form.Form(cfg);
40511 if ( this.form.allItems.length) {
40512 this.form.render(el.dom);
40516 // should only have one of theses..
40517 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40518 // views.. should not be just added - used named prop 'view''
40520 cfg.el = this.el.appendChild(document.createElement("div"));
40523 var ret = new Roo.factory(cfg);
40525 ret.render && ret.render(false, ''); // render blank..
40535 * @class Roo.bootstrap.panel.Grid
40536 * @extends Roo.bootstrap.panel.Content
40538 * Create a new GridPanel.
40539 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40540 * @param {Object} config A the config object
40546 Roo.bootstrap.panel.Grid = function(config)
40550 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40551 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40553 config.el = this.wrapper;
40554 //this.el = this.wrapper;
40556 if (config.container) {
40557 // ctor'ed from a Border/panel.grid
40560 this.wrapper.setStyle("overflow", "hidden");
40561 this.wrapper.addClass('roo-grid-container');
40566 if(config.toolbar){
40567 var tool_el = this.wrapper.createChild();
40568 this.toolbar = Roo.factory(config.toolbar);
40570 if (config.toolbar.items) {
40571 ti = config.toolbar.items ;
40572 delete config.toolbar.items ;
40576 this.toolbar.render(tool_el);
40577 for(var i =0;i < ti.length;i++) {
40578 // Roo.log(['add child', items[i]]);
40579 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40581 this.toolbar.items = nitems;
40583 delete config.toolbar;
40586 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40587 config.grid.scrollBody = true;;
40588 config.grid.monitorWindowResize = false; // turn off autosizing
40589 config.grid.autoHeight = false;
40590 config.grid.autoWidth = false;
40592 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40594 if (config.background) {
40595 // render grid on panel activation (if panel background)
40596 this.on('activate', function(gp) {
40597 if (!gp.grid.rendered) {
40598 gp.grid.render(this.wrapper);
40599 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40604 this.grid.render(this.wrapper);
40605 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40608 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40609 // ??? needed ??? config.el = this.wrapper;
40614 // xtype created footer. - not sure if will work as we normally have to render first..
40615 if (this.footer && !this.footer.el && this.footer.xtype) {
40617 var ctr = this.grid.getView().getFooterPanel(true);
40618 this.footer.dataSource = this.grid.dataSource;
40619 this.footer = Roo.factory(this.footer, Roo);
40620 this.footer.render(ctr);
40630 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40631 getId : function(){
40632 return this.grid.id;
40636 * Returns the grid for this panel
40637 * @return {Roo.bootstrap.Table}
40639 getGrid : function(){
40643 setSize : function(width, height){
40644 if(!this.ignoreResize(width, height)){
40645 var grid = this.grid;
40646 var size = this.adjustForComponents(width, height);
40647 // tfoot is not a footer?
40650 var gridel = grid.getGridEl();
40651 gridel.setSize(size.width, size.height);
40653 var tbd = grid.getGridEl().select('tbody', true).first();
40654 var thd = grid.getGridEl().select('thead',true).first();
40655 var tbf= grid.getGridEl().select('tfoot', true).first();
40658 size.height -= tbf.getHeight();
40661 size.height -= thd.getHeight();
40664 tbd.setSize(size.width, size.height );
40665 // this is for the account management tab -seems to work there.
40666 var thd = grid.getGridEl().select('thead',true).first();
40668 // tbd.setSize(size.width, size.height - thd.getHeight());
40677 beforeSlide : function(){
40678 this.grid.getView().scroller.clip();
40681 afterSlide : function(){
40682 this.grid.getView().scroller.unclip();
40685 destroy : function(){
40686 this.grid.destroy();
40688 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40693 * @class Roo.bootstrap.panel.Nest
40694 * @extends Roo.bootstrap.panel.Content
40696 * Create a new Panel, that can contain a layout.Border.
40699 * @param {Roo.BorderLayout} layout The layout for this panel
40700 * @param {String/Object} config A string to set only the title or a config object
40702 Roo.bootstrap.panel.Nest = function(config)
40704 // construct with only one argument..
40705 /* FIXME - implement nicer consturctors
40706 if (layout.layout) {
40708 layout = config.layout;
40709 delete config.layout;
40711 if (layout.xtype && !layout.getEl) {
40712 // then layout needs constructing..
40713 layout = Roo.factory(layout, Roo);
40717 config.el = config.layout.getEl();
40719 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40721 config.layout.monitorWindowResize = false; // turn off autosizing
40722 this.layout = config.layout;
40723 this.layout.getEl().addClass("roo-layout-nested-layout");
40724 this.layout.parent = this;
40731 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40733 setSize : function(width, height){
40734 if(!this.ignoreResize(width, height)){
40735 var size = this.adjustForComponents(width, height);
40736 var el = this.layout.getEl();
40737 if (size.height < 1) {
40738 el.setWidth(size.width);
40740 el.setSize(size.width, size.height);
40742 var touch = el.dom.offsetWidth;
40743 this.layout.layout();
40744 // ie requires a double layout on the first pass
40745 if(Roo.isIE && !this.initialized){
40746 this.initialized = true;
40747 this.layout.layout();
40752 // activate all subpanels if not currently active..
40754 setActiveState : function(active){
40755 this.active = active;
40756 this.setActiveClass(active);
40759 this.fireEvent("deactivate", this);
40763 this.fireEvent("activate", this);
40764 // not sure if this should happen before or after..
40765 if (!this.layout) {
40766 return; // should not happen..
40769 for (var r in this.layout.regions) {
40770 reg = this.layout.getRegion(r);
40771 if (reg.getActivePanel()) {
40772 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40773 reg.setActivePanel(reg.getActivePanel());
40776 if (!reg.panels.length) {
40779 reg.showPanel(reg.getPanel(0));
40788 * Returns the nested BorderLayout for this panel
40789 * @return {Roo.BorderLayout}
40791 getLayout : function(){
40792 return this.layout;
40796 * Adds a xtype elements to the layout of the nested panel
40800 xtype : 'ContentPanel',
40807 xtype : 'NestedLayoutPanel',
40813 items : [ ... list of content panels or nested layout panels.. ]
40817 * @param {Object} cfg Xtype definition of item to add.
40819 addxtype : function(cfg) {
40820 return this.layout.addxtype(cfg);
40825 * Ext JS Library 1.1.1
40826 * Copyright(c) 2006-2007, Ext JS, LLC.
40828 * Originally Released Under LGPL - original licence link has changed is not relivant.
40831 * <script type="text/javascript">
40834 * @class Roo.TabPanel
40835 * @extends Roo.util.Observable
40836 * A lightweight tab container.
40840 // basic tabs 1, built from existing content
40841 var tabs = new Roo.TabPanel("tabs1");
40842 tabs.addTab("script", "View Script");
40843 tabs.addTab("markup", "View Markup");
40844 tabs.activate("script");
40846 // more advanced tabs, built from javascript
40847 var jtabs = new Roo.TabPanel("jtabs");
40848 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40850 // set up the UpdateManager
40851 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40852 var updater = tab2.getUpdateManager();
40853 updater.setDefaultUrl("ajax1.htm");
40854 tab2.on('activate', updater.refresh, updater, true);
40856 // Use setUrl for Ajax loading
40857 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40858 tab3.setUrl("ajax2.htm", null, true);
40861 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40864 jtabs.activate("jtabs-1");
40867 * Create a new TabPanel.
40868 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40869 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40871 Roo.bootstrap.panel.Tabs = function(config){
40873 * The container element for this TabPanel.
40874 * @type Roo.Element
40876 this.el = Roo.get(config.el);
40879 if(typeof config == "boolean"){
40880 this.tabPosition = config ? "bottom" : "top";
40882 Roo.apply(this, config);
40886 if(this.tabPosition == "bottom"){
40887 // if tabs are at the bottom = create the body first.
40888 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40889 this.el.addClass("roo-tabs-bottom");
40891 // next create the tabs holders
40893 if (this.tabPosition == "west"){
40895 var reg = this.region; // fake it..
40897 if (!reg.mgr.parent) {
40900 reg = reg.mgr.parent.region;
40902 Roo.log("got nest?");
40904 if (reg.mgr.getRegion('west')) {
40905 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40906 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40907 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40908 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40909 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40917 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40918 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40919 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40920 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40925 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40928 // finally - if tabs are at the top, then create the body last..
40929 if(this.tabPosition != "bottom"){
40930 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40931 * @type Roo.Element
40933 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40934 this.el.addClass("roo-tabs-top");
40938 this.bodyEl.setStyle("position", "relative");
40940 this.active = null;
40941 this.activateDelegate = this.activate.createDelegate(this);
40946 * Fires when the active tab changes
40947 * @param {Roo.TabPanel} this
40948 * @param {Roo.TabPanelItem} activePanel The new active tab
40952 * @event beforetabchange
40953 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40954 * @param {Roo.TabPanel} this
40955 * @param {Object} e Set cancel to true on this object to cancel the tab change
40956 * @param {Roo.TabPanelItem} tab The tab being changed to
40958 "beforetabchange" : true
40961 Roo.EventManager.onWindowResize(this.onResize, this);
40962 this.cpad = this.el.getPadding("lr");
40963 this.hiddenCount = 0;
40966 // toolbar on the tabbar support...
40967 if (this.toolbar) {
40968 alert("no toolbar support yet");
40969 this.toolbar = false;
40971 var tcfg = this.toolbar;
40972 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40973 this.toolbar = new Roo.Toolbar(tcfg);
40974 if (Roo.isSafari) {
40975 var tbl = tcfg.container.child('table', true);
40976 tbl.setAttribute('width', '100%');
40984 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40987 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40989 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40991 tabPosition : "top",
40993 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40995 currentTabWidth : 0,
40997 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41001 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41005 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41007 preferredTabWidth : 175,
41009 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41011 resizeTabs : false,
41013 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41015 monitorResize : true,
41017 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41019 toolbar : false, // set by caller..
41021 region : false, /// set by caller
41023 disableTooltips : true, // not used yet...
41026 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41027 * @param {String} id The id of the div to use <b>or create</b>
41028 * @param {String} text The text for the tab
41029 * @param {String} content (optional) Content to put in the TabPanelItem body
41030 * @param {Boolean} closable (optional) True to create a close icon on the tab
41031 * @return {Roo.TabPanelItem} The created TabPanelItem
41033 addTab : function(id, text, content, closable, tpl)
41035 var item = new Roo.bootstrap.panel.TabItem({
41039 closable : closable,
41042 this.addTabItem(item);
41044 item.setContent(content);
41050 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41051 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41052 * @return {Roo.TabPanelItem}
41054 getTab : function(id){
41055 return this.items[id];
41059 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41060 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41062 hideTab : function(id){
41063 var t = this.items[id];
41066 this.hiddenCount++;
41067 this.autoSizeTabs();
41072 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41073 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41075 unhideTab : function(id){
41076 var t = this.items[id];
41078 t.setHidden(false);
41079 this.hiddenCount--;
41080 this.autoSizeTabs();
41085 * Adds an existing {@link Roo.TabPanelItem}.
41086 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41088 addTabItem : function(item)
41090 this.items[item.id] = item;
41091 this.items.push(item);
41092 this.autoSizeTabs();
41093 // if(this.resizeTabs){
41094 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41095 // this.autoSizeTabs();
41097 // item.autoSize();
41102 * Removes a {@link Roo.TabPanelItem}.
41103 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41105 removeTab : function(id){
41106 var items = this.items;
41107 var tab = items[id];
41108 if(!tab) { return; }
41109 var index = items.indexOf(tab);
41110 if(this.active == tab && items.length > 1){
41111 var newTab = this.getNextAvailable(index);
41116 this.stripEl.dom.removeChild(tab.pnode.dom);
41117 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41118 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41120 items.splice(index, 1);
41121 delete this.items[tab.id];
41122 tab.fireEvent("close", tab);
41123 tab.purgeListeners();
41124 this.autoSizeTabs();
41127 getNextAvailable : function(start){
41128 var items = this.items;
41130 // look for a next tab that will slide over to
41131 // replace the one being removed
41132 while(index < items.length){
41133 var item = items[++index];
41134 if(item && !item.isHidden()){
41138 // if one isn't found select the previous tab (on the left)
41141 var item = items[--index];
41142 if(item && !item.isHidden()){
41150 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41151 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41153 disableTab : function(id){
41154 var tab = this.items[id];
41155 if(tab && this.active != tab){
41161 * Enables a {@link Roo.TabPanelItem} that is disabled.
41162 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41164 enableTab : function(id){
41165 var tab = this.items[id];
41170 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41171 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41172 * @return {Roo.TabPanelItem} The TabPanelItem.
41174 activate : function(id)
41176 //Roo.log('activite:' + id);
41178 var tab = this.items[id];
41182 if(tab == this.active || tab.disabled){
41186 this.fireEvent("beforetabchange", this, e, tab);
41187 if(e.cancel !== true && !tab.disabled){
41189 this.active.hide();
41191 this.active = this.items[id];
41192 this.active.show();
41193 this.fireEvent("tabchange", this, this.active);
41199 * Gets the active {@link Roo.TabPanelItem}.
41200 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41202 getActiveTab : function(){
41203 return this.active;
41207 * Updates the tab body element to fit the height of the container element
41208 * for overflow scrolling
41209 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41211 syncHeight : function(targetHeight){
41212 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41213 var bm = this.bodyEl.getMargins();
41214 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41215 this.bodyEl.setHeight(newHeight);
41219 onResize : function(){
41220 if(this.monitorResize){
41221 this.autoSizeTabs();
41226 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41228 beginUpdate : function(){
41229 this.updating = true;
41233 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41235 endUpdate : function(){
41236 this.updating = false;
41237 this.autoSizeTabs();
41241 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41243 autoSizeTabs : function()
41245 var count = this.items.length;
41246 var vcount = count - this.hiddenCount;
41249 this.stripEl.hide();
41251 this.stripEl.show();
41254 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41259 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41260 var availWidth = Math.floor(w / vcount);
41261 var b = this.stripBody;
41262 if(b.getWidth() > w){
41263 var tabs = this.items;
41264 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41265 if(availWidth < this.minTabWidth){
41266 /*if(!this.sleft){ // incomplete scrolling code
41267 this.createScrollButtons();
41270 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41273 if(this.currentTabWidth < this.preferredTabWidth){
41274 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41280 * Returns the number of tabs in this TabPanel.
41283 getCount : function(){
41284 return this.items.length;
41288 * Resizes all the tabs to the passed width
41289 * @param {Number} The new width
41291 setTabWidth : function(width){
41292 this.currentTabWidth = width;
41293 for(var i = 0, len = this.items.length; i < len; i++) {
41294 if(!this.items[i].isHidden()) {
41295 this.items[i].setWidth(width);
41301 * Destroys this TabPanel
41302 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41304 destroy : function(removeEl){
41305 Roo.EventManager.removeResizeListener(this.onResize, this);
41306 for(var i = 0, len = this.items.length; i < len; i++){
41307 this.items[i].purgeListeners();
41309 if(removeEl === true){
41310 this.el.update("");
41315 createStrip : function(container)
41317 var strip = document.createElement("nav");
41318 strip.className = Roo.bootstrap.version == 4 ?
41319 "navbar-light bg-light" :
41320 "navbar navbar-default"; //"x-tabs-wrap";
41321 container.appendChild(strip);
41325 createStripList : function(strip)
41327 // div wrapper for retard IE
41328 // returns the "tr" element.
41329 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41330 //'<div class="x-tabs-strip-wrap">'+
41331 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41332 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41333 return strip.firstChild; //.firstChild.firstChild.firstChild;
41335 createBody : function(container)
41337 var body = document.createElement("div");
41338 Roo.id(body, "tab-body");
41339 //Roo.fly(body).addClass("x-tabs-body");
41340 Roo.fly(body).addClass("tab-content");
41341 container.appendChild(body);
41344 createItemBody :function(bodyEl, id){
41345 var body = Roo.getDom(id);
41347 body = document.createElement("div");
41350 //Roo.fly(body).addClass("x-tabs-item-body");
41351 Roo.fly(body).addClass("tab-pane");
41352 bodyEl.insertBefore(body, bodyEl.firstChild);
41356 createStripElements : function(stripEl, text, closable, tpl)
41358 var td = document.createElement("li"); // was td..
41359 td.className = 'nav-item';
41361 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41364 stripEl.appendChild(td);
41366 td.className = "x-tabs-closable";
41367 if(!this.closeTpl){
41368 this.closeTpl = new Roo.Template(
41369 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41370 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41371 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41374 var el = this.closeTpl.overwrite(td, {"text": text});
41375 var close = el.getElementsByTagName("div")[0];
41376 var inner = el.getElementsByTagName("em")[0];
41377 return {"el": el, "close": close, "inner": inner};
41380 // not sure what this is..
41381 // if(!this.tabTpl){
41382 //this.tabTpl = new Roo.Template(
41383 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41384 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41386 // this.tabTpl = new Roo.Template(
41387 // '<a href="#">' +
41388 // '<span unselectable="on"' +
41389 // (this.disableTooltips ? '' : ' title="{text}"') +
41390 // ' >{text}</span></a>'
41396 var template = tpl || this.tabTpl || false;
41399 template = new Roo.Template(
41400 Roo.bootstrap.version == 4 ?
41402 '<a class="nav-link" href="#" unselectable="on"' +
41403 (this.disableTooltips ? '' : ' title="{text}"') +
41406 '<a class="nav-link" href="#">' +
41407 '<span unselectable="on"' +
41408 (this.disableTooltips ? '' : ' title="{text}"') +
41409 ' >{text}</span></a>'
41414 switch (typeof(template)) {
41418 template = new Roo.Template(template);
41424 var el = template.overwrite(td, {"text": text});
41426 var inner = el.getElementsByTagName("span")[0];
41428 return {"el": el, "inner": inner};
41436 * @class Roo.TabPanelItem
41437 * @extends Roo.util.Observable
41438 * Represents an individual item (tab plus body) in a TabPanel.
41439 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41440 * @param {String} id The id of this TabPanelItem
41441 * @param {String} text The text for the tab of this TabPanelItem
41442 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41444 Roo.bootstrap.panel.TabItem = function(config){
41446 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41447 * @type Roo.TabPanel
41449 this.tabPanel = config.panel;
41451 * The id for this TabPanelItem
41454 this.id = config.id;
41456 this.disabled = false;
41458 this.text = config.text;
41460 this.loaded = false;
41461 this.closable = config.closable;
41464 * The body element for this TabPanelItem.
41465 * @type Roo.Element
41467 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41468 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41469 this.bodyEl.setStyle("display", "block");
41470 this.bodyEl.setStyle("zoom", "1");
41471 //this.hideAction();
41473 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41475 this.el = Roo.get(els.el);
41476 this.inner = Roo.get(els.inner, true);
41477 this.textEl = Roo.bootstrap.version == 4 ?
41478 this.el : Roo.get(this.el.dom.firstChild, true);
41480 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41481 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41484 // this.el.on("mousedown", this.onTabMouseDown, this);
41485 this.el.on("click", this.onTabClick, this);
41487 if(config.closable){
41488 var c = Roo.get(els.close, true);
41489 c.dom.title = this.closeText;
41490 c.addClassOnOver("close-over");
41491 c.on("click", this.closeClick, this);
41497 * Fires when this tab becomes the active tab.
41498 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41499 * @param {Roo.TabPanelItem} this
41503 * @event beforeclose
41504 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41505 * @param {Roo.TabPanelItem} this
41506 * @param {Object} e Set cancel to true on this object to cancel the close.
41508 "beforeclose": true,
41511 * Fires when this tab is closed.
41512 * @param {Roo.TabPanelItem} this
41516 * @event deactivate
41517 * Fires when this tab is no longer the active tab.
41518 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41519 * @param {Roo.TabPanelItem} this
41521 "deactivate" : true
41523 this.hidden = false;
41525 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41528 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41530 purgeListeners : function(){
41531 Roo.util.Observable.prototype.purgeListeners.call(this);
41532 this.el.removeAllListeners();
41535 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41538 this.status_node.addClass("active");
41541 this.tabPanel.stripWrap.repaint();
41543 this.fireEvent("activate", this.tabPanel, this);
41547 * Returns true if this tab is the active tab.
41548 * @return {Boolean}
41550 isActive : function(){
41551 return this.tabPanel.getActiveTab() == this;
41555 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41558 this.status_node.removeClass("active");
41560 this.fireEvent("deactivate", this.tabPanel, this);
41563 hideAction : function(){
41564 this.bodyEl.hide();
41565 this.bodyEl.setStyle("position", "absolute");
41566 this.bodyEl.setLeft("-20000px");
41567 this.bodyEl.setTop("-20000px");
41570 showAction : function(){
41571 this.bodyEl.setStyle("position", "relative");
41572 this.bodyEl.setTop("");
41573 this.bodyEl.setLeft("");
41574 this.bodyEl.show();
41578 * Set the tooltip for the tab.
41579 * @param {String} tooltip The tab's tooltip
41581 setTooltip : function(text){
41582 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41583 this.textEl.dom.qtip = text;
41584 this.textEl.dom.removeAttribute('title');
41586 this.textEl.dom.title = text;
41590 onTabClick : function(e){
41591 e.preventDefault();
41592 this.tabPanel.activate(this.id);
41595 onTabMouseDown : function(e){
41596 e.preventDefault();
41597 this.tabPanel.activate(this.id);
41600 getWidth : function(){
41601 return this.inner.getWidth();
41604 setWidth : function(width){
41605 var iwidth = width - this.linode.getPadding("lr");
41606 this.inner.setWidth(iwidth);
41607 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41608 this.linode.setWidth(width);
41612 * Show or hide the tab
41613 * @param {Boolean} hidden True to hide or false to show.
41615 setHidden : function(hidden){
41616 this.hidden = hidden;
41617 this.linode.setStyle("display", hidden ? "none" : "");
41621 * Returns true if this tab is "hidden"
41622 * @return {Boolean}
41624 isHidden : function(){
41625 return this.hidden;
41629 * Returns the text for this tab
41632 getText : function(){
41636 autoSize : function(){
41637 //this.el.beginMeasure();
41638 this.textEl.setWidth(1);
41640 * #2804 [new] Tabs in Roojs
41641 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41643 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41644 //this.el.endMeasure();
41648 * Sets the text for the tab (Note: this also sets the tooltip text)
41649 * @param {String} text The tab's text and tooltip
41651 setText : function(text){
41653 this.textEl.update(text);
41654 this.setTooltip(text);
41655 //if(!this.tabPanel.resizeTabs){
41656 // this.autoSize();
41660 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41662 activate : function(){
41663 this.tabPanel.activate(this.id);
41667 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41669 disable : function(){
41670 if(this.tabPanel.active != this){
41671 this.disabled = true;
41672 this.status_node.addClass("disabled");
41677 * Enables this TabPanelItem if it was previously disabled.
41679 enable : function(){
41680 this.disabled = false;
41681 this.status_node.removeClass("disabled");
41685 * Sets the content for this TabPanelItem.
41686 * @param {String} content The content
41687 * @param {Boolean} loadScripts true to look for and load scripts
41689 setContent : function(content, loadScripts){
41690 this.bodyEl.update(content, loadScripts);
41694 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41695 * @return {Roo.UpdateManager} The UpdateManager
41697 getUpdateManager : function(){
41698 return this.bodyEl.getUpdateManager();
41702 * Set a URL to be used to load the content for this TabPanelItem.
41703 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41704 * @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)
41705 * @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)
41706 * @return {Roo.UpdateManager} The UpdateManager
41708 setUrl : function(url, params, loadOnce){
41709 if(this.refreshDelegate){
41710 this.un('activate', this.refreshDelegate);
41712 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41713 this.on("activate", this.refreshDelegate);
41714 return this.bodyEl.getUpdateManager();
41718 _handleRefresh : function(url, params, loadOnce){
41719 if(!loadOnce || !this.loaded){
41720 var updater = this.bodyEl.getUpdateManager();
41721 updater.update(url, params, this._setLoaded.createDelegate(this));
41726 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41727 * Will fail silently if the setUrl method has not been called.
41728 * This does not activate the panel, just updates its content.
41730 refresh : function(){
41731 if(this.refreshDelegate){
41732 this.loaded = false;
41733 this.refreshDelegate();
41738 _setLoaded : function(){
41739 this.loaded = true;
41743 closeClick : function(e){
41746 this.fireEvent("beforeclose", this, o);
41747 if(o.cancel !== true){
41748 this.tabPanel.removeTab(this.id);
41752 * The text displayed in the tooltip for the close icon.
41755 closeText : "Close this tab"
41758 * This script refer to:
41759 * Title: International Telephone Input
41760 * Author: Jack O'Connor
41761 * Code version: v12.1.12
41762 * Availability: https://github.com/jackocnr/intl-tel-input.git
41765 Roo.bootstrap.PhoneInputData = function() {
41768 "Afghanistan (افغانستان)",
41773 "Albania (Shqipëri)",
41778 "Algeria (الجزائر)",
41803 "Antigua and Barbuda",
41813 "Armenia (Հայաստան)",
41829 "Austria (Österreich)",
41834 "Azerbaijan (Azərbaycan)",
41844 "Bahrain (البحرين)",
41849 "Bangladesh (বাংলাদেশ)",
41859 "Belarus (Беларусь)",
41864 "Belgium (België)",
41894 "Bosnia and Herzegovina (Босна и Херцеговина)",
41909 "British Indian Ocean Territory",
41914 "British Virgin Islands",
41924 "Bulgaria (България)",
41934 "Burundi (Uburundi)",
41939 "Cambodia (កម្ពុជា)",
41944 "Cameroon (Cameroun)",
41953 ["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"]
41956 "Cape Verde (Kabu Verdi)",
41961 "Caribbean Netherlands",
41972 "Central African Republic (République centrafricaine)",
41992 "Christmas Island",
41998 "Cocos (Keeling) Islands",
42009 "Comoros (جزر القمر)",
42014 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42019 "Congo (Republic) (Congo-Brazzaville)",
42039 "Croatia (Hrvatska)",
42060 "Czech Republic (Česká republika)",
42065 "Denmark (Danmark)",
42080 "Dominican Republic (República Dominicana)",
42084 ["809", "829", "849"]
42102 "Equatorial Guinea (Guinea Ecuatorial)",
42122 "Falkland Islands (Islas Malvinas)",
42127 "Faroe Islands (Føroyar)",
42148 "French Guiana (Guyane française)",
42153 "French Polynesia (Polynésie française)",
42168 "Georgia (საქართველო)",
42173 "Germany (Deutschland)",
42193 "Greenland (Kalaallit Nunaat)",
42230 "Guinea-Bissau (Guiné Bissau)",
42255 "Hungary (Magyarország)",
42260 "Iceland (Ísland)",
42280 "Iraq (العراق)",
42296 "Israel (ישראל)",
42323 "Jordan (الأردن)",
42328 "Kazakhstan (Казахстан)",
42349 "Kuwait (الكويت)",
42354 "Kyrgyzstan (Кыргызстан)",
42364 "Latvia (Latvija)",
42369 "Lebanon (لبنان)",
42384 "Libya (ليبيا)",
42394 "Lithuania (Lietuva)",
42409 "Macedonia (FYROM) (Македонија)",
42414 "Madagascar (Madagasikara)",
42444 "Marshall Islands",
42454 "Mauritania (موريتانيا)",
42459 "Mauritius (Moris)",
42480 "Moldova (Republica Moldova)",
42490 "Mongolia (Монгол)",
42495 "Montenegro (Crna Gora)",
42505 "Morocco (المغرب)",
42511 "Mozambique (Moçambique)",
42516 "Myanmar (Burma) (မြန်မာ)",
42521 "Namibia (Namibië)",
42536 "Netherlands (Nederland)",
42541 "New Caledonia (Nouvelle-Calédonie)",
42576 "North Korea (조선 민주주의 인민 공화국)",
42581 "Northern Mariana Islands",
42597 "Pakistan (پاکستان)",
42607 "Palestine (فلسطين)",
42617 "Papua New Guinea",
42659 "Réunion (La Réunion)",
42665 "Romania (România)",
42681 "Saint Barthélemy",
42692 "Saint Kitts and Nevis",
42702 "Saint Martin (Saint-Martin (partie française))",
42708 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42713 "Saint Vincent and the Grenadines",
42728 "São Tomé and Príncipe (São Tomé e Príncipe)",
42733 "Saudi Arabia (المملكة العربية السعودية)",
42738 "Senegal (Sénégal)",
42768 "Slovakia (Slovensko)",
42773 "Slovenia (Slovenija)",
42783 "Somalia (Soomaaliya)",
42793 "South Korea (대한민국)",
42798 "South Sudan (جنوب السودان)",
42808 "Sri Lanka (ශ්රී ලංකාව)",
42813 "Sudan (السودان)",
42823 "Svalbard and Jan Mayen",
42834 "Sweden (Sverige)",
42839 "Switzerland (Schweiz)",
42844 "Syria (سوريا)",
42889 "Trinidad and Tobago",
42894 "Tunisia (تونس)",
42899 "Turkey (Türkiye)",
42909 "Turks and Caicos Islands",
42919 "U.S. Virgin Islands",
42929 "Ukraine (Україна)",
42934 "United Arab Emirates (الإمارات العربية المتحدة)",
42956 "Uzbekistan (Oʻzbekiston)",
42966 "Vatican City (Città del Vaticano)",
42977 "Vietnam (Việt Nam)",
42982 "Wallis and Futuna (Wallis-et-Futuna)",
42987 "Western Sahara (الصحراء الغربية)",
42993 "Yemen (اليمن)",
43017 * This script refer to:
43018 * Title: International Telephone Input
43019 * Author: Jack O'Connor
43020 * Code version: v12.1.12
43021 * Availability: https://github.com/jackocnr/intl-tel-input.git
43025 * @class Roo.bootstrap.PhoneInput
43026 * @extends Roo.bootstrap.TriggerField
43027 * An input with International dial-code selection
43029 * @cfg {String} defaultDialCode default '+852'
43030 * @cfg {Array} preferedCountries default []
43033 * Create a new PhoneInput.
43034 * @param {Object} config Configuration options
43037 Roo.bootstrap.PhoneInput = function(config) {
43038 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43041 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43043 listWidth: undefined,
43045 selectedClass: 'active',
43047 invalidClass : "has-warning",
43049 validClass: 'has-success',
43051 allowed: '0123456789',
43056 * @cfg {String} defaultDialCode The default dial code when initializing the input
43058 defaultDialCode: '+852',
43061 * @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
43063 preferedCountries: false,
43065 getAutoCreate : function()
43067 var data = Roo.bootstrap.PhoneInputData();
43068 var align = this.labelAlign || this.parentLabelAlign();
43071 this.allCountries = [];
43072 this.dialCodeMapping = [];
43074 for (var i = 0; i < data.length; i++) {
43076 this.allCountries[i] = {
43080 priority: c[3] || 0,
43081 areaCodes: c[4] || null
43083 this.dialCodeMapping[c[2]] = {
43086 priority: c[3] || 0,
43087 areaCodes: c[4] || null
43099 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43100 maxlength: this.max_length,
43101 cls : 'form-control tel-input',
43102 autocomplete: 'new-password'
43105 var hiddenInput = {
43108 cls: 'hidden-tel-input'
43112 hiddenInput.name = this.name;
43115 if (this.disabled) {
43116 input.disabled = true;
43119 var flag_container = {
43136 cls: this.hasFeedback ? 'has-feedback' : '',
43142 cls: 'dial-code-holder',
43149 cls: 'roo-select2-container input-group',
43156 if (this.fieldLabel.length) {
43159 tooltip: 'This field is required'
43165 cls: 'control-label',
43171 html: this.fieldLabel
43174 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43180 if(this.indicatorpos == 'right') {
43181 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43188 if(align == 'left') {
43196 if(this.labelWidth > 12){
43197 label.style = "width: " + this.labelWidth + 'px';
43199 if(this.labelWidth < 13 && this.labelmd == 0){
43200 this.labelmd = this.labelWidth;
43202 if(this.labellg > 0){
43203 label.cls += ' col-lg-' + this.labellg;
43204 input.cls += ' col-lg-' + (12 - this.labellg);
43206 if(this.labelmd > 0){
43207 label.cls += ' col-md-' + this.labelmd;
43208 container.cls += ' col-md-' + (12 - this.labelmd);
43210 if(this.labelsm > 0){
43211 label.cls += ' col-sm-' + this.labelsm;
43212 container.cls += ' col-sm-' + (12 - this.labelsm);
43214 if(this.labelxs > 0){
43215 label.cls += ' col-xs-' + this.labelxs;
43216 container.cls += ' col-xs-' + (12 - this.labelxs);
43226 var settings = this;
43228 ['xs','sm','md','lg'].map(function(size){
43229 if (settings[size]) {
43230 cfg.cls += ' col-' + size + '-' + settings[size];
43234 this.store = new Roo.data.Store({
43235 proxy : new Roo.data.MemoryProxy({}),
43236 reader : new Roo.data.JsonReader({
43247 'name' : 'dialCode',
43251 'name' : 'priority',
43255 'name' : 'areaCodes',
43262 if(!this.preferedCountries) {
43263 this.preferedCountries = [
43270 var p = this.preferedCountries.reverse();
43273 for (var i = 0; i < p.length; i++) {
43274 for (var j = 0; j < this.allCountries.length; j++) {
43275 if(this.allCountries[j].iso2 == p[i]) {
43276 var t = this.allCountries[j];
43277 this.allCountries.splice(j,1);
43278 this.allCountries.unshift(t);
43284 this.store.proxy.data = {
43286 data: this.allCountries
43292 initEvents : function()
43295 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43297 this.indicator = this.indicatorEl();
43298 this.flag = this.flagEl();
43299 this.dialCodeHolder = this.dialCodeHolderEl();
43301 this.trigger = this.el.select('div.flag-box',true).first();
43302 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43307 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43308 _this.list.setWidth(lw);
43311 this.list.on('mouseover', this.onViewOver, this);
43312 this.list.on('mousemove', this.onViewMove, this);
43313 this.inputEl().on("keyup", this.onKeyUp, this);
43314 this.inputEl().on("keypress", this.onKeyPress, this);
43316 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43318 this.view = new Roo.View(this.list, this.tpl, {
43319 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43322 this.view.on('click', this.onViewClick, this);
43323 this.setValue(this.defaultDialCode);
43326 onTriggerClick : function(e)
43328 Roo.log('trigger click');
43333 if(this.isExpanded()){
43335 this.hasFocus = false;
43337 this.store.load({});
43338 this.hasFocus = true;
43343 isExpanded : function()
43345 return this.list.isVisible();
43348 collapse : function()
43350 if(!this.isExpanded()){
43354 Roo.get(document).un('mousedown', this.collapseIf, this);
43355 Roo.get(document).un('mousewheel', this.collapseIf, this);
43356 this.fireEvent('collapse', this);
43360 expand : function()
43364 if(this.isExpanded() || !this.hasFocus){
43368 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43369 this.list.setWidth(lw);
43372 this.restrictHeight();
43374 Roo.get(document).on('mousedown', this.collapseIf, this);
43375 Roo.get(document).on('mousewheel', this.collapseIf, this);
43377 this.fireEvent('expand', this);
43380 restrictHeight : function()
43382 this.list.alignTo(this.inputEl(), this.listAlign);
43383 this.list.alignTo(this.inputEl(), this.listAlign);
43386 onViewOver : function(e, t)
43388 if(this.inKeyMode){
43391 var item = this.view.findItemFromChild(t);
43394 var index = this.view.indexOf(item);
43395 this.select(index, false);
43400 onViewClick : function(view, doFocus, el, e)
43402 var index = this.view.getSelectedIndexes()[0];
43404 var r = this.store.getAt(index);
43407 this.onSelect(r, index);
43409 if(doFocus !== false && !this.blockFocus){
43410 this.inputEl().focus();
43414 onViewMove : function(e, t)
43416 this.inKeyMode = false;
43419 select : function(index, scrollIntoView)
43421 this.selectedIndex = index;
43422 this.view.select(index);
43423 if(scrollIntoView !== false){
43424 var el = this.view.getNode(index);
43426 this.list.scrollChildIntoView(el, false);
43431 createList : function()
43433 this.list = Roo.get(document.body).createChild({
43435 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43436 style: 'display:none'
43439 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43442 collapseIf : function(e)
43444 var in_combo = e.within(this.el);
43445 var in_list = e.within(this.list);
43446 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43448 if (in_combo || in_list || is_list) {
43454 onSelect : function(record, index)
43456 if(this.fireEvent('beforeselect', this, record, index) !== false){
43458 this.setFlagClass(record.data.iso2);
43459 this.setDialCode(record.data.dialCode);
43460 this.hasFocus = false;
43462 this.fireEvent('select', this, record, index);
43466 flagEl : function()
43468 var flag = this.el.select('div.flag',true).first();
43475 dialCodeHolderEl : function()
43477 var d = this.el.select('input.dial-code-holder',true).first();
43484 setDialCode : function(v)
43486 this.dialCodeHolder.dom.value = '+'+v;
43489 setFlagClass : function(n)
43491 this.flag.dom.className = 'flag '+n;
43494 getValue : function()
43496 var v = this.inputEl().getValue();
43497 if(this.dialCodeHolder) {
43498 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43503 setValue : function(v)
43505 var d = this.getDialCode(v);
43507 //invalid dial code
43508 if(v.length == 0 || !d || d.length == 0) {
43510 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43511 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43517 this.setFlagClass(this.dialCodeMapping[d].iso2);
43518 this.setDialCode(d);
43519 this.inputEl().dom.value = v.replace('+'+d,'');
43520 this.hiddenEl().dom.value = this.getValue();
43525 getDialCode : function(v)
43529 if (v.length == 0) {
43530 return this.dialCodeHolder.dom.value;
43534 if (v.charAt(0) != "+") {
43537 var numericChars = "";
43538 for (var i = 1; i < v.length; i++) {
43539 var c = v.charAt(i);
43542 if (this.dialCodeMapping[numericChars]) {
43543 dialCode = v.substr(1, i);
43545 if (numericChars.length == 4) {
43555 this.setValue(this.defaultDialCode);
43559 hiddenEl : function()
43561 return this.el.select('input.hidden-tel-input',true).first();
43564 // after setting val
43565 onKeyUp : function(e){
43566 this.setValue(this.getValue());
43569 onKeyPress : function(e){
43570 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43577 * @class Roo.bootstrap.MoneyField
43578 * @extends Roo.bootstrap.ComboBox
43579 * Bootstrap MoneyField class
43582 * Create a new MoneyField.
43583 * @param {Object} config Configuration options
43586 Roo.bootstrap.MoneyField = function(config) {
43588 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43592 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43595 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43597 allowDecimals : true,
43599 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43601 decimalSeparator : ".",
43603 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43605 decimalPrecision : 0,
43607 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43609 allowNegative : true,
43611 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43615 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43617 minValue : Number.NEGATIVE_INFINITY,
43619 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43621 maxValue : Number.MAX_VALUE,
43623 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43625 minText : "The minimum value for this field is {0}",
43627 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43629 maxText : "The maximum value for this field is {0}",
43631 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43632 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43634 nanText : "{0} is not a valid number",
43636 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43640 * @cfg {String} defaults currency of the MoneyField
43641 * value should be in lkey
43643 defaultCurrency : false,
43645 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43647 thousandsDelimiter : false,
43649 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43660 getAutoCreate : function()
43662 var align = this.labelAlign || this.parentLabelAlign();
43674 cls : 'form-control roo-money-amount-input',
43675 autocomplete: 'new-password'
43678 var hiddenInput = {
43682 cls: 'hidden-number-input'
43685 if(this.max_length) {
43686 input.maxlength = this.max_length;
43690 hiddenInput.name = this.name;
43693 if (this.disabled) {
43694 input.disabled = true;
43697 var clg = 12 - this.inputlg;
43698 var cmd = 12 - this.inputmd;
43699 var csm = 12 - this.inputsm;
43700 var cxs = 12 - this.inputxs;
43704 cls : 'row roo-money-field',
43708 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43712 cls: 'roo-select2-container input-group',
43716 cls : 'form-control roo-money-currency-input',
43717 autocomplete: 'new-password',
43719 name : this.currencyName
43723 cls : 'input-group-addon',
43737 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43741 cls: this.hasFeedback ? 'has-feedback' : '',
43752 if (this.fieldLabel.length) {
43755 tooltip: 'This field is required'
43761 cls: 'control-label',
43767 html: this.fieldLabel
43770 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43776 if(this.indicatorpos == 'right') {
43777 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43784 if(align == 'left') {
43792 if(this.labelWidth > 12){
43793 label.style = "width: " + this.labelWidth + 'px';
43795 if(this.labelWidth < 13 && this.labelmd == 0){
43796 this.labelmd = this.labelWidth;
43798 if(this.labellg > 0){
43799 label.cls += ' col-lg-' + this.labellg;
43800 input.cls += ' col-lg-' + (12 - this.labellg);
43802 if(this.labelmd > 0){
43803 label.cls += ' col-md-' + this.labelmd;
43804 container.cls += ' col-md-' + (12 - this.labelmd);
43806 if(this.labelsm > 0){
43807 label.cls += ' col-sm-' + this.labelsm;
43808 container.cls += ' col-sm-' + (12 - this.labelsm);
43810 if(this.labelxs > 0){
43811 label.cls += ' col-xs-' + this.labelxs;
43812 container.cls += ' col-xs-' + (12 - this.labelxs);
43823 var settings = this;
43825 ['xs','sm','md','lg'].map(function(size){
43826 if (settings[size]) {
43827 cfg.cls += ' col-' + size + '-' + settings[size];
43834 initEvents : function()
43836 this.indicator = this.indicatorEl();
43838 this.initCurrencyEvent();
43840 this.initNumberEvent();
43843 initCurrencyEvent : function()
43846 throw "can not find store for combo";
43849 this.store = Roo.factory(this.store, Roo.data);
43850 this.store.parent = this;
43854 this.triggerEl = this.el.select('.input-group-addon', true).first();
43856 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43861 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43862 _this.list.setWidth(lw);
43865 this.list.on('mouseover', this.onViewOver, this);
43866 this.list.on('mousemove', this.onViewMove, this);
43867 this.list.on('scroll', this.onViewScroll, this);
43870 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43873 this.view = new Roo.View(this.list, this.tpl, {
43874 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43877 this.view.on('click', this.onViewClick, this);
43879 this.store.on('beforeload', this.onBeforeLoad, this);
43880 this.store.on('load', this.onLoad, this);
43881 this.store.on('loadexception', this.onLoadException, this);
43883 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43884 "up" : function(e){
43885 this.inKeyMode = true;
43889 "down" : function(e){
43890 if(!this.isExpanded()){
43891 this.onTriggerClick();
43893 this.inKeyMode = true;
43898 "enter" : function(e){
43901 if(this.fireEvent("specialkey", this, e)){
43902 this.onViewClick(false);
43908 "esc" : function(e){
43912 "tab" : function(e){
43915 if(this.fireEvent("specialkey", this, e)){
43916 this.onViewClick(false);
43924 doRelay : function(foo, bar, hname){
43925 if(hname == 'down' || this.scope.isExpanded()){
43926 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43934 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43938 initNumberEvent : function(e)
43940 this.inputEl().on("keydown" , this.fireKey, this);
43941 this.inputEl().on("focus", this.onFocus, this);
43942 this.inputEl().on("blur", this.onBlur, this);
43944 this.inputEl().relayEvent('keyup', this);
43946 if(this.indicator){
43947 this.indicator.addClass('invisible');
43950 this.originalValue = this.getValue();
43952 if(this.validationEvent == 'keyup'){
43953 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43954 this.inputEl().on('keyup', this.filterValidation, this);
43956 else if(this.validationEvent !== false){
43957 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43960 if(this.selectOnFocus){
43961 this.on("focus", this.preFocus, this);
43964 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43965 this.inputEl().on("keypress", this.filterKeys, this);
43967 this.inputEl().relayEvent('keypress', this);
43970 var allowed = "0123456789";
43972 if(this.allowDecimals){
43973 allowed += this.decimalSeparator;
43976 if(this.allowNegative){
43980 if(this.thousandsDelimiter) {
43984 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43986 var keyPress = function(e){
43988 var k = e.getKey();
43990 var c = e.getCharCode();
43993 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43994 allowed.indexOf(String.fromCharCode(c)) === -1
44000 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44004 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44009 this.inputEl().on("keypress", keyPress, this);
44013 onTriggerClick : function(e)
44020 this.loadNext = false;
44022 if(this.isExpanded()){
44027 this.hasFocus = true;
44029 if(this.triggerAction == 'all') {
44030 this.doQuery(this.allQuery, true);
44034 this.doQuery(this.getRawValue());
44037 getCurrency : function()
44039 var v = this.currencyEl().getValue();
44044 restrictHeight : function()
44046 this.list.alignTo(this.currencyEl(), this.listAlign);
44047 this.list.alignTo(this.currencyEl(), this.listAlign);
44050 onViewClick : function(view, doFocus, el, e)
44052 var index = this.view.getSelectedIndexes()[0];
44054 var r = this.store.getAt(index);
44057 this.onSelect(r, index);
44061 onSelect : function(record, index){
44063 if(this.fireEvent('beforeselect', this, record, index) !== false){
44065 this.setFromCurrencyData(index > -1 ? record.data : false);
44069 this.fireEvent('select', this, record, index);
44073 setFromCurrencyData : function(o)
44077 this.lastCurrency = o;
44079 if (this.currencyField) {
44080 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44082 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44085 this.lastSelectionText = currency;
44087 //setting default currency
44088 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44089 this.setCurrency(this.defaultCurrency);
44093 this.setCurrency(currency);
44096 setFromData : function(o)
44100 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44102 this.setFromCurrencyData(c);
44107 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44109 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44112 this.setValue(value);
44116 setCurrency : function(v)
44118 this.currencyValue = v;
44121 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44126 setValue : function(v)
44128 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44134 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44136 this.inputEl().dom.value = (v == '') ? '' :
44137 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44139 if(!this.allowZero && v === '0') {
44140 this.hiddenEl().dom.value = '';
44141 this.inputEl().dom.value = '';
44148 getRawValue : function()
44150 var v = this.inputEl().getValue();
44155 getValue : function()
44157 return this.fixPrecision(this.parseValue(this.getRawValue()));
44160 parseValue : function(value)
44162 if(this.thousandsDelimiter) {
44164 r = new RegExp(",", "g");
44165 value = value.replace(r, "");
44168 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44169 return isNaN(value) ? '' : value;
44173 fixPrecision : function(value)
44175 if(this.thousandsDelimiter) {
44177 r = new RegExp(",", "g");
44178 value = value.replace(r, "");
44181 var nan = isNaN(value);
44183 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44184 return nan ? '' : value;
44186 return parseFloat(value).toFixed(this.decimalPrecision);
44189 decimalPrecisionFcn : function(v)
44191 return Math.floor(v);
44194 validateValue : function(value)
44196 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44200 var num = this.parseValue(value);
44203 this.markInvalid(String.format(this.nanText, value));
44207 if(num < this.minValue){
44208 this.markInvalid(String.format(this.minText, this.minValue));
44212 if(num > this.maxValue){
44213 this.markInvalid(String.format(this.maxText, this.maxValue));
44220 validate : function()
44222 if(this.disabled || this.allowBlank){
44227 var currency = this.getCurrency();
44229 if(this.validateValue(this.getRawValue()) && currency.length){
44234 this.markInvalid();
44238 getName: function()
44243 beforeBlur : function()
44249 var v = this.parseValue(this.getRawValue());
44256 onBlur : function()
44260 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44261 //this.el.removeClass(this.focusClass);
44264 this.hasFocus = false;
44266 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44270 var v = this.getValue();
44272 if(String(v) !== String(this.startValue)){
44273 this.fireEvent('change', this, v, this.startValue);
44276 this.fireEvent("blur", this);
44279 inputEl : function()
44281 return this.el.select('.roo-money-amount-input', true).first();
44284 currencyEl : function()
44286 return this.el.select('.roo-money-currency-input', true).first();
44289 hiddenEl : function()
44291 return this.el.select('input.hidden-number-input',true).first();
44295 * @class Roo.bootstrap.BezierSignature
44296 * @extends Roo.bootstrap.Component
44297 * Bootstrap BezierSignature class
44298 * This script refer to:
44299 * Title: Signature Pad
44301 * Availability: https://github.com/szimek/signature_pad
44304 * Create a new BezierSignature
44305 * @param {Object} config The config object
44308 Roo.bootstrap.BezierSignature = function(config){
44309 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44315 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44322 mouse_btn_down: true,
44325 * @cfg {int} canvas height
44327 canvas_height: '200px',
44330 * @cfg {float|function} Radius of a single dot.
44335 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44340 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44345 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44350 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44355 * @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.
44357 bg_color: 'rgba(0, 0, 0, 0)',
44360 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44362 dot_color: 'black',
44365 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44367 velocity_filter_weight: 0.7,
44370 * @cfg {function} Callback when stroke begin.
44375 * @cfg {function} Callback when stroke end.
44379 getAutoCreate : function()
44381 var cls = 'roo-signature column';
44384 cls += ' ' + this.cls;
44394 for(var i = 0; i < col_sizes.length; i++) {
44395 if(this[col_sizes[i]]) {
44396 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44406 cls: 'roo-signature-body',
44410 cls: 'roo-signature-body-canvas',
44411 height: this.canvas_height,
44412 width: this.canvas_width
44419 style: 'display: none'
44427 initEvents: function()
44429 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44431 var canvas = this.canvasEl();
44433 // mouse && touch event swapping...
44434 canvas.dom.style.touchAction = 'none';
44435 canvas.dom.style.msTouchAction = 'none';
44437 this.mouse_btn_down = false;
44438 canvas.on('mousedown', this._handleMouseDown, this);
44439 canvas.on('mousemove', this._handleMouseMove, this);
44440 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44442 if (window.PointerEvent) {
44443 canvas.on('pointerdown', this._handleMouseDown, this);
44444 canvas.on('pointermove', this._handleMouseMove, this);
44445 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44448 if ('ontouchstart' in window) {
44449 canvas.on('touchstart', this._handleTouchStart, this);
44450 canvas.on('touchmove', this._handleTouchMove, this);
44451 canvas.on('touchend', this._handleTouchEnd, this);
44454 Roo.EventManager.onWindowResize(this.resize, this, true);
44456 // file input event
44457 this.fileEl().on('change', this.uploadImage, this);
44464 resize: function(){
44466 var canvas = this.canvasEl().dom;
44467 var ctx = this.canvasElCtx();
44468 var img_data = false;
44470 if(canvas.width > 0) {
44471 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44473 // setting canvas width will clean img data
44476 var style = window.getComputedStyle ?
44477 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44479 var padding_left = parseInt(style.paddingLeft) || 0;
44480 var padding_right = parseInt(style.paddingRight) || 0;
44482 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44485 ctx.putImageData(img_data, 0, 0);
44489 _handleMouseDown: function(e)
44491 if (e.browserEvent.which === 1) {
44492 this.mouse_btn_down = true;
44493 this.strokeBegin(e);
44497 _handleMouseMove: function (e)
44499 if (this.mouse_btn_down) {
44500 this.strokeMoveUpdate(e);
44504 _handleMouseUp: function (e)
44506 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44507 this.mouse_btn_down = false;
44512 _handleTouchStart: function (e) {
44514 e.preventDefault();
44515 if (e.browserEvent.targetTouches.length === 1) {
44516 // var touch = e.browserEvent.changedTouches[0];
44517 // this.strokeBegin(touch);
44519 this.strokeBegin(e); // assume e catching the correct xy...
44523 _handleTouchMove: function (e) {
44524 e.preventDefault();
44525 // var touch = event.targetTouches[0];
44526 // _this._strokeMoveUpdate(touch);
44527 this.strokeMoveUpdate(e);
44530 _handleTouchEnd: function (e) {
44531 var wasCanvasTouched = e.target === this.canvasEl().dom;
44532 if (wasCanvasTouched) {
44533 e.preventDefault();
44534 // var touch = event.changedTouches[0];
44535 // _this._strokeEnd(touch);
44540 reset: function () {
44541 this._lastPoints = [];
44542 this._lastVelocity = 0;
44543 this._lastWidth = (this.min_width + this.max_width) / 2;
44544 this.canvasElCtx().fillStyle = this.dot_color;
44547 strokeMoveUpdate: function(e)
44549 this.strokeUpdate(e);
44551 if (this.throttle) {
44552 this.throttleStroke(this.strokeUpdate, this.throttle);
44555 this.strokeUpdate(e);
44559 strokeBegin: function(e)
44561 var newPointGroup = {
44562 color: this.dot_color,
44566 if (typeof this.onBegin === 'function') {
44570 this.curve_data.push(newPointGroup);
44572 this.strokeUpdate(e);
44575 strokeUpdate: function(e)
44577 var rect = this.canvasEl().dom.getBoundingClientRect();
44578 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44579 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44580 var lastPoints = lastPointGroup.points;
44581 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44582 var isLastPointTooClose = lastPoint
44583 ? point.distanceTo(lastPoint) <= this.min_distance
44585 var color = lastPointGroup.color;
44586 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44587 var curve = this.addPoint(point);
44589 this.drawDot({color: color, point: point});
44592 this.drawCurve({color: color, curve: curve});
44602 strokeEnd: function(e)
44604 this.strokeUpdate(e);
44605 if (typeof this.onEnd === 'function') {
44610 addPoint: function (point) {
44611 var _lastPoints = this._lastPoints;
44612 _lastPoints.push(point);
44613 if (_lastPoints.length > 2) {
44614 if (_lastPoints.length === 3) {
44615 _lastPoints.unshift(_lastPoints[0]);
44617 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44618 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44619 _lastPoints.shift();
44625 calculateCurveWidths: function (startPoint, endPoint) {
44626 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44627 (1 - this.velocity_filter_weight) * this._lastVelocity;
44629 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44632 start: this._lastWidth
44635 this._lastVelocity = velocity;
44636 this._lastWidth = newWidth;
44640 drawDot: function (_a) {
44641 var color = _a.color, point = _a.point;
44642 var ctx = this.canvasElCtx();
44643 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44645 this.drawCurveSegment(point.x, point.y, width);
44647 ctx.fillStyle = color;
44651 drawCurve: function (_a) {
44652 var color = _a.color, curve = _a.curve;
44653 var ctx = this.canvasElCtx();
44654 var widthDelta = curve.endWidth - curve.startWidth;
44655 var drawSteps = Math.floor(curve.length()) * 2;
44657 ctx.fillStyle = color;
44658 for (var i = 0; i < drawSteps; i += 1) {
44659 var t = i / drawSteps;
44665 var x = uuu * curve.startPoint.x;
44666 x += 3 * uu * t * curve.control1.x;
44667 x += 3 * u * tt * curve.control2.x;
44668 x += ttt * curve.endPoint.x;
44669 var y = uuu * curve.startPoint.y;
44670 y += 3 * uu * t * curve.control1.y;
44671 y += 3 * u * tt * curve.control2.y;
44672 y += ttt * curve.endPoint.y;
44673 var width = curve.startWidth + ttt * widthDelta;
44674 this.drawCurveSegment(x, y, width);
44680 drawCurveSegment: function (x, y, width) {
44681 var ctx = this.canvasElCtx();
44683 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44684 this.is_empty = false;
44689 var ctx = this.canvasElCtx();
44690 var canvas = this.canvasEl().dom;
44691 ctx.fillStyle = this.bg_color;
44692 ctx.clearRect(0, 0, canvas.width, canvas.height);
44693 ctx.fillRect(0, 0, canvas.width, canvas.height);
44694 this.curve_data = [];
44696 this.is_empty = true;
44701 return this.el.select('input',true).first();
44704 canvasEl: function()
44706 return this.el.select('canvas',true).first();
44709 canvasElCtx: function()
44711 return this.el.select('canvas',true).first().dom.getContext('2d');
44714 getImage: function(type)
44716 if(this.is_empty) {
44721 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44724 drawFromImage: function(img_src)
44726 var img = new Image();
44728 img.onload = function(){
44729 this.canvasElCtx().drawImage(img, 0, 0);
44734 this.is_empty = false;
44737 selectImage: function()
44739 this.fileEl().dom.click();
44742 uploadImage: function(e)
44744 var reader = new FileReader();
44746 reader.onload = function(e){
44747 var img = new Image();
44748 img.onload = function(){
44750 this.canvasElCtx().drawImage(img, 0, 0);
44752 img.src = e.target.result;
44755 reader.readAsDataURL(e.target.files[0]);
44758 // Bezier Point Constructor
44759 Point: (function () {
44760 function Point(x, y, time) {
44763 this.time = time || Date.now();
44765 Point.prototype.distanceTo = function (start) {
44766 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44768 Point.prototype.equals = function (other) {
44769 return this.x === other.x && this.y === other.y && this.time === other.time;
44771 Point.prototype.velocityFrom = function (start) {
44772 return this.time !== start.time
44773 ? this.distanceTo(start) / (this.time - start.time)
44780 // Bezier Constructor
44781 Bezier: (function () {
44782 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44783 this.startPoint = startPoint;
44784 this.control2 = control2;
44785 this.control1 = control1;
44786 this.endPoint = endPoint;
44787 this.startWidth = startWidth;
44788 this.endWidth = endWidth;
44790 Bezier.fromPoints = function (points, widths, scope) {
44791 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44792 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44793 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44795 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44796 var dx1 = s1.x - s2.x;
44797 var dy1 = s1.y - s2.y;
44798 var dx2 = s2.x - s3.x;
44799 var dy2 = s2.y - s3.y;
44800 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44801 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44802 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44803 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44804 var dxm = m1.x - m2.x;
44805 var dym = m1.y - m2.y;
44806 var k = l2 / (l1 + l2);
44807 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44808 var tx = s2.x - cm.x;
44809 var ty = s2.y - cm.y;
44811 c1: new scope.Point(m1.x + tx, m1.y + ty),
44812 c2: new scope.Point(m2.x + tx, m2.y + ty)
44815 Bezier.prototype.length = function () {
44820 for (var i = 0; i <= steps; i += 1) {
44822 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44823 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44825 var xdiff = cx - px;
44826 var ydiff = cy - py;
44827 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44834 Bezier.prototype.point = function (t, start, c1, c2, end) {
44835 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44836 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44837 + (3.0 * c2 * (1.0 - t) * t * t)
44838 + (end * t * t * t);
44843 throttleStroke: function(fn, wait) {
44844 if (wait === void 0) { wait = 250; }
44846 var timeout = null;
44850 var later = function () {
44851 previous = Date.now();
44853 result = fn.apply(storedContext, storedArgs);
44855 storedContext = null;
44859 return function wrapper() {
44861 for (var _i = 0; _i < arguments.length; _i++) {
44862 args[_i] = arguments[_i];
44864 var now = Date.now();
44865 var remaining = wait - (now - previous);
44866 storedContext = this;
44868 if (remaining <= 0 || remaining > wait) {
44870 clearTimeout(timeout);
44874 result = fn.apply(storedContext, storedArgs);
44876 storedContext = null;
44880 else if (!timeout) {
44881 timeout = window.setTimeout(later, remaining);